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