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