]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/gpt-auto-generator/gpt-auto-generator.c
keymap: add Pavilion dv7 keys
[thirdparty/systemd.git] / src / gpt-auto-generator / gpt-auto-generator.c
CommitLineData
1a14a53c
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <unistd.h>
23#include <stdlib.h>
24#include <fcntl.h>
1a14a53c
LP
25#include <sys/ioctl.h>
26#include <sys/statfs.h>
27#include <blkid.h>
28
4b357e15
MM
29#ifdef HAVE_LINUX_BTRFS_H
30#include <linux/btrfs.h>
31#endif
32
1a14a53c
LP
33#include "path-util.h"
34#include "util.h"
35#include "mkdir.h"
36#include "missing.h"
37#include "sd-id128.h"
38#include "libudev.h"
39#include "special.h"
40#include "unit-name.h"
41
42/* TODO:
43 *
44 * - Properly handle cryptsetup partitions
45 * - Define new partition type for encrypted swap
4b1b14a6 46 * - Make /home automount rather than mount
1a14a53c
LP
47 *
48 */
49
50static const char *arg_dest = "/tmp";
51
73841465
ZJS
52static inline void blkid_free_probep(blkid_probe *b) {
53 if (*b)
54 blkid_free_probe(*b);
55}
56#define _cleanup_blkid_freep_probe_ _cleanup_(blkid_free_probep)
57
1a14a53c
LP
58static int verify_gpt_partition(dev_t dev, sd_id128_t *type, unsigned *nr, char **fstype) {
59 _cleanup_free_ char *t = NULL;
73841465 60 _cleanup_blkid_freep_probe_ blkid_probe b = NULL;
1a14a53c
LP
61 const char *v;
62 int r;
63
64 r = asprintf(&t, "/dev/block/%u:%u", major(dev), minor(dev));
65 if (r < 0)
66 return -ENOMEM;
67
68 errno = 0;
69 b = blkid_new_probe_from_filename(t);
73841465
ZJS
70 if (!b)
71 return errno != 0 ? -errno : -ENOMEM;
1a14a53c
LP
72
73 blkid_probe_enable_superblocks(b, 1);
74 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
75 blkid_probe_enable_partitions(b, 1);
76 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
77
78 errno = 0;
79 r = blkid_do_safeprobe(b);
73841465
ZJS
80 if (r == -2)
81 return -ENODEV;
82 else if (r == 1)
83 return -ENODEV;
84 else if (r != 0)
85 return errno ? -errno : -EIO;
1a14a53c
LP
86
87 errno = 0;
88 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
73841465 89 if (r != 0)
091526ab
ZJS
90 /* return 0 if we're not on GPT */
91 return errno ? -errno : 0;
1a14a53c 92
73841465
ZJS
93 if (strcmp(v, "gpt") != 0)
94 return 0;
1a14a53c
LP
95
96 if (type) {
97 errno = 0;
98 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
73841465
ZJS
99 if (r != 0)
100 return errno ? -errno : -EIO;
1a14a53c
LP
101
102 r = sd_id128_from_string(v, type);
103 if (r < 0)
104 return r;
105 }
106
107 if (nr) {
108 errno = 0;
109 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
73841465
ZJS
110 if (r != 0)
111 return errno ? -errno : -EIO;
1a14a53c
LP
112
113 r = safe_atou(v, nr);
114 if (r < 0)
115 return r;
116 }
117
118
119 if (fstype) {
120 char *fst;
121
122 errno = 0;
123 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
124 if (r != 0)
125 *fstype = NULL;
126 else {
127 fst = strdup(v);
73841465
ZJS
128 if (!fst)
129 return -ENOMEM;
1a14a53c
LP
130
131 *fstype = fst;
132 }
133 }
134
135 return 1;
1a14a53c
LP
136}
137
138static int add_swap(const char *path, const char *fstype) {
139 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
140 _cleanup_fclose_ FILE *f = NULL;
141
142 log_debug("Adding swap: %s %s", path, fstype);
143
144 name = unit_name_from_path(path, ".swap");
145 if (!name)
146 return log_oom();
147
148 unit = strjoin(arg_dest, "/", name, NULL);
149 if (!unit)
150 return log_oom();
151
152 f = fopen(unit, "wxe");
153 if (!f) {
154 log_error("Failed to create unit file %s: %m", unit);
155 return -errno;
156 }
157
158 fprintf(f,
159 "# Automatically generated by systemd-gpt-auto-generator\n\n"
160 "[Unit]\n"
161 "DefaultDependencies=no\n"
162 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
163 "Before=" SPECIAL_UMOUNT_TARGET " " SPECIAL_SWAP_TARGET "\n\n"
ee530d8b 164 "[Swap]\n"
1a14a53c
LP
165 "What=%s\n",
166 path);
167
168 fflush(f);
169 if (ferror(f)) {
170 log_error("Failed to write unit file %s: %m", unit);
171 return -errno;
172 }
173
174 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
175 if (!lnk)
176 return log_oom();
177
178 mkdir_parents_label(lnk, 0755);
179 if (symlink(unit, lnk) < 0) {
180 log_error("Failed to create symlink %s: %m", lnk);
181 return -errno;
182 }
183
184 return 0;
185}
186
187static int add_home(const char *path, const char *fstype) {
188 _cleanup_free_ char *unit = NULL, *lnk = NULL;
189 _cleanup_fclose_ FILE *f = NULL;
190
4b1b14a6
LP
191 if (dir_is_empty("/home") <= 0)
192 return 0;
193
1a14a53c
LP
194 log_debug("Adding home: %s %s", path, fstype);
195
196 unit = strappend(arg_dest, "/home.mount");
197 if (!unit)
198 return log_oom();
199
200 f = fopen(unit, "wxe");
201 if (!f) {
202 log_error("Failed to create unit file %s: %m", unit);
203 return -errno;
204 }
205
206 fprintf(f,
207 "# Automatically generated by systemd-gpt-auto-generator\n\n"
208 "[Unit]\n"
209 "DefaultDependencies=no\n"
210 "After=" SPECIAL_LOCAL_FS_PRE_TARGET "\n"
211 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
212 "Before=" SPECIAL_UMOUNT_TARGET " " SPECIAL_LOCAL_FS_TARGET "\n\n"
213 "[Mount]\n"
214 "What=%s\n"
215 "Where=/home\n"
216 "Type=%s\n"
217 "FsckPassNo=2\n",
218 path, fstype);
219
220 fflush(f);
221 if (ferror(f)) {
222 log_error("Failed to write unit file %s: %m", unit);
223 return -errno;
224 }
225
226 lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".requires/home.mount", NULL);
227 if (!lnk)
228 return log_oom();
229
230
231 mkdir_parents_label(lnk, 0755);
232 if (symlink(unit, lnk) < 0) {
233 log_error("Failed to create symlink %s: %m", lnk);
234 return -errno;
235 }
236
237 return 0;
238}
239
240static int enumerate_partitions(dev_t dev) {
241 struct udev *udev;
242 struct udev_enumerate *e = NULL;
243 struct udev_device *parent = NULL, *d = NULL;
244 struct udev_list_entry *first, *item;
245 unsigned home_nr = (unsigned) -1;
246 _cleanup_free_ char *home = NULL, *home_fstype = NULL;
247 int r;
248
249 udev = udev_new();
250 if (!udev)
251 return log_oom();
252
253 e = udev_enumerate_new(udev);
254 if (!e) {
255 r = log_oom();
256 goto finish;
257 }
258
259 d = udev_device_new_from_devnum(udev, 'b', dev);
260 if (!d) {
261 r = log_oom();
262 goto finish;
263 }
264
265 parent = udev_device_get_parent(d);
266 if (!parent) {
267 r = log_oom();
268 goto finish;
269 }
270
271 r = udev_enumerate_add_match_parent(e, parent);
272 if (r < 0) {
273 r = log_oom();
274 goto finish;
275 }
276
277 r = udev_enumerate_add_match_subsystem(e, "block");
278 if (r < 0) {
279 r = log_oom();
280 goto finish;
281 }
282
283 r = udev_enumerate_scan_devices(e);
284 if (r < 0) {
d98cc1c0
ZJS
285 log_error("Failed to enumerate partitions on /dev/block/%u:%u: %s",
286 major(dev), minor(dev), strerror(-r));
1a14a53c
LP
287 goto finish;
288 }
289
290 first = udev_enumerate_get_list_entry(e);
291 udev_list_entry_foreach(item, first) {
292 _cleanup_free_ char *fstype = NULL;
293 const char *node = NULL;
294 struct udev_device *q;
295 sd_id128_t type_id;
296 unsigned nr;
d98cc1c0 297 dev_t sub;
1a14a53c
LP
298
299 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
300 if (!q) {
301 r = log_oom();
302 goto finish;
303 }
304
305 if (udev_device_get_devnum(q) == udev_device_get_devnum(d))
306 goto skip;
307
308 if (udev_device_get_devnum(q) == udev_device_get_devnum(parent))
309 goto skip;
310
311 node = udev_device_get_devnode(q);
312 if (!node) {
313 r = log_oom();
314 goto finish;
315 }
316
d98cc1c0
ZJS
317 sub = udev_device_get_devnum(q);
318
319 r = verify_gpt_partition(sub, &type_id, &nr, &fstype);
1a14a53c 320 if (r < 0) {
d98cc1c0
ZJS
321 log_error("Failed to verify GPT partition /dev/block/%u:%u: %s",
322 major(sub), minor(sub), strerror(-r));
1a14a53c
LP
323 udev_device_unref(q);
324 goto finish;
325 }
326 if (r == 0)
327 goto skip;
328
329 if (sd_id128_equal(type_id, SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)))
330 add_swap(node, fstype);
331 else if (sd_id128_equal(type_id, SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15))) {
332
333 if (!home || nr < home_nr) {
334 free(home);
335 home = strdup(node);
336 if (!home) {
337 r = log_oom();
338 goto finish;
339 }
340
341 home_nr = nr;
342
343 free(home_fstype);
344 home_fstype = fstype;
345 fstype = NULL;
346 }
347 }
348
349 skip:
350 udev_device_unref(q);
351 }
352
353 if (home && home_fstype)
354 add_home(home, home_fstype);
355
356finish:
357 if (d)
358 udev_device_unref(d);
359
360 if (e)
361 udev_enumerate_unref(e);
362
363 if (udev)
364 udev_unref(udev);
365
366 return r;
367}
368
369static int get_btrfs_block_device(const char *path, dev_t *dev) {
370 struct btrfs_ioctl_fs_info_args fsi;
371 _cleanup_close_ int fd = -1;
372 uint64_t id;
373
374 assert(path);
375 assert(dev);
376
377 fd = open(path, O_DIRECTORY|O_CLOEXEC);
378 if (fd < 0)
379 return -errno;
380
381 zero(fsi);
382 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
383 return -errno;
384
385 /* We won't do this for btrfs RAID */
386 if (fsi.num_devices != 1)
387 return 0;
388
389 for (id = 1; id <= fsi.max_id; id++) {
390 struct btrfs_ioctl_dev_info_args di;
391 struct stat st;
392
393 zero(di);
394 di.devid = id;
395
396 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
397 if (errno == ENODEV)
398 continue;
399
400 return -errno;
401 }
402
403 if (stat((char*) di.path, &st) < 0)
404 return -errno;
405
406 if (!S_ISBLK(st.st_mode))
407 return -ENODEV;
408
409 if (major(st.st_rdev) == 0)
410 return -ENODEV;
411
412 *dev = st.st_rdev;
413 return 1;
414 }
415
416 return -ENODEV;
417}
418
419static int get_block_device(const char *path, dev_t *dev) {
420 struct stat st;
421 struct statfs sfs;
422
423 assert(path);
424 assert(dev);
425
426 if (lstat("/", &st))
427 return -errno;
428
429 if (major(st.st_dev) != 0) {
430 *dev = st.st_dev;
431 return 1;
432 }
433
434 if (statfs("/", &sfs) < 0)
435 return -errno;
436
437 if (F_TYPE_CMP(sfs.f_type, BTRFS_SUPER_MAGIC))
438 return get_btrfs_block_device(path, dev);
439
440 return 0;
441}
442
443int main(int argc, char *argv[]) {
444 dev_t dev;
445 int r;
446
447 if (argc > 1 && argc != 4) {
448 log_error("This program takes three or no arguments.");
449 return EXIT_FAILURE;
450 }
451
452 if (argc > 1)
453 arg_dest = argv[3];
454
455 log_set_target(LOG_TARGET_SAFE);
456 log_parse_environment();
457 log_open();
458
459 umask(0022);
460
461 if (in_initrd())
462 return EXIT_SUCCESS;
463
464 r = get_block_device("/", &dev);
465 if (r < 0) {
466 log_error("Failed to determine block device of root file system: %s", strerror(-r));
467 return EXIT_FAILURE;
468 }
469 if (r == 0) {
470 log_debug("Root file system not on a (single) block device.");
471 return EXIT_SUCCESS;
472 }
473
d98cc1c0 474 log_debug("Root device /dev/block/%u:%u.", major(dev), minor(dev));
1a14a53c
LP
475
476 r = verify_gpt_partition(dev, NULL, NULL, NULL);
477 if (r < 0) {
d98cc1c0
ZJS
478 log_error("Failed to verify GPT partition /dev/block/%u:%u: %s",
479 major(dev), minor(dev), strerror(-r));
1a14a53c
LP
480 return EXIT_FAILURE;
481 }
482 if (r == 0)
483 return EXIT_SUCCESS;
484
485 r = enumerate_partitions(dev);
486
487 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
488}