mirror of
https://github.com/systemd/systemd.git
synced 2025-03-11 20:58:27 +03:00
Merge pull request #29339 from bluca/mount_namespace_new_api
Use new mount API for bind/image mount tunnel
This commit is contained in:
commit
df3e378a5d
@ -674,6 +674,21 @@ bool mount_propagation_flag_is_valid(unsigned long flag) {
|
||||
return IN_SET(flag, 0, MS_SHARED, MS_PRIVATE, MS_SLAVE);
|
||||
}
|
||||
|
||||
bool mount_new_api_supported(void) {
|
||||
static int cache = -1;
|
||||
int r;
|
||||
|
||||
if (cache >= 0)
|
||||
return cache;
|
||||
|
||||
/* This is the newer API among the ones we use, so use it as boundary */
|
||||
r = RET_NERRNO(mount_setattr(-EBADF, NULL, 0, NULL, 0));
|
||||
if (r == 0 || ERRNO_IS_NOT_SUPPORTED(r)) /* This should return an error if it is working properly */
|
||||
return (cache = false);
|
||||
|
||||
return (cache = true);
|
||||
}
|
||||
|
||||
unsigned long ms_nosymfollow_supported(void) {
|
||||
_cleanup_close_ int fsfd = -EBADF, mntfd = -EBADF;
|
||||
static int cache = -1;
|
||||
@ -683,6 +698,9 @@ unsigned long ms_nosymfollow_supported(void) {
|
||||
if (cache >= 0)
|
||||
return cache ? MS_NOSYMFOLLOW : 0;
|
||||
|
||||
if (!mount_new_api_supported())
|
||||
goto not_supported;
|
||||
|
||||
/* Checks if MS_NOSYMFOLLOW is supported (which was added in 5.10). We use the new mount API's
|
||||
* mount_setattr() call for that, which was added in 5.12, which is close enough. */
|
||||
|
||||
|
@ -65,6 +65,7 @@ const char *mount_propagation_flag_to_string(unsigned long flags);
|
||||
int mount_propagation_flag_from_string(const char *name, unsigned long *ret);
|
||||
bool mount_propagation_flag_is_valid(unsigned long flag);
|
||||
|
||||
bool mount_new_api_supported(void);
|
||||
unsigned long ms_nosymfollow_supported(void);
|
||||
|
||||
int mount_option_supported(const char *fstype, const char *key, const char *value);
|
||||
|
@ -1311,7 +1311,8 @@ static int mount_image(
|
||||
host_os_release_id,
|
||||
host_os_release_version_id,
|
||||
host_os_release_level,
|
||||
NULL);
|
||||
/* required_sysext_scope= */ NULL,
|
||||
/* ret_image= */ NULL);
|
||||
if (r == -ENOENT && m->ignore)
|
||||
return 0;
|
||||
if (r == -ESTALE && host_os_release_id)
|
||||
@ -2494,7 +2495,13 @@ int setup_namespace(
|
||||
|
||||
if (root_image) {
|
||||
/* A root image is specified, mount it to the right place */
|
||||
r = dissected_image_mount(dissected_image, root, UID_INVALID, UID_INVALID, dissect_image_flags);
|
||||
r = dissected_image_mount(
|
||||
dissected_image,
|
||||
root,
|
||||
/* uid_shift= */ UID_INVALID,
|
||||
/* uid_range= */ UID_INVALID,
|
||||
/* userns_fd= */ -EBADF,
|
||||
dissect_image_flags);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to mount root image: %m");
|
||||
goto finish;
|
||||
|
@ -1063,7 +1063,13 @@ static int action_mount(DissectedImage *m, LoopDevice *d) {
|
||||
assert(d);
|
||||
assert(arg_action == ACTION_MOUNT);
|
||||
|
||||
r = dissected_image_mount_and_warn(m, arg_path, UID_INVALID, UID_INVALID, arg_flags);
|
||||
r = dissected_image_mount_and_warn(
|
||||
m,
|
||||
arg_path,
|
||||
/* uid_shift= */ UID_INVALID,
|
||||
/* uid_range= */ UID_INVALID,
|
||||
/* userns_fd= */ -EBADF,
|
||||
arg_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1298,7 +1304,13 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
|
||||
|
||||
created_dir = TAKE_PTR(temp);
|
||||
|
||||
r = dissected_image_mount_and_warn(m, created_dir, UID_INVALID, UID_INVALID, arg_flags);
|
||||
r = dissected_image_mount_and_warn(
|
||||
m,
|
||||
created_dir,
|
||||
/* uid_shift= */ UID_INVALID,
|
||||
/* uid_range= */ UID_INVALID,
|
||||
/* userns_fd= */ -EBADF,
|
||||
arg_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1549,7 +1561,13 @@ static int action_with(DissectedImage *m, LoopDevice *d) {
|
||||
|
||||
created_dir = TAKE_PTR(temp);
|
||||
|
||||
r = dissected_image_mount_and_warn(m, created_dir, UID_INVALID, UID_INVALID, arg_flags);
|
||||
r = dissected_image_mount_and_warn(
|
||||
m,
|
||||
created_dir,
|
||||
/* uid_shift= */ UID_INVALID,
|
||||
/* uid_range= */ UID_INVALID,
|
||||
/* userns_fd= */ -EBADF,
|
||||
arg_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -3681,6 +3681,7 @@ static int outer_child(
|
||||
directory,
|
||||
arg_uid_shift,
|
||||
arg_uid_range,
|
||||
/* userns_fd= */ -EBADF,
|
||||
DISSECT_IMAGE_MOUNT_ROOT_ONLY|
|
||||
DISSECT_IMAGE_DISCARD_ON_LOOP|
|
||||
DISSECT_IMAGE_USR_NO_ROOT|
|
||||
@ -3845,6 +3846,7 @@ static int outer_child(
|
||||
directory,
|
||||
arg_uid_shift,
|
||||
arg_uid_range,
|
||||
/* userns_fd= */ -EBADF,
|
||||
DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|
|
||||
DISSECT_IMAGE_DISCARD_ON_LOOP|
|
||||
DISSECT_IMAGE_USR_NO_ROOT|
|
||||
|
@ -424,7 +424,13 @@ static int portable_extract_by_path(
|
||||
else
|
||||
flags |= DISSECT_IMAGE_VALIDATE_OS;
|
||||
|
||||
r = dissected_image_mount(m, tmpdir, UID_INVALID, UID_INVALID, flags);
|
||||
r = dissected_image_mount(
|
||||
m,
|
||||
tmpdir,
|
||||
/* uid_shift= */ UID_INVALID,
|
||||
/* uid_range= */ UID_INVALID,
|
||||
/* userns_fd= */ -EBADF,
|
||||
flags);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to mount dissected image: %m");
|
||||
goto child_finish;
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "import-util.h"
|
||||
#include "io-util.h"
|
||||
#include "missing_mount.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "mkdir-label.h"
|
||||
#include "mount-util.h"
|
||||
#include "mountpoint-util.h"
|
||||
@ -515,6 +516,7 @@ static void dissected_partition_done(DissectedPartition *p) {
|
||||
free(p->decrypted_node);
|
||||
free(p->mount_options);
|
||||
safe_close(p->mount_node_fd);
|
||||
safe_close(p->fsmount_fd);
|
||||
|
||||
*p = DISSECTED_PARTITION_NULL;
|
||||
}
|
||||
@ -836,6 +838,7 @@ static int dissect_image(
|
||||
.mount_node_fd = TAKE_FD(mount_node_fd),
|
||||
.offset = 0,
|
||||
.size = UINT64_MAX,
|
||||
.fsmount_fd = -EBADF,
|
||||
};
|
||||
|
||||
return 0;
|
||||
@ -1252,6 +1255,7 @@ static int dissect_image(
|
||||
.offset = (uint64_t) start * 512,
|
||||
.size = (uint64_t) size * 512,
|
||||
.gpt_flags = pflags,
|
||||
.fsmount_fd = -EBADF,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1326,6 +1330,7 @@ static int dissect_image(
|
||||
.mount_node_fd = TAKE_FD(mount_node_fd),
|
||||
.offset = (uint64_t) start * 512,
|
||||
.size = (uint64_t) size * 512,
|
||||
.fsmount_fd = -EBADF,
|
||||
};
|
||||
|
||||
break;
|
||||
@ -1416,6 +1421,7 @@ static int dissect_image(
|
||||
.mount_node_fd = TAKE_FD(mount_node_fd),
|
||||
.offset = UINT64_MAX,
|
||||
.size = UINT64_MAX,
|
||||
.fsmount_fd = -EBADF,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1739,11 +1745,15 @@ static int run_fsck(int node_fd, const char *fstype) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fs_grow(const char *node_path, const char *mount_path) {
|
||||
_cleanup_close_ int mount_fd = -EBADF, node_fd = -EBADF;
|
||||
static int fs_grow(const char *node_path, int mount_fd, const char *mount_path) {
|
||||
_cleanup_close_ int _mount_fd = -EBADF, node_fd = -EBADF;
|
||||
uint64_t size, newsize;
|
||||
const char *id;
|
||||
int r;
|
||||
|
||||
assert(node_path);
|
||||
assert(mount_fd >= 0 || mount_path);
|
||||
|
||||
node_fd = open(node_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
||||
if (node_fd < 0)
|
||||
return log_debug_errno(errno, "Failed to open node device %s: %m", node_path);
|
||||
@ -1751,22 +1761,34 @@ static int fs_grow(const char *node_path, const char *mount_path) {
|
||||
if (ioctl(node_fd, BLKGETSIZE64, &size) != 0)
|
||||
return log_debug_errno(errno, "Failed to get block device size of %s: %m", node_path);
|
||||
|
||||
mount_fd = open(mount_path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (mount_fd < 0)
|
||||
return log_debug_errno(errno, "Failed to open mountd file system %s: %m", mount_path);
|
||||
if (mount_fd < 0) {
|
||||
assert(mount_path);
|
||||
|
||||
log_debug("Resizing \"%s\" to %"PRIu64" bytes...", mount_path, size);
|
||||
_mount_fd = open(mount_path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (_mount_fd < 0)
|
||||
return log_debug_errno(errno, "Failed to open mounted file system %s: %m", mount_path);
|
||||
|
||||
mount_fd = _mount_fd;
|
||||
} else {
|
||||
mount_fd = fd_reopen_condition(mount_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC, O_RDONLY|O_DIRECTORY|O_CLOEXEC, &_mount_fd);
|
||||
if (mount_fd < 0)
|
||||
return log_debug_errno(errno, "Failed to reopen mount node: %m");
|
||||
}
|
||||
|
||||
id = mount_path ?: node_path;
|
||||
|
||||
log_debug("Resizing \"%s\" to %"PRIu64" bytes...", id, size);
|
||||
r = resize_fs(mount_fd, size, &newsize);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to resize \"%s\" to %"PRIu64" bytes: %m", mount_path, size);
|
||||
return log_debug_errno(r, "Failed to resize \"%s\" to %"PRIu64" bytes: %m", id, size);
|
||||
|
||||
if (newsize == size)
|
||||
log_debug("Successfully resized \"%s\" to %s bytes.",
|
||||
mount_path, FORMAT_BYTES(newsize));
|
||||
id, FORMAT_BYTES(newsize));
|
||||
else {
|
||||
assert(newsize < size);
|
||||
log_debug("Successfully resized \"%s\" to %s bytes (%"PRIu64" bytes lost due to blocksize).",
|
||||
mount_path, FORMAT_BYTES(newsize), size - newsize);
|
||||
id, FORMAT_BYTES(newsize), size - newsize);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1863,6 +1885,14 @@ int partition_pick_mount_options(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool need_user_mapping(uid_t uid_shift, uid_t uid_range) {
|
||||
|
||||
if (!uid_is_valid(uid_shift))
|
||||
return false;
|
||||
|
||||
return uid_shift != 0 || uid_range != UINT32_MAX;
|
||||
}
|
||||
|
||||
static int mount_partition(
|
||||
PartitionDesignator d,
|
||||
DissectedPartition *m,
|
||||
@ -1870,81 +1900,92 @@ static int mount_partition(
|
||||
const char *directory,
|
||||
uid_t uid_shift,
|
||||
uid_t uid_range,
|
||||
int userns_fd,
|
||||
DissectImageFlags flags) {
|
||||
|
||||
_cleanup_free_ char *chased = NULL, *options = NULL;
|
||||
bool rw, discard, remap_uid_gid = false;
|
||||
const char *p, *node, *fstype;
|
||||
const char *p = NULL, *node, *fstype = NULL;
|
||||
bool rw, discard, grow;
|
||||
unsigned long ms_flags;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(where);
|
||||
|
||||
if (m->mount_node_fd < 0)
|
||||
if (!m->found)
|
||||
return 0;
|
||||
|
||||
/* Use decrypted node and matching fstype if available, otherwise use the original device */
|
||||
node = FORMAT_PROC_FD_PATH(m->mount_node_fd);
|
||||
fstype = dissected_partition_fstype(m);
|
||||
/* Check the various combinations when we can't do anything anymore */
|
||||
if (m->fsmount_fd < 0 && m->mount_node_fd < 0)
|
||||
return 0;
|
||||
if (m->fsmount_fd >= 0 && !where)
|
||||
return 0;
|
||||
if (!where && m->mount_node_fd < 0)
|
||||
return 0;
|
||||
|
||||
if (!fstype)
|
||||
return -EAFNOSUPPORT;
|
||||
if (m->fsmount_fd < 0) {
|
||||
fstype = dissected_partition_fstype(m);
|
||||
if (!fstype)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
/* We are looking at an encrypted partition? This either means stacked encryption, or the caller
|
||||
* didn't call dissected_image_decrypt() beforehand. Let's return a recognizable error for this
|
||||
* case. */
|
||||
if (streq(fstype, "crypto_LUKS"))
|
||||
return -EUNATCH;
|
||||
/* We are looking at an encrypted partition? This either means stacked encryption, or the
|
||||
* caller didn't call dissected_image_decrypt() beforehand. Let's return a recognizable error
|
||||
* for this case. */
|
||||
if (streq(fstype, "crypto_LUKS"))
|
||||
return -EUNATCH;
|
||||
|
||||
r = dissect_fstype_ok(fstype);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
return -EIDRM; /* Recognizable error */
|
||||
r = dissect_fstype_ok(fstype);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
return -EIDRM; /* Recognizable error */
|
||||
}
|
||||
|
||||
node = m->mount_node_fd < 0 ? NULL : FORMAT_PROC_FD_PATH(m->mount_node_fd);
|
||||
rw = m->rw && !(flags & DISSECT_IMAGE_MOUNT_READ_ONLY);
|
||||
|
||||
discard = ((flags & DISSECT_IMAGE_DISCARD) ||
|
||||
((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node) > 0));
|
||||
((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && (m->node && is_loop_device(m->node) > 0)));
|
||||
|
||||
if (FLAGS_SET(flags, DISSECT_IMAGE_FSCK) && rw) {
|
||||
grow = rw && m->growfs && FLAGS_SET(flags, DISSECT_IMAGE_GROWFS);
|
||||
|
||||
if (FLAGS_SET(flags, DISSECT_IMAGE_FSCK) && rw && m->mount_node_fd >= 0 && m->fsmount_fd < 0) {
|
||||
r = run_fsck(m->mount_node_fd, fstype);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (directory) {
|
||||
/* Automatically create missing mount points inside the image, if necessary. */
|
||||
r = mkdir_p_root(where, directory, uid_shift, (gid_t) uid_shift, 0755, NULL);
|
||||
if (r < 0 && r != -EROFS)
|
||||
return r;
|
||||
if (where) {
|
||||
if (directory) {
|
||||
/* Automatically create missing mount points inside the image, if necessary. */
|
||||
r = mkdir_p_root(where, directory, uid_shift, (gid_t) uid_shift, 0755, NULL);
|
||||
if (r < 0 && r != -EROFS)
|
||||
return r;
|
||||
|
||||
r = chase(directory, where, CHASE_PREFIX_ROOT, &chased, NULL);
|
||||
r = chase(directory, where, CHASE_PREFIX_ROOT, &chased, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = chased;
|
||||
} else {
|
||||
/* Create top-level mount if missing – but only if this is asked for. This won't modify the
|
||||
* image (as the branch above does) but the host hierarchy, and the created directory might
|
||||
* survive our mount in the host hierarchy hence. */
|
||||
if (FLAGS_SET(flags, DISSECT_IMAGE_MKDIR)) {
|
||||
r = mkdir_p(where, 0755);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
p = where;
|
||||
}
|
||||
}
|
||||
|
||||
if (m->fsmount_fd < 0) {
|
||||
r = partition_pick_mount_options(d, fstype, rw, discard, &options, &ms_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = chased;
|
||||
} else {
|
||||
/* Create top-level mount if missing – but only if this is asked for. This won't modify the
|
||||
* image (as the branch above does) but the host hierarchy, and the created directory might
|
||||
* survive our mount in the host hierarchy hence. */
|
||||
if (FLAGS_SET(flags, DISSECT_IMAGE_MKDIR)) {
|
||||
r = mkdir_p(where, 0755);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
p = where;
|
||||
}
|
||||
|
||||
r = partition_pick_mount_options(d, dissected_partition_fstype(m), rw, discard, &options, &ms_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (uid_is_valid(uid_shift) && uid_shift != 0) {
|
||||
|
||||
if (fstype_can_uid_gid(fstype)) {
|
||||
if (need_user_mapping(uid_shift, uid_range) && fstype_can_uid_gid(fstype)) {
|
||||
_cleanup_free_ char *uid_option = NULL;
|
||||
|
||||
if (asprintf(&uid_option, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
|
||||
@ -1952,31 +1993,56 @@ static int mount_partition(
|
||||
|
||||
if (!strextend_with_separator(&options, ",", uid_option))
|
||||
return -ENOMEM;
|
||||
} else if (FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_IDMAPPED))
|
||||
remap_uid_gid = true;
|
||||
|
||||
userns_fd = -EBADF; /* Not needed */
|
||||
}
|
||||
|
||||
if (!isempty(m->mount_options))
|
||||
if (!strextend_with_separator(&options, ",", m->mount_options))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!isempty(m->mount_options))
|
||||
if (!strextend_with_separator(&options, ",", m->mount_options))
|
||||
return -ENOMEM;
|
||||
if (p) {
|
||||
if (m->fsmount_fd >= 0) {
|
||||
/* Case #1: Attach existing fsmount fd to the file system */
|
||||
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, node, p, fstype, ms_flags, options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (move_mount(m->fsmount_fd, "", -EBADF, p, MOVE_MOUNT_F_EMPTY_PATH) < 0)
|
||||
return -errno;
|
||||
|
||||
if (rw && m->growfs && FLAGS_SET(flags, DISSECT_IMAGE_GROWFS))
|
||||
(void) fs_grow(node, p);
|
||||
} else {
|
||||
assert(node);
|
||||
|
||||
if (remap_uid_gid) {
|
||||
r = remount_idmap(p, uid_shift, uid_range, UID_INVALID, REMOUNT_IDMAPPING_HOST_ROOT);
|
||||
if (r < 0)
|
||||
return r;
|
||||
/* Case #2: Mount directly into place */
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, node, p, fstype, ms_flags, options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (grow)
|
||||
(void) fs_grow(node, -EBADF, p);
|
||||
|
||||
if (userns_fd >= 0) {
|
||||
r = remount_idmap_fd(p, userns_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert(node);
|
||||
|
||||
/* Case #3: Create fsmount fd */
|
||||
|
||||
m->fsmount_fd = make_fsmount(LOG_DEBUG, node, fstype, ms_flags, options, userns_fd);
|
||||
if (m->fsmount_fd < 0)
|
||||
return m->fsmount_fd;
|
||||
|
||||
if (grow)
|
||||
(void) fs_grow(node, m->fsmount_fd, NULL);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mount_root_tmpfs(const char *where, uid_t uid_shift, DissectImageFlags flags) {
|
||||
static int mount_root_tmpfs(const char *where, uid_t uid_shift, uid_t uid_range, DissectImageFlags flags) {
|
||||
_cleanup_free_ char *options = NULL;
|
||||
int r;
|
||||
|
||||
@ -1990,7 +2056,7 @@ static int mount_root_tmpfs(const char *where, uid_t uid_shift, DissectImageFlag
|
||||
return r;
|
||||
}
|
||||
|
||||
if (uid_is_valid(uid_shift)) {
|
||||
if (need_user_mapping(uid_shift, uid_range)) {
|
||||
if (asprintf(&options, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
@ -2028,14 +2094,25 @@ int dissected_image_mount(
|
||||
const char *where,
|
||||
uid_t uid_shift,
|
||||
uid_t uid_range,
|
||||
int userns_fd,
|
||||
DissectImageFlags flags) {
|
||||
|
||||
_cleanup_close_ int my_userns_fd = -EBADF;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(where);
|
||||
|
||||
/* Returns:
|
||||
/* If 'where' is NULL then we'll use the new mount API to create fsmount() fds for the mounts and
|
||||
* store them in DissectedPartition.fsmount_fd.
|
||||
*
|
||||
* If 'where' is not NULL then we'll either mount the partitions to the right places ourselves,
|
||||
* or use DissectedPartition.fsmount_fd and bind it to the right places.
|
||||
*
|
||||
* This allows splitting the setting up up the superblocks and the binding to file systems paths into
|
||||
* two distinct and differently privileged components: one that gets the fsmount fds, and the other
|
||||
* that then applies them.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* -ENXIO → No root partition found
|
||||
* -EMEDIUMTYPE → DISSECT_IMAGE_VALIDATE_OS set but no os-release/extension-release file found
|
||||
@ -2046,82 +2123,103 @@ int dissected_image_mount(
|
||||
* -EIDRM → File system is not among allowlisted "common" file systems
|
||||
*/
|
||||
|
||||
if (!where && (flags & (DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_VALIDATE_OS_EXT)) != 0)
|
||||
return -EOPNOTSUPP; /* for now, not supported */
|
||||
|
||||
if (!(m->partitions[PARTITION_ROOT].found ||
|
||||
(m->partitions[PARTITION_USR].found && FLAGS_SET(flags, DISSECT_IMAGE_USR_NO_ROOT))))
|
||||
return -ENXIO; /* Require a root fs or at least a /usr/ fs (the latter is subject to a flag of its own) */
|
||||
|
||||
if (userns_fd < 0 && need_user_mapping(uid_shift, uid_range) && FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_IDMAPPED)) {
|
||||
|
||||
my_userns_fd = make_userns(uid_shift, uid_range, UID_INVALID, REMOUNT_IDMAPPING_HOST_ROOT);
|
||||
if (my_userns_fd < 0)
|
||||
return my_userns_fd;
|
||||
|
||||
userns_fd = my_userns_fd;
|
||||
}
|
||||
|
||||
if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
|
||||
|
||||
/* First mount the root fs. If there's none we use a tmpfs. */
|
||||
if (m->partitions[PARTITION_ROOT].found)
|
||||
r = mount_partition(PARTITION_ROOT, m->partitions + PARTITION_ROOT, where, NULL, uid_shift, uid_range, flags);
|
||||
else
|
||||
r = mount_root_tmpfs(where, uid_shift, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (m->partitions[PARTITION_ROOT].found) {
|
||||
r = mount_partition(PARTITION_ROOT, m->partitions + PARTITION_ROOT, where, NULL, uid_shift, uid_range, userns_fd, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
} else if (where) {
|
||||
r = mount_root_tmpfs(where, uid_shift, uid_range, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* For us mounting root always means mounting /usr as well */
|
||||
r = mount_partition(PARTITION_USR, m->partitions + PARTITION_USR, where, "/usr", uid_shift, uid_range, flags);
|
||||
r = mount_partition(PARTITION_USR, m->partitions + PARTITION_USR, where, "/usr", uid_shift, uid_range, userns_fd, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((flags & (DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_VALIDATE_OS_EXT)) != 0) {
|
||||
/* If either one of the validation flags are set, ensure that the image qualifies
|
||||
* as one or the other (or both). */
|
||||
bool ok = false;
|
||||
if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0 &&
|
||||
(flags & (DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_VALIDATE_OS_EXT)) != 0) {
|
||||
/* If either one of the validation flags are set, ensure that the image qualifies as
|
||||
* one or the other (or both). */
|
||||
bool ok = false;
|
||||
|
||||
if (FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS)) {
|
||||
r = path_is_os_tree(where);
|
||||
assert(where);
|
||||
|
||||
if (FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS)) {
|
||||
r = path_is_os_tree(where);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
ok = true;
|
||||
}
|
||||
if (!ok && FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS_EXT)) {
|
||||
r = extension_has_forbidden_content(where);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
r = path_is_extension_tree(IMAGE_SYSEXT, where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_EXTENSION_CHECK));
|
||||
if (r == 0)
|
||||
r = path_is_extension_tree(IMAGE_CONFEXT, where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_EXTENSION_CHECK));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
ok = true;
|
||||
}
|
||||
if (!ok && FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS_EXT)) {
|
||||
r = extension_has_forbidden_content(where);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
r = path_is_extension_tree(IMAGE_SYSEXT, where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_EXTENSION_CHECK));
|
||||
if (r == 0)
|
||||
r = path_is_extension_tree(IMAGE_CONFEXT, where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_EXTENSION_CHECK));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
if (flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)
|
||||
return 0;
|
||||
|
||||
r = mount_partition(PARTITION_HOME, m->partitions + PARTITION_HOME, where, "/home", uid_shift, uid_range, flags);
|
||||
r = mount_partition(PARTITION_HOME, m->partitions + PARTITION_HOME, where, "/home", uid_shift, uid_range, userns_fd, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mount_partition(PARTITION_SRV, m->partitions + PARTITION_SRV, where, "/srv", uid_shift, uid_range, flags);
|
||||
r = mount_partition(PARTITION_SRV, m->partitions + PARTITION_SRV, where, "/srv", uid_shift, uid_range, userns_fd, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mount_partition(PARTITION_VAR, m->partitions + PARTITION_VAR, where, "/var", uid_shift, uid_range, flags);
|
||||
r = mount_partition(PARTITION_VAR, m->partitions + PARTITION_VAR, where, "/var", uid_shift, uid_range, userns_fd, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mount_partition(PARTITION_TMP, m->partitions + PARTITION_TMP, where, "/var/tmp", uid_shift, uid_range, flags);
|
||||
r = mount_partition(PARTITION_TMP, m->partitions + PARTITION_TMP, where, "/var/tmp", uid_shift, uid_range, userns_fd, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
int slash_boot_is_available;
|
||||
r = slash_boot_is_available = mount_point_is_available(where, "/boot", /* missing_ok = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
r = mount_partition(PARTITION_XBOOTLDR, m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, uid_range, flags);
|
||||
int slash_boot_is_available = 0;
|
||||
if (where) {
|
||||
r = slash_boot_is_available = mount_point_is_available(where, "/boot", /* missing_ok = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
if (!where || slash_boot_is_available) {
|
||||
r = mount_partition(PARTITION_XBOOTLDR, m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, uid_range, userns_fd, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
slash_boot_is_available = !r;
|
||||
@ -2130,31 +2228,32 @@ int dissected_image_mount(
|
||||
if (m->partitions[PARTITION_ESP].found) {
|
||||
const char *esp_path = NULL;
|
||||
|
||||
/* Mount the ESP to /boot/ if it exists and is empty and we didn't already mount the XBOOTLDR
|
||||
* partition into it. Otherwise, use /efi instead, but only if it exists and is empty. */
|
||||
if (where) {
|
||||
/* Mount the ESP to /boot/ if it exists and is empty and we didn't already mount the
|
||||
* XBOOTLDR partition into it. Otherwise, use /efi instead, but only if it exists
|
||||
* and is empty. */
|
||||
|
||||
if (slash_boot_is_available) {
|
||||
r = mount_point_is_available(where, "/boot", /* missing_ok = */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
esp_path = "/boot";
|
||||
if (slash_boot_is_available) {
|
||||
r = mount_point_is_available(where, "/boot", /* missing_ok = */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
esp_path = "/boot";
|
||||
}
|
||||
|
||||
if (!esp_path) {
|
||||
r = mount_point_is_available(where, "/efi", /* missing_ok = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
esp_path = "/efi";
|
||||
}
|
||||
}
|
||||
|
||||
if (!esp_path) {
|
||||
r = mount_point_is_available(where, "/efi", /* missing_ok = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
esp_path = "/efi";
|
||||
}
|
||||
|
||||
if (esp_path) {
|
||||
/* OK, let's mount the ESP now (possibly creating the dir if missing) */
|
||||
r = mount_partition(PARTITION_ESP, m->partitions + PARTITION_ESP, where, esp_path, uid_shift, uid_range, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
/* OK, let's mount the ESP now (possibly creating the dir if missing) */
|
||||
r = mount_partition(PARTITION_ESP, m->partitions + PARTITION_ESP, where, esp_path, uid_shift, uid_range, userns_fd, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -2165,14 +2264,14 @@ int dissected_image_mount_and_warn(
|
||||
const char *where,
|
||||
uid_t uid_shift,
|
||||
uid_t uid_range,
|
||||
int userns_fd,
|
||||
DissectImageFlags flags) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(where);
|
||||
|
||||
r = dissected_image_mount(m, where, uid_shift, uid_range, flags);
|
||||
r = dissected_image_mount(m, where, uid_shift, uid_range, userns_fd, flags);
|
||||
if (r == -ENXIO)
|
||||
return log_error_errno(r, "Not root file system found in image.");
|
||||
if (r == -EMEDIUMTYPE)
|
||||
@ -3315,8 +3414,9 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_
|
||||
r = dissected_image_mount(
|
||||
m,
|
||||
t,
|
||||
UID_INVALID,
|
||||
UID_INVALID,
|
||||
/* uid_shift= */ UID_INVALID,
|
||||
/* uid_range= */ UID_INVALID,
|
||||
/* userns_fd= */ -EBADF,
|
||||
extra_flags |
|
||||
DISSECT_IMAGE_READ_ONLY |
|
||||
DISSECT_IMAGE_MOUNT_ROOT_ONLY |
|
||||
@ -3755,6 +3855,7 @@ int mount_image_privately_interactively(
|
||||
"/run/systemd/mount-rootfs",
|
||||
/* uid_shift= */ UID_INVALID,
|
||||
/* uid_range= */ UID_INVALID,
|
||||
/* userns_fd= */ -EBADF,
|
||||
flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -3808,7 +3909,8 @@ int verity_dissect_and_mount(
|
||||
const char *required_host_os_release_id,
|
||||
const char *required_host_os_release_version_id,
|
||||
const char *required_host_os_release_sysext_level,
|
||||
const char *required_sysext_scope) {
|
||||
const char *required_sysext_scope,
|
||||
DissectedImage **ret_image) {
|
||||
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
|
||||
@ -3818,7 +3920,9 @@ int verity_dissect_and_mount(
|
||||
int r;
|
||||
|
||||
assert(src);
|
||||
assert(dest);
|
||||
/* Verifying release metadata requires mounted image for now, so ensure the check is skipped when
|
||||
* opening an image without mounting it immediately (i.e.: 'dest' is NULL). */
|
||||
assert(!required_host_os_release_id || dest);
|
||||
|
||||
relax_extension_release_check = mount_options_relax_extension_release_checks(options);
|
||||
|
||||
@ -3875,14 +3979,22 @@ int verity_dissect_and_mount(
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to decrypt dissected image: %m");
|
||||
|
||||
r = mkdir_p_label(dest, 0755);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create destination directory %s: %m", dest);
|
||||
r = umount_recursive(dest, 0);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to umount under destination directory %s: %m", dest);
|
||||
if (dest) {
|
||||
r = mkdir_p_label(dest, 0755);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create destination directory %s: %m", dest);
|
||||
r = umount_recursive(dest, 0);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to umount under destination directory %s: %m", dest);
|
||||
}
|
||||
|
||||
r = dissected_image_mount(dissected_image, dest, UID_INVALID, UID_INVALID, dissect_image_flags);
|
||||
r = dissected_image_mount(
|
||||
dissected_image,
|
||||
dest,
|
||||
/* uid_shift= */ UID_INVALID,
|
||||
/* uid_range= */ UID_INVALID,
|
||||
/* userns_fd= */ -EBADF,
|
||||
dissect_image_flags);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to mount image: %m");
|
||||
|
||||
@ -3928,5 +4040,8 @@ int verity_dissect_and_mount(
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to relinquish dissected image: %m");
|
||||
|
||||
if (ret_image)
|
||||
*ret_image = TAKE_PTR(dissected_image);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ struct DissectedPartition {
|
||||
uint64_t size;
|
||||
uint64_t offset;
|
||||
uint64_t gpt_flags;
|
||||
int fsmount_fd;
|
||||
};
|
||||
|
||||
#define DISSECTED_PARTITION_NULL \
|
||||
@ -43,6 +44,7 @@ struct DissectedPartition {
|
||||
.partno = -1, \
|
||||
.architecture = _ARCHITECTURE_INVALID, \
|
||||
.mount_node_fd = -EBADF, \
|
||||
.fsmount_fd = -EBADF, \
|
||||
})
|
||||
#define TAKE_PARTITION(p) \
|
||||
({ \
|
||||
@ -160,8 +162,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
|
||||
|
||||
int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags);
|
||||
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags);
|
||||
int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, uid_t uid_range, DissectImageFlags flags);
|
||||
int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, uid_t uid_range, DissectImageFlags flags);
|
||||
int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, uid_t uid_range, int userns_fd, DissectImageFlags flags);
|
||||
int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, uid_t uid_range, int userns_fd, DissectImageFlags flags);
|
||||
|
||||
int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_flags);
|
||||
|
||||
@ -192,7 +194,7 @@ bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesi
|
||||
|
||||
int mount_image_privately_interactively(const char *path, const ImagePolicy *image_policy, DissectImageFlags flags, char **ret_directory, int *ret_dir_fd, LoopDevice **ret_loop_device);
|
||||
|
||||
int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const ImagePolicy *image_policy, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_sysext_scope);
|
||||
int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const ImagePolicy *image_policy, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_sysext_scope, DissectedImage **ret_image);
|
||||
|
||||
int dissect_fstype_ok(const char *fstype);
|
||||
|
||||
|
@ -797,12 +797,16 @@ int mount_option_mangle(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mount_in_namespace(
|
||||
pid_t target,
|
||||
static int mount_in_namespace_legacy(
|
||||
const char *chased_src_path,
|
||||
int chased_src_fd,
|
||||
struct stat *chased_src_st,
|
||||
const char *propagate_path,
|
||||
const char *incoming_path,
|
||||
const char *src,
|
||||
const char *dest,
|
||||
int pidns_fd,
|
||||
int mntns_fd,
|
||||
int root_fd,
|
||||
bool read_only,
|
||||
bool make_file_or_directory,
|
||||
const MountOptions *options,
|
||||
@ -810,52 +814,29 @@ static int mount_in_namespace(
|
||||
bool is_image) {
|
||||
|
||||
_cleanup_close_pair_ int errno_pipe_fd[2] = PIPE_EBADF;
|
||||
_cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF, pidns_fd = -EBADF, chased_src_fd = -EBADF;
|
||||
char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
|
||||
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_path = NULL;
|
||||
struct stat st;
|
||||
pid_t child;
|
||||
int r;
|
||||
|
||||
assert(target > 0);
|
||||
assert(chased_src_path);
|
||||
assert(chased_src_fd >= 0);
|
||||
assert(chased_src_st);
|
||||
assert(propagate_path);
|
||||
assert(incoming_path);
|
||||
assert(src);
|
||||
assert(dest);
|
||||
assert(pidns_fd >= 0);
|
||||
assert(mntns_fd >= 0);
|
||||
assert(root_fd >= 0);
|
||||
assert(!options || is_image);
|
||||
|
||||
r = namespace_open(target, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to retrieve FDs of the target process' namespace: %m");
|
||||
|
||||
r = in_same_namespace(target, 0, NAMESPACE_MOUNT);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to determine if mount namespaces are equal: %m");
|
||||
/* We can't add new mounts at runtime if the process wasn't started in a namespace */
|
||||
if (r > 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to activate bind mount in target, not running in a mount namespace");
|
||||
|
||||
/* One day, when bind mounting /proc/self/fd/n works across namespace boundaries we should rework
|
||||
* this logic to make use of it... */
|
||||
|
||||
p = strjoina(propagate_path, "/");
|
||||
r = laccess(p, F_OK);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r == -ENOENT ? SYNTHETIC_ERRNO(EOPNOTSUPP) : r, "Target does not allow propagation of mount points");
|
||||
|
||||
r = chase(src, NULL, 0, &chased_src_path, &chased_src_fd);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to resolve source path of %s: %m", src);
|
||||
log_debug("Chased source path of %s to %s", src, chased_src_path);
|
||||
|
||||
if (fstat(chased_src_fd, &st) < 0)
|
||||
return log_debug_errno(errno, "Failed to stat() resolved source path %s: %m", src);
|
||||
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 log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Source directory %s can't be a symbolic link", src);
|
||||
|
||||
/* Our goal is to install a new bind mount into the container,
|
||||
possibly read-only. This is irritatingly complex
|
||||
unfortunately, currently.
|
||||
@ -885,7 +866,7 @@ static int mount_in_namespace(
|
||||
if (is_image)
|
||||
r = mkdir_p(mount_tmp, 0700);
|
||||
else
|
||||
r = make_mount_point_inode_from_stat(&st, mount_tmp, 0700);
|
||||
r = make_mount_point_inode_from_stat(chased_src_st, mount_tmp, 0700);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to create temporary mount point %s: %m", mount_tmp);
|
||||
goto finish;
|
||||
@ -894,7 +875,17 @@ static int mount_in_namespace(
|
||||
mount_tmp_created = true;
|
||||
|
||||
if (is_image)
|
||||
r = verity_dissect_and_mount(chased_src_fd, chased_src_path, mount_tmp, options, image_policy, NULL, NULL, NULL, NULL);
|
||||
r = verity_dissect_and_mount(
|
||||
chased_src_fd,
|
||||
chased_src_path,
|
||||
mount_tmp,
|
||||
options,
|
||||
image_policy,
|
||||
/* required_host_os_release_id= */ NULL,
|
||||
/* required_host_os_release_version_id= */ NULL,
|
||||
/* required_host_os_release_sysext_level= */ NULL,
|
||||
/* required_sysext_scope= */ NULL,
|
||||
/* ret_image= */ NULL);
|
||||
else
|
||||
r = mount_follow_verbose(LOG_DEBUG, FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, NULL, MS_BIND, NULL);
|
||||
if (r < 0)
|
||||
@ -913,7 +904,7 @@ static int mount_in_namespace(
|
||||
* right-away. */
|
||||
|
||||
mount_outside = strjoina(propagate_path, "/XXXXXX");
|
||||
if (is_image || S_ISDIR(st.st_mode))
|
||||
if (is_image || S_ISDIR(chased_src_st->st_mode))
|
||||
r = mkdtemp(mount_outside) ? 0 : -errno;
|
||||
else {
|
||||
r = mkostemp_safe(mount_outside);
|
||||
@ -933,7 +924,7 @@ static int mount_in_namespace(
|
||||
mount_outside_mounted = true;
|
||||
mount_tmp_mounted = false;
|
||||
|
||||
if (is_image || S_ISDIR(st.st_mode))
|
||||
if (is_image || S_ISDIR(chased_src_st->st_mode))
|
||||
(void) rmdir(mount_tmp);
|
||||
else
|
||||
(void) unlink(mount_tmp);
|
||||
@ -962,7 +953,7 @@ static int mount_in_namespace(
|
||||
if (make_file_or_directory) {
|
||||
if (!is_image) {
|
||||
(void) mkdir_parents(dest, 0755);
|
||||
(void) make_mount_point_inode_from_stat(&st, dest, 0700);
|
||||
(void) make_mount_point_inode_from_stat(chased_src_st, dest, 0700);
|
||||
} else
|
||||
(void) mkdir_p(dest, 0755);
|
||||
}
|
||||
@ -1012,7 +1003,7 @@ finish:
|
||||
if (mount_outside_mounted)
|
||||
(void) umount_verbose(LOG_DEBUG, mount_outside, UMOUNT_NOFOLLOW);
|
||||
if (mount_outside_created) {
|
||||
if (is_image || S_ISDIR(st.st_mode))
|
||||
if (is_image || S_ISDIR(chased_src_st->st_mode))
|
||||
(void) rmdir(mount_outside);
|
||||
else
|
||||
(void) unlink(mount_outside);
|
||||
@ -1021,7 +1012,7 @@ finish:
|
||||
if (mount_tmp_mounted)
|
||||
(void) umount_verbose(LOG_DEBUG, mount_tmp, UMOUNT_NOFOLLOW);
|
||||
if (mount_tmp_created) {
|
||||
if (is_image || S_ISDIR(st.st_mode))
|
||||
if (is_image || S_ISDIR(chased_src_st->st_mode))
|
||||
(void) rmdir(mount_tmp);
|
||||
else
|
||||
(void) unlink(mount_tmp);
|
||||
@ -1035,6 +1026,183 @@ finish:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int mount_in_namespace(
|
||||
pid_t target,
|
||||
const char *propagate_path,
|
||||
const char *incoming_path,
|
||||
const char *src,
|
||||
const char *dest,
|
||||
bool read_only,
|
||||
bool make_file_or_directory,
|
||||
const MountOptions *options,
|
||||
const ImagePolicy *image_policy,
|
||||
bool is_image) {
|
||||
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *img = NULL;
|
||||
_cleanup_close_pair_ int errno_pipe_fd[2] = PIPE_EBADF;
|
||||
_cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF, pidns_fd = -EBADF, chased_src_fd = -EBADF,
|
||||
new_mount_fd = -EBADF;
|
||||
_cleanup_free_ char *chased_src_path = NULL;
|
||||
struct stat st;
|
||||
pid_t child;
|
||||
int r;
|
||||
|
||||
assert(target > 0);
|
||||
assert(propagate_path);
|
||||
assert(incoming_path);
|
||||
assert(src);
|
||||
assert(dest);
|
||||
assert(!options || is_image);
|
||||
|
||||
r = namespace_open(target, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to retrieve FDs of the target process' namespace: %m");
|
||||
|
||||
r = in_same_namespace(target, 0, NAMESPACE_MOUNT);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to determine if mount namespaces are equal: %m");
|
||||
/* We can't add new mounts at runtime if the process wasn't started in a namespace */
|
||||
if (r > 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to activate bind mount in target, not running in a mount namespace");
|
||||
|
||||
r = chase(src, NULL, 0, &chased_src_path, &chased_src_fd);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to resolve source path of %s: %m", src);
|
||||
log_debug("Chased source path of %s to %s", src, chased_src_path);
|
||||
|
||||
if (fstat(chased_src_fd, &st) < 0)
|
||||
return log_debug_errno(errno, "Failed to stat() resolved source path %s: %m", src);
|
||||
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 log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Source directory %s can't be a symbolic link", src);
|
||||
|
||||
if (!mount_new_api_supported()) /* Fallback if we can't use the new mount API */
|
||||
return mount_in_namespace_legacy(
|
||||
chased_src_path,
|
||||
chased_src_fd,
|
||||
&st,
|
||||
propagate_path,
|
||||
incoming_path,
|
||||
dest,
|
||||
pidns_fd,
|
||||
mntns_fd,
|
||||
root_fd,
|
||||
read_only,
|
||||
make_file_or_directory,
|
||||
options,
|
||||
image_policy,
|
||||
is_image);
|
||||
|
||||
if (is_image) {
|
||||
r = verity_dissect_and_mount(
|
||||
chased_src_fd,
|
||||
chased_src_path,
|
||||
/* dest= */ NULL,
|
||||
options,
|
||||
image_policy,
|
||||
/* required_host_os_release_id= */ NULL,
|
||||
/* required_host_os_release_version_id= */ NULL,
|
||||
/* required_host_os_release_sysext_level= */ NULL,
|
||||
/* required_sysext_scope= */ NULL,
|
||||
&img);
|
||||
if (r < 0)
|
||||
return log_debug_errno(
|
||||
r,
|
||||
"Failed to dissect and mount image %s: %m",
|
||||
chased_src_path);
|
||||
} else {
|
||||
new_mount_fd = open_tree(
|
||||
chased_src_fd,
|
||||
"",
|
||||
OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC|AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH);
|
||||
if (new_mount_fd < 0)
|
||||
return log_debug_errno(
|
||||
errno,
|
||||
"Failed to open mount point \"%s\": %m",
|
||||
chased_src_path);
|
||||
|
||||
if (read_only && mount_setattr(new_mount_fd, "", AT_EMPTY_PATH,
|
||||
&(struct mount_attr) {
|
||||
.attr_set = MOUNT_ATTR_RDONLY,
|
||||
}, MOUNT_ATTR_SIZE_VER0) < 0)
|
||||
return log_debug_errno(
|
||||
errno,
|
||||
"Failed to set mount flags for \"%s\": %m",
|
||||
chased_src_path);
|
||||
}
|
||||
|
||||
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
|
||||
return log_debug_errno(errno, "Failed to create pipe: %m");
|
||||
|
||||
r = namespace_fork("(sd-bindmnt)",
|
||||
"(sd-bindmnt-inner)",
|
||||
/* except_fds= */ NULL,
|
||||
/* n_except_fds= */ 0,
|
||||
FORK_RESET_SIGNALS|FORK_DEATHSIG,
|
||||
pidns_fd,
|
||||
mntns_fd,
|
||||
/* netns_fd= */ -1,
|
||||
/* userns_fd= */ -1,
|
||||
root_fd,
|
||||
&child);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to fork off: %m");
|
||||
if (r == 0) {
|
||||
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
|
||||
|
||||
if (make_file_or_directory)
|
||||
(void) mkdir_parents(dest, 0755);
|
||||
|
||||
if (img) {
|
||||
DissectImageFlags f = 0;
|
||||
|
||||
if (make_file_or_directory)
|
||||
f |= DISSECT_IMAGE_MKDIR;
|
||||
|
||||
if (read_only)
|
||||
f |= DISSECT_IMAGE_READ_ONLY;
|
||||
|
||||
r = dissected_image_mount(
|
||||
img,
|
||||
dest,
|
||||
/* uid_shift= */ UID_INVALID,
|
||||
/* uid_range= */ UID_INVALID,
|
||||
/* userns_fd= */ -EBADF,
|
||||
f);
|
||||
} else {
|
||||
if (make_file_or_directory)
|
||||
(void) make_mount_point_inode_from_stat(&st, dest, 0700);
|
||||
|
||||
r = RET_NERRNO(move_mount(new_mount_fd,
|
||||
"",
|
||||
-EBADF,
|
||||
dest,
|
||||
MOVE_MOUNT_F_EMPTY_PATH));
|
||||
}
|
||||
if (r < 0) {
|
||||
(void) write(errno_pipe_fd[1], &r, sizeof(r));
|
||||
errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
|
||||
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
|
||||
|
||||
r = wait_for_terminate_and_check("(sd-bindmnt)", child, 0);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to wait for child: %m");
|
||||
if (r != EXIT_SUCCESS) {
|
||||
if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r))
|
||||
return log_debug_errno(r, "Failed to mount: %m");
|
||||
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EPROTO), "Child failed.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bind_mount_in_namespace(
|
||||
pid_t target,
|
||||
const char *propagate_path,
|
||||
@ -1460,3 +1628,88 @@ int mount_credentials_fs(const char *path, size_t size, bool ro) {
|
||||
credentials_fs_mount_flags(ro),
|
||||
opts);
|
||||
}
|
||||
|
||||
int make_fsmount(
|
||||
int error_log_level,
|
||||
const char *what,
|
||||
const char *type,
|
||||
unsigned long flags,
|
||||
const char *options,
|
||||
int userns_fd) {
|
||||
|
||||
_cleanup_close_ int fs_fd = -EBADF, mnt_fd = -EBADF;
|
||||
_cleanup_free_ char *o = NULL;
|
||||
unsigned long f;
|
||||
int r;
|
||||
|
||||
assert(type);
|
||||
assert(what);
|
||||
|
||||
r = mount_option_mangle(options, flags, &f, &o);
|
||||
if (r < 0)
|
||||
return log_full_errno(
|
||||
error_log_level, r, "Failed to mangle mount options %s: %m",
|
||||
strempty(options));
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *fl = NULL;
|
||||
(void) mount_flags_to_string(f, &fl);
|
||||
|
||||
log_debug("Creating mount fd for %s (%s) (%s \"%s\")...",
|
||||
strna(what), strna(type), strnull(fl), strempty(o));
|
||||
}
|
||||
|
||||
fs_fd = fsopen(type, FSOPEN_CLOEXEC);
|
||||
if (fs_fd < 0)
|
||||
return log_full_errno(error_log_level, errno, "Failed to open superblock for \"%s\": %m", type);
|
||||
|
||||
if (fsconfig(fs_fd, FSCONFIG_SET_STRING, "source", what, 0) < 0)
|
||||
return log_full_errno(error_log_level, errno, "Failed to set mount source for \"%s\" to \"%s\": %m", type, what);
|
||||
|
||||
if (FLAGS_SET(f, MS_RDONLY))
|
||||
if (fsconfig(fs_fd, FSCONFIG_SET_FLAG, "ro", NULL, 0) < 0)
|
||||
return log_full_errno(error_log_level, errno, "Failed to set read only mount flag for \"%s\": %m", type);
|
||||
|
||||
for (const char *p = o;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
char *eq;
|
||||
|
||||
r = extract_first_word(&p, &word, ",", EXTRACT_KEEP_QUOTE);
|
||||
if (r < 0)
|
||||
return log_full_errno(error_log_level, r, "Failed to parse mount option string \"%s\": %m", o);
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
eq = strchr(word, '=');
|
||||
if (eq) {
|
||||
*eq = 0;
|
||||
eq++;
|
||||
|
||||
if (fsconfig(fs_fd, FSCONFIG_SET_STRING, word, eq, 0) < 0)
|
||||
return log_full_errno(error_log_level, errno, "Failed to set mount option \"%s=%s\" for \"%s\": %m", word, eq, type);
|
||||
} else {
|
||||
if (fsconfig(fs_fd, FSCONFIG_SET_FLAG, word, NULL, 0) < 0)
|
||||
return log_full_errno(error_log_level, errno, "Failed to set mount flag \"%s\" for \"%s\": %m", word, type);
|
||||
}
|
||||
}
|
||||
|
||||
if (fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0)
|
||||
return log_full_errno(error_log_level, errno, "Failed to realize fs fd for \"%s\" (\"%s\"): %m", what, type);
|
||||
|
||||
mnt_fd = fsmount(fs_fd, FSMOUNT_CLOEXEC, 0);
|
||||
if (mnt_fd < 0)
|
||||
return log_full_errno(error_log_level, errno, "Failed to create mount fd for \"%s\" (\"%s\"): %m", what, type);
|
||||
|
||||
if (mount_setattr(mnt_fd, "", AT_EMPTY_PATH|AT_RECURSIVE,
|
||||
&(struct mount_attr) {
|
||||
.attr_set = ms_flags_to_mount_attr(f) | (userns_fd >= 0 ? MOUNT_ATTR_IDMAP : 0),
|
||||
.userns_fd = userns_fd,
|
||||
}, MOUNT_ATTR_SIZE_VER0) < 0)
|
||||
return log_full_errno(error_log_level,
|
||||
errno,
|
||||
"Failed to set mount flags for \"%s\" (\"%s\"): %m",
|
||||
what,
|
||||
type);
|
||||
|
||||
return TAKE_FD(mnt_fd);
|
||||
}
|
||||
|
@ -136,3 +136,5 @@ int trigger_automount_at(int dir_fd, const char *path);
|
||||
|
||||
unsigned long credentials_fs_mount_flags(bool ro);
|
||||
int mount_credentials_fs(const char *path, size_t size, bool ro);
|
||||
|
||||
int make_fsmount(int error_log_level, const char *what, const char *type, unsigned long flags, const char *options, int userns_fd);
|
||||
|
@ -712,8 +712,9 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
||||
r = dissected_image_mount_and_warn(
|
||||
m,
|
||||
p,
|
||||
UID_INVALID,
|
||||
UID_INVALID,
|
||||
/* uid_shift= */ UID_INVALID,
|
||||
/* uid_range= */ UID_INVALID,
|
||||
/* userns_fd= */ -EBADF,
|
||||
flags);
|
||||
if (r < 0 && r != -ENOMEDIUM)
|
||||
return r;
|
||||
|
@ -101,7 +101,13 @@ static void* thread_func(void *ptr) {
|
||||
|
||||
verify_dissected_image(dissected);
|
||||
|
||||
r = dissected_image_mount(dissected, mounted, UID_INVALID, UID_INVALID, DISSECT_IMAGE_READ_ONLY);
|
||||
r = dissected_image_mount(
|
||||
dissected,
|
||||
mounted,
|
||||
/* uid_shift= */ UID_INVALID,
|
||||
/* uid_range= */ UID_INVALID,
|
||||
/* userns_fd= */ -EBADF,
|
||||
DISSECT_IMAGE_READ_ONLY);
|
||||
log_notice_errno(r, "Mounted %s → %s: %m", loop->node, mounted);
|
||||
assert_se(r >= 0);
|
||||
|
||||
@ -291,7 +297,13 @@ static int run(int argc, char *argv[]) {
|
||||
assert_se(detach_mount_namespace() >= 0);
|
||||
|
||||
/* This first (writable) mount will initialize the mount point dirs, so that the subsequent read-only ones can work */
|
||||
assert_se(dissected_image_mount(dissected, mounted, UID_INVALID, UID_INVALID, 0) >= 0);
|
||||
assert_se(dissected_image_mount(
|
||||
dissected,
|
||||
mounted,
|
||||
/* uid_shift= */ UID_INVALID,
|
||||
/* uid_range= */ UID_INVALID,
|
||||
/* usernfs_fd= */ -EBADF,
|
||||
0) >= 0);
|
||||
|
||||
/* Now we mounted everything, the partitions are pinned. Now it's fine to release the lock
|
||||
* fully. This means udev could now issue BLKRRPART again, but that's OK given this will fail because
|
||||
|
@ -351,7 +351,7 @@ MountAPIVFS=yes
|
||||
PrivateTmp=yes
|
||||
ExecStart=/bin/sh -c ' \\
|
||||
systemd-notify --ready; \\
|
||||
while [[ ! -f /tmp/img/usr/lib/os-release ]] || ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do \\
|
||||
while [ ! -f /tmp/img/usr/lib/os-release ] || ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do \\
|
||||
sleep 0.1; \\
|
||||
done; \\
|
||||
mount; \\
|
||||
|
Loading…
x
Reference in New Issue
Block a user