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