1
0
mirror of https://github.com/systemd/systemd synced 2025-12-23 01:14:45 +01:00

Compare commits

..

13 Commits

Author SHA1 Message Date
Daan De Meyer
f9708c53aa Follow ups for #39806
We drop the optimization for verify_sigchld as the
check differs based on the options passed and the
extra syscalls are certainly not going to matter
compared to the cost of forking a child process.

Fixes #40166
2025-12-21 08:16:03 +01:00
Lennart Poettering
471fe52d57
preparatory work from the OCI PR (#40149)
This adds 11 commits with minor preparatory commits for PR #39621. That
PR has tests that cover all this, even if this one doesn't, but the bits
added here are relatively minor.
2025-12-21 07:38:29 +01:00
Lennart Poettering
9cfefbf983 dissect-image: automatically convert O_PATH fd to proper fd in mountfsd client
Let's make the mountfsd client code a bit easier to use, and convert an
O_PATH fd to a real one if necessary, automatically.
2025-12-21 07:04:42 +01:00
Lennart Poettering
b7d1d17b9c dissect-image: teach mountfsd_make_directory() the new access mode parameter 2025-12-21 07:04:42 +01:00
Lennart Poettering
a9b60de478 mountfsd: return a better error when invalid file flags are specified on image fd
We already defined an error for this that we return for invalid dir fds
passed in, let's add the same niceness to the image fds.
2025-12-21 07:04:42 +01:00
Lennart Poettering
d4f8be786c mountfsd: allow O_NONBLOCK on image fds
Let's accept O_NONBLOCK on image fds. It's usually wise to open block
device with O_NONBLOCK as it means that drivers with removable media
won't block until a medium is inserted. Otherwise it has no effect. It
also has no effect when opening regular files. Since block devices and
raw devices are the only two things we accept in mountfsd, it's hence
safe to allow it.
2025-12-21 07:04:42 +01:00
Lennart Poettering
46341769b7 mountfsd: optionally accept an access mode for the dir created by MakeDirectory()
So far we expected the client to access the access mode themselves, and
set a restrictive 0700 server-side.

However, in some scenarios it's handy if the inode is created
server-side already with the right mode, in particular when the client
doesn't do anything with the dir just yet, and just needs it to be there
with the right mode. If it has to adjust the mode client-side it might
otherwise need to fork something off, join the selected userns, adjust
the mode, and kill the child off again.
2025-12-21 07:04:42 +01:00
Lennart Poettering
6e07fc6bcc json-util: add a generic parser for parsing access mode/umask
Let's move this from user-record.c into generic code and let's beef it
up a bit: allow parsing octal strings. This is particular relevant given
json_variant_new_stat() generates the mode in that format, and we
probably should be able to parse our own fields (even though we
currently don't do that for the data from json_variant_new_stat()).
2025-12-21 07:04:42 +01:00
Lennart Poettering
00421e3166 dissect-image: make verity params for mountfsd_mount_image() optional 2025-12-21 07:04:42 +01:00
Lennart Poettering
468cfd25a0 dissect-image: add fd-based flavour of mountfsd_mount_image() 2025-12-21 07:04:42 +01:00
Lennart Poettering
e742d749d3 dissect-image: make mountfsd_make_directory_fd() return param optional 2025-12-21 07:04:42 +01:00
Lennart Poettering
7e3bdf90d9 iovec-util: add iovec_done_many_and_free() 2025-12-21 07:04:42 +01:00
Lennart Poettering
b6aa78f707 recursedir: introduce new RECURSE_DIR_UNLINK callback return value
This introduces RECURSE_DIR_UNLINK + RECURSE_DIR_UNLINK_GRACEFUL as new
return values for recurse_dir() callbacks. If either is returned the
inode currently processed will be removed and iteration continues with
the next item.

This is useful to have in the recurse_dir() mechanism itself (rather
than implement in the callback itself), due to ordering: we want to pin
the inodes via an fd while calling the callbacks, but we have to close
it before removal of the inodes. By moving this into the recurse_dir()
infra, we can implement this easily.
2025-12-21 07:04:42 +01:00
23 changed files with 270 additions and 145 deletions

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/stat.h>
#include <unistd.h>
#include "alloc-util.h"
#include "dirent-util.h"
@ -433,30 +434,25 @@ int recurse_dir(
i,
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
userdata);
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
break;
if (r == RECURSE_DIR_SKIP_ENTRY)
continue;
if (r != RECURSE_DIR_CONTINUE)
return r;
if (r == RECURSE_DIR_CONTINUE) {
r = recurse_dir(subdir_fd,
p,
statx_mask,
n_depth_max - 1,
flags & ~RECURSE_DIR_TOPLEVEL, /* we already called the callback for this entry */
func,
userdata);
if (r != 0)
return r;
r = recurse_dir(subdir_fd,
p,
statx_mask,
n_depth_max - 1,
flags & ~RECURSE_DIR_TOPLEVEL, /* we already called the callback for this entry */
func,
userdata);
if (r != 0)
return r;
r = func(RECURSE_DIR_LEAVE,
p,
dir_fd,
subdir_fd,
i,
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
userdata);
r = func(RECURSE_DIR_LEAVE,
p,
dir_fd,
subdir_fd,
i,
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
userdata);
}
} else
/* Non-directory inode */
r = func(RECURSE_DIR_ENTRY,
@ -469,7 +465,21 @@ int recurse_dir(
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
break;
if (!IN_SET(r, RECURSE_DIR_SKIP_ENTRY, RECURSE_DIR_CONTINUE))
if (IN_SET(r, RECURSE_DIR_UNLINK, RECURSE_DIR_UNLINK_GRACEFUL)) {
int f = subdir_fd >= 0 ? AT_REMOVEDIR : 0;
/* Close inodes before we try to delete them */
subdir_fd = safe_close(subdir_fd);
inode_fd = safe_close(inode_fd);
if (unlinkat(dir_fd, i->d_name, f) < 0) {
if (r != RECURSE_DIR_UNLINK_GRACEFUL)
return -errno;
log_debug_errno(errno, "Unable to remove '%s', ignoring: %m", p);
}
} else if (!IN_SET(r, RECURSE_DIR_SKIP_ENTRY, RECURSE_DIR_CONTINUE))
return r;
}

View File

@ -53,6 +53,8 @@ typedef enum RecurseDirEvent {
#define RECURSE_DIR_CONTINUE 0
#define RECURSE_DIR_LEAVE_DIRECTORY INT_MIN
#define RECURSE_DIR_SKIP_ENTRY (INT_MIN+1)
#define RECURSE_DIR_UNLINK (INT_MIN+2)
#define RECURSE_DIR_UNLINK_GRACEFUL (INT_MIN+3)
/* Make sure that the negative errno range and these two special returns don't overlap */
assert_cc(RECURSE_DIR_LEAVE_DIRECTORY < -ERRNO_MAX);

View File

@ -31,6 +31,15 @@ static inline void iovec_done(struct iovec *iovec) {
iovec->iov_len = 0;
}
static inline void iovec_done_many_and_free(struct iovec *iovec, size_t n) {
if (n > 0) {
assert(iovec);
FOREACH_ARRAY(j, iovec, n)
iovec_done(j);
}
free(iovec);
}
static inline bool iovec_is_set(const struct iovec *iovec) {
/* Checks if the iovec points to a non-empty chunk of memory */
return iovec && iovec->iov_len > 0 && iovec->iov_base;

View File

@ -7,7 +7,6 @@
#include <sys/ioctl.h>
#include <sys/xattr.h>
#include <unistd.h>
#include "pidref.h"
#if HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
@ -52,6 +51,7 @@
#include "openssl-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "pidref.h"
#include "process-util.h"
#include "random-util.h"
#include "reread-partition-table.h"
@ -2606,7 +2606,7 @@ static int ext4_offline_resize_fs(
_cleanup_free_ char *size_str = NULL;
bool re_open = false, re_mount = false;
_cleanup_(pidref_done) PidRef resize_pidref = PIDREF_NULL, fsck_pidref = PIDREF_NULL;
_cleanup_(pidref_done) PidRef fsck_pidref = PIDREF_NULL;
int r, exit_status;
assert(setup);
@ -2665,7 +2665,7 @@ static int ext4_offline_resize_fs(
r = pidref_safe_fork(
"(e2resize)",
FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS,
&resize_pidref);
/* ret_pid= */ NULL);
if (r < 0)
return r;
if (r == 0) {

View File

@ -257,7 +257,7 @@ static int tar_import_fork_tar(TarImport *i) {
return r;
_cleanup_close_ int directory_fd = -EBADF;
r = mountfsd_make_directory(d, /* flags= */ 0, &directory_fd);
r = mountfsd_make_directory(d, MODE_INVALID, /* flags= */ 0, &directory_fd);
if (r < 0)
return r;

View File

@ -297,7 +297,7 @@ static int tar_pull_make_local_copy(TarPull *p) {
}
_cleanup_close_ int directory_fd = -EBADF;
r = mountfsd_make_directory(t, /* flags= */ 0, &directory_fd);
r = mountfsd_make_directory(t, MODE_INVALID, /* flags= */ 0, &directory_fd);
if (r < 0)
return r;
@ -609,7 +609,7 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) {
return r;
_cleanup_close_ int directory_fd = -EBADF;
r = mountfsd_make_directory(where, /* flags= */ 0, &directory_fd);
r = mountfsd_make_directory(where, MODE_INVALID, /* flags= */ 0, &directory_fd);
if (r < 0)
return r;

View File

@ -1619,12 +1619,9 @@ _public_ int sd_event_add_child(
if (!callback)
callback = child_exit_callback;
/* As an optimization we only do these checks on the first child event source created. */
if (e->n_online_child_sources == 0) {
r = verify_sigchld(options);
if (r < 0)
return r;
}
r = verify_sigchld(options);
if (r < 0)
return r;
r = hashmap_ensure_allocated(&e->child_sources, NULL);
if (r < 0)
@ -1704,11 +1701,9 @@ _public_ int sd_event_add_child_pidfd(
if (!callback)
callback = child_exit_callback;
if (e->n_online_child_sources == 0) {
r = verify_sigchld(options);
if (r < 0)
return r;
}
r = verify_sigchld(options);
if (r < 0)
return r;
r = hashmap_ensure_allocated(&e->child_sources, NULL);
if (r < 0)

View File

@ -701,3 +701,45 @@ int json_variant_new_fd_info(sd_json_variant **ret, int fd) {
JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE("mountId", mntid),
SD_JSON_BUILD_PAIR_VARIANT("fileHandle", w));
}
int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
mode_t *m = ASSERT_PTR(userdata);
int r;
if (sd_json_variant_is_null(variant)) {
*m = MODE_INVALID;
return 0;
}
/* Let the SD_JSON_STRICT determine if we allow suid/sgid/sticky or not */
mode_t limit = FLAGS_SET(flags, SD_JSON_STRICT) ? 0777 : 07777;
if (sd_json_variant_is_string(variant)) {
/* NB: we parse the mode in the usual octal if a string is specified. */
mode_t mode;
r = parse_mode(sd_json_variant_string(variant), &mode);
if (r < 0)
return json_log(variant, flags, r, "JSON field '%s' is not a valid access mode string.", strna(name));
if (mode > limit)
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
"JSON field '%s' outside of valid range 0%s0%o.",
strna(name), glyph(GLYPH_ELLIPSIS), limit);
*m = mode;
} else if (sd_json_variant_is_unsigned(variant)) {
uint64_t k = sd_json_variant_unsigned(variant);
if (k > (uint64_t) limit)
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
"JSON field '%s' outside of valid range 0%s0%o.",
strna(name), glyph(GLYPH_ELLIPSIS), limit);
*m = (mode_t) k;
} else
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is neither a number nor a string.", strna(name));
return 0;
}

View File

@ -127,6 +127,7 @@ int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dis
int json_dispatch_ifindex(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_log_level(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_strv_environment(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
static inline int json_variant_unbase64_iovec(sd_json_variant *v, struct iovec *ret) {
return sd_json_variant_unbase64(v, ret ? &ret->iov_base : NULL, ret ? &ret->iov_len : NULL);

View File

@ -1242,14 +1242,13 @@ int machine_copy_from_to_operation(
errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
// TODO: port to PidRef and donate child rather than destroying it
Operation *operation;
r = operation_new(manager, machine, &child, errno_pipe_fd[0], &operation);
if (r < 0)
return r;
TAKE_FD(errno_pipe_fd[0]);
pidref_done(&child);
TAKE_PIDREF(child);
*ret = operation;
return 0;

View File

@ -111,7 +111,7 @@ int operation_new(Manager *manager, Machine *machine, PidRef *child, int errno_f
return -ENOMEM;
*o = (Operation) {
.pidref = TAKE_PIDREF(*child),
.pidref = *child,
.errno_fd = errno_fd,
.extra_fd = -EBADF
};

View File

@ -124,7 +124,7 @@ static int validate_image_fd(int fd, MountImageParameters *p) {
return r;
}
fl = fd_verify_safe_flags(fd);
fl = fd_verify_safe_flags_full(fd, O_NONBLOCK);
if (fl < 0)
return log_debug_errno(fl, "Image file descriptor has unsafe flags set: %m");
@ -353,6 +353,8 @@ static int vl_method_mount_image(
}
r = validate_image_fd(image_fd, &p);
if (r == -EREMOTEIO)
return sd_varlink_errorbo(link, "io.systemd.MountFileSystem.BadFileDescriptorFlags", SD_JSON_BUILD_PAIR_STRING("parameter", "imageFileDescriptor"));
if (r < 0)
return r;
@ -1003,6 +1005,7 @@ static int vl_method_mount_directory(
typedef struct MakeDirectoryParameters {
unsigned parent_fd_idx;
const char *name;
mode_t mode;
} MakeDirectoryParameters;
static int vl_method_make_directory(
@ -1012,14 +1015,16 @@ static int vl_method_make_directory(
void *userdata) {
static const sd_json_dispatch_field dispatch_table[] = {
{ "parentFileDescriptor", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(MakeDirectoryParameters, parent_fd_idx), SD_JSON_MANDATORY },
{ "name", SD_JSON_VARIANT_STRING, json_dispatch_const_filename, offsetof(MakeDirectoryParameters, name), SD_JSON_MANDATORY },
{ "parentFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(MakeDirectoryParameters, parent_fd_idx), SD_JSON_MANDATORY },
{ "name", SD_JSON_VARIANT_STRING, json_dispatch_const_filename, offsetof(MakeDirectoryParameters, name), SD_JSON_MANDATORY },
{ "mode", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(MakeDirectoryParameters, mode), SD_JSON_STRICT },
VARLINK_DISPATCH_POLKIT_FIELD,
{}
};
MakeDirectoryParameters p = {
.parent_fd_idx = UINT_MAX,
.mode = MODE_INVALID,
};
Hashmap **polkit_registry = ASSERT_PTR(userdata);
int r;
@ -1028,6 +1033,11 @@ static int vl_method_make_directory(
if (r != 0)
return r;
if (p.mode == MODE_INVALID)
p.mode = 0700;
else
p.mode &= 0775; /* refuse generating world writable dirs */
if (p.parent_fd_idx == UINT_MAX)
return sd_varlink_error_invalid_parameter_name(link, "parentFileDescriptor");
@ -1089,11 +1099,11 @@ static int vl_method_make_directory(
if (r < 0)
return r;
_cleanup_close_ int fd = open_mkdir_at(parent_fd, t, O_CLOEXEC, 0700);
_cleanup_close_ int fd = open_mkdir_at(parent_fd, t, O_CLOEXEC, p.mode);
if (fd < 0)
return fd;
r = RET_NERRNO(fchmod(fd, 0700)); /* Set mode explicitly, as paranoia regarding umask games */
r = RET_NERRNO(fchmod(fd, p.mode)); /* Set mode explicitly, as paranoia regarding umask games */
if (r < 0)
goto fail;

View File

@ -542,7 +542,7 @@ static int portable_extract_by_path(
if (r < 0)
return r;
TAKE_PIDREF(child);
pidref_done(&child);
}
if (!os_release)

View File

@ -554,7 +554,7 @@ int bus_image_common_remove(
if (r < 0)
return r;
TAKE_PIDREF(child);
/* We don't need to disarm child cleanup here because operation_new() takes over ownership internally. */
errno_pipe_fd[0] = -EBADF;
return 1;

View File

@ -1491,6 +1491,7 @@ static int unpriviled_clone(Image *i, const char *new_path) {
_cleanup_close_ int new_fd = -EBADF;
r = mountfsd_make_directory(
new_path,
MODE_INVALID,
/* flags= */ 0,
&new_fd);
if (r < 0)

View File

@ -630,7 +630,35 @@ static void check_partition_flags(
log_debug("Unexpected partition flag %llu set on %s!", bit, node);
}
}
#endif
static int make_image_name(const char *path, char **ret) {
int r;
assert(path);
assert(ret);
_cleanup_free_ char *filename = NULL;
r = path_extract_filename(path, &filename);
if (r < 0)
return r;
_cleanup_free_ char *name = NULL;
r = raw_strip_suffixes(filename, &name);
if (r < 0)
return r;
if (!image_name_is_valid(name)) {
log_debug("Image name %s is not valid, ignoring.", strna(name));
*ret = NULL;
return 0;
}
*ret = TAKE_PTR(name);
return 1;
}
#if HAVE_BLKID
static int dissected_image_new(const char *path, DissectedImage **ret) {
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
_cleanup_free_ char *name = NULL;
@ -639,20 +667,9 @@ static int dissected_image_new(const char *path, DissectedImage **ret) {
assert(ret);
if (path) {
_cleanup_free_ char *filename = NULL;
r = path_extract_filename(path, &filename);
r = make_image_name(path, &name);
if (r < 0)
return r;
r = raw_strip_suffixes(filename, &name);
if (r < 0)
return r;
if (!image_name_is_valid(name)) {
log_debug("Image name %s is not valid, ignoring.", strna(name));
name = mfree(name);
}
}
m = new(DissectedImage, 1);
@ -4428,7 +4445,7 @@ int dissected_image_acquire_metadata(
if (r < 0)
goto finish;
TAKE_PIDREF(child);
pidref_done(&child);
n = read(error_pipe[0], &v, sizeof(v));
if (n < 0) {
@ -5022,8 +5039,8 @@ static void mount_image_reply_parameters_done(MountImageReplyParameters *p) {
#endif
int mountfsd_mount_image(
const char *path,
int mountfsd_mount_image_fd(
int image_fd,
int userns_fd,
const ImagePolicy *image_policy,
const VeritySettings *verity,
@ -5043,13 +5060,13 @@ int mountfsd_mount_image(
};
_cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
_cleanup_close_ int image_fd = -EBADF, verity_data_fd = -EBADF;
_cleanup_close_ int verity_data_fd = -EBADF;
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
_cleanup_free_ char *ps = NULL;
const char *error_id;
int r;
assert(path);
assert(image_fd >= 0);
assert(ret);
r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem");
@ -5064,9 +5081,11 @@ int mountfsd_mount_image(
if (r < 0)
return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
image_fd = open(path, O_RDONLY|O_CLOEXEC);
_cleanup_close_ int reopened_fd = -EBADF;
image_fd = fd_reopen_condition(image_fd, O_CLOEXEC|O_NOCTTY|O_NONBLOCK|(FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY) ? O_RDONLY : O_RDWR), O_PATH, &reopened_fd);
if (image_fd < 0)
return log_error_errno(errno, "Failed to open '%s': %m", path);
return log_error_errno(image_fd, "Failed to reopen fd: %m");
r = sd_varlink_push_dup_fd(vl, image_fd);
if (r < 0)
@ -5159,7 +5178,7 @@ int mountfsd_mount_image(
assert(pp.designator >= 0);
if (!di) {
r = dissected_image_new(path, &di);
r = dissected_image_new(/* path= */ NULL, &di);
if (r < 0)
return log_error_errno(r, "Failed to allocated new dissected image structure: %m");
}
@ -5194,6 +5213,38 @@ int mountfsd_mount_image(
#endif
}
int mountfsd_mount_image(
const char *path,
int userns_fd,
const ImagePolicy *image_policy,
const VeritySettings *verity,
DissectImageFlags flags,
DissectedImage **ret) {
int r;
assert(path);
assert(ret);
_cleanup_close_ int image_fd = open(path, O_RDONLY|O_CLOEXEC);
if (image_fd < 0)
return log_error_errno(errno, "Failed to open '%s': %m", path);
_cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
r = mountfsd_mount_image_fd(image_fd, userns_fd, image_policy, verity, flags, &di);
if (r < 0)
return r;
if (!di->image_name) {
r = make_image_name(path, &di->image_name);
if (r < 0)
return r;
}
*ret = TAKE_PTR(di);
return 0;
}
int mountfsd_mount_directory_fd(
int directory_fd,
int userns_fd,
@ -5284,6 +5335,7 @@ int mountfsd_mount_directory(
int mountfsd_make_directory_fd(
int parent_fd,
const char *name,
mode_t mode,
DissectImageFlags flags,
int *ret_directory_fd) {
@ -5291,7 +5343,6 @@ int mountfsd_make_directory_fd(
assert(parent_fd >= 0);
assert(name);
assert(ret_directory_fd);
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem");
@ -5319,6 +5370,7 @@ int mountfsd_make_directory_fd(
&error_id,
SD_JSON_BUILD_PAIR_UNSIGNED("parentFileDescriptor", 0),
SD_JSON_BUILD_PAIR_STRING("name", name),
SD_JSON_BUILD_PAIR_CONDITION(!IN_SET(mode, MODE_INVALID, 0700), "mode", SD_JSON_BUILD_UNSIGNED(mode)), /* suppress this field if default/unset */
SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH)));
if (r < 0)
return r;
@ -5337,12 +5389,14 @@ int mountfsd_make_directory_fd(
if (directory_fd < 0)
return log_error_errno(directory_fd, "Failed to take directory fd from Varlink connection: %m");
*ret_directory_fd = TAKE_FD(directory_fd);
if (ret_directory_fd)
*ret_directory_fd = TAKE_FD(directory_fd);
return 0;
}
int mountfsd_make_directory(
const char *path,
mode_t mode,
DissectImageFlags flags,
int *ret_directory_fd) {
@ -5362,5 +5416,5 @@ int mountfsd_make_directory(
if (fd < 0)
return log_error_errno(r, "Failed to open '%s': %m", parent);
return mountfsd_make_directory_fd(fd, dirname, flags, ret_directory_fd);
return mountfsd_make_directory_fd(fd, dirname, mode, flags, ret_directory_fd);
}

View File

@ -268,9 +268,10 @@ static inline const char* dissected_partition_fstype(const DissectedPartition *m
int get_common_dissect_directory(char **ret);
int mountfsd_mount_image_fd(int image_fd, int userns_fd, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, DissectedImage **ret);
int mountfsd_mount_image(const char *path, int userns_fd, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, DissectedImage **ret);
int mountfsd_mount_directory_fd(int directory_fd, int userns_fd, DissectImageFlags flags, int *ret_mount_fd);
int mountfsd_mount_directory(const char *path, int userns_fd, DissectImageFlags flags, int *ret_mount_fd);
int mountfsd_make_directory_fd(int parent_fd, const char *name, DissectImageFlags flags, int *ret_directory_fd);
int mountfsd_make_directory(const char *path, DissectImageFlags flags, int *ret_directory_fd);
int mountfsd_make_directory_fd(int parent_fd, const char *name, mode_t mode, DissectImageFlags flags, int *ret_directory_fd);
int mountfsd_make_directory(const char *path, mode_t mode, DissectImageFlags flags, int *ret_directory_fd);

View File

@ -174,7 +174,7 @@ static int do_execute(
_cleanup_(pidref_freep) PidRef *dup = NULL;
r = pidref_dup(&pidref, &dup);
if (r < 0)
return r;
return log_error_errno(r, "Failed to duplicate pid reference: %m");
r = hashmap_ensure_put(&pids, &pidref_hash_ops_free_free, dup, t);
if (r < 0)
@ -222,11 +222,10 @@ static int do_execute(
}
while (!hashmap_isempty(pids)) {
_cleanup_(pidref_freep) PidRef *pidref = NULL;
_cleanup_free_ char *t = NULL;
void *p;
t = ASSERT_PTR(hashmap_steal_first_key_and_value(pids, &p));
_cleanup_(pidref_freep) PidRef *pidref = p;
t = ASSERT_PTR(hashmap_steal_first_key_and_value(pids, (void**) &pidref));
r = pidref_wait_for_terminate_and_check(t, pidref, WAIT_LOG);
if (r < 0)
@ -275,7 +274,7 @@ int execute_strv(
const char *process_name = strjoina("(", name, ")");
PidRef executor_pidref = PIDREF_NULL;
_cleanup_(pidref_done) PidRef executor_pidref = PIDREF_NULL;
r = pidref_safe_fork(process_name, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_LOG, &executor_pidref);
if (r < 0)
return r;

View File

@ -462,50 +462,6 @@ static int json_dispatch_image_path(const char *name, sd_json_variant *variant,
return 0;
}
static int json_dispatch_umask(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
mode_t *m = userdata;
uint64_t k;
if (sd_json_variant_is_null(variant)) {
*m = MODE_INVALID;
return 0;
}
if (!sd_json_variant_is_unsigned(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name));
k = sd_json_variant_unsigned(variant);
if (k > 0777)
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
"JSON field '%s' outside of valid range 0%s0777.",
strna(name), glyph(GLYPH_ELLIPSIS));
*m = (mode_t) k;
return 0;
}
static int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
mode_t *m = userdata;
uint64_t k;
if (sd_json_variant_is_null(variant)) {
*m = MODE_INVALID;
return 0;
}
if (!sd_json_variant_is_unsigned(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name));
k = sd_json_variant_unsigned(variant);
if (k > 07777)
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
"JSON field '%s' outside of valid range 0%s07777.",
strna(name), glyph(GLYPH_ELLIPSIS));
*m = (mode_t) k;
return 0;
}
static int json_dispatch_locale(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
char **s = userdata;
const char *n;
@ -1273,7 +1229,7 @@ static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_j
{ "iconName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, icon_name), SD_JSON_STRICT },
{ "location", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, location), 0 },
{ "shell", SD_JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 },
{ "umask", SD_JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 },
{ "umask", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, umask), SD_JSON_STRICT },
{ "environment", SD_JSON_VARIANT_ARRAY, json_dispatch_strv_environment, offsetof(UserRecord, environment), 0 },
{ "timeZone", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, time_zone), SD_JSON_STRICT },
{ "preferredLanguage", SD_JSON_VARIANT_STRING, json_dispatch_locale, offsetof(UserRecord, preferred_language), 0 },
@ -1287,7 +1243,7 @@ static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_j
{ "diskSize", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, disk_size), 0 },
{ "diskSizeRelative", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 },
{ "skeletonDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), SD_JSON_STRICT },
{ "accessMode", SD_JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
{ "accessMode", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
{ "tasksMax", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 },
{ "memoryHigh", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 },
{ "memoryMax", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 },
@ -1397,7 +1353,7 @@ static int dispatch_status(const char *name, sd_json_variant *variant, sd_json_d
{ "rateLimitBeginUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, ratelimit_begin_usec), 0 },
{ "rateLimitCount", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, ratelimit_count), 0 },
{ "removable", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(UserRecord, removable), 0 },
{ "accessMode", SD_JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
{ "accessMode", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
{ "fileSystemType", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, file_system_type), SD_JSON_STRICT },
{ "fallbackShell", SD_JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, fallback_shell), 0 },
{ "fallbackHomeDirectory", SD_JSON_VARIANT_STRING, json_dispatch_home_directory, offsetof(UserRecord, fallback_home_directory), 0 },
@ -1633,7 +1589,7 @@ int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load
{ "lastChangeUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, last_change_usec), 0 },
{ "lastPasswordChangeUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, last_password_change_usec), 0 },
{ "shell", SD_JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 },
{ "umask", SD_JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 },
{ "umask", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, umask), SD_JSON_STRICT },
{ "environment", SD_JSON_VARIANT_ARRAY, json_dispatch_strv_environment, offsetof(UserRecord, environment), 0 },
{ "timeZone", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, time_zone), SD_JSON_STRICT },
{ "preferredLanguage", SD_JSON_VARIANT_STRING, json_dispatch_locale, offsetof(UserRecord, preferred_language), 0 },
@ -1647,7 +1603,7 @@ int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load
{ "diskSize", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, disk_size), 0 },
{ "diskSizeRelative", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 },
{ "skeletonDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), SD_JSON_STRICT },
{ "accessMode", SD_JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
{ "accessMode", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
{ "tasksMax", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 },
{ "memoryHigh", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 },
{ "memoryMax", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 },

View File

@ -113,6 +113,8 @@ static SD_VARLINK_DEFINE_METHOD(
SD_VARLINK_DEFINE_INPUT(parentFileDescriptor, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("Name of the directory to create."),
SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Access mode of the directory to create. Note that the suid, sgid, sticky, world-writable bit is unconditionally masked off."),
SD_VARLINK_DEFINE_INPUT(mode, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
VARLINK_DEFINE_POLKIT_INPUT,
SD_VARLINK_FIELD_COMMENT("File descriptor referencing the newly created directory."),
SD_VARLINK_DEFINE_OUTPUT(directoryFileDescriptor, SD_VARLINK_INT, 0));

View File

@ -294,18 +294,17 @@ static int remount_with_timeout(MountPoint *m, bool last_try) {
else if (si.si_code != CLD_EXITED || si.si_status != 0) {
/* Try to read error code from child */
r = read_errno(pfd[0]);
if (r == -EIO)
if (r < 0 && r != -EIO)
log_debug_errno(r,
"Remounting '%s' failed abnormally, child process " PID_FMT " failed: %m",
m->path, pidref.pid);
else
r = log_debug_errno(
SYNTHETIC_ERRNO(EPROTO),
"Remounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.",
m->path, pidref.pid);
else if (r < 0)
log_debug_errno(
r,
"Remounting '%s' failed abnormally, child process " PID_FMT " failed: %m",
m->path, pidref.pid);
TAKE_PIDREF(pidref); /* child exited (just not as we expected) hence don't kill anymore */
pidref_done(&pidref); /* child exited (just not as we expected) hence don't kill anymore */
}
return r;
@ -364,18 +363,17 @@ static int umount_with_timeout(MountPoint *m, bool last_try) {
else if (si.si_code != CLD_EXITED || si.si_status != 0) {
/* Try to read error code from child */
r = read_errno(pfd[0]);
if (r == -EIO)
if (r < 0 && r != -EIO)
log_debug_errno(r,
"Unmounting '%s' failed abnormally, child process " PID_FMT " failed: %m",
m->path, pidref.pid);
else
r = log_debug_errno(
SYNTHETIC_ERRNO(EPROTO),
"Unmounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.",
m->path, pidref.pid);
else if (r < 0)
log_debug_errno(
r,
"Unmounting '%s' failed abnormally, child process " PID_FMT " failed: %m",
m->path, pidref.pid);
TAKE_PIDREF(pidref); /* It died, but abnormally, no purpose in killing */
pidref_done(&pidref); /* It died, but abnormally, no purpose in killing */
}
return r;

View File

@ -1477,4 +1477,50 @@ TEST(unit_name) {
&data), EINVAL);
}
TEST(access_mode) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
ASSERT_OK(sd_json_parse("{"
" \"a\" : \"0755\", "
" \"b\" : 448, "
" \"c\" : null, "
" \"d\" : \"01755\" "
"}",
/* flags= */ 0,
&v,
/* reterr_line= */ NULL,
/* reterr_column= */ NULL));
struct {
mode_t a, b, c, d;
} mm = { 1, 2, 3, 4 };
ASSERT_OK(sd_json_dispatch(
v,
(const sd_json_dispatch_field[]) {
{ "a", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, a), 0 },
{ "b", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, b), 0 },
{ "c", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, c), 0 },
{ "d", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, d), 0 },
{},
},
/* flags= */ 0,
&mm));
ASSERT_EQ(mm.a, (mode_t) 0755);
ASSERT_EQ(mm.b, (mode_t) 0700);
ASSERT_EQ(mm.c, MODE_INVALID);
ASSERT_EQ(mm.d, (mode_t) 01755);
/* retry with SD_JSON_STRICT, where 'd' should not parse anymore */
ASSERT_ERROR(sd_json_dispatch(
v,
(const sd_json_dispatch_field[]) {
{ "d", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, d), SD_JSON_STRICT },
{},
},
/* flags= */ SD_JSON_ALLOW_EXTENSIONS,
&mm), ERANGE);
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -1085,7 +1085,7 @@ static int on_child_exit(sd_event_source *s, const siginfo_t *si, void *userdata
si->si_pid, signal_to_string(si->si_status));
else
ret = log_error_errno(SYNTHETIC_ERRNO(EPROTO),
"Got unexpected exit code %i from child,",
"Got unexpected exit code %i from child.",
si->si_code);
/* Regardless of whether the main qemu process or an auxiliary process died, let's exit either way