diff options
Diffstat (limited to 'src/shared/machine-image.c')
-rw-r--r-- | src/shared/machine-image.c | 158 |
1 files changed, 140 insertions, 18 deletions
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 70220bdd14..8ed3ad7f44 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -19,16 +19,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/statfs.h> -#include <linux/fs.h> #include <fcntl.h> +#include <linux/fs.h> +#include <sys/statfs.h> -#include "utf8.h" #include "btrfs-util.h" -#include "path-util.h" #include "copy.h" #include "mkdir.h" +#include "path-util.h" #include "rm-rf.h" +#include "strv.h" +#include "utf8.h" + #include "machine-image.h" static const char image_search_path[] = @@ -47,6 +49,38 @@ Image *image_unref(Image *i) { return NULL; } +static char **image_settings_path(Image *image) { + _cleanup_strv_free_ char **l = NULL; + char **ret; + const char *fn, *s; + unsigned i = 0; + + assert(image); + + l = new0(char*, 4); + if (!l) + return NULL; + + fn = strjoina(image->name, ".nspawn"); + + FOREACH_STRING(s, "/etc/systemd/nspawn/", "/run/systemd/nspawn/") { + l[i] = strappend(s, fn); + if (!l[i]) + return NULL; + + i++; + } + + l[i] = file_in_same_dir(image->path, fn); + if (!l[i]) + return NULL; + + ret = l; + l = NULL; + + return ret; +} + static int image_new( ImageType t, const char *pretty, @@ -142,11 +176,10 @@ static int image_make( return r; if (r) { BtrfsSubvolInfo info; - BtrfsQuotaInfo quota; /* It's a btrfs subvolume */ - r = btrfs_subvol_get_info_fd(fd, &info); + r = btrfs_subvol_get_info_fd(fd, 0, &info); if (r < 0) return r; @@ -161,13 +194,17 @@ static int image_make( if (r < 0) return r; - r = btrfs_subvol_get_quota_fd(fd, "a); - if (r >= 0) { - (*ret)->usage = quota.referenced; - (*ret)->usage_exclusive = quota.exclusive; + if (btrfs_quota_scan_ongoing(fd) == 0) { + BtrfsQuotaInfo quota; - (*ret)->limit = quota.referenced_max; - (*ret)->limit_exclusive = quota.exclusive_max; + r = btrfs_subvol_get_subtree_quota_fd(fd, 0, "a); + if (r >= 0) { + (*ret)->usage = quota.referenced; + (*ret)->usage_exclusive = quota.exclusive; + + (*ret)->limit = quota.referenced_max; + (*ret)->limit_exclusive = quota.exclusive_max; + } } return 1; @@ -341,6 +378,8 @@ void image_hashmap_free(Hashmap *map) { int image_remove(Image *i) { _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; + _cleanup_strv_free_ char **settings = NULL; + char **j; int r; assert(i); @@ -349,6 +388,10 @@ int image_remove(Image *i) { path_startswith(i->path, "/usr")) return -EROFS; + settings = image_settings_path(i); + if (!settings) + return -ENOMEM; + /* Make sure we don't interfere with a running nspawn */ r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); if (r < 0) @@ -357,28 +400,56 @@ int image_remove(Image *i) { switch (i->type) { case IMAGE_SUBVOLUME: - return btrfs_subvol_remove(i->path, true); + r = btrfs_subvol_remove(i->path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); + if (r < 0) + return r; + break; case IMAGE_DIRECTORY: /* Allow deletion of read-only directories */ (void) chattr_path(i->path, false, FS_IMMUTABLE_FL); - return rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + if (r < 0) + return r; + + break; case IMAGE_RAW: if (unlink(i->path) < 0) return -errno; - - return 0; + break; default: return -EOPNOTSUPP; } + + STRV_FOREACH(j, settings) { + if (unlink(*j) < 0 && errno != ENOENT) + log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j); + } + + return 0; +} + +static int rename_settings_file(const char *path, const char *new_name) { + _cleanup_free_ char *rs = NULL; + const char *fn; + + fn = strjoina(new_name, ".nspawn"); + + rs = file_in_same_dir(path, fn); + if (!rs) + return -ENOMEM; + + return rename_noreplace(AT_FDCWD, path, AT_FDCWD, rs); } int image_rename(Image *i, const char *new_name) { _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT; _cleanup_free_ char *new_path = NULL, *nn = NULL; + _cleanup_strv_free_ char **settings = NULL; unsigned file_attr = 0; + char **j; int r; assert(i); @@ -390,6 +461,10 @@ int image_rename(Image *i, const char *new_name) { path_startswith(i->path, "/usr")) return -EROFS; + settings = image_settings_path(i); + if (!settings) + return -ENOMEM; + /* Make sure we don't interfere with a running nspawn */ r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); if (r < 0) @@ -458,12 +533,33 @@ int image_rename(Image *i, const char *new_name) { i->name = nn; nn = NULL; + STRV_FOREACH(j, settings) { + r = rename_settings_file(*j, new_name); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j); + } + return 0; } +static int clone_settings_file(const char *path, const char *new_name) { + _cleanup_free_ char *rs = NULL; + const char *fn; + + fn = strjoina(new_name, ".nspawn"); + + rs = file_in_same_dir(path, fn); + if (!rs) + return -ENOMEM; + + return copy_file_atomic(path, rs, 0664, false, 0); +} + int image_clone(Image *i, const char *new_name, bool read_only) { _cleanup_release_lock_file_ LockFile name_lock = LOCK_FILE_INIT; + _cleanup_strv_free_ char **settings = NULL; const char *new_path; + char **j; int r; assert(i); @@ -471,6 +567,10 @@ int image_clone(Image *i, const char *new_name, bool read_only) { if (!image_name_is_valid(new_name)) return -EINVAL; + settings = image_settings_path(i); + if (!settings) + return -ENOMEM; + /* Make sure nobody takes the new name, between the time we * checked it is currently unused in all search paths, and the * time we take possesion of it */ @@ -490,7 +590,12 @@ int image_clone(Image *i, const char *new_name, bool read_only) { case IMAGE_DIRECTORY: new_path = strjoina("/var/lib/machines/", new_name); - r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE); + r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA); + + /* Enable "subtree" quotas for the copy, if we didn't + * copy any quota from the source. */ + (void) btrfs_subvol_auto_qgroup(i->path, 0, true); + break; case IMAGE_RAW: @@ -506,6 +611,12 @@ int image_clone(Image *i, const char *new_name, bool read_only) { if (r < 0) return r; + STRV_FOREACH(j, settings) { + r = clone_settings_file(*j, new_name); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j); + } + return 0; } @@ -526,6 +637,10 @@ int image_read_only(Image *i, bool b) { switch (i->type) { case IMAGE_SUBVOLUME: + + /* Note that we set the flag only on the top-level + * subvolume of the image. */ + r = btrfs_subvol_set_read_only(i->path, b); if (r < 0) return r; @@ -626,7 +741,14 @@ int image_set_limit(Image *i, uint64_t referenced_max) { if (i->type != IMAGE_SUBVOLUME) return -EOPNOTSUPP; - return btrfs_quota_limit(i->path, referenced_max); + /* We set the quota both for the subvolume as well as for the + * subtree. The latter is mostly for historical reasons, since + * we didn't use to have a concept of subtree quota, and hence + * only modified the subvolume quota. */ + + (void) btrfs_qgroup_set_limit(i->path, 0, referenced_max); + (void) btrfs_subvol_auto_qgroup(i->path, 0, true); + return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max); } int image_name_lock(const char *name, int operation, LockFile *ret) { |