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