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