chiark / gitweb /
a26655f096fe6ddf4a62b1c8bec58b7d79a18330
[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: %s", strerror(-r));
303                 goto finish;
304         }
305
306         first = udev_enumerate_get_list_entry(e);
307         udev_list_entry_foreach(item, first) {
308                 _cleanup_free_ char *fstype = NULL;
309                 const char *node = NULL;
310                 struct udev_device *q;
311                 sd_id128_t type_id;
312                 unsigned nr;
313
314                 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
315                 if (!q) {
316                         r = log_oom();
317                         goto finish;
318                 }
319
320                 if (udev_device_get_devnum(q) == udev_device_get_devnum(d))
321                         goto skip;
322
323                 if (udev_device_get_devnum(q) == udev_device_get_devnum(parent))
324                         goto skip;
325
326                 node = udev_device_get_devnode(q);
327                 if (!node) {
328                         r = log_oom();
329                         goto finish;
330                 }
331
332                 r = verify_gpt_partition(udev_device_get_devnum(q), &type_id, &nr, &fstype);
333                 if (r < 0) {
334                         log_error("Failed to verify GPT partition: %s", strerror(-r));
335                         udev_device_unref(q);
336                         goto finish;
337                 }
338                 if (r == 0)
339                         goto skip;
340
341                 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)))
342                         add_swap(node, fstype);
343                 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))) {
344
345                         if (!home || nr < home_nr) {
346                                 free(home);
347                                 home = strdup(node);
348                                 if (!home) {
349                                         r = log_oom();
350                                         goto finish;
351                                 }
352
353                                 home_nr = nr;
354
355                                 free(home_fstype);
356                                 home_fstype = fstype;
357                                 fstype = NULL;
358                         }
359                 }
360
361         skip:
362                 udev_device_unref(q);
363         }
364
365         if (home && home_fstype)
366                 add_home(home, home_fstype);
367
368 finish:
369         if (d)
370                 udev_device_unref(d);
371
372         if (e)
373                 udev_enumerate_unref(e);
374
375         if (udev)
376                 udev_unref(udev);
377
378         return r;
379 }
380
381 static int get_btrfs_block_device(const char *path, dev_t *dev) {
382         struct btrfs_ioctl_fs_info_args fsi;
383         _cleanup_close_ int fd = -1;
384         uint64_t id;
385
386         assert(path);
387         assert(dev);
388
389         fd = open(path, O_DIRECTORY|O_CLOEXEC);
390         if (fd < 0)
391                 return -errno;
392
393         zero(fsi);
394         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
395                 return -errno;
396
397         /* We won't do this for btrfs RAID */
398         if (fsi.num_devices != 1)
399                 return 0;
400
401         for (id = 1; id <= fsi.max_id; id++) {
402                 struct btrfs_ioctl_dev_info_args di;
403                 struct stat st;
404
405                 zero(di);
406                 di.devid = id;
407
408                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
409                         if (errno == ENODEV)
410                                 continue;
411
412                         return -errno;
413                 }
414
415                 if (stat((char*) di.path, &st) < 0)
416                         return -errno;
417
418                 if (!S_ISBLK(st.st_mode))
419                         return -ENODEV;
420
421                 if (major(st.st_rdev) == 0)
422                         return -ENODEV;
423
424                 *dev = st.st_rdev;
425                 return 1;
426         }
427
428         return -ENODEV;
429 }
430
431 static int get_block_device(const char *path, dev_t *dev) {
432         struct stat st;
433         struct statfs sfs;
434
435         assert(path);
436         assert(dev);
437
438         if (lstat("/", &st))
439                 return -errno;
440
441         if (major(st.st_dev) != 0) {
442                 *dev = st.st_dev;
443                 return 1;
444         }
445
446         if (statfs("/", &sfs) < 0)
447                 return -errno;
448
449         if (F_TYPE_CMP(sfs.f_type, BTRFS_SUPER_MAGIC))
450                 return get_btrfs_block_device(path, dev);
451
452         return 0;
453 }
454
455 int main(int argc, char *argv[]) {
456         dev_t dev;
457         int r;
458
459         if (argc > 1 && argc != 4) {
460                 log_error("This program takes three or no arguments.");
461                 return EXIT_FAILURE;
462         }
463
464         if (argc > 1)
465                 arg_dest = argv[3];
466
467         log_set_target(LOG_TARGET_SAFE);
468         log_parse_environment();
469         log_open();
470
471         umask(0022);
472
473         if (in_initrd())
474                 return EXIT_SUCCESS;
475
476         r = get_block_device("/", &dev);
477         if (r < 0) {
478                 log_error("Failed to determine block device of root file system: %s", strerror(-r));
479                 return EXIT_FAILURE;
480         }
481         if (r == 0) {
482                 log_debug("Root file system not on a (single) block device.");
483                 return EXIT_SUCCESS;
484         }
485
486         log_debug("Root device %u:%u.", major(dev), minor(dev));
487
488         r = verify_gpt_partition(dev, NULL, NULL, NULL);
489         if (r < 0) {
490                 log_error("Failed to verify GPT partition: %s", strerror(-r));
491                 return EXIT_FAILURE;
492         }
493         if (r == 0)
494                 return EXIT_SUCCESS;
495
496         r = enumerate_partitions(dev);
497
498         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
499 }