]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/gpt-auto-generator/gpt-auto-generator.c
man: ipv4 link-local
[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>
96115cdf 27#include <blkid/blkid.h>
1a14a53c 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"
1ca208fb 39#include "udev-util.h"
1a14a53c
LP
40#include "special.h"
41#include "unit-name.h"
9a5cb137 42#include "virt.h"
1a14a53c
LP
43
44/* TODO:
45 *
46 * - Properly handle cryptsetup partitions
47 * - Define new partition type for encrypted swap
4b1b14a6 48 * - Make /home automount rather than mount
1a14a53c
LP
49 *
50 */
51
b47d419c
ZJS
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
1a14a53c
LP
55static const char *arg_dest = "/tmp";
56
14bf2c9d 57DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe);
73841465
ZJS
58#define _cleanup_blkid_freep_probe_ _cleanup_(blkid_free_probep)
59
3db604b9 60static int verify_gpt_partition(const char *node, sd_id128_t *type, unsigned *nr, char **fstype) {
73841465 61 _cleanup_blkid_freep_probe_ blkid_probe b = NULL;
1a14a53c
LP
62 const char *v;
63 int r;
64
1a14a53c 65 errno = 0;
3db604b9 66 b = blkid_new_probe_from_filename(node);
73841465
ZJS
67 if (!b)
68 return errno != 0 ? -errno : -ENOMEM;
1a14a53c
LP
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);
b9480180
ZJS
77 if (r == -2 || r == 1) /* no result or uncertain */
78 return -EBADSLT;
73841465
ZJS
79 else if (r != 0)
80 return errno ? -errno : -EIO;
1a14a53c
LP
81
82 errno = 0;
83 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
73841465 84 if (r != 0)
091526ab
ZJS
85 /* return 0 if we're not on GPT */
86 return errno ? -errno : 0;
1a14a53c 87
73841465
ZJS
88 if (strcmp(v, "gpt") != 0)
89 return 0;
1a14a53c
LP
90
91 if (type) {
92 errno = 0;
93 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
73841465
ZJS
94 if (r != 0)
95 return errno ? -errno : -EIO;
1a14a53c
LP
96
97 r = sd_id128_from_string(v, type);
98 if (r < 0)
99 return r;
100 }
101
102 if (nr) {
103 errno = 0;
104 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
73841465
ZJS
105 if (r != 0)
106 return errno ? -errno : -EIO;
1a14a53c
LP
107
108 r = safe_atou(v, nr);
109 if (r < 0)
110 return r;
111 }
112
113
114 if (fstype) {
1a14a53c
LP
115 errno = 0;
116 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
117 if (r != 0)
118 *fstype = NULL;
119 else {
b47d419c
ZJS
120 char *fst;
121
1a14a53c 122 fst = strdup(v);
73841465
ZJS
123 if (!fst)
124 return -ENOMEM;
1a14a53c
LP
125
126 *fstype = fst;
127 }
128 }
129
130 return 1;
1a14a53c
LP
131}
132
133static int add_swap(const char *path, const char *fstype) {
134 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
135 _cleanup_fclose_ FILE *f = NULL;
136
137 log_debug("Adding swap: %s %s", path, fstype);
138
139 name = unit_name_from_path(path, ".swap");
140 if (!name)
141 return log_oom();
142
143 unit = strjoin(arg_dest, "/", name, NULL);
144 if (!unit)
145 return log_oom();
146
147 f = fopen(unit, "wxe");
148 if (!f) {
149 log_error("Failed to create unit file %s: %m", unit);
150 return -errno;
151 }
152
153 fprintf(f,
154 "# Automatically generated by systemd-gpt-auto-generator\n\n"
155 "[Unit]\n"
156 "DefaultDependencies=no\n"
157 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
158 "Before=" SPECIAL_UMOUNT_TARGET " " SPECIAL_SWAP_TARGET "\n\n"
ee530d8b 159 "[Swap]\n"
1a14a53c
LP
160 "What=%s\n",
161 path);
162
163 fflush(f);
164 if (ferror(f)) {
165 log_error("Failed to write unit file %s: %m", unit);
166 return -errno;
167 }
168
169 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
170 if (!lnk)
171 return log_oom();
172
173 mkdir_parents_label(lnk, 0755);
174 if (symlink(unit, lnk) < 0) {
175 log_error("Failed to create symlink %s: %m", lnk);
176 return -errno;
177 }
178
179 return 0;
180}
181
182static int add_home(const char *path, const char *fstype) {
4c8bda24 183 _cleanup_free_ char *unit = NULL, *lnk = NULL, *fsck = NULL;
1a14a53c
LP
184 _cleanup_fclose_ FILE *f = NULL;
185
4b1b14a6
LP
186 if (dir_is_empty("/home") <= 0)
187 return 0;
188
1a14a53c
LP
189 log_debug("Adding home: %s %s", path, fstype);
190
191 unit = strappend(arg_dest, "/home.mount");
192 if (!unit)
193 return log_oom();
194
195 f = fopen(unit, "wxe");
196 if (!f) {
197 log_error("Failed to create unit file %s: %m", unit);
198 return -errno;
199 }
200
4c8bda24
TB
201 fsck = unit_name_from_path_instance("systemd-fsck", path, ".service");
202 if (!fsck)
203 return log_oom();
204
1a14a53c
LP
205 fprintf(f,
206 "# Automatically generated by systemd-gpt-auto-generator\n\n"
207 "[Unit]\n"
208 "DefaultDependencies=no\n"
4c8bda24
TB
209 "Requires=%s\n"
210 "After=" SPECIAL_LOCAL_FS_PRE_TARGET " %s\n"
1a14a53c
LP
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"
4c8bda24
TB
216 "Type=%s\n",
217 fsck, fsck, path, fstype);
1a14a53c
LP
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
1a14a53c
LP
229 mkdir_parents_label(lnk, 0755);
230 if (symlink(unit, lnk) < 0) {
231 log_error("Failed to create symlink %s: %m", lnk);
232 return -errno;
233 }
234
235 return 0;
236}
237
3db604b9 238static int enumerate_partitions(struct udev *udev, dev_t dev) {
1ca208fb
ZJS
239 struct udev_device *parent = NULL;
240 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
241 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
1a14a53c
LP
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);
1ca208fb
ZJS
252 if (!d)
253 return log_oom();
1a14a53c
LP
254
255 parent = udev_device_get_parent(d);
1ca208fb
ZJS
256 if (!parent)
257 return log_oom();
1a14a53c
LP
258
259 r = udev_enumerate_add_match_parent(e, parent);
1ca208fb
ZJS
260 if (r < 0)
261 return log_oom();
1a14a53c
LP
262
263 r = udev_enumerate_add_match_subsystem(e, "block");
1ca208fb
ZJS
264 if (r < 0)
265 return log_oom();
1a14a53c
LP
266
267 r = udev_enumerate_scan_devices(e);
268 if (r < 0) {
d98cc1c0
ZJS
269 log_error("Failed to enumerate partitions on /dev/block/%u:%u: %s",
270 major(dev), minor(dev), strerror(-r));
1ca208fb 271 return r;
1a14a53c
LP
272 }
273
274 first = udev_enumerate_get_list_entry(e);
275 udev_list_entry_foreach(item, first) {
276 _cleanup_free_ char *fstype = NULL;
277 const char *node = NULL;
1ca208fb 278 _cleanup_udev_device_unref_ struct udev_device *q;
1a14a53c
LP
279 sd_id128_t type_id;
280 unsigned nr;
281
282 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
1ca208fb
ZJS
283 if (!q)
284 return log_oom();
1a14a53c
LP
285
286 if (udev_device_get_devnum(q) == udev_device_get_devnum(d))
1ca208fb 287 continue;
1a14a53c
LP
288
289 if (udev_device_get_devnum(q) == udev_device_get_devnum(parent))
1ca208fb 290 continue;
1a14a53c
LP
291
292 node = udev_device_get_devnode(q);
1ca208fb
ZJS
293 if (!node)
294 return log_oom();
1a14a53c 295
3db604b9 296 r = verify_gpt_partition(node, &type_id, &nr, &fstype);
1a14a53c 297 if (r < 0) {
843f737a 298 /* skip child devices which are not detected properly */
b9480180 299 if (r == -EBADSLT)
843f737a 300 continue;
3db604b9
LP
301 log_error("Failed to verify GPT partition %s: %s",
302 node, strerror(-r));
1ca208fb 303 return r;
1a14a53c
LP
304 }
305 if (r == 0)
1ca208fb 306 continue;
1a14a53c 307
b47d419c 308 if (sd_id128_equal(type_id, GPT_SWAP))
1a14a53c 309 add_swap(node, fstype);
b47d419c 310 else if (sd_id128_equal(type_id, GPT_HOME)) {
1a14a53c
LP
311 if (!home || nr < home_nr) {
312 free(home);
313 home = strdup(node);
1ca208fb
ZJS
314 if (!home)
315 return log_oom();
1a14a53c
LP
316
317 home_nr = nr;
318
319 free(home_fstype);
320 home_fstype = fstype;
321 fstype = NULL;
322 }
323 }
1a14a53c
LP
324 }
325
326 if (home && home_fstype)
327 add_home(home, home_fstype);
328
1a14a53c
LP
329 return r;
330}
331
332static int get_btrfs_block_device(const char *path, dev_t *dev) {
b47d419c 333 struct btrfs_ioctl_fs_info_args fsi = {};
1a14a53c
LP
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
1a14a53c
LP
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++) {
b47d419c
ZJS
352 struct btrfs_ioctl_dev_info_args di = {
353 .devid = id,
354 };
1a14a53c
LP
355 struct stat st;
356
1a14a53c
LP
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
380static 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
c51cf056 398 if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
1a14a53c
LP
399 return get_btrfs_block_device(path, dev);
400
401 return 0;
402}
403
3db604b9 404static int devno_to_devnode(struct udev *udev, dev_t devno, char **ret) {
1ca208fb 405 _cleanup_udev_device_unref_ struct udev_device *d;
3db604b9
LP
406 const char *t;
407 char *n;
3db604b9
LP
408
409 d = udev_device_new_from_devnum(udev, 'b', devno);
b47d419c
ZJS
410 if (!d)
411 return log_oom();
3db604b9
LP
412
413 t = udev_device_get_devnode(d);
1ca208fb
ZJS
414 if (!t)
415 return -ENODEV;
3db604b9
LP
416
417 n = strdup(t);
1ca208fb
ZJS
418 if (!n)
419 return -ENOMEM;
3db604b9
LP
420
421 *ret = n;
1ca208fb 422 return 0;
3db604b9
LP
423}
424
1a14a53c 425int main(int argc, char *argv[]) {
3db604b9 426 _cleanup_free_ char *node = NULL;
1ca208fb 427 _cleanup_udev_unref_ struct udev *udev = NULL;
3db604b9 428 dev_t devno;
1ca208fb 429 int r = 0;
1a14a53c
LP
430
431 if (argc > 1 && argc != 4) {
432 log_error("This program takes three or no arguments.");
3db604b9
LP
433 r = -EINVAL;
434 goto finish;
1a14a53c
LP
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
3db604b9 446 if (in_initrd()) {
9a5cb137 447 log_debug("In initrd, exiting.");
9a5cb137
ZJS
448 goto finish;
449 }
450
451 if (detect_container(NULL) > 0) {
452 log_debug("In a container, exiting.");
3db604b9
LP
453 goto finish;
454 }
1a14a53c 455
3db604b9 456 r = get_block_device("/", &devno);
1a14a53c
LP
457 if (r < 0) {
458 log_error("Failed to determine block device of root file system: %s", strerror(-r));
3db604b9 459 goto finish;
1a14a53c
LP
460 }
461 if (r == 0) {
462 log_debug("Root file system not on a (single) block device.");
3db604b9
LP
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;
1a14a53c
LP
476 }
477
3db604b9 478 log_debug("Root device %s.", node);
1a14a53c 479
3db604b9 480 r = verify_gpt_partition(node, NULL, NULL, NULL);
1a14a53c 481 if (r < 0) {
3db604b9
LP
482 log_error("Failed to verify GPT partition %s: %s", node, strerror(-r));
483 goto finish;
1a14a53c
LP
484 }
485 if (r == 0)
3db604b9
LP
486 goto finish;
487
488 r = enumerate_partitions(udev, devno);
1a14a53c 489
3db604b9 490finish:
1a14a53c
LP
491 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
492}