dissect-image: add a generic varlink client side for mountfsd

This commit is contained in:
Lennart Poettering
2023-12-04 22:09:15 +01:00
parent 702a52f4b5
commit fe7d8235e1
2 changed files with 211 additions and 0 deletions

View File

@@ -74,6 +74,7 @@
#include "tmpfile-util.h"
#include "udev-util.h"
#include "user-util.h"
#include "varlink.h"
#include "xattr-util.h"
/* how many times to wait for the device nodes to appear */
@@ -4156,3 +4157,210 @@ int get_common_dissect_directory(char **ret) {
return 0;
}
#if HAVE_BLKID
static JSON_DISPATCH_ENUM_DEFINE(dispatch_architecture, Architecture, architecture_from_string);
static JSON_DISPATCH_ENUM_DEFINE(dispatch_partition_designator, PartitionDesignator, partition_designator_from_string);
typedef struct PartitionFields {
PartitionDesignator designator;
bool rw;
bool growfs;
unsigned partno;
Architecture architecture;
sd_id128_t uuid;
char *fstype;
char *label;
uint64_t size;
uint64_t offset;
unsigned fsmount_fd_idx;
} PartitionFields;
static void partition_fields_done(PartitionFields *f) {
assert(f);
f->fstype = mfree(f->fstype);
f->label = mfree(f->label);
}
typedef struct ReplyParameters {
JsonVariant *partitions;
char *image_policy;
uint64_t image_size;
uint32_t sector_size;
sd_id128_t image_uuid;
} ReplyParameters;
static void reply_parameters_done(ReplyParameters *p) {
assert(p);
p->image_policy = mfree(p->image_policy);
p->partitions = json_variant_unref(p->partitions);
}
#endif
int mountfsd_mount_image(
const char *path,
int userns_fd,
const ImagePolicy *image_policy,
DissectImageFlags flags,
DissectedImage **ret) {
#if HAVE_BLKID
_cleanup_(reply_parameters_done) ReplyParameters p = {};
static const JsonDispatch dispatch_table[] = {
{ "partitions", JSON_VARIANT_ARRAY, json_dispatch_variant, offsetof(struct ReplyParameters, partitions), JSON_MANDATORY },
{ "imagePolicy", JSON_VARIANT_STRING, json_dispatch_string, offsetof(struct ReplyParameters, image_policy), 0 },
{ "imageSize", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(struct ReplyParameters, image_size), JSON_MANDATORY },
{ "sectorSize", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint32, offsetof(struct ReplyParameters, sector_size), JSON_MANDATORY },
{ "imageUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(struct ReplyParameters, image_uuid), 0 },
{}
};
_cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
_cleanup_close_ int image_fd = -EBADF;
_cleanup_(varlink_unrefp) Varlink *vl = NULL;
_cleanup_free_ char *ps = NULL;
unsigned max_fd = UINT_MAX;
const char *error_id;
int r;
assert(path);
assert(ret);
r = varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem");
if (r < 0)
return log_error_errno(r, "Failed to connect to mountfsd: %m");
r = varlink_set_allow_fd_passing_input(vl, true);
if (r < 0)
return log_error_errno(r, "Failed to enable varlink fd passing for read: %m");
r = varlink_set_allow_fd_passing_output(vl, true);
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);
if (image_fd < 0)
return log_error_errno(errno, "Failed to open '%s': %m", path);
r = varlink_push_dup_fd(vl, image_fd);
if (r < 0)
return log_error_errno(r, "Failed to push image fd into varlink connection: %m");
if (userns_fd >= 0) {
r = varlink_push_dup_fd(vl, userns_fd);
if (r < 0)
return log_error_errno(r, "Failed to push image fd into varlink connection: %m");
}
if (image_policy) {
r = image_policy_to_string(image_policy, /* simplify= */ false, &ps);
if (r < 0)
return log_error_errno(r, "Failed format image policy to string: %m");
}
JsonVariant *reply = NULL;
r = varlink_callb(
vl,
"io.systemd.MountFileSystem.MountImage",
&reply,
&error_id,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("imageFileDescriptor", JSON_BUILD_UNSIGNED(0)),
JSON_BUILD_PAIR_CONDITION(userns_fd >= 0, "userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(1)),
JSON_BUILD_PAIR("readOnly", JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY))),
JSON_BUILD_PAIR("growFileSystems", JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_GROWFS))),
JSON_BUILD_PAIR_CONDITION(ps, "imagePolicy", JSON_BUILD_STRING(ps)),
JSON_BUILD_PAIR("allowInteractiveAuthentication", JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH)))));
if (r < 0)
return log_error_errno(r, "Failed to call MountImage() varlink call: %m");
if (!isempty(error_id))
return log_error_errno(varlink_error_to_errno(error_id, reply), "Failed to call MountImage() varlink call: %s", error_id);
r = json_dispatch(reply, dispatch_table, JSON_ALLOW_EXTENSIONS, &p);
if (r < 0)
return log_error_errno(r, "Failed to parse MountImage() reply: %m");
log_debug("Effective image policy: %s", p.image_policy);
JsonVariant *i;
JSON_VARIANT_ARRAY_FOREACH(i, p.partitions) {
_cleanup_close_ int fsmount_fd = -EBADF;
_cleanup_(partition_fields_done) PartitionFields pp = {
.designator = _PARTITION_DESIGNATOR_INVALID,
.architecture = _ARCHITECTURE_INVALID,
.size = UINT64_MAX,
.offset = UINT64_MAX,
.fsmount_fd_idx = UINT_MAX,
};
static const JsonDispatch partition_dispatch_table[] = {
{ "designator", JSON_VARIANT_STRING, dispatch_partition_designator, offsetof(struct PartitionFields, designator), JSON_MANDATORY },
{ "writable", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(struct PartitionFields, rw), JSON_MANDATORY },
{ "growFileSystem", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(struct PartitionFields, growfs), JSON_MANDATORY },
{ "partitionNumber", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(struct PartitionFields, partno), 0 },
{ "architecture", JSON_VARIANT_STRING, dispatch_architecture, offsetof(struct PartitionFields, architecture), 0 },
{ "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(struct PartitionFields, uuid), 0 },
{ "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(struct PartitionFields, fstype), JSON_MANDATORY },
{ "partitionLabel", JSON_VARIANT_STRING, json_dispatch_string, offsetof(struct PartitionFields, label), 0 },
{ "size", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(struct PartitionFields, size), JSON_MANDATORY },
{ "offset", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(struct PartitionFields, offset), JSON_MANDATORY },
{ "mountFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(struct PartitionFields, fsmount_fd_idx), JSON_MANDATORY },
{}
};
r = json_dispatch(i, partition_dispatch_table, JSON_ALLOW_EXTENSIONS, &pp);
if (r < 0)
return log_error_errno(r, "Failed to parse partition data: %m");
if (pp.fsmount_fd_idx != UINT_MAX) {
if (max_fd == UINT_MAX || pp.fsmount_fd_idx > max_fd)
max_fd = pp.fsmount_fd_idx;
fsmount_fd = varlink_take_fd(vl, pp.fsmount_fd_idx);
if (fsmount_fd < 0)
return fsmount_fd;
}
assert(pp.designator >= 0);
if (!di) {
r = dissected_image_new(path, &di);
if (r < 0)
return log_error_errno(r, "Failed to allocated new dissected image structure: %m");
}
if (di->partitions[pp.designator].found)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate partition data for '%s'.", partition_designator_to_string(pp.designator));
di->partitions[pp.designator] = (DissectedPartition) {
.found = true,
.rw = pp.rw,
.growfs = pp.growfs,
.partno = pp.partno,
.architecture = pp.architecture,
.uuid = pp.uuid,
.fstype = TAKE_PTR(pp.fstype),
.label = TAKE_PTR(pp.label),
.mount_node_fd = -EBADF,
.size = pp.size,
.offset = pp.offset,
.fsmount_fd = TAKE_FD(fsmount_fd),
};
}
di->image_size = p.image_size;
di->sector_size = p.sector_size;
di->image_uuid = p.image_uuid;
*ret = TAKE_PTR(di);
return 0;
#else
return -EOPNOTSUPP;
#endif
}

View File

@@ -88,6 +88,7 @@ typedef enum DissectImageFlags {
DISSECT_IMAGE_ALLOW_EMPTY = 1 << 24, /* Allow that no usable partitions is present */
DISSECT_IMAGE_TRY_ATOMIC_MOUNT_EXCHANGE = 1 << 25, /* Try to mount the image beneath the specified mountpoint, rather than on top of it, and then umount the top */
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY = 1 << 26, /* Allow userspace verity keyring in /etc/verity.d/ and related dirs */
DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH = 1 << 27, /* Allow interactive authorization when going through mountfsd */
} DissectImageFlags;
struct DissectedImage {
@@ -241,3 +242,5 @@ static inline const char *dissected_partition_fstype(const DissectedPartition *m
}
int get_common_dissect_directory(char **ret);
int mountfsd_mount_image(const char *path, int userns_fd, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);