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