diff --git a/man/ostree.repo-config.xml b/man/ostree.repo-config.xml index cbc605f7..5a95cafa 100644 --- a/man/ostree.repo-config.xml +++ b/man/ostree.repo-config.xml @@ -124,6 +124,15 @@ Boston, MA 02111-1307, USA. keep free. The default value is 3. + + min-free-space-size + Value (in MB, GB or TB) that specifies a minimum space (in blocks) + in the underlying filesystem to keep free. Also, note that min-free-space-percent + and min-free-space-size are mutually exclusive. Examples of acceptable values: + 500MB, 1GB etc. + + + add-remotes-config-dir diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index 8285a1a0..06ade885 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -889,7 +889,7 @@ write_content_object (OstreeRepo *self, size = 0; /* Free space check; only applies during transactions */ - if (self->min_free_space_percent > 0 && self->in_transaction) + if ((self->min_free_space_percent > 0 || self->min_free_space_size > 0) && self->in_transaction) { g_mutex_lock (&self->txn_lock); g_assert_cmpint (self->txn.blocksize, >, 0); @@ -898,8 +898,12 @@ write_content_object (OstreeRepo *self, { g_mutex_unlock (&self->txn_lock); g_autofree char *formatted_required = g_format_size ((guint64)object_blocks * self->txn.blocksize); - return glnx_throw (error, "min-free-space-percent '%u%%' would be exceeded, %s more required", - self->min_free_space_percent, formatted_required); + if (self->min_free_space_percent > 0) + return glnx_throw (error, "min-free-space-percent '%u%%' would be exceeded, %s more required", + self->min_free_space_percent, formatted_required); + else + return glnx_throw (error, "min-free-space-size %luMB would be exceeded, %s more required", + self->min_free_space_size, formatted_required); } /* This is the main bit that needs mutex protection */ self->txn.max_blocks -= object_blocks; @@ -1491,6 +1495,25 @@ devino_cache_lookup (OstreeRepo *self, return dev_ino_val->checksum; } +static guint64 +min_free_space_calculate_reserved_blocks (OstreeRepo *self, struct statvfs *stvfsbuf) +{ + guint64 reserved_blocks = 0; + + if (self->min_free_space_size > 0) + { + reserved_blocks = (self->min_free_space_size << 20) / stvfsbuf->f_bsize; + } + else if (self->min_free_space_percent > 0) + { + /* Convert fragment to blocks to compute the total */ + guint64 total_blocks = (stvfsbuf->f_frsize * stvfsbuf->f_blocks) / stvfsbuf->f_bsize; + reserved_blocks = ((double)total_blocks) * (self->min_free_space_percent/100.0); + } + + return reserved_blocks; +} + /** * ostree_repo_scan_hardlinks: * @self: An #OstreeRepo @@ -1572,26 +1595,28 @@ ostree_repo_prepare_transaction (OstreeRepo *self, return FALSE; self->in_transaction = TRUE; - if (self->min_free_space_percent > 0) + if (self->min_free_space_percent >= 0 || self->min_free_space_size >= 0) { struct statvfs stvfsbuf; if (TEMP_FAILURE_RETRY (fstatvfs (self->repo_dir_fd, &stvfsbuf)) < 0) return glnx_throw_errno_prefix (error, "fstatvfs"); g_mutex_lock (&self->txn_lock); self->txn.blocksize = stvfsbuf.f_bsize; - /* Convert fragment to blocks to compute the total */ - guint64 total_blocks = (stvfsbuf.f_frsize * stvfsbuf.f_blocks) / stvfsbuf.f_bsize; + guint64 reserved_blocks = min_free_space_calculate_reserved_blocks (self, &stvfsbuf); /* Use the appropriate free block count if we're unprivileged */ guint64 bfree = (getuid () != 0 ? stvfsbuf.f_bavail : stvfsbuf.f_bfree); - guint64 reserved_blocks = ((double)total_blocks) * (self->min_free_space_percent/100.0); if (bfree > reserved_blocks) self->txn.max_blocks = bfree - reserved_blocks; else { g_mutex_unlock (&self->txn_lock); g_autofree char *formatted_free = g_format_size (bfree * self->txn.blocksize); - return glnx_throw (error, "min-free-space-percent '%u%%' would be exceeded, %s available", - self->min_free_space_percent, formatted_free); + if (self->min_free_space_percent > 0) + return glnx_throw (error, "min-free-space-percent '%u%%' would be exceeded, %s available", + self->min_free_space_percent, formatted_free); + else + return glnx_throw (error, "min-free-space-size %luMB would be exceeded, %s available", + self->min_free_space_size, formatted_free); } g_mutex_unlock (&self->txn_lock); } diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index 63aa451a..eefb80e3 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -151,6 +151,7 @@ struct OstreeRepo { uid_t target_owner_uid; /* Ensure files are chowned to this uid/gid */ gid_t target_owner_gid; guint min_free_space_percent; /* See the min-free-space-percent config option */ + guint64 min_free_space_size; /* See the min-free-space-size config option */ guint test_error_flags; /* OstreeRepoTestErrorFlags */ diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index f3da1cae..fa5a9bf8 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -2654,6 +2654,48 @@ get_remotes_d_dir (OstreeRepo *self, return g_file_resolve_relative_path (sysroot, SYSCONF_REMOTES); } +static gboolean +min_free_space_size_validate_and_convert (OstreeRepo *self, + const char *min_free_space_size_str, + GError **error) +{ + static GRegex *regex; + static gsize regex_initialized; + if (g_once_init_enter (®ex_initialized)) + { + regex = g_regex_new ("^([0-9]+)(G|M|T)B$", 0, 0, NULL); + g_assert (regex); + g_once_init_leave (®ex_initialized, 1); + } + + g_autoptr(GMatchInfo) match = NULL; + if (!g_regex_match (regex, min_free_space_size_str, 0, &match)) + return glnx_prefix_error (error, "Error parsing min-free-space-size parameter: '%s'", min_free_space_size_str); + + g_autofree char *size_str = g_match_info_fetch (match, 1); + g_autofree char *unit = g_match_info_fetch (match, 2); + guint shifts; + + switch (*unit) + { + case 'M': + shifts = 0; + break; + case 'G': + shifts = 10; + break; + case 'T': + shifts = 20; + break; + default: + g_assert_not_reached (); + } + + self->min_free_space_size = g_ascii_strtoull (size_str, NULL, 10) << shifts; + + return TRUE; +} + static gboolean reload_core_config (OstreeRepo *self, GCancellable *cancellable, @@ -2771,18 +2813,39 @@ reload_core_config (OstreeRepo *self, self->zlib_compression_level = OSTREE_ARCHIVE_DEFAULT_COMPRESSION_LEVEL; } - { g_autofree char *min_free_space_percent_str = NULL; - /* If changing this, be sure to change the man page too */ - const char *default_min_free_space = "3"; + { + if (g_key_file_has_key (self->config, "core", "min-free-space-size", error) && + g_key_file_has_key (self->config, "core", "min-free-space-percent", error)) + { + return glnx_throw (error, "min-free-space-percent and min-free-space-size are mutually exclusive."); + } + else if (g_key_file_has_key (self->config, "core", "min-free-space-size", error)) + { + g_autofree char *min_free_space_size_str = NULL; - if (!ot_keyfile_get_value_with_default (self->config, "core", "min-free-space-percent", - default_min_free_space, - &min_free_space_percent_str, error)) - return FALSE; + if (!ot_keyfile_get_value_with_default (self->config, "core", "min-free-space-size", + NULL, &min_free_space_size_str, error)) + return FALSE; - self->min_free_space_percent = g_ascii_strtoull (min_free_space_percent_str, NULL, 10); - if (self->min_free_space_percent > 99) - return glnx_throw (error, "Invalid min-free-space-percent '%s'", min_free_space_percent_str); + /* Validate the string and convert the size to MBs */ + if (!min_free_space_size_validate_and_convert (self, min_free_space_size_str, error)) + return glnx_throw (error, "Invalid min-free-space-size '%s'", min_free_space_size_str); + } + else + { + g_autofree char *min_free_space_percent_str = NULL; + /* If changing this, be sure to change the man page too */ + const char *default_min_free_space = "3"; + + if (!ot_keyfile_get_value_with_default (self->config, "core", "min-free-space-percent", + default_min_free_space, + &min_free_space_percent_str, error)) + return FALSE; + + self->min_free_space_percent = g_ascii_strtoull (min_free_space_percent_str, NULL, 10); + if (self->min_free_space_percent > 99) + return glnx_throw (error, "Invalid min-free-space-percent '%s'", min_free_space_percent_str); + } } { diff --git a/tests/installed/nondestructive/itest-pull-space.sh b/tests/installed/nondestructive/itest-pull-space.sh index 925629b2..bb3f1546 100755 --- a/tests/installed/nondestructive/itest-pull-space.sh +++ b/tests/installed/nondestructive/itest-pull-space.sh @@ -16,12 +16,27 @@ blkdev=$(losetup --find --show $(pwd)/testblk.img) mkfs.xfs ${blkdev} mkdir mnt mount ${blkdev} mnt + +# first test min-free-space-percent ostree --repo=mnt/repo init --mode=bare-user echo 'fsync=false' >> mnt/repo/config if ostree --repo=mnt/repo pull-local /ostree/repo ${host_commit} 2>err.txt; then fatal "succeeded in doing a pull with no free space" fi assert_file_has_content err.txt "min-free-space-percent" +echo "ok min-free-space-percent" + +# now test min-free-space-size +rm -rf mnt/repo +ostree --repo=mnt/repo init --mode=bare-user +echo 'fsync=false' >> mnt/repo/config +echo 'min-free-space-size=10MB' >> mnt/repo/config +if ostree --repo=mnt/repo pull-local /ostree/repo ${host_commit} 2>err.txt; then + fatal "succeeded in doing a pull with no free space" +fi +assert_file_has_content err.txt "min-free-space-size" +echo "ok min-free-space-size" + umount mnt losetup -d ${blkdev} date