From e1b3319b6c36dec45667706e6c0fabc53ebaa4cd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 15 Jul 2025 12:13:27 +0200 Subject: [PATCH] discover-image: support runtime scope also for .nspawn settings files and the pool dir discover-image.[ch] largely already supports per-scope operations, let's extend this however to also cover finding .nspawn settings files and managing the pool dir. --- src/machine/image-dbus.c | 2 +- src/machine/image-varlink.c | 4 +- src/machine/image.c | 2 +- src/machine/machined-dbus.c | 2 +- src/portable/portabled-image-bus.c | 2 +- src/shared/discover-image.c | 148 +++++++++++++++++++++++++---- src/shared/discover-image.h | 4 +- 7 files changed, 135 insertions(+), 29 deletions(-) diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 8bc65650797..8aa129fab7d 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -65,7 +65,7 @@ int bus_image_method_remove( return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); if (r == 0) { errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); - r = image_remove(image); + r = image_remove(image, m->runtime_scope); report_errno_and_exit(errno_pipe_fd[1], r); } diff --git a/src/machine/image-varlink.c b/src/machine/image-varlink.c index 42f53049b73..8d61304741e 100644 --- a/src/machine/image-varlink.c +++ b/src/machine/image-varlink.c @@ -225,7 +225,7 @@ int vl_method_remove_image(sd_varlink *link, sd_json_variant *parameters, sd_var return log_debug_errno(r, "Failed to fork: %m"); if (r == 0) { errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); - r = image_remove(image); + r = image_remove(image, manager->runtime_scope); report_errno_and_exit(errno_pipe_fd[1], r); } @@ -276,7 +276,7 @@ int vl_method_set_pool_limit(sd_varlink *link, sd_json_variant *parameters, sd_v if (r < 0) return r; - r = image_set_pool_limit(IMAGE_MACHINE, limit); + r = image_set_pool_limit(manager->runtime_scope, IMAGE_MACHINE, limit); if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) return sd_varlink_error(link, VARLINK_ERROR_MACHINE_IMAGE_NOT_SUPPORTED, NULL); if (r < 0) diff --git a/src/machine/image.c b/src/machine/image.c index 9b080e3dc02..0a5b6d27ae0 100644 --- a/src/machine/image.c +++ b/src/machine/image.c @@ -143,7 +143,7 @@ int image_clean_pool_operation(Manager *manager, ImageCleanPoolMode mode, Operat if (mode == IMAGE_CLEAN_POOL_REMOVE_HIDDEN && !image_is_hidden(image)) continue; - r = image_remove(image); + r = image_remove(image, manager->runtime_scope); if (r == -EBUSY) { log_debug("Keeping image '%s' because it's currently used.", image->name); continue; diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 7d2e3dfb62c..182d3dd447f 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -1043,7 +1043,7 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus if (r < 0) return r; - r = image_set_pool_limit(IMAGE_MACHINE, limit); + r = image_set_pool_limit(m->runtime_scope, IMAGE_MACHINE, limit); if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs."); if (r < 0) diff --git a/src/portable/portabled-image-bus.c b/src/portable/portabled-image-bus.c index e8bcb900efc..332b62a7214 100644 --- a/src/portable/portabled-image-bus.c +++ b/src/portable/portabled-image-bus.c @@ -539,7 +539,7 @@ int bus_image_common_remove( if (r == 0) { errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); - r = image_remove(image); + r = image_remove(image, m->runtime_scope); if (r < 0) { (void) write(errno_pipe_fd[1], &r, sizeof(r)); _exit(EXIT_FAILURE); diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 58777c683b2..ad4f21c99dd 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -119,6 +119,15 @@ static const char *const image_root_runtime_table[_IMAGE_CLASS_MAX] = { DEFINE_STRING_TABLE_LOOKUP_TO_STRING(image_root_runtime, ImageClass); +static const char *const image_dirname_table[_IMAGE_CLASS_MAX] = { + [IMAGE_MACHINE] = "machines", + [IMAGE_PORTABLE] = "portables", + [IMAGE_SYSEXT] = "extensions", + [IMAGE_CONFEXT] = "confexts", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(image_dirname, ImageClass); + static Image* image_free(Image *i) { assert(i); @@ -138,7 +147,7 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(Image, image, image_free); DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(image_hash_ops, char, string_hash_func, string_compare_func, Image, image_unref); -static char** image_settings_path(Image *image) { +static char** image_settings_path(Image *image, RuntimeScope scope) { _cleanup_strv_free_ char **l = NULL; _cleanup_free_ char *fn = NULL; size_t i = 0; @@ -146,7 +155,7 @@ static char** image_settings_path(Image *image) { assert(image); - l = new0(char*, 4); + l = new0(char*, 5); if (!l) return NULL; @@ -154,7 +163,41 @@ static char** image_settings_path(Image *image) { if (!fn) return NULL; - FOREACH_STRING(s, "/etc/systemd/nspawn", "/run/systemd/nspawn") { + static const uint64_t system_locations[] = { + SD_PATH_SYSTEM_CONFIGURATION, + SD_PATH_SYSTEM_RUNTIME, + SD_PATH_SYSTEM_LIBRARY_PRIVATE, + UINT64_MAX + }; + static const uint64_t user_locations[] = { + SD_PATH_USER_CONFIGURATION, + SD_PATH_USER_RUNTIME, + SD_PATH_USER_LIBRARY_PRIVATE, + UINT64_MAX + }; + const uint64_t *locations; + + switch (scope) { + case RUNTIME_SCOPE_SYSTEM: + locations = system_locations; + break; + + case RUNTIME_SCOPE_USER: + locations = user_locations; + break; + + default: + assert_not_reached(); + } + + for (size_t k = 0; locations[k] != UINT64_MAX; k++) { + _cleanup_free_ char *s = NULL; + r = sd_path_lookup(locations[k], "systemd/nspawn", &s); + if (r == -ENXIO) + continue; + if (r < 0) + return NULL; + l[i] = path_join(s, fn); if (!l[i]) return NULL; @@ -1086,7 +1129,7 @@ int image_discover( return 0; } -int image_remove(Image *i) { +int image_remove(Image *i, RuntimeScope scope) { _cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; _cleanup_strv_free_ char **settings = NULL; _cleanup_free_ char *roothash = NULL; @@ -1097,7 +1140,7 @@ int image_remove(Image *i) { if (image_is_vendor(i) || image_is_host(i)) return -EROFS; - settings = image_settings_path(i); + settings = image_settings_path(i, scope); if (!settings) return -ENOMEM; @@ -1192,7 +1235,7 @@ int image_rename(Image *i, const char *new_name, RuntimeScope scope) { if (image_is_vendor(i) || image_is_host(i)) return -EROFS; - settings = image_settings_path(i); + settings = image_settings_path(i, scope); if (!settings) return -ENOMEM; @@ -1299,11 +1342,64 @@ static int clone_auxiliary_file(const char *path, const char *new_name, const ch return copy_file_atomic(path, rs, 0664, COPY_REFLINK); } +static int get_pool_directory( + RuntimeScope scope, + ImageClass class, + const char *fname, + const char *suffix, + char **ret) { + + int r; + + assert(scope >= 0); + assert(scope < _RUNTIME_SCOPE_MAX); + assert(class >= 0); + assert(class < _IMAGE_CLASS_MAX); + assert(ret); + + _cleanup_free_ char *root = NULL; + switch (scope) { + + case RUNTIME_SCOPE_SYSTEM: + r = sd_path_lookup(SD_PATH_SYSTEM_STATE_PRIVATE, /* suffix= */ NULL, &root); + break; + + case RUNTIME_SCOPE_USER: + r = sd_path_lookup(SD_PATH_USER_STATE_PRIVATE, /* suffix= */ NULL, &root); + break; + + default: + return -EOPNOTSUPP; + } + if (r < 0) + return r; + + const char *n = image_dirname_to_string(class); + if (!n) + return -EOPNOTSUPP; + + _cleanup_free_ char *j = NULL; + const char *fn; + if (fname && suffix) { + j = strjoin(fname, suffix); + if (!j) + return -ENOMEM; + fn = j; + } else + fn = fname ?: suffix; + + _cleanup_free_ char *p = path_join(root, n, fn); + if (!p) + return -ENOMEM; + + *ret = TAKE_PTR(p); + return 0; +} + int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope scope) { _cleanup_(release_lock_file) LockFile name_lock = LOCK_FILE_INIT; _cleanup_strv_free_ char **settings = NULL; _cleanup_free_ char *roothash = NULL; - const char *new_path; int r; assert(i); @@ -1311,7 +1407,7 @@ int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope sco if (!image_name_is_valid(new_name)) return -EINVAL; - settings = image_settings_path(i); + settings = image_settings_path(i, scope); if (!settings) return -ENOMEM; @@ -1326,7 +1422,7 @@ int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope sco if (r < 0) return r; - r = image_find(scope, IMAGE_MACHINE, new_name, NULL, NULL); + r = image_find(scope, i->class, new_name, NULL, NULL); if (r >= 0) return -EEXIST; if (r != -ENOENT) @@ -1335,11 +1431,14 @@ int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope sco switch (i->type) { case IMAGE_SUBVOLUME: - case IMAGE_DIRECTORY: + case IMAGE_DIRECTORY: { /* If we can we'll always try to create a new btrfs subvolume here, even if the source is a plain * directory. */ - new_path = strjoina("/var/lib/machines/", new_name); + _cleanup_free_ char *new_path = NULL; + r = get_pool_directory(scope, i->class, new_name, /* suffix= */ NULL, &new_path); + if (r < 0) + return r; r = btrfs_subvol_snapshot_at(AT_FDCWD, i->path, AT_FDCWD, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | @@ -1353,19 +1452,23 @@ int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope sco (void) btrfs_subvol_auto_qgroup(new_path, 0, true); break; + } - case IMAGE_RAW: - new_path = strjoina("/var/lib/machines/", new_name, ".raw"); + case IMAGE_RAW: { + _cleanup_free_ char *new_path = NULL; + r = get_pool_directory(scope, i->class, new_name, ".raw", &new_path); + if (r < 0) + return r; r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, COPY_REFLINK|COPY_CRTIME|COPY_NOCOW_AFTER); break; + } case IMAGE_BLOCK: default: return -EOPNOTSUPP; } - if (r < 0) return r; @@ -1599,25 +1702,28 @@ int image_set_limit(Image *i, uint64_t referenced_max) { return 0; } -int image_set_pool_limit(ImageClass class, uint64_t referenced_max) { - const char *dir; +int image_set_pool_limit(RuntimeScope scope, ImageClass class, uint64_t referenced_max) { int r; + assert(scope >= 0 && scope < _RUNTIME_SCOPE_MAX); assert(class >= 0 && class < _IMAGE_CLASS_MAX); - dir = image_root_to_string(class); + _cleanup_free_ char *pool = NULL; + r = get_pool_directory(scope, class, /* fname= */ NULL, /* suffix= */ NULL, &pool); + if (r < 0) + return r; - r = btrfs_qgroup_set_limit(dir, /* qgroupid = */ 0, referenced_max); + r = btrfs_qgroup_set_limit(pool, /* qgroupid = */ 0, referenced_max); if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) return r; if (r < 0) - log_debug_errno(r, "Failed to set limit on btrfs quota group for '%s', ignoring: %m", dir); + log_debug_errno(r, "Failed to set limit on btrfs quota group for '%s', ignoring: %m", pool); - r = btrfs_subvol_set_subtree_quota_limit(dir, /* subvol_id = */ 0, referenced_max); + r = btrfs_subvol_set_subtree_quota_limit(pool, /* subvol_id = */ 0, referenced_max); if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) return r; if (r < 0) - return log_debug_errno(r, "Failed to set subtree quota limit for '%s': %m", dir); + return log_debug_errno(r, "Failed to set subtree quota limit for '%s': %m", pool); return 0; } diff --git a/src/shared/discover-image.h b/src/shared/discover-image.h index 60f5a4dce13..6e5813b9759 100644 --- a/src/shared/discover-image.h +++ b/src/shared/discover-image.h @@ -55,7 +55,7 @@ int image_from_path(const char *path, Image **ret); int image_find_harder(RuntimeScope scope, ImageClass class, const char *name_or_path, const char *root, Image **ret); int image_discover(RuntimeScope scope, ImageClass class, const char *root, Hashmap **images); -int image_remove(Image *i); +int image_remove(Image *i, RuntimeScope scope); int image_rename(Image *i, const char *new_name, RuntimeScope scope); int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope scope); int image_read_only(Image *i, bool b); @@ -67,7 +67,7 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile int image_name_lock(const char *name, int operation, LockFile *ret); int image_set_limit(Image *i, uint64_t referenced_max); -int image_set_pool_limit(ImageClass class, uint64_t referenced_max); +int image_set_pool_limit(RuntimeScope scope, ImageClass class, uint64_t referenced_max); int image_read_metadata(Image *i, const ImagePolicy *image_policy); -- 2.47.3