]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
shared: add storage_acquire_volume() helper
authorChristian Brauner <brauner@kernel.org>
Fri, 1 May 2026 11:31:21 +0000 (13:31 +0200)
committerChristian Brauner <brauner@kernel.org>
Wed, 6 May 2026 08:30:16 +0000 (10:30 +0200)
storagectl's mount.storage helper bundles "open StorageProvider socket
+ Acquire() + dispatch reply + take fd" inline. Future consumers
(systemd-vmspawn boot-time --bind-volume, machinectl bind-volume) need
the same dance.

Factor it into a single libshared helper that takes the Acquire()
parameters by value and returns the fd plus the actual type/read-only
flags. Library code, so no logging — varlink errors are surfaced via
sd_varlink_error_to_errno() and the StorageProvider error_id is
returned to the caller via reterr_error_id (caller decides how to
format messages).

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
src/libsystemd/sd-json/json-util.h
src/shared/storage-util.c
src/shared/storage-util.h

index cea2d368b43db520e368857281f0f9a4f954c11f..0db1e445e62ac08c95e31d8b7c9221ea7d103484 100644 (file)
@@ -268,6 +268,8 @@ enum {
         SD_JSON_BUILD_PAIR_CONDITION(condition, name, SD_JSON_BUILD_UNSIGNED(value))
 #define JSON_BUILD_PAIR_CONDITION_BOOLEAN(condition, name, value) \
         SD_JSON_BUILD_PAIR_CONDITION(condition, name, SD_JSON_BUILD_BOOLEAN(value))
+#define JSON_BUILD_PAIR_CONDITION_STRING(condition, name, value) \
+        SD_JSON_BUILD_PAIR_CONDITION(condition, name, SD_JSON_BUILD_STRING(value))
 #define JSON_BUILD_PAIR_CONDITION_STRV(condition, name, value) \
         SD_JSON_BUILD_PAIR_CONDITION(condition, name, SD_JSON_BUILD_STRV(value))
 
index 793946c03a63efc1439ed17331caf2d6bc90aed4..3a3c1e6c57d3f2057ed1b27f8dfaf19b1bb2322e 100644 (file)
@@ -1,6 +1,14 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include "sd-varlink.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
 #include "json-util.h"
+#include "machine-util.h"
+#include "path-lookup.h"
+#include "path-util.h"
+#include "runtime-scope.h"
 #include "string-table.h"
 #include "storage-util.h"
 
@@ -21,3 +29,102 @@ DEFINE_STRING_TABLE_LOOKUP(create_mode, CreateMode);
 
 JSON_DISPATCH_ENUM_DEFINE(json_dispatch_volume_type, VolumeType, volume_type_from_string);
 JSON_DISPATCH_ENUM_DEFINE(json_dispatch_create_mode, CreateMode, create_mode_from_string);
+
+void storage_acquire_reply_done(StorageAcquireReply *reply) {
+        if (!reply)
+                return;
+
+        reply->fd = safe_close(reply->fd);
+}
+
+int storage_acquire_volume(
+                RuntimeScope scope,
+                const BindVolume *bv,
+                bool allow_interactive_auth,
+                char **reterr_error_id,
+                StorageAcquireReply *ret) {
+
+        int r;
+
+        assert(bv);
+        assert(bv->provider);
+        assert(bv->volume);
+        assert(ret);
+
+        /* Defense-in-depth: this is a libshared helper that may grow new callers; reject
+         * provider names that could escape the StorageProvider runtime directory. */
+        if (!storage_provider_name_is_valid(bv->provider))
+                return -EINVAL;
+
+        _cleanup_free_ char *socket_path = NULL;
+        r = runtime_directory_generic(scope, "systemd/io.systemd.StorageProvider", &socket_path);
+        if (r < 0)
+                return r;
+
+        if (!path_extend(&socket_path, bv->provider))
+                return -ENOMEM;
+
+        _cleanup_(sd_varlink_unrefp) sd_varlink *link = NULL;
+        r = sd_varlink_connect_address(&link, socket_path);
+        if (r < 0)
+                return r;
+
+        r = sd_varlink_set_allow_fd_passing_input(link, true);
+        if (r < 0)
+                return r;
+
+        sd_json_variant *mreply = NULL;
+        const char *merror_id = NULL;
+        r = sd_varlink_callbo(
+                        link,
+                        "io.systemd.StorageProvider.Acquire",
+                        &mreply,
+                        &merror_id,
+                        SD_JSON_BUILD_PAIR_STRING("name", bv->volume),
+                        JSON_BUILD_PAIR_CONDITION_STRING(bv->create_mode >= 0, "createMode", create_mode_to_string(bv->create_mode)),
+                        JSON_BUILD_PAIR_STRING_NON_EMPTY("template", bv->template),
+                        JSON_BUILD_PAIR_TRISTATE_NON_NULL("readOnly", read_only_mode_to_tristate(bv->read_only)),
+                        JSON_BUILD_PAIR_CONDITION_STRING(bv->request_as >= 0, "requestAs", volume_type_to_string(bv->request_as)),
+                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("createSizeBytes", bv->create_size_bytes, UINT64_MAX),
+                        SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", allow_interactive_auth));
+        if (r < 0)
+                return r;
+
+        if (merror_id) {
+                if (reterr_error_id) {
+                        char *copy = strdup(merror_id);
+                        if (!copy)
+                                return -ENOMEM;
+                        *reterr_error_id = copy;
+                }
+
+                r = sd_varlink_error_to_errno(merror_id, mreply);
+                return r == -EBADR ? -EPROTO : r;
+        }
+
+        /* tmp.fd holds the JSON fd index until sd_varlink_take_fd() swaps it for the real fd. */
+        StorageAcquireReply tmp = STORAGE_ACQUIRE_REPLY_INIT;
+
+        static const sd_json_dispatch_field dispatch_table[] = {
+                { "fileDescriptorIndex", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int,         voffsetof(StorageAcquireReply, fd),        SD_JSON_MANDATORY },
+                { "readOnly",            SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,    voffsetof(StorageAcquireReply, read_only), 0                 },
+                { "type",                SD_JSON_VARIANT_STRING,        json_dispatch_volume_type,    voffsetof(StorageAcquireReply, type),      SD_JSON_MANDATORY },
+                { "baseUID",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,     voffsetof(StorageAcquireReply, base_uid),  0                 },
+                { "baseGID",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,     voffsetof(StorageAcquireReply, base_gid),  0                 },
+                {}
+        };
+
+        r = sd_json_dispatch(mreply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &tmp);
+        if (r < 0)
+                return r;
+        if (tmp.fd < 0)
+                return -EBADMSG;
+
+        _cleanup_close_ int fd = sd_varlink_take_fd(link, tmp.fd);
+        if (fd < 0)
+                return fd;
+
+        tmp.fd = TAKE_FD(fd);
+        *ret = tmp;
+        return 0;
+}
index f7a62aeec0835b99e7b995b50cdcc26e5ea10878..b37515bedbd43954930bf759512e6497f0c182d3 100644 (file)
@@ -41,3 +41,32 @@ static inline bool storage_template_name_is_valid(const char *n) {
 static inline bool storage_provider_name_is_valid(const char *n) {
         return string_is_safe(n, STRING_FILENAME);
 }
+
+typedef struct StorageAcquireReply {
+        int fd;
+        VolumeType type;
+        int read_only;
+        uid_t base_uid;
+        gid_t base_gid;
+} StorageAcquireReply;
+
+#define STORAGE_ACQUIRE_REPLY_INIT                                              \
+        (StorageAcquireReply) {                                                 \
+                .fd        = -EBADF,                                            \
+                .type      = _VOLUME_TYPE_INVALID,                              \
+                .read_only = -1,                                                \
+                .base_uid  = UID_INVALID,                                       \
+                .base_gid  = GID_INVALID,                                       \
+        }
+
+void storage_acquire_reply_done(StorageAcquireReply *reply);
+
+/* On varlink failure, reterr_error_id (if non-NULL) is set to the io.systemd.StorageProvider.*
+ * error name. The reply is untouched on any error. */
+typedef struct BindVolume BindVolume;
+int storage_acquire_volume(
+                RuntimeScope scope,
+                const BindVolume *bv,
+                bool allow_interactive_auth,
+                char **reterr_error_id,
+                StorageAcquireReply *ret);