From d3590acede08bae0c4880e7bf3982ddbe2a6c850 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 19 Nov 2017 14:23:29 +0100 Subject: [PATCH] machined: support "machinectl bind" on non-directories (#7349) Fixes: #7195 --- man/machinectl.xml | 26 ++++++------ src/machine/machine-dbus.c | 84 +++++++++++++++++++++++++++----------- 2 files changed, 74 insertions(+), 36 deletions(-) diff --git a/man/machinectl.xml b/man/machinectl.xml index cf46fe8024..8c71880cf2 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -208,16 +208,16 @@ - When used with bind, creates - the destination directory before applying the bind - mount. + When used with bind, creates the destination file or directory before + applying the bind mount. Note that even though the name of this option suggests that it is suitable only for + directories, this option also creates the destination file node to mount over if the the object to mount is not + a directory, but a regular file, device node, socket or FIFO. - When used with bind, applies - a read-only bind mount. + When used with bind, creates a read-only bind mount. When used with clone, import-raw or import-tar a read-only container or VM image is created. @@ -528,14 +528,16 @@ bind NAME PATH [PATH] - Bind mounts a directory from the host into the specified container. The first directory - argument is the source directory on the host, the second directory argument is the destination directory in the - container. When the latter is omitted, the destination path in the container is the same as the source path on - the host. When combined with the switch, a ready-only bind mount is created. When - combined with the switch, the destination path is first created before the mount is - applied. Note that this option is currently only supported for + Bind mounts a file or directory from the host into the specified container. The first path + argument is the source file or directory on the host, the second path argument is the destination file or + directory in the container. When the latter is omitted, the destination path in the container is the same as + the source path on the host. When combined with the switch, a ready-only bind + mount is created. When combined with the switch, the destination path is first created + before the mount is applied. Note that this option is currently only supported for systemd-nspawn1 containers, - and only if user namespacing () is not used. + and only if user namespacing () is not used. This command supports bind + mounting directories, regular files, device nodes, AF_UNIX socket nodes, as well as + FIFOs. diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 3cb90be67d..28d05c088a 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -836,11 +836,13 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu bool mount_slave_created = false, mount_slave_mounted = false, mount_tmp_created = false, mount_tmp_mounted = false, mount_outside_created = false, mount_outside_mounted = false; + _cleanup_free_ char *chased_src = NULL; + int read_only, make_file_or_directory; const char *dest, *src; Machine *m = userdata; - int read_only, make_directory; - pid_t child; + struct stat st; siginfo_t si; + pid_t child; uid_t uid; int r; @@ -850,7 +852,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu if (m->class != MACHINE_CONTAINER) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines."); - r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_directory); + r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_file_or_directory); if (r < 0) return r; @@ -890,6 +892,15 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu if (laccess(p, F_OK) < 0) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Container does not allow propagation of mount points."); + r = chase_symlinks(src, NULL, 0, &chased_src); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to resolve source path: %m"); + + if (lstat(chased_src, &st) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to stat() source path: %m"); + if (S_ISLNK(st.st_mode)) /* This shouldn't really happen, given that we just chased the symlinks above, but let's better be safeā€¦ */ + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Source directory can't be a symbolic link"); + /* Our goal is to install a new bind mount into the container, possibly read-only. This is irritatingly complex unfortunately, currently. @@ -916,18 +927,21 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu goto finish; } - /* Second, we mount the source directory to a directory inside - of our MS_SLAVE playground. */ + /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */ mount_tmp = strjoina(mount_slave, "/mount"); - if (mkdir(mount_tmp, 0700) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount point %s: %m", mount_tmp); + if (S_ISDIR(st.st_mode)) + r = mkdir(mount_tmp, 0700) < 0 ? -errno : 0; + else + r = touch(mount_tmp); + if (r < 0) { + sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount point %s: %m", mount_tmp); goto finish; } mount_tmp_created = true; - if (mount(src, mount_tmp, NULL, MS_BIND, NULL) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to mount %s: %m", src); + if (mount(chased_src, mount_tmp, NULL, MS_BIND, NULL) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to mount %s: %m", chased_src); goto finish; } @@ -940,13 +954,18 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu goto finish; } - /* Fourth, we move the new bind mount into the propagation - * directory. This way it will appear there read-only + /* Fourth, we move the new bind mount into the propagation directory. This way it will appear there read-only * right-away. */ mount_outside = strjoina("/run/systemd/nspawn/propagate/", m->name, "/XXXXXX"); - if (!mkdtemp(mount_outside)) { - r = sd_bus_error_set_errnof(error, errno, "Cannot create propagation directory %s: %m", mount_outside); + if (S_ISDIR(st.st_mode)) + r = mkdtemp(mount_outside) ? 0 : -errno; + else { + r = mkostemp_safe(mount_outside); + safe_close(r); + } + if (r < 0) { + sd_bus_error_set_errnof(error, errno, "Cannot create propagation file or directory %s: %m", mount_outside); goto finish; } @@ -960,7 +979,10 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu mount_outside_mounted = true; mount_tmp_mounted = false; - (void) rmdir(mount_tmp); + if (S_ISDIR(st.st_mode)) + (void) rmdir(mount_tmp); + else + (void) unlink(mount_tmp); mount_tmp_created = false; (void) umount(mount_slave); @@ -999,8 +1021,14 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu goto child_fail; } - if (make_directory) - (void) mkdir_p(dest, 0755); + if (make_file_or_directory) { + if (S_ISDIR(st.st_mode)) + (void) mkdir_p(dest, 0755); + else { + (void) mkdir_parents(dest, 0755); + safe_close(open(dest, O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600)); + } + } /* Fifth, move the mount to the right place inside */ mount_inside = strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside)); @@ -1042,19 +1070,27 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu finish: if (mount_outside_mounted) - umount(mount_outside); - if (mount_outside_created) - rmdir(mount_outside); + (void) umount(mount_outside); + if (mount_outside_created) { + if (S_ISDIR(st.st_mode)) + (void) rmdir(mount_outside); + else + (void) unlink(mount_outside); + } if (mount_tmp_mounted) - umount(mount_tmp); - if (mount_tmp_created) - rmdir(mount_tmp); + (void) umount(mount_tmp); + if (mount_tmp_created) { + if (S_ISDIR(st.st_mode)) + (void) rmdir(mount_tmp); + else + (void) unlink(mount_tmp); + } if (mount_slave_mounted) - umount(mount_slave); + (void) umount(mount_slave); if (mount_slave_created) - rmdir(mount_slave); + (void) rmdir(mount_slave); return r; }