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