New upstream version 2021.3

This commit is contained in:
Simon McVittie committed 2021-07-25 18:38:13 +01:00
commit 5e89318de3
106 files changed
+4795 -3705

No files matched your search

+1 -1
View File
@@ -28,7 +28,7 @@ After=systemd-remount-fs.service
# But we run *before* most other core bootup services that need write access to /etc and /var
Before=local-fs.target umount.target
Before=systemd-random-seed.service plymouth-read-write.service systemd-journal-flush.service
Before=systemd-tmpfiles-setup.service
Before=systemd-tmpfiles-setup.service systemd-rfkill.service systemd-rfkill.socket
[Service]
Type=oneshot
+8
View File
@@ -653,6 +653,14 @@ global:
ostree_content_writer_finish;
} LIBOSTREE_2021.1;
LIBOSTREE_2021.3 {
global:
ostree_repo_auto_lock_push;
ostree_repo_auto_lock_cleanup;
ostree_repo_lock_push;
ostree_repo_lock_pop;
} LIBOSTREE_2021.2;
/* NOTE: Only add more content here in release commits! See the
* comments at the top of this file.
*/
+4 -4
View File
@@ -160,11 +160,11 @@ ostree_validate_checksum_string (const char *sha256,
* @out_ref: (out) (not nullable) (optional): Return location for the ref name
* @error: Error
*
* Split a refspec like `gnome-ostree:gnome-ostree/buildmaster` or just
* `gnome-ostree/buildmaster` into two parts. In the first case, @out_remote
* will be set to `gnome-ostree`, and @out_ref to `gnome-ostree/buildmaster`.
* Split a refspec like `gnome-ostree:gnome-ostree/buildmain` or just
* `gnome-ostree/buildmain` into two parts. In the first case, @out_remote
* will be set to `gnome-ostree`, and @out_ref to `gnome-ostree/buildmain`.
* In the second case (a local ref), @out_remote will be %NULL, and @out_ref
* will be `gnome-ostree/buildmaster`. In both cases, %TRUE will be returned.
* will be `gnome-ostree/buildmain`. In both cases, %TRUE will be returned.
*
* Returns: %TRUE on successful parsing, %FALSE otherwise
*/
+18
View File
@@ -117,3 +117,21 @@ _ostree_fetcher_uri_to_string (OstreeFetcherURI *uri)
{
return soup_uri_to_string ((SoupURI*)uri, FALSE);
}
/* Only accept http, https, and file; particularly curl has a ton of other
* backends like sftp that we don't want, and this also gracefully filters
* out invalid input.
*/
gboolean
_ostree_fetcher_uri_validate (OstreeFetcherURI *uri, GError **error)
{
const char *scheme = soup_uri_get_scheme ((SoupURI*)uri);
// TODO only allow file if explicitly requested by a higher level
if (!(g_str_equal (scheme, "http") || g_str_equal (scheme, "https") || g_str_equal (scheme, "file")))
{
g_autofree char *s = _ostree_fetcher_uri_to_string (uri);
return glnx_throw (error, "Invalid URI scheme in %s", s);
}
return TRUE;
}
+3
View File
@@ -90,6 +90,9 @@ _ostree_fetcher_uri_get_path (OstreeFetcherURI *uri);
char *
_ostree_fetcher_uri_to_string (OstreeFetcherURI *uri);
gboolean
_ostree_fetcher_uri_validate (OstreeFetcherURI *uri, GError **error);
GType _ostree_fetcher_get_type (void) G_GNUC_CONST;
OstreeFetcher *_ostree_fetcher_new (int tmpdir_dfd,
+53 -7
View File
@@ -113,13 +113,43 @@ stateroot_from_ostree_cmdline (const char *ostree_cmdline,
}
#endif
/* Implementation of ostree-system-generator */
gboolean
_ostree_impl_system_generator (const char *ostree_cmdline,
const char *normal_dir,
const char *early_dir,
const char *late_dir,
GError **error)
/* Forcibly enable our internal units, since we detected ostree= on the kernel cmdline */
static gboolean
require_internal_units (const char *normal_dir,
const char *early_dir,
const char *late_dir,
GError **error)
{
#ifdef SYSTEM_DATA_UNIT_PATH
GCancellable *cancellable = NULL;
glnx_autofd int normal_dir_dfd = -1;
if (!glnx_opendirat (AT_FDCWD, normal_dir, TRUE, &normal_dir_dfd, error))
return FALSE;
if (!glnx_shutil_mkdir_p_at (normal_dir_dfd, "local-fs.target.requires", 0755, cancellable, error))
return FALSE;
if (symlinkat (SYSTEM_DATA_UNIT_PATH "/ostree-remount.service", normal_dir_dfd, "local-fs.target.requires/ostree-remount.service") < 0)
return glnx_throw_errno_prefix (error, "symlinkat");
if (!glnx_shutil_mkdir_p_at (normal_dir_dfd, "multi-user.target.wants", 0755, cancellable, error))
return FALSE;
if (symlinkat (SYSTEM_DATA_UNIT_PATH "/ostree-finalize-staged.path", normal_dir_dfd, "multi-user.target.wants/ostree-finalize-staged.path") < 0)
return glnx_throw_errno_prefix (error, "symlinkat");
return TRUE;
#else
return glnx_throw (error, "Not implemented");
#endif
}
/* Generate var.mount */
static gboolean
fstab_generator (const char *ostree_cmdline,
const char *normal_dir,
const char *early_dir,
const char *late_dir,
GError **error)
{
#ifdef HAVE_LIBMOUNT
/* Not currently cancellable, but define a var in case we care later */
@@ -225,3 +255,19 @@ _ostree_impl_system_generator (const char *ostree_cmdline,
return glnx_throw (error, "Not implemented");
#endif
}
/* Implementation of ostree-system-generator */
gboolean
_ostree_impl_system_generator (const char *ostree_cmdline,
const char *normal_dir,
const char *early_dir,
const char *late_dir,
GError **error)
{
if (!require_internal_units (normal_dir, early_dir, late_dir, error))
return FALSE;
if (!fstab_generator (ostree_cmdline, normal_dir, early_dir, late_dir, error))
return FALSE;
return TRUE;
}
+27 -6
View File
@@ -1684,8 +1684,8 @@ ostree_repo_prepare_transaction (OstreeRepo *self,
memset (&self->txn.stats, 0, sizeof (OstreeRepoTransactionStats));
self->txn_locked = _ostree_repo_lock_push (self, OSTREE_REPO_LOCK_SHARED,
cancellable, error);
self->txn_locked = ostree_repo_lock_push (self, OSTREE_REPO_LOCK_SHARED,
cancellable, error);
if (!self->txn_locked)
return FALSE;
@@ -2341,7 +2341,7 @@ ostree_repo_commit_transaction (OstreeRepo *self,
if (self->txn_locked)
{
if (!_ostree_repo_lock_pop (self, cancellable, error))
if (!ostree_repo_lock_pop (self, OSTREE_REPO_LOCK_SHARED, cancellable, error))
return FALSE;
self->txn_locked = FALSE;
}
@@ -2399,7 +2399,7 @@ ostree_repo_abort_transaction (OstreeRepo *self,
if (self->txn_locked)
{
if (!_ostree_repo_lock_pop (self, cancellable, error))
if (!ostree_repo_lock_pop (self, OSTREE_REPO_LOCK_SHARED, cancellable, error))
return FALSE;
self->txn_locked = FALSE;
}
@@ -3020,6 +3020,10 @@ create_empty_gvariant_dict (void)
*
* Write a commit metadata object, referencing @root_contents_checksum
* and @root_metadata_checksum.
* This uses the current time as the commit timestamp, but it can be
* overridden with an explicit timestamp via the
* [standard](https://reproducible-builds.org/specs/source-date-epoch/)
* `SOURCE_DATE_EPOCH` environment flag.
*/
gboolean
ostree_repo_write_commit (OstreeRepo *self,
@@ -3032,9 +3036,26 @@ ostree_repo_write_commit (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GDateTime) now = g_date_time_new_now_utc ();
gint64 timestamp = 0;
const gchar *env_timestamp = g_getenv ("SOURCE_DATE_EPOCH");
if (env_timestamp == NULL)
{
g_autoptr(GDateTime) now = g_date_time_new_now_utc ();
timestamp = g_date_time_to_unix (now);
}
else
{
gchar *ret = NULL;
errno = 0;
timestamp = g_ascii_strtoll (env_timestamp, &ret, 10);
if (errno != 0)
return glnx_throw_errno_prefix (error, "Parsing SOURCE_DATE_EPOCH");
if (ret == env_timestamp)
return glnx_throw (error, "Failed to convert SOURCE_DATE_EPOCH");
}
return ostree_repo_write_commit_with_time (self, parent, subject, body,
metadata, root, g_date_time_to_unix (now),
metadata, root, timestamp,
out_commit, cancellable, error);
}
+9 -24
View File
@@ -104,6 +104,13 @@ typedef struct {
fsblkcnt_t max_blocks;
} OstreeRepoTxn;
typedef struct {
GMutex mutex; /* All other members should only be accessed with this held */
int fd; /* The open file or flock file descriptor */
guint shared; /* Number of shared locks curently held */
guint exclusive; /* Number of exclusive locks currently held */
} OstreeRepoLock;
typedef enum {
_OSTREE_FEATURE_NO,
_OSTREE_FEATURE_MAYBE,
@@ -159,6 +166,8 @@ struct OstreeRepo {
GWeakRef sysroot; /* Weak to avoid a circular ref; see also `is_system` */
char *remotes_config_dir;
OstreeRepoLock lock;
GMutex txn_lock;
OstreeRepoTxn txn;
gboolean txn_locked;
@@ -506,30 +515,6 @@ _ostree_repo_maybe_regenerate_summary (OstreeRepo *self,
GCancellable *cancellable,
GError **error);
/* Locking APIs are currently private.
* See https://github.com/ostreedev/ostree/pull/1555
*/
typedef enum {
OSTREE_REPO_LOCK_SHARED,
OSTREE_REPO_LOCK_EXCLUSIVE
} OstreeRepoLockType;
gboolean _ostree_repo_lock_push (OstreeRepo *self,
OstreeRepoLockType lock_type,
GCancellable *cancellable,
GError **error);
gboolean _ostree_repo_lock_pop (OstreeRepo *self,
GCancellable *cancellable,
GError **error);
typedef OstreeRepo OstreeRepoAutoLock;
OstreeRepoAutoLock * _ostree_repo_auto_lock_push (OstreeRepo *self,
OstreeRepoLockType lock_type,
GCancellable *cancellable,
GError **error);
void _ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoLock, _ostree_repo_auto_lock_cleanup)
gboolean _ostree_repo_parse_fsverity_config (OstreeRepo *self, GError **error);
+4 -4
View File
@@ -204,7 +204,7 @@ ostree_repo_prune_static_deltas (OstreeRepo *self, const char *commit,
GError **error)
{
g_autoptr(OstreeRepoAutoLock) lock =
_ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error);
ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error);
if (!lock)
return FALSE;
@@ -325,7 +325,7 @@ ostree_repo_traverse_reachable_refs (OstreeRepo *self,
GError **error)
{
g_autoptr(OstreeRepoAutoLock) lock =
_ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_SHARED, cancellable, error);
ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_SHARED, cancellable, error);
if (!lock)
return FALSE;
@@ -400,7 +400,7 @@ ostree_repo_prune (OstreeRepo *self,
GError **error)
{
g_autoptr(OstreeRepoAutoLock) lock =
_ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error);
ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error);
if (!lock)
return FALSE;
@@ -486,7 +486,7 @@ ostree_repo_prune_from_reachable (OstreeRepo *self,
GError **error)
{
g_autoptr(OstreeRepoAutoLock) lock =
_ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error);
ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error);
if (!lock)
return FALSE;
+3
View File
@@ -3446,6 +3446,9 @@ compute_effective_mirrorlist (OstreeRepo *self,
if (!baseuri)
return FALSE;
if (!_ostree_fetcher_uri_validate (baseuri, error))
return FALSE;
*out_mirrorlist =
g_ptr_array_new_with_free_func ((GDestroyNotify) _ostree_fetcher_uri_free);
g_ptr_array_add (*out_mirrorlist, g_steal_pointer (&baseuri));
@@ -1270,7 +1270,7 @@ ostree_repo_static_delta_reindex (OstreeRepo *repo,
/* Protect against parallel prune operation */
g_autoptr(OstreeRepoAutoLock) lock =
_ostree_repo_auto_lock_push (repo, OSTREE_REPO_LOCK_SHARED, cancellable, error);
ostree_repo_auto_lock_push (repo, OSTREE_REPO_LOCK_SHARED, cancellable, error);
if (!lock)
return FALSE;
+204 -180
View File
@@ -172,98 +172,84 @@ G_DEFINE_TYPE (OstreeRepo, ostree_repo, G_TYPE_OBJECT)
/* Repository locking
*
* To guard against objects being deleted (e.g., prune) while they're in
* use by another operation is accessing them (e.g., commit), the
* use by another operation that is accessing them (e.g., commit), the
* repository must be locked by concurrent writers.
*
* The locking is implemented by maintaining a thread local table of
* lock stacks per repository. This allows thread safe locking since
* each thread maintains its own lock stack. See the OstreeRepoLock type
* below.
* The repository locking has several important features:
*
* The actual locking is done using either open file descriptor locks or
* flock locks. This allows the locking to work with concurrent
* processes. The lock file is held on the ".lock" file within the
* repository.
* * There are 2 states - shared and exclusive. Multiple users can hold
* a shared lock concurrently while only one user can hold an
* exclusive lock.
*
* * The lock can be taken recursively so long as each acquisition is paired
* with a matching release. The recursion is also latched to the strongest
* state. Once an exclusive lock has been taken, it will remain exclusive
* until all exclusive locks have been released.
*
* * It is both multiprocess- and multithread-safe. Threads that share
* an OstreeRepo use the lock cooperatively while processes and
* threads using separate OstreeRepo structures will block when
* acquiring incompatible lock states.
*
* The actual locking is implemented using either open file descriptor
* locks or flock locks. This allows the locking to work with concurrent
* processes or concurrent threads using a separate OstreeRepo. The lock
* file is held on the ".lock" file within the repository.
*
* The intended usage is to take a shared lock when writing objects or
* reading objects in critical sections. Exclusive locks are taken when
* deleting objects.
*
* To allow fine grained locking within libostree, the lock is
* maintained as a stack. The core APIs then push or pop from the stack.
* When pushing or popping a lock state identical to the existing or
* next state, the stack is simply updated. Only when upgrading or
* downgrading the lock (changing to/from unlocked, pushing exclusive on
* shared or popping exclusive to shared) are actual locking operations
* performed.
* To allow fine grained locking, the lock state is maintained in shared and
* exclusive counters. Callers then push or pop lock types to increment or
* decrement the counters. When pushing or popping a lock type identical to
* the existing or next state, the lock state is simply updated. Only when
* upgrading or downgrading the lock (changing to/from unlocked, pushing
* exclusive on shared or popping exclusive to shared) are actual locking
* operations performed.
*/
static void
free_repo_lock_table (gpointer data)
{
GHashTable *lock_table = data;
if (lock_table != NULL)
{
g_debug ("Free lock table");
g_hash_table_destroy (lock_table);
}
}
static GPrivate repo_lock_table = G_PRIVATE_INIT (free_repo_lock_table);
typedef struct {
int fd;
GQueue stack;
} OstreeRepoLock;
typedef struct {
guint len;
int state;
const char *name;
} OstreeRepoLockInfo;
static void
repo_lock_info (OstreeRepoLock *lock, OstreeRepoLockInfo *out_info)
static const char *
lock_state_name (int state)
{
g_assert (lock != NULL);
g_assert (out_info != NULL);
OstreeRepoLockInfo info;
info.len = g_queue_get_length (&lock->stack);
if (info.len == 0)
switch (state)
{
info.state = LOCK_UN;
info.name = "unlocked";
case LOCK_EX:
return "exclusive";
case LOCK_SH:
return "shared";
case LOCK_UN:
return "unlocked";
default:
g_assert_not_reached ();
}
else
{
info.state = GPOINTER_TO_INT (g_queue_peek_head (&lock->stack));
info.name = (info.state == LOCK_EX) ? "exclusive" : "shared";
}
*out_info = info;
}
static void
free_repo_lock (gpointer data)
repo_lock_info (OstreeRepo *self, GMutexLocker *locker,
OstreeRepoLockInfo *out_info)
{
OstreeRepoLock *lock = data;
g_assert (self != NULL);
g_assert (locker != NULL);
g_assert (out_info != NULL);
if (lock != NULL)
{
OstreeRepoLockInfo info;
repo_lock_info (lock, &info);
OstreeRepoLockInfo info;
info.len = self->lock.shared + self->lock.exclusive;
if (info.len == 0)
info.state = LOCK_UN;
else if (self->lock.exclusive > 0)
info.state = LOCK_EX;
else
info.state = LOCK_SH;
info.name = lock_state_name (info.state);
g_debug ("Free lock: state=%s, depth=%u", info.name, info.len);
g_queue_clear (&lock->stack);
if (lock->fd >= 0)
{
g_debug ("Closing repo lock file");
(void) close (lock->fd);
}
g_free (lock);
}
*out_info = info;
}
/* Wrapper to handle flock vs OFD locking based on GLnxLockFile */
@@ -339,125 +325,148 @@ push_repo_lock (OstreeRepo *self,
GError **error)
{
int flags = (lock_type == OSTREE_REPO_LOCK_EXCLUSIVE) ? LOCK_EX : LOCK_SH;
int next_state = flags;
if (!blocking)
flags |= LOCK_NB;
GHashTable *lock_table = g_private_get (&repo_lock_table);
if (lock_table == NULL)
{
g_debug ("Creating repo lock table");
lock_table = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify)free_repo_lock);
g_private_set (&repo_lock_table, lock_table);
}
g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->lock.mutex);
OstreeRepoLock *lock = g_hash_table_lookup (lock_table, self);
if (lock == NULL)
if (self->lock.fd == -1)
{
lock = g_new0 (OstreeRepoLock, 1);
g_queue_init (&lock->stack);
g_debug ("Opening repo lock file");
lock->fd = TEMP_FAILURE_RETRY (openat (self->repo_dir_fd, ".lock",
O_CREAT | O_RDWR | O_CLOEXEC,
DEFAULT_REGFILE_MODE));
if (lock->fd < 0)
{
free_repo_lock (lock);
return glnx_throw_errno_prefix (error,
"Opening lock file %s/.lock failed",
gs_file_get_path_cached (self->repodir));
}
g_hash_table_insert (lock_table, self, lock);
self->lock.fd = TEMP_FAILURE_RETRY (openat (self->repo_dir_fd, ".lock",
O_CREAT | O_RDWR | O_CLOEXEC,
DEFAULT_REGFILE_MODE));
if (self->lock.fd < 0)
return glnx_throw_errno_prefix (error,
"Opening lock file %s/.lock failed",
gs_file_get_path_cached (self->repodir));
}
OstreeRepoLockInfo info;
repo_lock_info (lock, &info);
repo_lock_info (self, locker, &info);
g_debug ("Push lock: state=%s, depth=%u", info.name, info.len);
if (info.state == LOCK_EX)
guint *counter;
if (next_state == LOCK_EX)
counter = &(self->lock.exclusive);
else
counter = &(self->lock.shared);
/* Check for overflow */
if (*counter == G_MAXUINT)
g_error ("Repo lock %s counter would overflow", lock_state_name (next_state));
if (info.state == LOCK_EX || info.state == next_state)
{
g_debug ("Repo already locked exclusively, extending stack");
g_queue_push_head (&lock->stack, GINT_TO_POINTER (LOCK_EX));
g_debug ("Repo already locked %s, maintaining state", info.name);
}
else
{
int next_state = (flags & LOCK_EX) ? LOCK_EX : LOCK_SH;
const char *next_state_name = (flags & LOCK_EX) ? "exclusive" : "shared";
/* We should never upgrade from exclusive to shared */
g_assert (!(info.state == LOCK_EX && next_state == LOCK_SH));
const char *next_state_name = lock_state_name (next_state);
g_debug ("Locking repo %s", next_state_name);
if (!do_repo_lock (lock->fd, flags))
if (!do_repo_lock (self->lock.fd, flags))
return glnx_throw_errno_prefix (error, "Locking repo %s failed",
next_state_name);
g_queue_push_head (&lock->stack, GINT_TO_POINTER (next_state));
}
/* Update state */
(*counter)++;
return TRUE;
}
static gboolean
pop_repo_lock (OstreeRepo *self,
gboolean blocking,
GError **error)
pop_repo_lock (OstreeRepo *self,
OstreeRepoLockType lock_type,
gboolean blocking,
GError **error)
{
int flags = blocking ? 0 : LOCK_NB;
GHashTable *lock_table = g_private_get (&repo_lock_table);
g_return_val_if_fail (lock_table != NULL, FALSE);
OstreeRepoLock *lock = g_hash_table_lookup (lock_table, self);
g_return_val_if_fail (lock != NULL, FALSE);
g_return_val_if_fail (lock->fd != -1, FALSE);
g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->lock.mutex);
if (self->lock.fd == -1)
g_error ("Cannot pop repo never locked repo lock");
OstreeRepoLockInfo info;
repo_lock_info (lock, &info);
g_return_val_if_fail (info.len > 0, FALSE);
repo_lock_info (self, locker, &info);
g_debug ("Pop lock: state=%s, depth=%u", info.name, info.len);
if (info.len > 1)
{
int next_state = GPOINTER_TO_INT (g_queue_peek_nth (&lock->stack, 1));
/* Drop back to the previous lock state if it differs */
if (next_state != info.state)
{
/* We should never drop from shared to exclusive */
g_return_val_if_fail (next_state == LOCK_SH, FALSE);
g_debug ("Returning lock state to shared");
if (!do_repo_lock (lock->fd, next_state | flags))
return glnx_throw_errno_prefix (error,
"Setting repo lock to shared failed");
}
else
g_debug ("Maintaining lock state as %s", info.name);
if (info.len == 0 || info.state == LOCK_UN)
g_error ("Cannot pop already unlocked repo lock");
int state_to_drop;
guint *counter;
if (lock_type == OSTREE_REPO_LOCK_EXCLUSIVE)
{
state_to_drop = LOCK_EX;
counter = &(self->lock.exclusive);
}
else
{
/* Lock stack will be empty, unlock */
g_debug ("Unlocking repo");
if (!do_repo_unlock (lock->fd, flags))
return glnx_throw_errno_prefix (error, "Unlocking repo failed");
state_to_drop = LOCK_SH;
counter = &(self->lock.shared);
}
g_queue_pop_head (&lock->stack);
/* Make sure caller specified a valid type to release */
if (*counter == 0)
g_error ("Repo %s lock pop requested, but none have been taken",
lock_state_name (state_to_drop));
int next_state;
if (info.len == 1)
{
/* Lock counters will be empty, unlock */
next_state = LOCK_UN;
}
else if (state_to_drop == LOCK_EX)
next_state = (self->lock.exclusive > 1) ? LOCK_EX : LOCK_SH;
else
next_state = (self->lock.exclusive > 0) ? LOCK_EX : LOCK_SH;
if (next_state == LOCK_UN)
{
g_debug ("Unlocking repo");
if (!do_repo_unlock (self->lock.fd, flags))
return glnx_throw_errno_prefix (error, "Unlocking repo failed");
}
else if (info.state == next_state)
{
g_debug ("Maintaining lock state as %s", info.name);
}
else
{
/* We should never drop from shared to exclusive */
g_assert (next_state == LOCK_SH);
g_debug ("Returning lock state to shared");
if (!do_repo_lock (self->lock.fd, next_state | flags))
return glnx_throw_errno_prefix (error,
"Setting repo lock to shared failed");
}
/* Update state */
(*counter)--;
return TRUE;
}
/*
/**
* ostree_repo_lock_push:
* @self: a #OstreeRepo
* @lock_type: the type of lock to acquire
* @cancellable: a #GCancellable
* @error: a #GError
*
* Takes a lock on the repository and adds it to the lock stack. If @lock_type
* Takes a lock on the repository and adds it to the lock state. If @lock_type
* is %OSTREE_REPO_LOCK_SHARED, a shared lock is taken. If @lock_type is
* %OSTREE_REPO_LOCK_EXCLUSIVE, an exclusive lock is taken. The actual lock
* state is only changed when locking a previously unlocked repository or
* upgrading the lock from shared to exclusive. If the requested lock state is
* upgrading the lock from shared to exclusive. If the requested lock type is
* unchanged or would represent a downgrade (exclusive to shared), the lock
* state is not changed and the stack is simply updated.
* state is not changed.
*
* ostree_repo_lock_push() waits for the lock depending on the repository's
* lock-timeout-secs configuration. When lock-timeout-secs is -1, a blocking lock is
@@ -470,12 +479,13 @@ pop_repo_lock (OstreeRepo *self,
* %TRUE is returned.
*
* Returns: %TRUE on success, otherwise %FALSE with @error set
* Since: 2021.3
*/
gboolean
_ostree_repo_lock_push (OstreeRepo *self,
OstreeRepoLockType lock_type,
GCancellable *cancellable,
GError **error)
ostree_repo_lock_push (OstreeRepo *self,
OstreeRepoLockType lock_type,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
@@ -538,16 +548,19 @@ _ostree_repo_lock_push (OstreeRepo *self,
}
}
/*
* _ostree_repo_lock_pop:
/**
* ostree_repo_lock_pop:
* @self: a #OstreeRepo
* @lock_type: the type of lock to release
* @cancellable: a #GCancellable
* @error: a #GError
*
* Remove the current repository lock state from the lock stack. If the lock
* stack becomes empty, the repository is unlocked. Otherwise, the lock state
* only changes when transitioning from an exclusive lock back to a shared
* lock.
* Release a lock of type @lock_type from the lock state. If the lock state
* becomes empty, the repository is unlocked. Otherwise, the lock state only
* changes when transitioning from an exclusive lock back to a shared lock. The
* requested @lock_type must be the same type that was requested in the call to
* ostree_repo_lock_push(). It is a programmer error if these do not match and
* the program may abort if the lock would reach an invalid state.
*
* ostree_repo_lock_pop() waits for the lock depending on the repository's
* lock-timeout-secs configuration. When lock-timeout-secs is -1, a blocking lock is
@@ -560,11 +573,13 @@ _ostree_repo_lock_push (OstreeRepo *self,
* %TRUE is returned.
*
* Returns: %TRUE on success, otherwise %FALSE with @error set
* Since: 2021.3
*/
gboolean
_ostree_repo_lock_pop (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
ostree_repo_lock_pop (OstreeRepo *self,
OstreeRepoLockType lock_type,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
@@ -581,7 +596,7 @@ _ostree_repo_lock_pop (OstreeRepo *self,
else if (self->lock_timeout_seconds == REPO_LOCK_BLOCKING)
{
g_debug ("Popping lock blocking");
return pop_repo_lock (self, TRUE, error);
return pop_repo_lock (self, lock_type, TRUE, error);
}
else
{
@@ -596,7 +611,7 @@ _ostree_repo_lock_pop (OstreeRepo *self,
return FALSE;
g_autoptr(GError) local_error = NULL;
if (pop_repo_lock (self, FALSE, &local_error))
if (pop_repo_lock (self, lock_type, FALSE, &local_error))
return TRUE;
if (!g_error_matches (local_error, G_IO_ERROR,
@@ -627,60 +642,72 @@ _ostree_repo_lock_pop (OstreeRepo *self,
}
}
/*
* _ostree_repo_auto_lock_push: (skip)
struct OstreeRepoAutoLock {
OstreeRepo *repo;
OstreeRepoLockType lock_type;
};
/**
* ostree_repo_auto_lock_push: (skip)
* @self: a #OstreeRepo
* @lock_type: the type of lock to acquire
* @cancellable: a #GCancellable
* @error: a #GError
*
* Like ostree_repo_lock_push(), but for usage with #OstreeRepoAutoLock.
* The intended usage is to declare the #OstreeRepoAutoLock with
* g_autoptr() so that ostree_repo_auto_lock_cleanup() is called when it
* goes out of scope. This will automatically pop the lock status off
* the stack if it was acquired successfully.
* Like ostree_repo_lock_push(), but for usage with #OstreeRepoAutoLock. The
* intended usage is to declare the #OstreeRepoAutoLock with g_autoptr() so
* that ostree_repo_auto_lock_cleanup() is called when it goes out of scope.
* This will automatically release the lock if it was acquired successfully.
*
* |[<!-- language="C" -->
* g_autoptr(OstreeRepoAutoLock) lock = NULL;
* lock = _ostree_repo_auto_lock_push (repo, lock_type, cancellable, error);
* lock = ostree_repo_auto_lock_push (repo, lock_type, cancellable, error);
* if (!lock)
* return FALSE;
* ]|
*
* Returns: @self on success, otherwise %NULL with @error set
* Since: 2021.3
*/
OstreeRepoAutoLock *
_ostree_repo_auto_lock_push (OstreeRepo *self,
OstreeRepoLockType lock_type,
GCancellable *cancellable,
GError **error)
ostree_repo_auto_lock_push (OstreeRepo *self,
OstreeRepoLockType lock_type,
GCancellable *cancellable,
GError **error)
{
if (!_ostree_repo_lock_push (self, lock_type, cancellable, error))
if (!ostree_repo_lock_push (self, lock_type, cancellable, error))
return NULL;
return (OstreeRepoAutoLock *)self;
OstreeRepoAutoLock *auto_lock = g_new (OstreeRepoAutoLock, 1);
auto_lock->repo = self;
auto_lock->lock_type = lock_type;
return auto_lock;
}
/*
* _ostree_repo_auto_lock_cleanup: (skip)
/**
* ostree_repo_auto_lock_cleanup: (skip)
* @lock: a #OstreeRepoAutoLock
*
* A cleanup handler for use with ostree_repo_auto_lock_push(). If @lock is
* not %NULL, ostree_repo_lock_pop() will be called on it. If
* ostree_repo_lock_pop() fails, a critical warning will be emitted.
*
* Since: 2021.3
*/
void
_ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock)
ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *auto_lock)
{
OstreeRepo *repo = lock;
if (repo)
if (auto_lock != NULL)
{
g_autoptr(GError) error = NULL;
int errsv = errno;
if (!_ostree_repo_lock_pop (repo, NULL, &error))
if (!ostree_repo_lock_pop (auto_lock->repo, auto_lock->lock_type, NULL, &error))
g_critical ("Cleanup repo lock failed: %s", error->message);
errno = errsv;
g_free (auto_lock);
}
}
@@ -1052,13 +1079,8 @@ ostree_repo_finalize (GObject *object)
g_clear_pointer (&self->remotes, g_hash_table_destroy);
g_mutex_clear (&self->remotes_lock);
GHashTable *lock_table = g_private_get (&repo_lock_table);
if (lock_table)
{
g_hash_table_remove (lock_table, self);
if (g_hash_table_size (lock_table) == 0)
g_private_replace (&repo_lock_table, NULL);
}
glnx_close_fd (&self->lock.fd);
g_mutex_clear (&self->lock.mutex);
G_OBJECT_CLASS (ostree_repo_parent_class)->finalize (object);
}
@@ -1220,6 +1242,7 @@ ostree_repo_init (OstreeRepo *self)
self->test_error_flags = g_parse_debug_string (g_getenv ("OSTREE_REPO_TEST_ERROR"),
test_error_keys, G_N_ELEMENTS (test_error_keys));
g_mutex_init (&self->lock.mutex);
g_mutex_init (&self->cache_lock);
g_mutex_init (&self->txn_lock);
@@ -1233,6 +1256,7 @@ ostree_repo_init (OstreeRepo *self)
self->tmp_dir_fd = -1;
self->objects_dir_fd = -1;
self->uncompressed_objects_dir_fd = -1;
self->lock.fd = -1;
self->sysroot_kind = OSTREE_REPO_SYSROOT_KIND_UNKNOWN;
}
@@ -3291,7 +3315,7 @@ ostree_repo_open (OstreeRepo *self,
if (!glnx_opendirat (self->repo_dir_fd, "tmp", TRUE, &self->tmp_dir_fd, error))
return FALSE;
if (self->writable)
if (self->writable && getenv ("OSTREE_SKIP_CACHE") == NULL)
{
if (!glnx_shutil_mkdir_p_at (self->tmp_dir_fd, _OSTREE_CACHE_DIR, DEFAULT_DIRECTORY_MODE, cancellable, error))
return FALSE;
@@ -5791,8 +5815,8 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
g_autoptr(OstreeRepoAutoLock) lock = NULL;
gboolean no_deltas_in_summary = FALSE;
lock = _ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE,
cancellable, error);
lock = ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE,
cancellable, error);
if (!lock)
return FALSE;
+51
View File
@@ -1498,6 +1498,57 @@ gboolean ostree_repo_regenerate_summary (OstreeRepo *self,
GCancellable *cancellable,
GError **error);
/**
* OstreeRepoLockType:
* @OSTREE_REPO_LOCK_SHARED: A "read only" lock; multiple readers are allowed.
* @OSTREE_REPO_LOCK_EXCLUSIVE: A writable lock at most one writer can be active, and zero readers.
*
* Flags controlling repository locking.
*
* Since: 2021.3
*/
typedef enum {
OSTREE_REPO_LOCK_SHARED,
OSTREE_REPO_LOCK_EXCLUSIVE
} OstreeRepoLockType;
_OSTREE_PUBLIC
gboolean ostree_repo_lock_push (OstreeRepo *self,
OstreeRepoLockType lock_type,
GCancellable *cancellable,
GError **error);
_OSTREE_PUBLIC
gboolean ostree_repo_lock_pop (OstreeRepo *self,
OstreeRepoLockType lock_type,
GCancellable *cancellable,
GError **error);
/* C convenience API only */
#ifndef __GI_SCANNER__
/**
* OstreeRepoAutoLock: (skip)
*
* An opaque type for use with ostree_repo_auto_lock_push().
*
* Since: 2021.3
*/
typedef struct OstreeRepoAutoLock OstreeRepoAutoLock;
_OSTREE_PUBLIC
OstreeRepoAutoLock * ostree_repo_auto_lock_push (OstreeRepo *self,
OstreeRepoLockType lock_type,
GCancellable *cancellable,
GError **error);
_OSTREE_PUBLIC
void ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoLock, ostree_repo_auto_lock_cleanup)
#endif
/**
* OSTREE_REPO_METADATA_REF:
*
+1 -1
View File
@@ -505,7 +505,7 @@ ostree_sysroot_cleanup_prune_repo (OstreeSysroot *sysroot,
* the prune.
*/
g_autoptr(OstreeRepoAutoLock) lock =
_ostree_repo_auto_lock_push (repo, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error);
ostree_repo_auto_lock_push (repo, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error);
if (!lock)
return FALSE;
+36 -22
View File
@@ -2632,6 +2632,39 @@ _ostree_deployment_set_bootconfig_from_kargs (OstreeDeployment *deployment,
}
}
// Perform some basic static analysis and emit warnings for things
// that are likely to fail later. This function only returns
// a hard error if something unexpected (e.g. I/O error) occurs.
static gboolean
lint_deployment_fs (OstreeSysroot *self,
OstreeDeployment *deployment,
int deployment_dfd,
GCancellable *cancellable,
GError **error)
{
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
glnx_autofd int dest_dfd = -1;
gboolean exists;
if (!ot_dfd_iter_init_allow_noent (deployment_dfd, "var", &dfd_iter, &exists, error))
return FALSE;
while (exists)
{
struct dirent *dent;
if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
return FALSE;
if (dent == NULL)
break;
fprintf (stderr, "note: Deploying commit %s which contains content in /var/%s that will be ignored.\n",
ostree_deployment_get_csum (deployment),
dent->d_name);
}
return TRUE;
}
/* The first part of writing a deployment. This primarily means doing the
* hardlink farm checkout, but we also compute some initial state.
*/
@@ -2680,6 +2713,9 @@ sysroot_initialize_deployment (OstreeSysroot *self,
cancellable, error))
return FALSE;
if (!lint_deployment_fs (self, new_deployment, deployment_dfd, cancellable, error))
return FALSE;
ot_transfer_out_value (out_new_deployment, &new_deployment);
return TRUE;
}
@@ -3070,28 +3106,6 @@ ostree_sysroot_stage_tree_with_options (OstreeSysroot *self,
if (booted_deployment == NULL)
return glnx_prefix_error (error, "Cannot stage deployment");
/* This is used by the testsuite to exercise the path unit, until it becomes the default
* (which is pending on the preset making it everywhere). */
if ((self->debug_flags & OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH) == 0)
{
/* This is a bit of a hack. When adding a new service we have to end up getting
* into the presets for downstream distros; see e.g. https://src.fedoraproject.org/rpms/ostree/pull-request/7
*
* Then again, it's perhaps a bit nicer to only start the service on-demand anyways.
*/
const char *const systemctl_argv[] = {"systemctl", "start", "ostree-finalize-staged.service", NULL};
int estatus;
if (!g_spawn_sync (NULL, (char**)systemctl_argv, NULL, G_SPAWN_SEARCH_PATH,
NULL, NULL, NULL, NULL, &estatus, error))
return FALSE;
if (!g_spawn_check_exit_status (estatus, error))
return FALSE;
}
else
{
g_print ("test-staged-path: Not running `systemctl start`\n");
} /* OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH */
g_autoptr(OstreeDeployment) deployment = NULL;
if (!sysroot_initialize_deployment (self, osname, revision, origin, opts, &deployment,
cancellable, error))
+1 -2
View File
@@ -37,8 +37,7 @@ typedef enum {
OSTREE_SYSROOT_DEBUG_TEST_FIFREEZE = 1 << 2,
/* This is a temporary flag until we fully drop the explicit `systemctl start
* ostree-finalize-staged.service` so that tests can exercise the new path unit. */
OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH = 1 << 3,
OSTREE_SYSROOT_DEBUG_TEST_NO_DTB = 1 << 4, /* https://github.com/ostreedev/ostree/issues/2154 */
OSTREE_SYSROOT_DEBUG_TEST_NO_DTB = 1 << 3, /* https://github.com/ostreedev/ostree/issues/2154 */
} OstreeSysrootDebugFlags;
typedef enum {
+1 -2
View File
@@ -190,7 +190,6 @@ ostree_sysroot_init (OstreeSysroot *self)
{ "mutable-deployments", OSTREE_SYSROOT_DEBUG_MUTABLE_DEPLOYMENTS },
{ "test-fifreeze", OSTREE_SYSROOT_DEBUG_TEST_FIFREEZE },
{ "no-xattrs", OSTREE_SYSROOT_DEBUG_NO_XATTRS },
{ "test-staged-path", OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH },
{ "no-dtb", OSTREE_SYSROOT_DEBUG_TEST_NO_DTB },
};
@@ -1063,7 +1062,7 @@ _ostree_sysroot_reload_staged (OstreeSysroot *self,
return TRUE;
}
/* Loads the current bootversion, subbootversion, and deplyments, starting from the
/* Loads the current bootversion, subbootversion, and deployments, starting from the
* bootloader configs which are the source of truth.
*/
static gboolean
+4 -4
View File
@@ -43,7 +43,7 @@
*
* Since: 2017.4
*/
#define OSTREE_RELEASE_VERSION (2)
#define OSTREE_RELEASE_VERSION (3)
/**
* OSTREE_VERSION
@@ -52,7 +52,7 @@
*
* Since: 2017.4
*/
#define OSTREE_VERSION (2021.2)
#define OSTREE_VERSION (2021.3)
/**
* OSTREE_VERSION_S:
@@ -62,7 +62,7 @@
*
* Since: 2017.4
*/
#define OSTREE_VERSION_S "2021.2"
#define OSTREE_VERSION_S "2021.3"
#define OSTREE_ENCODE_VERSION(year,release) \
((year) << 16 | (release))
@@ -100,4 +100,4 @@
*
* Since: 2019.3
*/
#define OSTREE_BUILT_FEATURES "libcurl libsoup gpgme ex-fsverity libarchive selinux openssl libmount systemd release p2p"
#define OSTREE_BUILT_FEATURES "libsoup gpgme ex-fsverity libarchive selinux avahi libmount systemd release p2p"