chiark / gitweb /
222d6a0da7959ad7fdd54a0b7a70ada87cb7d859
[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
42 /* TODO:
43  *
44  * - Properly handle cryptsetup partitions
45  * - Define new partition type for encrypted swap
46  * - Make /home automount rather than mount
47  *
48  */
49
50 static const char *arg_dest = "/tmp";
51
52 static inline void blkid_free_probep(blkid_probe *b) {
53         if (*b)
54                 blkid_free_probe(*b);
55 }
56 #define _cleanup_blkid_freep_probe_ _cleanup_(blkid_free_probep)
57
58 static int verify_gpt_partition(dev_t dev, sd_id128_t *type, unsigned *nr, char **fstype) {
59         _cleanup_free_ char *t = NULL;
60         _cleanup_blkid_freep_probe_ blkid_probe b = NULL;
61         const char *v;
62         int r;
63
64         r = asprintf(&t, "/dev/block/%u:%u", major(dev), minor(dev));
65         if (r < 0)
66                 return -ENOMEM;
67
68         errno = 0;
69         b = blkid_new_probe_from_filename(t);
70         if (!b)
71                 return errno != 0 ? -errno : -ENOMEM;
72
73         blkid_probe_enable_superblocks(b, 1);
74         blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
75         blkid_probe_enable_partitions(b, 1);
76         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
77
78         errno = 0;
79         r = blkid_do_safeprobe(b);
80         if (r == -2)
81                 return -ENODEV;
82         else if (r == 1)
83                 return -ENODEV;
84         else if (r != 0)
85                 return errno ? -errno : -EIO;
86
87         errno = 0;
88         r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
89         if (r != 0)
90                 return errno ? -errno : -EIO;
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                 char *fst;
120
121                 errno = 0;
122                 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
123                 if (r != 0)
124                         *fstype = NULL;
125                 else {
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(dev_t dev) {
240         struct udev *udev;
241         struct udev_enumerate *e = NULL;
242         struct udev_device *parent = NULL, *d = NULL;
243         struct udev_list_entry *first, *item;
244         unsigned home_nr = (unsigned) -1;
245         _cleanup_free_ char *home = NULL, *home_fstype = NULL;
246         int r;
247
248         udev = udev_new();
249         if (!udev)
250                 return log_oom();
251
252         e = udev_enumerate_new(udev);
253         if (!e) {
254                 r = log_oom();
255                 goto finish;
256         }
257
258         d = udev_device_new_from_devnum(udev, 'b', dev);
259         if (!d) {
260                 r = log_oom();
261                 goto finish;
262         }
263
264         parent = udev_device_get_parent(d);
265         if (!parent) {
266                 r = log_oom();
267                 goto finish;
268         }
269
270         r = udev_enumerate_add_match_parent(e, parent);
271         if (r < 0) {
272                 r = log_oom();
273                 goto finish;
274         }
275
276         r = udev_enumerate_add_match_subsystem(e, "block");
277         if (r < 0) {
278                 r = log_oom();
279                 goto finish;
280         }
281
282         r = udev_enumerate_scan_devices(e);
283         if (r < 0) {
284                 log_error("Failed to enumerate partitions on /dev/block/%u:%u: %s",
285                           major(dev), minor(dev), strerror(-r));
286                 goto finish;
287         }
288
289         first = udev_enumerate_get_list_entry(e);
290         udev_list_entry_foreach(item, first) {
291                 _cleanup_free_ char *fstype = NULL;
292                 const char *node = NULL;
293                 struct udev_device *q;
294                 sd_id128_t type_id;
295                 unsigned nr;
296                 dev_t sub;
297
298                 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
299                 if (!q) {
300                         r = log_oom();
301                         goto finish;
302                 }
303
304                 if (udev_device_get_devnum(q) == udev_device_get_devnum(d))
305                         goto skip;
306
307                 if (udev_device_get_devnum(q) == udev_device_get_devnum(parent))
308                         goto skip;
309
310                 node = udev_device_get_devnode(q);
311                 if (!node) {
312                         r = log_oom();
313                         goto finish;
314                 }
315
316                 sub = udev_device_get_devnum(q);
317
318                 r = verify_gpt_partition(sub, &type_id, &nr, &fstype);
319                 if (r < 0) {
320                         log_error("Failed to verify GPT partition /dev/block/%u:%u: %s",
321                                   major(sub), minor(sub), strerror(-r));
322                         udev_device_unref(q);
323                         goto finish;
324                 }
325                 if (r == 0)
326                         goto skip;
327
328                 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)))
329                         add_swap(node, fstype);
330                 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))) {
331
332                         if (!home || nr < home_nr) {
333                                 free(home);
334                                 home = strdup(node);
335                                 if (!home) {
336                                         r = log_oom();
337                                         goto finish;
338                                 }
339
340                                 home_nr = nr;
341
342                                 free(home_fstype);
343                                 home_fstype = fstype;
344                                 fstype = NULL;
345                         }
346                 }
347
348         skip:
349                 udev_device_unref(q);
350         }
351
352         if (home && home_fstype)
353                 add_home(home, home_fstype);
354
355 finish:
356         if (d)
357                 udev_device_unref(d);
358
359         if (e)
360                 udev_enumerate_unref(e);
361
362         if (udev)
363                 udev_unref(udev);
364
365         return r;
366 }
367
368 static int get_btrfs_block_device(const char *path, dev_t *dev) {
369         struct btrfs_ioctl_fs_info_args fsi;
370         _cleanup_close_ int fd = -1;
371         uint64_t id;
372
373         assert(path);
374         assert(dev);
375
376         fd = open(path, O_DIRECTORY|O_CLOEXEC);
377         if (fd < 0)
378                 return -errno;
379
380         zero(fsi);
381         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
382                 return -errno;
383
384         /* We won't do this for btrfs RAID */
385         if (fsi.num_devices != 1)
386                 return 0;
387
388         for (id = 1; id <= fsi.max_id; id++) {
389                 struct btrfs_ioctl_dev_info_args di;
390                 struct stat st;
391
392                 zero(di);
393                 di.devid = id;
394
395                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
396                         if (errno == ENODEV)
397                                 continue;
398
399                         return -errno;
400                 }
401
402                 if (stat((char*) di.path, &st) < 0)
403                         return -errno;
404
405                 if (!S_ISBLK(st.st_mode))
406                         return -ENODEV;
407
408                 if (major(st.st_rdev) == 0)
409                         return -ENODEV;
410
411                 *dev = st.st_rdev;
412                 return 1;
413         }
414
415         return -ENODEV;
416 }
417
418 static int get_block_device(const char *path, dev_t *dev) {
419         struct stat st;
420         struct statfs sfs;
421
422         assert(path);
423         assert(dev);
424
425         if (lstat("/", &st))
426                 return -errno;
427
428         if (major(st.st_dev) != 0) {
429                 *dev = st.st_dev;
430                 return 1;
431         }
432
433         if (statfs("/", &sfs) < 0)
434                 return -errno;
435
436         if (F_TYPE_CMP(sfs.f_type, BTRFS_SUPER_MAGIC))
437                 return get_btrfs_block_device(path, dev);
438
439         return 0;
440 }
441
442 int main(int argc, char *argv[]) {
443         dev_t dev;
444         int r;
445
446         if (argc > 1 && argc != 4) {
447                 log_error("This program takes three or no arguments.");
448                 return EXIT_FAILURE;
449         }
450
451         if (argc > 1)
452                 arg_dest = argv[3];
453
454         log_set_target(LOG_TARGET_SAFE);
455         log_parse_environment();
456         log_open();
457
458         umask(0022);
459
460         if (in_initrd())
461                 return EXIT_SUCCESS;
462
463         r = get_block_device("/", &dev);
464         if (r < 0) {
465                 log_error("Failed to determine block device of root file system: %s", strerror(-r));
466                 return EXIT_FAILURE;
467         }
468         if (r == 0) {
469                 log_debug("Root file system not on a (single) block device.");
470                 return EXIT_SUCCESS;
471         }
472
473         log_debug("Root device /dev/block/%u:%u.", major(dev), minor(dev));
474
475         r = verify_gpt_partition(dev, NULL, NULL, NULL);
476         if (r < 0) {
477                 log_error("Failed to verify GPT partition /dev/block/%u:%u: %s",
478                           major(dev), minor(dev), strerror(-r));
479                 return EXIT_FAILURE;
480         }
481         if (r == 0)
482                 return EXIT_SUCCESS;
483
484         r = enumerate_partitions(dev);
485
486         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
487 }