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