lib/repo: Add min-free-space-percent option, default 3%
For ostree-as-host, we're the superuser, so we'll blow past any reserved free space by default. While deltas have size metadata, if one happens to do a loose fetch, we can fill up the disk. Another case is flatpak: the system helper has similar concerns here as ostree-as-host, and for `flatpak --user`, we also want to be nice and avoid filling up the user's quota. Closes: https://github.com/ostreedev/ostree/issues/962 Closes: #987 Approved by: jlebon
This commit is contained in:
parent
8d4d638e99
commit
1f5ce1a9f7
|
|
@ -190,6 +190,14 @@ Boston, MA 02111-1307, USA.
|
||||||
<term><varname>unconfigured-state</varname></term>
|
<term><varname>unconfigured-state</varname></term>
|
||||||
<listitem><para>If set, pulls from this remote will fail with the configured text. This is intended for OS vendors which have a subscription process to access content.</para></listitem>
|
<listitem><para>If set, pulls from this remote will fail with the configured text. This is intended for OS vendors which have a subscription process to access content.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>min-free-space-percent</varname></term>
|
||||||
|
<listitem><para>Integer percentage value (0-99) that specifies a minimum
|
||||||
|
percentage of total space (in blocks) in the underlying filesystem to
|
||||||
|
keep free. The default value is 3.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <glib-unix.h>
|
#include <glib-unix.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
#include <gio/gfiledescriptorbased.h>
|
#include <gio/gfiledescriptorbased.h>
|
||||||
#include <gio/gunixinputstream.h>
|
#include <gio/gunixinputstream.h>
|
||||||
#include <gio/gunixoutputstream.h>
|
#include <gio/gunixoutputstream.h>
|
||||||
|
|
@ -574,6 +575,24 @@ write_content_object (OstreeRepo *self,
|
||||||
else
|
else
|
||||||
size = 0;
|
size = 0;
|
||||||
|
|
||||||
|
/* Free space check; only applies during transactions */
|
||||||
|
if (self->min_free_space_percent > 0 && self->in_transaction)
|
||||||
|
{
|
||||||
|
g_mutex_lock (&self->txn_stats_lock);
|
||||||
|
g_assert_cmpint (self->txn_blocksize, >, 0);
|
||||||
|
const fsblkcnt_t object_blocks = (size / self->txn_blocksize) + 1;
|
||||||
|
if (object_blocks > self->max_txn_blocks)
|
||||||
|
{
|
||||||
|
g_mutex_unlock (&self->txn_stats_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);
|
||||||
|
}
|
||||||
|
/* This is the main bit that needs mutex protection */
|
||||||
|
self->max_txn_blocks -= object_blocks;
|
||||||
|
g_mutex_unlock (&self->txn_stats_lock);
|
||||||
|
}
|
||||||
|
|
||||||
/* For regular files, we create them with default mode, and only
|
/* For regular files, we create them with default mode, and only
|
||||||
* later apply any xattrs and setuid bits. The rationale here
|
* later apply any xattrs and setuid bits. The rationale here
|
||||||
* is that an attacker on the network with the ability to MITM
|
* is that an attacker on the network with the ability to MITM
|
||||||
|
|
@ -1080,6 +1099,29 @@ ostree_repo_prepare_transaction (OstreeRepo *self,
|
||||||
memset (&self->txn_stats, 0, sizeof (OstreeRepoTransactionStats));
|
memset (&self->txn_stats, 0, sizeof (OstreeRepoTransactionStats));
|
||||||
|
|
||||||
self->in_transaction = TRUE;
|
self->in_transaction = TRUE;
|
||||||
|
if (self->min_free_space_percent > 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_stats_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;
|
||||||
|
/* 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->max_txn_blocks = bfree - reserved_blocks;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_mutex_unlock (&self->txn_stats_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);
|
||||||
|
}
|
||||||
|
g_mutex_unlock (&self->txn_stats_lock);
|
||||||
|
}
|
||||||
|
|
||||||
gboolean ret_transaction_resume = FALSE;
|
gboolean ret_transaction_resume = FALSE;
|
||||||
if (!_ostree_repo_allocate_tmpdir (self->tmp_dir_fd,
|
if (!_ostree_repo_allocate_tmpdir (self->tmp_dir_fd,
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/statvfs.h>
|
||||||
#include "ostree-ref.h"
|
#include "ostree-ref.h"
|
||||||
#include "ostree-repo.h"
|
#include "ostree-repo.h"
|
||||||
#include "ostree-remote-private.h"
|
#include "ostree-remote-private.h"
|
||||||
|
|
@ -102,6 +103,9 @@ struct OstreeRepo {
|
||||||
GHashTable *txn_collection_refs; /* (element-type OstreeCollectionRef utf8) */
|
GHashTable *txn_collection_refs; /* (element-type OstreeCollectionRef utf8) */
|
||||||
GMutex txn_stats_lock;
|
GMutex txn_stats_lock;
|
||||||
OstreeRepoTransactionStats txn_stats;
|
OstreeRepoTransactionStats txn_stats;
|
||||||
|
/* Implementation of min-free-space-percent */
|
||||||
|
gulong txn_blocksize;
|
||||||
|
fsblkcnt_t max_txn_blocks;
|
||||||
|
|
||||||
GMutex cache_lock;
|
GMutex cache_lock;
|
||||||
guint dirmeta_cache_refcount;
|
guint dirmeta_cache_refcount;
|
||||||
|
|
@ -123,6 +127,7 @@ struct OstreeRepo {
|
||||||
uid_t owner_uid;
|
uid_t owner_uid;
|
||||||
uid_t target_owner_uid;
|
uid_t target_owner_uid;
|
||||||
gid_t target_owner_gid;
|
gid_t target_owner_gid;
|
||||||
|
guint min_free_space_percent;
|
||||||
|
|
||||||
guint test_error_flags; /* OstreeRepoTestErrorFlags */
|
guint test_error_flags; /* OstreeRepoTestErrorFlags */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <glib/gstdio.h>
|
#include <glib/gstdio.h>
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:ostree-repo
|
* SECTION:ostree-repo
|
||||||
|
|
@ -1964,6 +1965,20 @@ reload_core_config (OstreeRepo *self,
|
||||||
self->zlib_compression_level = OSTREE_ARCHIVE_DEFAULT_COMPRESSION_LEVEL;
|
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 (!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);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
g_clear_pointer (&self->collection_id, g_free);
|
g_clear_pointer (&self->collection_id, g_free);
|
||||||
if (!ot_keyfile_get_value_with_default (self->config, "core", "collection-id",
|
if (!ot_keyfile_get_value_with_default (self->config, "core", "collection-id",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Test min-free-space-percent using loopback devices
|
||||||
|
|
||||||
|
set -xeuo pipefail
|
||||||
|
|
||||||
|
dn=$(dirname $0)
|
||||||
|
. ${dn}/libinsttest.sh
|
||||||
|
|
||||||
|
test_tmpdir=$(prepare_tmpdir)
|
||||||
|
trap _tmpdir_cleanup EXIT
|
||||||
|
|
||||||
|
cd ${test_tmpdir}
|
||||||
|
truncate -s 100MB testblk.img
|
||||||
|
blkdev=$(losetup --find --show $(pwd)/testblk.img)
|
||||||
|
mkfs.xfs ${blkdev}
|
||||||
|
mkdir mnt
|
||||||
|
mount ${blkdev} mnt
|
||||||
|
ostree --repo=mnt/repo init --mode=bare-user
|
||||||
|
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"
|
||||||
|
umount mnt
|
||||||
|
losetup -d ${blkdev}
|
||||||
Loading…
Reference in New Issue