diff --git a/src/import/import-dkr.c b/src/import/import-dkr.c index fb72f6cee3..2d4e9b398f 100644 --- a/src/import/import-dkr.c +++ b/src/import/import-dkr.c @@ -28,6 +28,7 @@ #include "btrfs-util.h" #include "utf8.h" #include "mkdir.h" +#include "path-util.h" #include "import-util.h" #include "curl-util.h" #include "aufs-util.h" @@ -72,6 +73,7 @@ struct DkrImport { char *local; bool force_local; + bool grow_machine_directory; char *temp_path; char *final_path; @@ -156,6 +158,8 @@ int dkr_import_new( if (!i->image_root) return -ENOMEM; + i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); + i->index_url = strdup(index_url); if (!i->index_url) return -ENOMEM; @@ -561,6 +565,7 @@ static int dkr_import_pull_layer(DkrImport *i) { i->layer_job->on_finished = dkr_import_job_on_finished; i->layer_job->on_open_disk = dkr_import_job_on_open_disk; i->layer_job->on_progress = dkr_import_job_on_progress; + i->layer_job->grow_machine_directory = i->grow_machine_directory; r = import_job_begin(i->layer_job); if (r < 0) diff --git a/src/import/import-job.c b/src/import/import-job.c index 809486500b..980b639b5d 100644 --- a/src/import/import-job.c +++ b/src/import/import-job.c @@ -22,8 +22,12 @@ #include #include "strv.h" +#include "machine-pool.h" #include "import-job.h" +/* Grow the /var/lib/machines directory after each 10MiB written */ +#define IMPORT_GROW_INTERVAL_BYTES (UINT64_C(10) * UINT64_C(1024) * UINT64_C(1024)) + ImportJob* import_job_unref(ImportJob *j) { if (!j) return NULL; @@ -197,6 +201,11 @@ static int import_job_write_uncompressed(ImportJob *j, void *p, size_t sz) { if (j->disk_fd >= 0) { + if (j->grow_machine_directory && j->written_since_last_grow >= IMPORT_GROW_INTERVAL_BYTES) { + j->written_since_last_grow = 0; + grow_machine_directory(); + } + if (j->allow_sparse) n = sparse_write(j->disk_fd, p, sz, 64); else @@ -219,6 +228,7 @@ static int import_job_write_uncompressed(ImportJob *j, void *p, size_t sz) { } j->written_uncompressed += sz; + j->written_since_last_grow += sz; return 0; } @@ -667,6 +677,9 @@ int import_job_begin(ImportJob *j) { if (j->state != IMPORT_JOB_INIT) return -EBUSY; + if (j->grow_machine_directory) + grow_machine_directory(); + r = curl_glue_make(&j->curl, j->url, j); if (r < 0) return r; diff --git a/src/import/import-job.h b/src/import/import-job.h index dcf89cb28c..2c01d723db 100644 --- a/src/import/import-job.h +++ b/src/import/import-job.h @@ -107,6 +107,9 @@ struct ImportJob { gcry_md_hd_t checksum_context; char *checksum; + + bool grow_machine_directory; + uint64_t written_since_last_grow; }; int import_job_new(ImportJob **job, const char *url, CurlGlue *glue, void *userdata); diff --git a/src/import/import-raw.c b/src/import/import-raw.c index 8d99f1085c..89c064cb3d 100644 --- a/src/import/import-raw.c +++ b/src/import/import-raw.c @@ -31,6 +31,7 @@ #include "util.h" #include "macro.h" #include "mkdir.h" +#include "path-util.h" #include "import-util.h" #include "curl-util.h" #include "qcow2-util.h" @@ -61,6 +62,7 @@ struct RawImport { char *local; bool force_local; + bool grow_machine_directory; char *temp_path; char *final_path; @@ -115,6 +117,8 @@ int raw_import_new( if (!i->image_root) return -ENOMEM; + i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); + if (event) i->event = sd_event_ref(event); else { @@ -480,6 +484,7 @@ int raw_import_pull(RawImport *i, const char *url, const char *local, bool force i->raw_job->on_open_disk = raw_import_job_on_open_disk; i->raw_job->on_progress = raw_import_job_on_progress; i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO; + i->raw_job->grow_machine_directory = i->grow_machine_directory; r = import_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags); if (r < 0) diff --git a/src/import/import-tar.c b/src/import/import-tar.c index 493252a132..472e336247 100644 --- a/src/import/import-tar.c +++ b/src/import/import-tar.c @@ -30,6 +30,7 @@ #include "util.h" #include "macro.h" #include "mkdir.h" +#include "path-util.h" #include "import-util.h" #include "curl-util.h" #include "import-job.h" @@ -58,6 +59,7 @@ struct TarImport { char *local; bool force_local; + bool grow_machine_directory; pid_t tar_pid; @@ -121,6 +123,8 @@ int tar_import_new( if (!i->image_root) return -ENOMEM; + i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); + if (event) i->event = sd_event_ref(event); else { @@ -376,6 +380,7 @@ int tar_import_pull(TarImport *i, const char *url, const char *local, bool force i->tar_job->on_open_disk = tar_import_job_on_open_disk; i->tar_job->on_progress = tar_import_job_on_progress; i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO; + i->tar_job->grow_machine_directory = i->grow_machine_directory; r = import_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags); if (r < 0) diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 5ab40b0410..adba8122f3 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -805,14 +805,16 @@ static int method_set_pool_limit(sd_bus *bus, sd_bus_message *message, void *use if (r < 0) return r; - r = btrfs_resize_loopback("/var/lib/machines", limit); - if (r < 0 && r != -ENODEV) + r = btrfs_resize_loopback("/var/lib/machines", limit, false); + if (r == -ENOTTY) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs."); + if (r < 0 && r != -ENODEV) /* ignore ENODEV, as that's what is returned if the file system is not on loopback */ return sd_bus_error_set_errnof(error, r, "Failed to adjust loopback limit: %m"); r = btrfs_quota_limit("/var/lib/machines", limit); if (r == -ENOTTY) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs."); - else if (r < 0) + if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m"); return sd_bus_reply_method_return(message, NULL); diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index 52fc5f4a17..256c5a6995 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -705,7 +705,7 @@ int btrfs_quota_limit(const char *path, uint64_t referred_max) { return btrfs_quota_limit_fd(fd, referred_max); } -int btrfs_resize_loopback_fd(int fd, uint64_t new_size) { +int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) { struct btrfs_ioctl_vol_args args = {}; _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL; _cleanup_close_ int loop_fd = -1, backing_fd = -1; @@ -726,6 +726,8 @@ int btrfs_resize_loopback_fd(int fd, uint64_t new_size) { if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0) return -ENOMEM; r = read_one_line_file(p, &backing); + if (r == -ENOENT) + return -ENODEV; if (r < 0) return r; if (isempty(backing) || !path_is_absolute(backing)) @@ -743,6 +745,9 @@ int btrfs_resize_loopback_fd(int fd, uint64_t new_size) { if (new_size == (uint64_t) st.st_size) return 0; + if (grow_only && new_size < (uint64_t) st.st_size) + return -EINVAL; + if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0) return -ENOMEM; loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY); @@ -770,15 +775,19 @@ int btrfs_resize_loopback_fd(int fd, uint64_t new_size) { return -errno; } - return 0; + /* Make sure the free disk space is correctly updated for both file systems */ + (void) fsync(fd); + (void) fsync(backing_fd); + + return 1; } -int btrfs_resize_loopback(const char *p, uint64_t new_size) { +int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) { _cleanup_close_ int fd = -1; fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC); if (fd < 0) return -errno; - return btrfs_resize_loopback_fd(fd, new_size); + return btrfs_resize_loopback_fd(fd, new_size, grow_only); } diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h index e654a3fea1..a2c246b8d6 100644 --- a/src/shared/btrfs-util.h +++ b/src/shared/btrfs-util.h @@ -72,5 +72,5 @@ int btrfs_quota_enable(const char *path, bool b); int btrfs_quota_limit_fd(int fd, uint64_t referred_max); int btrfs_quota_limit(const char *path, uint64_t referred_max); -int btrfs_resize_loopback_fd(int fd, uint64_t size); -int btrfs_resize_loopback(const char *path, uint64_t size); +int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only); +int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only); diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c index b74252dbc8..3eafb9443a 100644 --- a/src/shared/machine-pool.c +++ b/src/shared/machine-pool.c @@ -169,6 +169,7 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) { _cleanup_free_ char* loopdev = NULL; char tmpdir[] = "/tmp/import-mount.XXXXXX", *mntdir = NULL; bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false; + char buf[FORMAT_BYTES_MAX]; int r, nr = -1; /* btrfs cannot handle file systems < 16M, hence use this as minimum */ @@ -274,6 +275,10 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) { goto fail; } + (void) syncfs(fd); + + log_info("Set up /var/lib/machines as btrfs loopback file system of size %s mounted on /var/lib/machines.raw.", format_bytes(buf, sizeof(buf), size)); + (void) umount2(mntdir, MNT_DETACH); (void) rmdir(mntdir); (void) rmdir(tmpdir); @@ -299,3 +304,73 @@ fail: return r; } + +static int sync_path(const char *p) { + _cleanup_close_ int fd = -1; + + fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + if (syncfs(fd) < 0) + return -errno; + + return 0; +} + +int grow_machine_directory(void) { + char buf[FORMAT_BYTES_MAX]; + struct statvfs a, b; + uint64_t old_size, new_size, max_add; + int r; + + /* Ensure the disk space data is accurate */ + sync_path("/var/lib/machines"); + sync_path("/var/lib/machines.raw"); + + if (statvfs("/var/lib/machines.raw", &a) < 0) + return -errno; + + if (statvfs("/var/lib/machines", &b) < 0) + return -errno; + + /* Don't grow if not enough disk space is available on the host */ + if (((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) <= VAR_LIB_MACHINES_FREE_MIN) + return 0; + + /* Don't grow if at least 1/3th of the fs is still free */ + if (b.f_bavail > b.f_blocks / 3) + return 0; + + /* Calculate how much we are willing to add at maximum */ + max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN; + + /* Calculate the old size */ + old_size = (uint64_t) b.f_blocks * (uint64_t) b.f_bsize; + + /* Calculate the new size as three times the size of what is used right now */ + new_size = ((uint64_t) b.f_blocks - (uint64_t) b.f_bavail) * (uint64_t) b.f_bsize * 3; + + /* Always, grow at least to the start size */ + if (new_size < VAR_LIB_MACHINES_SIZE_START) + new_size = VAR_LIB_MACHINES_SIZE_START; + + /* If the new size is smaller than the old size, don't grow */ + if (new_size < old_size) + return 0; + + /* Ensure we never add more than the maximum */ + if (new_size > old_size + max_add) + new_size = old_size + max_add; + + r = btrfs_resize_loopback("/var/lib/machines", new_size, true); + if (r <= 0) + return r; + + r = btrfs_quota_limit("/var/lib/machines", new_size); + if (r < 0) + return r; + + log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf, sizeof(buf), new_size)); + return 1; +} diff --git a/src/shared/machine-pool.h b/src/shared/machine-pool.h index 9c9849f618..06c5d40583 100644 --- a/src/shared/machine-pool.h +++ b/src/shared/machine-pool.h @@ -24,3 +24,4 @@ #include "sd-bus.h" int setup_machine_directory(uint64_t size, sd_bus_error *error); +int grow_machine_directory(void);