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>
27 #include <blkid/blkid.h>
29 #ifdef HAVE_LINUX_BTRFS_H
30 #include <linux/btrfs.h>
33 #include "path-util.h"
39 #include "udev-util.h"
41 #include "unit-name.h"
46 * - Properly handle cryptsetup partitions
47 * - Define new partition type for encrypted swap
48 * - Make /home automount rather than mount
52 #define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
53 #define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
55 static const char *arg_dest = "/tmp";
57 DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe);
58 #define _cleanup_blkid_freep_probe_ _cleanup_(blkid_free_probep)
60 static int verify_gpt_partition(const char *node, sd_id128_t *type, unsigned *nr, char **fstype) {
61 _cleanup_blkid_freep_probe_ blkid_probe b = NULL;
66 b = blkid_new_probe_from_filename(node);
68 return errno != 0 ? -errno : -ENOMEM;
70 blkid_probe_enable_superblocks(b, 1);
71 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
72 blkid_probe_enable_partitions(b, 1);
73 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
76 r = blkid_do_safeprobe(b);
77 if (r == -2 || r == 1) /* no result or uncertain */
80 return errno ? -errno : -EIO;
83 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
85 /* return 0 if we're not on GPT */
86 return errno ? -errno : 0;
88 if (strcmp(v, "gpt") != 0)
93 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
95 return errno ? -errno : -EIO;
97 r = sd_id128_from_string(v, type);
104 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
106 return errno ? -errno : -EIO;
108 r = safe_atou(v, nr);
116 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
133 static int add_swap(const char *path, const char *fstype) {
134 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
135 _cleanup_fclose_ FILE *f = NULL;
137 log_debug("Adding swap: %s %s", path, fstype);
139 name = unit_name_from_path(path, ".swap");
143 unit = strjoin(arg_dest, "/", name, NULL);
147 f = fopen(unit, "wxe");
149 log_error("Failed to create unit file %s: %m", unit);
154 "# Automatically generated by systemd-gpt-auto-generator\n\n"
156 "DefaultDependencies=no\n"
157 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
158 "Before=" SPECIAL_UMOUNT_TARGET " " SPECIAL_SWAP_TARGET "\n\n"
165 log_error("Failed to write unit file %s: %m", unit);
169 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
173 mkdir_parents_label(lnk, 0755);
174 if (symlink(unit, lnk) < 0) {
175 log_error("Failed to create symlink %s: %m", lnk);
182 static int add_home(const char *path, const char *fstype) {
183 _cleanup_free_ char *unit = NULL, *lnk = NULL, *fsck = NULL;
184 _cleanup_fclose_ FILE *f = NULL;
186 if (dir_is_empty("/home") <= 0)
189 log_debug("Adding home: %s %s", path, fstype);
191 unit = strappend(arg_dest, "/home.mount");
195 f = fopen(unit, "wxe");
197 log_error("Failed to create unit file %s: %m", unit);
201 fsck = unit_name_from_path_instance("systemd-fsck", path, ".service");
206 "# Automatically generated by systemd-gpt-auto-generator\n\n"
208 "DefaultDependencies=no\n"
210 "After=" SPECIAL_LOCAL_FS_PRE_TARGET " %s\n"
211 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
212 "Before=" SPECIAL_UMOUNT_TARGET " " SPECIAL_LOCAL_FS_TARGET "\n\n"
217 fsck, fsck, path, fstype);
221 log_error("Failed to write unit file %s: %m", unit);
225 lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".requires/home.mount", NULL);
229 mkdir_parents_label(lnk, 0755);
230 if (symlink(unit, lnk) < 0) {
231 log_error("Failed to create symlink %s: %m", lnk);
238 static int enumerate_partitions(struct udev *udev, dev_t dev) {
239 struct udev_device *parent = NULL;
240 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
241 _cleanup_udev_device_unref_ struct udev_device *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);
255 parent = udev_device_get_parent(d);
259 r = udev_enumerate_add_match_parent(e, parent);
263 r = udev_enumerate_add_match_subsystem(e, "block");
267 r = udev_enumerate_scan_devices(e);
269 log_error("Failed to enumerate partitions on /dev/block/%u:%u: %s",
270 major(dev), minor(dev), strerror(-r));
274 first = udev_enumerate_get_list_entry(e);
275 udev_list_entry_foreach(item, first) {
276 _cleanup_free_ char *fstype = NULL;
277 const char *node = NULL;
278 _cleanup_udev_device_unref_ struct udev_device *q;
282 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
286 if (udev_device_get_devnum(q) == udev_device_get_devnum(d))
289 if (udev_device_get_devnum(q) == udev_device_get_devnum(parent))
292 node = udev_device_get_devnode(q);
296 r = verify_gpt_partition(node, &type_id, &nr, &fstype);
298 /* skip child devices which are not detected properly */
301 log_error("Failed to verify GPT partition %s: %s",
308 if (sd_id128_equal(type_id, GPT_SWAP))
309 add_swap(node, fstype);
310 else if (sd_id128_equal(type_id, GPT_HOME)) {
311 if (!home || nr < home_nr) {
320 home_fstype = fstype;
326 if (home && home_fstype)
327 add_home(home, home_fstype);
332 static int get_btrfs_block_device(const char *path, dev_t *dev) {
333 struct btrfs_ioctl_fs_info_args fsi = {};
334 _cleanup_close_ int fd = -1;
340 fd = open(path, O_DIRECTORY|O_CLOEXEC);
344 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
347 /* We won't do this for btrfs RAID */
348 if (fsi.num_devices != 1)
351 for (id = 1; id <= fsi.max_id; id++) {
352 struct btrfs_ioctl_dev_info_args di = {
357 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
364 if (stat((char*) di.path, &st) < 0)
367 if (!S_ISBLK(st.st_mode))
370 if (major(st.st_rdev) == 0)
380 static int get_block_device(const char *path, dev_t *dev) {
390 if (major(st.st_dev) != 0) {
395 if (statfs("/", &sfs) < 0)
398 if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
399 return get_btrfs_block_device(path, dev);
404 static int devno_to_devnode(struct udev *udev, dev_t devno, char **ret) {
405 _cleanup_udev_device_unref_ struct udev_device *d;
409 d = udev_device_new_from_devnum(udev, 'b', devno);
413 t = udev_device_get_devnode(d);
425 int main(int argc, char *argv[]) {
426 _cleanup_free_ char *node = NULL;
427 _cleanup_udev_unref_ struct udev *udev = NULL;
431 if (argc > 1 && argc != 4) {
432 log_error("This program takes three or no arguments.");
440 log_set_target(LOG_TARGET_SAFE);
441 log_parse_environment();
447 log_debug("In initrd, exiting.");
451 if (detect_container(NULL) > 0) {
452 log_debug("In a container, exiting.");
456 r = get_block_device("/", &devno);
458 log_error("Failed to determine block device of root file system: %s", strerror(-r));
462 log_debug("Root file system not on a (single) block device.");
472 r = devno_to_devnode(udev, devno, &node);
474 log_error("Failed to determine block device node from major/minor: %s", strerror(-r));
478 log_debug("Root device %s.", node);
480 r = verify_gpt_partition(node, NULL, NULL, NULL);
482 log_error("Failed to verify GPT partition %s: %s", node, strerror(-r));
488 r = enumerate_partitions(udev, devno);
491 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;