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"
44 * - Properly handle cryptsetup partitions
45 * - Define new partition type for encrypted swap
46 * - Make /home automount rather than mount
50 static const char *arg_dest = "/tmp";
52 static int verify_gpt_partition(dev_t dev, sd_id128_t *type, unsigned *nr, char **fstype) {
53 _cleanup_free_ char *t = NULL;
58 r = asprintf(&t, "/dev/block/%u:%u", major(dev), minor(dev));
63 b = blkid_new_probe_from_filename(t);
71 blkid_probe_enable_superblocks(b, 1);
72 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
73 blkid_probe_enable_partitions(b, 1);
74 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
77 r = blkid_do_safeprobe(b);
85 r = errno ? -errno : -EIO;
90 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
92 r = errno ? -errno : -EIO;
96 if (strcmp(v, "gpt") != 0) {
103 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
105 r = errno ? -errno : -EIO;
109 r = sd_id128_from_string(v, type);
116 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
118 r = errno ? -errno : -EIO;
122 r = safe_atou(v, nr);
132 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
155 static int add_swap(const char *path, const char *fstype) {
156 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
157 _cleanup_fclose_ FILE *f = NULL;
159 log_debug("Adding swap: %s %s", path, fstype);
161 name = unit_name_from_path(path, ".swap");
165 unit = strjoin(arg_dest, "/", name, NULL);
169 f = fopen(unit, "wxe");
171 log_error("Failed to create unit file %s: %m", unit);
176 "# Automatically generated by systemd-gpt-auto-generator\n\n"
178 "DefaultDependencies=no\n"
179 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
180 "Before=" SPECIAL_UMOUNT_TARGET " " SPECIAL_SWAP_TARGET "\n\n"
187 log_error("Failed to write unit file %s: %m", unit);
191 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
195 mkdir_parents_label(lnk, 0755);
196 if (symlink(unit, lnk) < 0) {
197 log_error("Failed to create symlink %s: %m", lnk);
204 static int add_home(const char *path, const char *fstype) {
205 _cleanup_free_ char *unit = NULL, *lnk = NULL;
206 _cleanup_fclose_ FILE *f = NULL;
208 if (dir_is_empty("/home") <= 0)
211 log_debug("Adding home: %s %s", path, fstype);
213 unit = strappend(arg_dest, "/home.mount");
217 f = fopen(unit, "wxe");
219 log_error("Failed to create unit file %s: %m", unit);
224 "# Automatically generated by systemd-gpt-auto-generator\n\n"
226 "DefaultDependencies=no\n"
227 "After=" SPECIAL_LOCAL_FS_PRE_TARGET "\n"
228 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
229 "Before=" SPECIAL_UMOUNT_TARGET " " SPECIAL_LOCAL_FS_TARGET "\n\n"
239 log_error("Failed to write unit file %s: %m", unit);
243 lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".requires/home.mount", NULL);
248 mkdir_parents_label(lnk, 0755);
249 if (symlink(unit, lnk) < 0) {
250 log_error("Failed to create symlink %s: %m", lnk);
257 static int enumerate_partitions(dev_t dev) {
259 struct udev_enumerate *e = NULL;
260 struct udev_device *parent = NULL, *d = NULL;
261 struct udev_list_entry *first, *item;
262 unsigned home_nr = (unsigned) -1;
263 _cleanup_free_ char *home = NULL, *home_fstype = NULL;
270 e = udev_enumerate_new(udev);
276 d = udev_device_new_from_devnum(udev, 'b', dev);
282 parent = udev_device_get_parent(d);
288 r = udev_enumerate_add_match_parent(e, parent);
294 r = udev_enumerate_add_match_subsystem(e, "block");
300 r = udev_enumerate_scan_devices(e);
302 log_error("Failed to enumerate partitions on /dev/block/%u:%u: %s",
303 major(dev), minor(dev), strerror(-r));
307 first = udev_enumerate_get_list_entry(e);
308 udev_list_entry_foreach(item, first) {
309 _cleanup_free_ char *fstype = NULL;
310 const char *node = NULL;
311 struct udev_device *q;
316 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
322 if (udev_device_get_devnum(q) == udev_device_get_devnum(d))
325 if (udev_device_get_devnum(q) == udev_device_get_devnum(parent))
328 node = udev_device_get_devnode(q);
334 sub = udev_device_get_devnum(q);
336 r = verify_gpt_partition(sub, &type_id, &nr, &fstype);
338 log_error("Failed to verify GPT partition /dev/block/%u:%u: %s",
339 major(sub), minor(sub), strerror(-r));
340 udev_device_unref(q);
346 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)))
347 add_swap(node, fstype);
348 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))) {
350 if (!home || nr < home_nr) {
361 home_fstype = fstype;
367 udev_device_unref(q);
370 if (home && home_fstype)
371 add_home(home, home_fstype);
375 udev_device_unref(d);
378 udev_enumerate_unref(e);
386 static int get_btrfs_block_device(const char *path, dev_t *dev) {
387 struct btrfs_ioctl_fs_info_args fsi;
388 _cleanup_close_ int fd = -1;
394 fd = open(path, O_DIRECTORY|O_CLOEXEC);
399 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
402 /* We won't do this for btrfs RAID */
403 if (fsi.num_devices != 1)
406 for (id = 1; id <= fsi.max_id; id++) {
407 struct btrfs_ioctl_dev_info_args di;
413 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
420 if (stat((char*) di.path, &st) < 0)
423 if (!S_ISBLK(st.st_mode))
426 if (major(st.st_rdev) == 0)
436 static int get_block_device(const char *path, dev_t *dev) {
446 if (major(st.st_dev) != 0) {
451 if (statfs("/", &sfs) < 0)
454 if (F_TYPE_CMP(sfs.f_type, BTRFS_SUPER_MAGIC))
455 return get_btrfs_block_device(path, dev);
460 int main(int argc, char *argv[]) {
464 if (argc > 1 && argc != 4) {
465 log_error("This program takes three or no arguments.");
472 log_set_target(LOG_TARGET_SAFE);
473 log_parse_environment();
481 r = get_block_device("/", &dev);
483 log_error("Failed to determine block device of root file system: %s", strerror(-r));
487 log_debug("Root file system not on a (single) block device.");
491 log_debug("Root device /dev/block/%u:%u.", major(dev), minor(dev));
493 r = verify_gpt_partition(dev, NULL, NULL, NULL);
495 log_error("Failed to verify GPT partition /dev/block/%u:%u: %s",
496 major(dev), minor(dev), strerror(-r));
502 r = enumerate_partitions(dev);
504 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;