chiark / gitweb /
nspawn: support dissecting GPT images that contain only a single generic linux partition
[elogind.git] / src / nspawn / nspawn.c
index 7f87e37a7f06fad437c43c3e0912a8f235552fc7..efeba596658079606376811963586f61da49df3a 100644 (file)
@@ -43,6 +43,8 @@
 #include <linux/veth.h>
 #include <sys/personality.h>
 #include <linux/loop.h>
+#include <poll.h>
+#include <sys/file.h>
 
 #ifdef HAVE_SELINUX
 #include <selinux/selinux.h>
@@ -1029,7 +1031,7 @@ static int mount_cgroup(const char *dest) {
                                 return r;
 
                         if (symlink(combined, target) < 0)
-                                return log_error_errno(errno, "Failed to create symlink for combined hiearchy: %m");
+                                return log_error_errno(errno, "Failed to create symlink for combined hierarchy: %m");
                 }
         }
 
@@ -1655,7 +1657,7 @@ static int watch_rtnl(sd_event *event, int recv_fd, union in_addr_union *exposed
         cmsg = CMSG_FIRSTHDR(&mh);
         assert(cmsg->cmsg_level == SOL_SOCKET);
         assert(cmsg->cmsg_type == SCM_RIGHTS);
-        assert(cmsg->cmsg_len = CMSG_LEN(sizeof(int)));
+        assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
         memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
 
         r = sd_rtnl_open_fd(&rtnl, fd, 1, RTNLGRP_IPV4_IFADDR);
@@ -2557,6 +2559,70 @@ static int setup_image(char **device_path, int *loop_nr) {
         return r;
 }
 
+static int wait_for_block_device(struct udev *udev, dev_t devnum, struct udev_device **ret) {
+        _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL;
+        int r;
+
+        assert(udev);
+        assert(ret);
+
+        for (;;) {
+                _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+                struct pollfd pfd = {
+                        .events = POLLIN
+                };
+
+                d = udev_device_new_from_devnum(udev, 'b', devnum);
+                if (!d)
+                        return log_oom();
+
+                r = udev_device_get_is_initialized(d);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to check if device is initialized: %m");
+                if (r > 0) {
+                        *ret = d;
+                        d = NULL;
+                        return 0;
+                }
+                d = udev_device_unref(d);
+
+                if (!monitor) {
+                        monitor = udev_monitor_new_from_netlink(udev, "udev");
+                        if (!monitor)
+                                return log_oom();
+
+                        r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "block", NULL);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add block match: %m");
+
+                        r = udev_monitor_enable_receiving(monitor);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to turn on monitor: %m");
+
+                        continue;
+                }
+
+                pfd.fd = udev_monitor_get_fd(monitor);
+                if (pfd.fd < 0)
+                        return log_error_errno(r, "Failed to get udev monitor fd: %m");
+
+                r = poll(&pfd, 1, -1);
+                if (r < 0)
+                        return log_error_errno(errno, "Failed to wait for device initialization: %m");
+
+                d = udev_monitor_receive_device(monitor);
+        }
+
+        return 0;
+}
+
+#define PARTITION_TABLE_BLURB \
+        "Note that the disk image needs to either contain only a single MBR partition of\n" \
+        "type 0x83 that is marked bootable, or a sinlge GPT partition of type" \
+        "0FC63DAF-8483-4772-8E79-3D69D8477DE4 or follow\n" \
+        "    http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n" \
+        "to be bootable with systemd-nspawn."
+
 static int dissect_image(
                 int fd,
                 char **root_device, bool *root_device_rw,
@@ -2572,18 +2638,18 @@ static int dissect_image(
 #ifdef GPT_ROOT_SECONDARY
         int secondary_root_nr = -1;
 #endif
-
-        _cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL;
+        _cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL, *generic = NULL;
         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
         _cleanup_blkid_free_probe_ blkid_probe b = NULL;
         _cleanup_udev_unref_ struct udev *udev = NULL;
         struct udev_list_entry *first, *item;
-        bool home_rw = true, root_rw = true, secondary_root_rw = true, srv_rw = true;
+        bool home_rw = true, root_rw = true, secondary_root_rw = true, srv_rw = true, generic_rw = true;
         const char *pttype = NULL;
         blkid_partlist pl;
         struct stat st;
         int r;
+        bool is_gpt, is_mbr, multiple_generic = false;
 
         assert(fd >= 0);
         assert(root_device);
@@ -2612,8 +2678,9 @@ static int dissect_image(
         errno = 0;
         r = blkid_do_safeprobe(b);
         if (r == -2 || r == 1) {
-                log_error("Failed to identify any partition table on %s.\n"
-                          "Note that the disk image needs to follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/ to be supported by systemd-nspawn.", arg_image);
+                log_error("Failed to identify any partition table on\n"
+                          "    %s\n"
+                          PARTITION_TABLE_BLURB, arg_image);
                 return -EINVAL;
         } else if (r != 0) {
                 if (errno == 0)
@@ -2623,9 +2690,14 @@ static int dissect_image(
         }
 
         blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
-        if (!streq_ptr(pttype, "gpt")) {
-                log_error("Image %s does not carry a GUID Partition Table.\n"
-                          "Note that the disk image needs to follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/ to be supported by systemd-nspawn.", arg_image);
+
+        is_gpt = streq_ptr(pttype, "gpt");
+        is_mbr = streq_ptr(pttype, "dos");
+
+        if (!is_gpt && !is_mbr) {
+                log_error("No GPT or MBR partition table discovered on\n"
+                          "    %s\n"
+                          PARTITION_TABLE_BLURB, arg_image);
                 return -EINVAL;
         }
 
@@ -2646,9 +2718,9 @@ static int dissect_image(
         if (fstat(fd, &st) < 0)
                 return log_error_errno(errno, "Failed to stat block device: %m");
 
-        d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
-        if (!d)
-                return log_oom();
+        r = wait_for_block_device(udev, st.st_rdev, &d);
+        if (r < 0)
+                return r;
 
         e = udev_enumerate_new(udev);
         if (!e)
@@ -2665,9 +2737,8 @@ static int dissect_image(
         first = udev_enumerate_get_list_entry(e);
         udev_list_entry_foreach(item, first) {
                 _cleanup_udev_device_unref_ struct udev_device *q;
-                const char *stype, *node;
+                const char *node;
                 unsigned long long flags;
-                sd_id128_t type_id;
                 blkid_partition pp;
                 dev_t qn;
                 int nr;
@@ -2698,82 +2769,110 @@ static int dissect_image(
                         continue;
 
                 flags = blkid_partition_get_flags(pp);
-                if (flags & GPT_FLAG_NO_AUTO)
-                        continue;
 
                 nr = blkid_partition_get_partno(pp);
                 if (nr < 0)
                         continue;
 
-                stype = blkid_partition_get_type_string(pp);
-                if (!stype)
-                        continue;
+                if (is_gpt) {
+                        sd_id128_t type_id;
+                        const char *stype;
 
-                if (sd_id128_from_string(stype, &type_id) < 0)
-                        continue;
+                        if (flags & GPT_FLAG_NO_AUTO)
+                                continue;
 
-                if (sd_id128_equal(type_id, GPT_HOME)) {
+                        stype = blkid_partition_get_type_string(pp);
+                        if (!stype)
+                                continue;
 
-                        if (home && nr >= home_nr)
+                        if (sd_id128_from_string(stype, &type_id) < 0)
                                 continue;
 
-                        home_nr = nr;
-                        home_rw = !(flags & GPT_FLAG_READ_ONLY);
+                        if (sd_id128_equal(type_id, GPT_HOME)) {
 
-                        free(home);
-                        home = strdup(node);
-                        if (!home)
-                                return log_oom();
-                } else if (sd_id128_equal(type_id, GPT_SRV)) {
+                                if (home && nr >= home_nr)
+                                        continue;
 
-                        if (srv && nr >= srv_nr)
-                                continue;
+                                home_nr = nr;
+                                home_rw = !(flags & GPT_FLAG_READ_ONLY);
 
-                        srv_nr = nr;
-                        srv_rw = !(flags & GPT_FLAG_READ_ONLY);
+                                r = free_and_strdup(&home, node);
+                                if (r < 0)
+                                        return log_oom();
 
-                        free(srv);
-                        srv = strdup(node);
-                        if (!srv)
-                                return log_oom();
-                }
+                        } else if (sd_id128_equal(type_id, GPT_SRV)) {
+
+                                if (srv && nr >= srv_nr)
+                                        continue;
+
+                                srv_nr = nr;
+                                srv_rw = !(flags & GPT_FLAG_READ_ONLY);
+
+                                r = free_and_strdup(&srv, node);
+                                if (r < 0)
+                                        return log_oom();
+                        }
 #ifdef GPT_ROOT_NATIVE
-                else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
+                        else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
 
-                        if (root && nr >= root_nr)
-                                continue;
+                                if (root && nr >= root_nr)
+                                        continue;
 
-                        root_nr = nr;
-                        root_rw = !(flags & GPT_FLAG_READ_ONLY);
+                                root_nr = nr;
+                                root_rw = !(flags & GPT_FLAG_READ_ONLY);
 
-                        free(root);
-                        root = strdup(node);
-                        if (!root)
-                                return log_oom();
-                }
+                                r = free_and_strdup(&root, node);
+                                if (r < 0)
+                                        return log_oom();
+                        }
 #endif
 #ifdef GPT_ROOT_SECONDARY
-                else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
+                        else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
+
+                                if (secondary_root && nr >= secondary_root_nr)
+                                        continue;
+
+                                secondary_root_nr = nr;
+                                secondary_root_rw = !(flags & GPT_FLAG_READ_ONLY);
+
+                                r = free_and_strdup(&secondary_root, node);
+                                if (r < 0)
+                                        return log_oom();
+                        }
+#endif
+                        else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
+
+                                if (generic)
+                                        multiple_generic = true;
+                                else {
+                                        generic_rw = !(flags & GPT_FLAG_READ_ONLY);
 
-                        if (secondary_root && nr >= secondary_root_nr)
+                                        r = free_and_strdup(&generic, node);
+                                        if (r < 0)
+                                                return log_oom();
+                                }
+                        }
+
+                } else if (is_mbr) {
+                        int type;
+
+                        if (flags != 0x80) /* Bootable flag */
                                 continue;
 
-                        secondary_root_nr = nr;
-                        secondary_root_rw = !(flags & GPT_FLAG_READ_ONLY);
+                        type = blkid_partition_get_type(pp);
+                        if (type != 0x83) /* Linux partition */
+                                continue;
 
+                        if (generic)
+                                multiple_generic = true;
+                        else {
+                                generic_rw = true;
 
-                        free(secondary_root);
-                        secondary_root = strdup(node);
-                        if (!secondary_root)
-                                return log_oom();
+                                r = free_and_strdup(&root, node);
+                                if (r < 0)
+                                        return log_oom();
+                        }
                 }
-#endif
-        }
-
-        if (!root && !secondary_root) {
-                log_error("Failed to identify root partition in disk image %s.\n"
-                          "Note that the disk image needs to follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/ to be supported by systemd-nspawn.", arg_image);
-                return -EINVAL;
         }
 
         if (root) {
@@ -2788,6 +2887,31 @@ static int dissect_image(
 
                 *root_device_rw = secondary_root_rw;
                 *secondary = true;
+        } else if (generic) {
+
+                /* There were no partitions with precise meanings
+                 * around, but we found generic partitions. In this
+                 * case, if there's only one, we can go ahead and boot
+                 * it, otherwise we bail out, because we really cannot
+                 * make any sense of it. */
+
+                if (multiple_generic) {
+                        log_error("Identified multiple bootable Linux partitions on\n"
+                                  "    %s\n"
+                                  PARTITION_TABLE_BLURB, arg_image);
+                        return -EINVAL;
+                }
+
+                *root_device = generic;
+                generic = NULL;
+
+                *root_device_rw = generic_rw;
+                *secondary = false;
+        } else {
+                log_error("Failed to identify root partition in disk image\n"
+                          "    %s\n"
+                          PARTITION_TABLE_BLURB, arg_image);
+                return -EINVAL;
         }
 
         if (home) {
@@ -2915,7 +3039,7 @@ static void loop_remove(int nr, int *image_fd) {
         if (image_fd && *image_fd >= 0) {
                 r = ioctl(*image_fd, LOOP_CLR_FD);
                 if (r < 0)
-                        log_warning_errno(errno, "Failed to close loop image: %m");
+                        log_debug_errno(errno, "Failed to close loop image: %m");
                 *image_fd = safe_close(*image_fd);
         }
 
@@ -2927,7 +3051,7 @@ static void loop_remove(int nr, int *image_fd) {
 
         r = ioctl(control, LOOP_CTL_REMOVE, nr);
         if (r < 0)
-                log_warning_errno(errno, "Failed to remove loop %d: %m", nr);
+                log_debug_errno(errno, "Failed to remove loop %d: %m", nr);
 }
 
 static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
@@ -3273,7 +3397,7 @@ static int determine_names(void) {
                                 return -ENOENT;
                         }
 
-                        if (i->type == IMAGE_GPT)
+                        if (i->type == IMAGE_RAW)
                                 r = set_sanitized_path(&arg_image, i->path);
                         else
                                 r = set_sanitized_path(&arg_directory, i->path);
@@ -3442,7 +3566,7 @@ int main(int argc, char *argv[]) {
                                         if (!arg_quiet)
                                                 log_info("Directory %s already exists, not populating from template %s.", arg_directory, arg_template);
                                 } else if (r < 0) {
-                                        log_error_errno(r, "Couldn't create snapshort %s from %s: %m", arg_directory, arg_template);
+                                        log_error_errno(r, "Couldn't create snapshot %s from %s: %m", arg_directory, arg_template);
                                         goto finish;
                                 } else {
                                         if (!arg_quiet)
@@ -3947,9 +4071,10 @@ int main(int argc, char *argv[]) {
                                 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
                                 char last_char = 0;
 
-                                sd_notify(false,
-                                          "READY=1\n"
-                                          "STATUS=Container running.");
+                                sd_notifyf(false,
+                                           "READY=1\n"
+                                           "STATUS=Container running.\n"
+                                           "X_NSPAWN_LEADER_PID=" PID_FMT, pid);
 
                                 r = sd_event_new(&event);
                                 if (r < 0) {