]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
discover-image: support runtime scope also for .nspawn settings files and the pool dir
authorLennart Poettering <lennart@poettering.net>
Tue, 15 Jul 2025 10:13:27 +0000 (12:13 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 25 Sep 2025 20:43:59 +0000 (22:43 +0200)
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
src/machine/image-varlink.c
src/machine/image.c
src/machine/machined-dbus.c
src/portable/portabled-image-bus.c
src/shared/discover-image.c
src/shared/discover-image.h

index 8bc6565079768569a6f32eda50f7431693aaf5ef..8aa129fab7da79337b788ff9ac48ae43f7407bcd 100644 (file)
@@ -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);
         }
 
index 42f53049b73efbaeee0528eba888ce8693a9e2e0..8d61304741e86d633e6b96aeebbf8ea8498cf608 100644 (file)
@@ -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)
index 9b080e3dc025625507dab54b6051878dec9ff9ae..0a5b6d27ae01e3ea8b007119aa0af7221a5c0fb5 100644 (file)
@@ -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;
index 7d2e3dfb62cc1905a5fa7b6727213e7851320506..182d3dd447f4c0d57c08d56d6c519a09bbda0b17 100644 (file)
@@ -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)
index e8bcb900efc71df6ca2c825760206b7f7127a5d8..332b62a72142a9c0df881c60bf2036197069ffa1 100644 (file)
@@ -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);
index 58777c683b2f90c04282a96e357678323c78d1ab..ad4f21c99ddc8ca4f783c27bb5c6c3e7c8c2a4cb 100644 (file)
@@ -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;
 }
index 60f5a4dce13574201c54e6bac8a2822012bdd1fb..6e5813b9759b8f69e508953e83032d5d9955d260 100644 (file)
@@ -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);