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