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