chiark / gitweb /
Modernization
[elogind.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 "special.h"
40 #include "unit-name.h"
41 #include "virt.h"
42
43 /* TODO:
44  *
45  * - Properly handle cryptsetup partitions
46  * - Define new partition type for encrypted swap
47  * - Make /home automount rather than mount
48  *
49  */
50
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
54 static const char *arg_dest = "/tmp";
55
56 static 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
62 static int verify_gpt_partition(const char *node, sd_id128_t *type, unsigned *nr, char **fstype) {
63         _cleanup_blkid_freep_probe_ blkid_probe b = NULL;
64         const char *v;
65         int r;
66
67         errno = 0;
68         b = blkid_new_probe_from_filename(node);
69         if (!b)
70                 return errno != 0 ? -errno : -ENOMEM;
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);
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;
85
86         errno = 0;
87         r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
88         if (r != 0)
89                 /* return 0 if we're not on GPT */
90                 return errno ? -errno : 0;
91
92         if (strcmp(v, "gpt") != 0)
93                 return 0;
94
95         if (type) {
96                 errno = 0;
97                 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
98                 if (r != 0)
99                         return errno ? -errno : -EIO;
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);
109                 if (r != 0)
110                         return errno ? -errno : -EIO;
111
112                 r = safe_atou(v, nr);
113                 if (r < 0)
114                         return r;
115         }
116
117
118         if (fstype) {
119                 errno = 0;
120                 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
121                 if (r != 0)
122                         *fstype = NULL;
123                 else {
124                         char *fst;
125
126                         fst = strdup(v);
127                         if (!fst)
128                                 return -ENOMEM;
129
130                         *fstype = fst;
131                 }
132         }
133
134         return 1;
135 }
136
137 static 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"
163                 "[Swap]\n"
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
186 static int add_home(const char *path, const char *fstype) {
187         _cleanup_free_ char *unit = NULL, *lnk = NULL;
188         _cleanup_fclose_ FILE *f = NULL;
189
190         if (dir_is_empty("/home") <= 0)
191                 return 0;
192
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
239 static int enumerate_partitions(struct udev *udev, dev_t dev) {
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
247         e = udev_enumerate_new(udev);
248         if (!e)
249                 return log_oom();
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) {
277                 log_error("Failed to enumerate partitions on /dev/block/%u:%u: %s",
278                           major(dev), minor(dev), strerror(-r));
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
308                 r = verify_gpt_partition(node, &type_id, &nr, &fstype);
309                 if (r < 0) {
310                         log_error("Failed to verify GPT partition %s: %s",
311                                   node, strerror(-r));
312                         udev_device_unref(q);
313                         goto finish;
314                 }
315                 if (r == 0)
316                         goto skip;
317
318                 if (sd_id128_equal(type_id, GPT_SWAP))
319                         add_swap(node, fstype);
320                 else if (sd_id128_equal(type_id, GPT_HOME)) {
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
344 finish:
345         if (d)
346                 udev_device_unref(d);
347
348         if (e)
349                 udev_enumerate_unref(e);
350
351
352         return r;
353 }
354
355 static int get_btrfs_block_device(const char *path, dev_t *dev) {
356         struct btrfs_ioctl_fs_info_args fsi = {};
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
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++) {
375                 struct btrfs_ioctl_dev_info_args di = {
376                         .devid = id,
377                 };
378                 struct stat st;
379
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
403 static 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
421         if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
422                 return get_btrfs_block_device(path, dev);
423
424         return 0;
425 }
426
427 static 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);
434         if (!d)
435                 return log_oom();
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
452 finish:
453         udev_device_unref(d);
454
455         return r;
456 }
457
458 int main(int argc, char *argv[]) {
459         _cleanup_free_ char *node = NULL;
460         struct udev *udev = NULL;
461         dev_t devno;
462         int r;
463
464         if (argc > 1 && argc != 4) {
465                 log_error("This program takes three or no arguments.");
466                 r = -EINVAL;
467                 goto finish;
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
479         if (in_initrd()) {
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.");
487                 r = 0;
488                 goto finish;
489         }
490
491         r = get_block_device("/", &devno);
492         if (r < 0) {
493                 log_error("Failed to determine block device of root file system: %s", strerror(-r));
494                 goto finish;
495         }
496         if (r == 0) {
497                 log_debug("Root file system not on a (single) block device.");
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;
511         }
512
513         log_debug("Root device %s.", node);
514
515         r = verify_gpt_partition(node, NULL, NULL, NULL);
516         if (r < 0) {
517                 log_error("Failed to verify GPT partition %s: %s", node, strerror(-r));
518                 goto finish;
519         }
520         if (r == 0)
521                 goto finish;
522
523         r = enumerate_partitions(udev, devno);
524
525 finish:
526         if (udev)
527                 udev_unref(udev);
528
529         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
530 }