X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fnspawn%2Fnspawn.c;h=2beb810e5def98b949e9305418d82878fdf8f96c;hb=51ddf61540976fc7b09ce5ace707b27f3acc3871;hp=588a8ae8ac8c4ff230d704eb25a7ef0342a35184;hpb=4bbfe7ad22b0666e82719e39e40be1c6cbb5cc91;p=elogind.git diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 588a8ae8a..2beb810e5 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -816,6 +816,7 @@ static int mount_all(const char *dest) { { "devpts", "/dev/pts", "devpts","newinstance,ptmxmode=0666,mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, true }, { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true }, { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true }, + { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true }, #ifdef HAVE_SELINUX { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false }, /* Bind mount first */ { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false }, /* Then, make it r/o */ @@ -913,8 +914,12 @@ static int mount_binds(const char *dest, char **l, bool ro) { r = stat(where, &dest_st); if (r == 0) { - if ((source_st.st_mode & S_IFMT) != (dest_st.st_mode & S_IFMT)) { - log_error("The file types of %s and %s do not match. Refusing bind mount", *x, where); + if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) { + log_error("Cannot bind mount directory %s on file %s.", *x, where); + return -EINVAL; + } + if (!S_ISDIR(source_st.st_mode) && S_ISDIR(dest_st.st_mode)) { + log_error("Cannot bind mount file %s on directory %s.", *x, where); return -EINVAL; } } else if (errno == ENOENT) { @@ -926,27 +931,18 @@ static int mount_binds(const char *dest, char **l, bool ro) { return -errno; } - /* Create the mount point, but be conservative -- refuse to create block - * and char devices. */ + /* Create the mount point. Any non-directory file can be + * mounted on any non-directory file (regular, fifo, socket, + * char, block). + */ if (S_ISDIR(source_st.st_mode)) { r = mkdir_label(where, 0755); if (r < 0 && errno != EEXIST) return log_error_errno(r, "Failed to create mount point %s: %m", where); - } else if (S_ISFIFO(source_st.st_mode)) { - r = mkfifo(where, 0644); - if (r < 0 && errno != EEXIST) - return log_error_errno(errno, "Failed to create mount point %s: %m", where); - } else if (S_ISSOCK(source_st.st_mode)) { - r = mknod(where, 0644 | S_IFSOCK, 0); - if (r < 0 && errno != EEXIST) - return log_error_errno(errno, "Failed to create mount point %s: %m", where); - } else if (S_ISREG(source_st.st_mode)) { + } else { r = touch(where); if (r < 0) return log_error_errno(r, "Failed to create mount point %s: %m", where); - } else { - log_error("Refusing to create mountpoint for file: %s", *x); - return -ENOTSUP; } if (mount(*x, where, "bind", MS_BIND, NULL) < 0) @@ -976,9 +972,17 @@ static int mount_cgroup_hierarchy(const char *dest, const char *controller, cons mkdir_p(to, 0755); - if (mount("cgroup", to, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV|(read_only ? MS_RDONLY : 0), controller) < 0) + /* The superblock mount options of the mount point need to be + * identical to the hosts', and hence writable... */ + if (mount("cgroup", to, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, controller) < 0) return log_error_errno(errno, "Failed to mount to %s: %m", to); + /* ... hence let's only make the bind mount read-only, not the + * superblock. */ + if (read_only) { + if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0) + return log_error_errno(errno, "Failed to remount %s read-only: %m", to); + } return 1; } @@ -1048,7 +1052,7 @@ static int mount_cgroup(const char *dest) { } } - r = mount_cgroup_hierarchy(dest, "name=systemd", "systemd", false); + r = mount_cgroup_hierarchy(dest, "name=systemd,xattr", "systemd", false); if (r < 0) return r; @@ -2653,63 +2657,6 @@ 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" \ @@ -2739,11 +2686,12 @@ static int dissect_image( _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, generic_rw = true; + bool is_gpt, is_mbr, multiple_generic = false; const char *pttype = NULL; blkid_partlist pl; struct stat st; + unsigned i; int r; - bool is_gpt, is_mbr, multiple_generic = false; assert(fd >= 0); assert(root_device); @@ -2812,21 +2760,81 @@ static int dissect_image( if (fstat(fd, &st) < 0) return log_error_errno(errno, "Failed to stat block device: %m"); - r = wait_for_block_device(udev, st.st_rdev, &d); - if (r < 0) - return r; - - e = udev_enumerate_new(udev); - if (!e) + d = udev_device_new_from_devnum(udev, 'b', st.st_rdev); + if (!d) return log_oom(); - r = udev_enumerate_add_match_parent(e, d); - if (r < 0) - return log_oom(); + for (i = 0;; i++) { + int n, m; - r = udev_enumerate_scan_devices(e); - if (r < 0) - return log_error_errno(r, "Failed to scan for partition devices of %s: %m", arg_image); + if (i >= 10) { + log_error("Kernel partitions never appeared."); + return -ENXIO; + } + + e = udev_enumerate_new(udev); + if (!e) + return log_oom(); + + r = udev_enumerate_add_match_parent(e, d); + if (r < 0) + return log_oom(); + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return log_error_errno(r, "Failed to scan for partition devices of %s: %m", arg_image); + + /* Count the partitions enumerated by the kernel */ + n = 0; + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) + n++; + + /* Count the partitions enumerated by blkid */ + m = blkid_partlist_numof_partitions(pl); + if (n == m + 1) + break; + if (n > m + 1) { + log_error("blkid and kernel partition list do not match."); + return -EIO; + } + if (n < m + 1) { + unsigned j; + + /* The kernel has probed fewer partitions than + * blkid? Maybe the kernel prober is still + * running or it got EBUSY because udev + * already opened the device. Let's reprobe + * the device, which is a synchronous call + * that waits until probing is complete. */ + + for (j = 0; j < 20; j++) { + + r = ioctl(fd, BLKRRPART, 0); + if (r < 0) + r = -errno; + if (r >= 0 || r != -EBUSY) + break; + + /* If something else has the device + * open, such as an udev rule, the + * ioctl will return EBUSY. Since + * there's no way to wait until it + * isn't busy anymore, let's just wait + * a bit, and try again. + * + * This is really something they + * should fix in the kernel! */ + + usleep(50 * USEC_PER_MSEC); + } + + if (r < 0) + return log_error_errno(r, "Failed to reread partition table: %m"); + } + + e = udev_enumerate_unref(e); + } first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) {