diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index b6632e56cb..74a294e494 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -502,16 +502,13 @@ static int bus_set_transient_exec_context_fd( assert(name); assert(p); assert(b); - assert(verify_mode == O_DIRECTORY || (verify_mode & ~O_ACCMODE_STRICT) == 0); + assert((verify_mode & ~O_ACCMODE_STRICT) == 0); r = sd_bus_message_read(message, "h", &fd); if (r < 0) return r; - if (verify_mode == O_DIRECTORY) - r = fd_verify_directory(fd); - else - r = fd_vet_accmode(fd, verify_mode); + r = fd_vet_accmode(fd, verify_mode); if (r < 0) return sd_bus_error_set_errnof(reterr_error, r, "%s passed is of incompatible type: %m", name); @@ -813,8 +810,34 @@ static int bus_service_set_transient_property( return 1; } - if (streq(name, "RootDirectoryFileDescriptor")) - return bus_set_transient_exec_context_fd(u, name, &s->root_directory_fd, &s->exec_context.root_directory_as_fd, O_DIRECTORY, message, flags, reterr_error); + if (streq(name, "RootDirectoryFileDescriptor")) { + int fd; + + r = sd_bus_message_read(message, "h", &fd); + if (r < 0) + return r; + + r = fd_verify_directory(fd); + if (r < 0) + return sd_bus_error_set_errnof(reterr_error, r, "RootDirectoryFileDescriptor= is not a directory: %m"); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + int fd_clone; + + /* Note that this invalidates the fd we got from the client. They won't be able to + * move_mount() it themselves. If they already move_mount()'ed it themselves, this + * will fail to clone the fd. */ + fd_clone = mount_fd_clone(fd, /* recursive= */ true, /* replacement_fd= */ NULL); + if (fd_clone < 0) + return fd_clone; + + /* We're closing our own clone here, which shouldn't need an asynchronous_close(). */ + close_and_replace(s->root_directory_fd, fd_clone); + s->exec_context.root_directory_as_fd = true; + } + + return 1; + } return 0; } diff --git a/src/core/execute.c b/src/core/execute.c index 08210edb1e..116496b91b 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2877,6 +2877,7 @@ void exec_params_shallow_clear(ExecParameters *p) { p->fd_names = strv_free(p->fd_names); p->files_env = strv_free(p->files_env); p->fds = mfree(p->fds); + p->root_directory_fd = safe_close(p->root_directory_fd); p->exec_fd = safe_close(p->exec_fd); p->user_lookup_fd = -EBADF; p->bpf_restrict_fs_map_fd = -EBADF; diff --git a/src/core/service.c b/src/core/service.c index 3f555912ca..1ca676329a 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1951,7 +1951,14 @@ static int service_spawn_internal( exec_params.stdin_fd = s->stdin_fd; exec_params.stdout_fd = s->stdout_fd; exec_params.stderr_fd = s->stderr_fd; - exec_params.root_directory_fd = s->root_directory_fd; + + if (s->root_directory_fd >= 0) { + r = mount_fd_clone(s->root_directory_fd, /* recursive= */ true, &s->root_directory_fd); + if (r < 0) + return r; + + exec_params.root_directory_fd = r; + } r = exec_spawn(UNIT(s), c,