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