mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
dissect-image: add a generic varlink client side for mountfsd
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user