1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
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.
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.
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/>.
25 #include <sys/ioctl.h>
26 #include <sys/statfs.h>
29 #ifdef HAVE_LINUX_BTRFS_H
30 #include <linux/btrfs.h>
33 #include "path-util.h"
40 #include "unit-name.h"
45 * - Properly handle cryptsetup partitions
46 * - Define new partition type for encrypted swap
47 * - Make /home automount rather than mount
51 #define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
52 #define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
54 static const char *arg_dest = "/tmp";
56 static inline void blkid_free_probep(blkid_probe *b) {
60 #define _cleanup_blkid_freep_probe_ _cleanup_(blkid_free_probep)
62 static int verify_gpt_partition(const char *node, sd_id128_t *type, unsigned *nr, char **fstype) {
63 _cleanup_blkid_freep_probe_ blkid_probe b = NULL;
68 b = blkid_new_probe_from_filename(node);
70 return errno != 0 ? -errno : -ENOMEM;
72 blkid_probe_enable_superblocks(b, 1);
73 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
74 blkid_probe_enable_partitions(b, 1);
75 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
78 r = blkid_do_safeprobe(b);
84 return errno ? -errno : -EIO;
87 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
89 /* return 0 if we're not on GPT */
90 return errno ? -errno : 0;
92 if (strcmp(v, "gpt") != 0)
97 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
99 return errno ? -errno : -EIO;
101 r = sd_id128_from_string(v, type);
108 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
110 return errno ? -errno : -EIO;
112 r = safe_atou(v, nr);
120 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
137 static int add_swap(const char *path, const char *fstype) {
138 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
139 _cleanup_fclose_ FILE *f = NULL;
141 log_debug("Adding swap: %s %s", path, fstype);
143 name = unit_name_from_path(path, ".swap");
147 unit = strjoin(arg_dest, "/", name, NULL);
151 f = fopen(unit, "wxe");
153 log_error("Failed to create unit file %s: %m", unit);
158 "# Automatically generated by systemd-gpt-auto-generator\n\n"
160 "DefaultDependencies=no\n"
161 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
162 "Before=" SPECIAL_UMOUNT_TARGET " " SPECIAL_SWAP_TARGET "\n\n"
169 log_error("Failed to write unit file %s: %m", unit);
173 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
177 mkdir_parents_label(lnk, 0755);
178 if (symlink(unit, lnk) < 0) {
179 log_error("Failed to create symlink %s: %m", lnk);
186 static int add_home(const char *path, const char *fstype) {
187 _cleanup_free_ char *unit = NULL, *lnk = NULL;
188 _cleanup_fclose_ FILE *f = NULL;
190 if (dir_is_empty("/home") <= 0)
193 log_debug("Adding home: %s %s", path, fstype);
195 unit = strappend(arg_dest, "/home.mount");
199 f = fopen(unit, "wxe");
201 log_error("Failed to create unit file %s: %m", unit);
206 "# Automatically generated by systemd-gpt-auto-generator\n\n"
208 "DefaultDependencies=no\n"
209 "After=" SPECIAL_LOCAL_FS_PRE_TARGET "\n"
210 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
211 "Before=" SPECIAL_UMOUNT_TARGET " " SPECIAL_LOCAL_FS_TARGET "\n\n"
221 log_error("Failed to write unit file %s: %m", unit);
225 lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".requires/home.mount", NULL);
230 mkdir_parents_label(lnk, 0755);
231 if (symlink(unit, lnk) < 0) {
232 log_error("Failed to create symlink %s: %m", lnk);
239 static int enumerate_partitions(struct udev *udev, dev_t dev) {
240 struct udev_enumerate *e = NULL;
241 struct udev_device *parent = NULL, *d = NULL;
242 struct udev_list_entry *first, *item;
243 unsigned home_nr = (unsigned) -1;
244 _cleanup_free_ char *home = NULL, *home_fstype = NULL;
247 e = udev_enumerate_new(udev);
251 d = udev_device_new_from_devnum(udev, 'b', dev);
257 parent = udev_device_get_parent(d);
263 r = udev_enumerate_add_match_parent(e, parent);
269 r = udev_enumerate_add_match_subsystem(e, "block");
275 r = udev_enumerate_scan_devices(e);
277 log_error("Failed to enumerate partitions on /dev/block/%u:%u: %s",
278 major(dev), minor(dev), strerror(-r));
282 first = udev_enumerate_get_list_entry(e);
283 udev_list_entry_foreach(item, first) {
284 _cleanup_free_ char *fstype = NULL;
285 const char *node = NULL;
286 struct udev_device *q;
290 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
296 if (udev_device_get_devnum(q) == udev_device_get_devnum(d))
299 if (udev_device_get_devnum(q) == udev_device_get_devnum(parent))
302 node = udev_device_get_devnode(q);
308 r = verify_gpt_partition(node, &type_id, &nr, &fstype);
310 log_error("Failed to verify GPT partition %s: %s",
312 udev_device_unref(q);
318 if (sd_id128_equal(type_id, GPT_SWAP))
319 add_swap(node, fstype);
320 else if (sd_id128_equal(type_id, GPT_HOME)) {
321 if (!home || nr < home_nr) {
332 home_fstype = fstype;
338 udev_device_unref(q);
341 if (home && home_fstype)
342 add_home(home, home_fstype);
346 udev_device_unref(d);
349 udev_enumerate_unref(e);
355 static int get_btrfs_block_device(const char *path, dev_t *dev) {
356 struct btrfs_ioctl_fs_info_args fsi = {};
357 _cleanup_close_ int fd = -1;
363 fd = open(path, O_DIRECTORY|O_CLOEXEC);
367 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
370 /* We won't do this for btrfs RAID */
371 if (fsi.num_devices != 1)
374 for (id = 1; id <= fsi.max_id; id++) {
375 struct btrfs_ioctl_dev_info_args di = {
380 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
387 if (stat((char*) di.path, &st) < 0)
390 if (!S_ISBLK(st.st_mode))
393 if (major(st.st_rdev) == 0)
403 static int get_block_device(const char *path, dev_t *dev) {
413 if (major(st.st_dev) != 0) {
418 if (statfs("/", &sfs) < 0)
421 if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
422 return get_btrfs_block_device(path, dev);
427 static int devno_to_devnode(struct udev *udev, dev_t devno, char **ret) {
428 struct udev_device *d = NULL;
433 d = udev_device_new_from_devnum(udev, 'b', devno);
437 t = udev_device_get_devnode(d);
453 udev_device_unref(d);
458 int main(int argc, char *argv[]) {
459 _cleanup_free_ char *node = NULL;
460 struct udev *udev = NULL;
464 if (argc > 1 && argc != 4) {
465 log_error("This program takes three or no arguments.");
473 log_set_target(LOG_TARGET_SAFE);
474 log_parse_environment();
480 log_debug("In initrd, exiting.");
485 if (detect_container(NULL) > 0) {
486 log_debug("In a container, exiting.");
491 r = get_block_device("/", &devno);
493 log_error("Failed to determine block device of root file system: %s", strerror(-r));
497 log_debug("Root file system not on a (single) block device.");
507 r = devno_to_devnode(udev, devno, &node);
509 log_error("Failed to determine block device node from major/minor: %s", strerror(-r));
513 log_debug("Root device %s.", node);
515 r = verify_gpt_partition(node, NULL, NULL, NULL);
517 log_error("Failed to verify GPT partition %s: %s", node, strerror(-r));
523 r = enumerate_partitions(udev, devno);
529 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;