Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Repository locking 🔒 #1292

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e297478
build: Define OSTREE_ENABLE_EXPERIMENTAL_API for g-ir-scanner
dbnicholson Oct 16, 2017
07b17fb
Skip cmdprivate components in GIR
dbnicholson Oct 19, 2017
50bfa38
lib/repo: Add repo locking mechanism
dbnicholson Oct 5, 2017
bb6ab8e
lib/repo: Add locking auto cleanup handler
dbnicholson Oct 13, 2017
7be3051
lib/commit: Add repository locking during transactions
dbnicholson Oct 6, 2017
d98bc52
lib/prune: Take exclusive repository lock
dbnicholson Oct 6, 2017
19a4dbf
tests: Test concurrent operations
cgwalters Oct 14, 2017
d613ae6
tests: Run python tests with stdout unbuffered
dbnicholson Oct 14, 2017
e9b586e
lib/checkout: Lock repository during checkout operations
dbnicholson Oct 6, 2017
5321998
repo: Add config option for default lock timeout
dbnicholson Oct 13, 2017
0ab32a4
tests: Add tests for repo locking APIs
dbnicholson Oct 16, 2017
fec6b1f
lib/commit: Take shared lock when writing commit detached metadata
dbnicholson Oct 16, 2017
eba8a17
lib/repo: Take shared lock while generating summary
dbnicholson Oct 16, 2017
23b7aa4
lib/repo: Take exclusive lock while signing summary
dbnicholson Oct 16, 2017
4b8d03e
lib/deltas: Take shared lock while generating
dbnicholson Oct 17, 2017
2567a01
lib/repo: Take lock when deleting objects
dbnicholson Oct 17, 2017
39a7211
lib/traverse: Take shared repo lock
dbnicholson Oct 18, 2017
1c87dfb
lib/deltas: Take exclusive lock when deleting delta
dbnicholson Oct 18, 2017
c2e091a
lib/repo: Add accessors for lock timeout
dbnicholson Oct 17, 2017
615b949
lib/cmdprivate: Add private access to ostree_repo_set_lock_timeout
dbnicholson Oct 19, 2017
cf2824a
bin/main: Add --lock-timeout option
dbnicholson Oct 17, 2017
1ccd3db
bin: Add --lock-timeout option to relevant commands
dbnicholson Oct 17, 2017
d31b39e
lib/cmdprivate: Add private access to ostree_repo_auto_lock_*
dbnicholson Oct 19, 2017
e3a8476
bin/main: Provide repo lock autoptr implementation
dbnicholson Oct 19, 2017
7c14891
bin/fsck: Take exclusive lock for entire operation
dbnicholson Oct 18, 2017
e415247
bin/admin: Add --lock-timeout option
dbnicholson Oct 18, 2017
7ff4b93
bin/admin: Add --lock-timeout option to relevant commands
dbnicholson Oct 18, 2017
8c9f518
squash! tests: Test concurrent operations
dbnicholson Oct 24, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile-libostree.am
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ OSTree-1.0.gir: libostree-1.la Makefile
OSTree_1_0_gir_EXPORT_PACKAGES = ostree-1
OSTree_1_0_gir_INCLUDES = Gio-2.0
OSTree_1_0_gir_CFLAGS = $(libostree_1_la_CFLAGS)
if ENABLE_EXPERIMENTAL_API
# When compiling this is set via config.h, but g-ir-scanner doesn't use that
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should just be able to pass config.h to OSTree_1_0_gir_FILES and it will be #included into the temporary C file which g-ir-scanner generates as part of the scanning process.

I think order is preserved, so list it first in OSTree_1_0_gir_FILES.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I didn't know that. I'll try that locally.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was separately fixed in #1322.

OSTree_1_0_gir_CFLAGS += -DOSTREE_ENABLE_EXPERIMENTAL_API=1
endif
OSTree_1_0_gir_LIBS = libostree-1.la
OSTree_1_0_gir_SCANNERFLAGS = --warn-all --identifier-prefix=Ostree --symbol-prefix=ostree $(GI_SCANNERFLAGS)
OSTree_1_0_gir_FILES = $(libostreeinclude_HEADERS) $(filter-out %-private.h %/ostree-soup-uri.h $(libostree_experimental_headers),$(libostree_1_la_SOURCES))
Expand Down
2 changes: 1 addition & 1 deletion Makefile-switchroot.am
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ systemdsystemgenerator_PROGRAMS = ostree-system-generator
GITIGNOREFILES += $(systemdsystemgenerator_PROGRAMS)
ostree_system_generator_SOURCES = src/switchroot/ostree-mount-util.h \
src/switchroot/ostree-system-generator.c
ostree_system_generator_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/libglnx -I$(srcdir)/src/libostree
ostree_system_generator_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/libglnx -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil
ostree_system_generator_CFLAGS = $(AM_CFLAGS) $(OT_INTERNAL_GIO_UNIX_CFLAGS)
ostree_system_generator_LDADD = $(AM_LDFLAGS) libglnx.la libostree-1.la $(OT_INTERNAL_GIO_UNIX_LIBS)

Expand Down
3 changes: 3 additions & 0 deletions Makefile-tests.am
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ AM_TESTS_ENVIRONMENT += OT_TESTS_DEBUG=1 \
LD_LIBRARY_PATH=$$(cd $(top_builddir)/.libs && pwd)$${LD_LIBRARY_PATH:+:$${LD_LIBRARY_PATH}} \
PATH=$$(cd $(top_builddir)/tests && pwd):$${PATH} \
OSTREE_FEATURES="$(OSTREE_FEATURES)" \
PYTHONUNBUFFERED=1 \
$(NULL)
if BUILDOPT_ASAN
AM_TESTS_ENVIRONMENT += OT_SKIP_READDIR_RAND=1 G_SLICE=always-malloc
Expand Down Expand Up @@ -106,6 +107,8 @@ _installed_or_uninstalled_test_scripts = \
tests/test-xattrs.sh \
tests/test-auto-summary.sh \
tests/test-prune.sh \
tests/test-concurrency.py \
tests/test-repo-locking.py \
tests/test-refs.sh \
tests/test-demo-buildsystem.sh \
tests/test-switchroot.sh \
Expand Down
7 changes: 7 additions & 0 deletions apidoc/ostree-experimental-sections.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ ostree_repo_finder_override_get_type

<SECTION>
<FILE>ostree-misc-experimental</FILE>
ostree_repo_lock_push
ostree_repo_lock_pop
OstreeRepoAutoLock
ostree_repo_auto_lock_push
ostree_repo_auto_lock_cleanup
ostree_repo_get_lock_timeout
ostree_repo_set_lock_timeout
ostree_repo_get_collection_id
ostree_repo_set_collection_id
ostree_validate_collection_id
Expand Down
16 changes: 16 additions & 0 deletions man/ostree-admin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,22 @@ Boston, MA 02111-1307, USA.
Prints the full path to the deployment directory for the currently active deployment in the specified sysroot to standard out. This is incompatible with specifying a subcommand.
</para></listitem>
</varlistentry>

<varlistentry>
<term><option>--lock-timeout</option>=TIMEOUT</term>

<listitem>
<para>
Some <command>ostree admin</command> commands require the
repository to be locked while operating. This option sets
the repository lock timeout to TIMEOUT seconds. If TIMEOUT
is <literal>0</literal>, then one attempt to acquire or
remove the lock will be made without waiting to try again.
If TIMEOUT is <literal>-1</literal>, then the locking will
block indefinitely. All other negative values are an error.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
</refentry>
16 changes: 16 additions & 0 deletions man/ostree.repo-config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,22 @@ Boston, MA 02111-1307, USA.
</listitem>
</varlistentry>

<varlistentry>
<term><varname>lock-timeout</varname></term>
<listitem>
<para>
Integer value controlling the default repository locking timeout
in seconds. This controls how long OSTree will wait attempting to
acquire or remove the repository lock. If the value is
<literal>0</literal>, then one attempt to acquire or remove the
lock will be made without waiting to try again. If the value is
<literal>-1</literal>, then ostree will block until the lock can
be acquired or removed. All other negative values are an error.
The default is <literal>30</literal> seconds.
</para>
</listitem>
</varlistentry>

</variablelist>
</refsect1>

Expand Down
16 changes: 16 additions & 0 deletions man/ostree.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,22 @@ Boston, MA 02111-1307, USA.
</para></listitem>
</varlistentry>

<varlistentry>
<term><option>--lock-timeout</option>=TIMEOUT</term>

<listitem>
<para>
Some <command>ostree</command> commands require the
repository to be locked while operating. This option sets
the repository lock timeout to TIMEOUT seconds. If TIMEOUT
is <literal>0</literal>, then one attempt to acquire or
remove the lock will be made without waiting to try again.
If TIMEOUT is <literal>-1</literal>, then the locking will
block indefinitely. All other negative values are an error.
</para>
</listitem>
</varlistentry>

<varlistentry>
<term><option>-v, --verbose</option></term>

Expand Down
6 changes: 6 additions & 0 deletions src/libostree/libostree-experimental.sym
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,10 @@ global:
ostree_repo_finder_override_add_uri;
ostree_repo_finder_override_get_type;
ostree_repo_finder_override_new;
ostree_repo_auto_lock_cleanup;
ostree_repo_auto_lock_push;
ostree_repo_get_lock_timeout;
ostree_repo_lock_pop;
ostree_repo_lock_push;
ostree_repo_set_lock_timeout;
} LIBOSTREE_2017.12_EXPERIMENTAL;
1 change: 1 addition & 0 deletions src/libostree/ostree-autocleanups.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSysrootUpgrader, g_object_unref)
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (OstreeRepoCommitTraverseIter, ostree_repo_commit_traverse_iter_clear)

#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoLock, ostree_repo_auto_lock_cleanup)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeCollectionRef, ostree_collection_ref_free)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeCollectionRefv, ostree_collection_ref_freev, NULL)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRemote, ostree_remote_unref)
Expand Down
12 changes: 10 additions & 2 deletions src/libostree/ostree-cmdprivate.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spurious blank line.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will address after #1343.

/*
* Copyright (C) 2014 Colin Walters <[email protected]>
*
Expand Down Expand Up @@ -35,7 +36,7 @@ impl_ostree_generate_grub2_config (OstreeSysroot *sysroot, int bootversion, int
}

/**
* ostree_cmdprivate: (skip)
* ostree_cmd__private__: (skip)
*
* Do not call this function; it is used to share private API between
* the OSTree commandline and the library.
Expand All @@ -48,7 +49,14 @@ ostree_cmd__private__ (void)
impl_ostree_generate_grub2_config,
_ostree_repo_static_delta_dump,
_ostree_repo_static_delta_query_exists,
_ostree_repo_static_delta_delete
_ostree_repo_static_delta_delete,
/* Remove this when ostree_repo_set_lock_timeout is no longer experimental */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a FIXME to this comment (and the one below) to make them more greppable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will address after #1343.

ostree_repo_set_lock_timeout,
/* Remove these when ostree_repo_auto_lock_push and
* ostree_repo_auto_lock_cleanup are no longer experimental
*/
ostree_repo_auto_lock_push,
ostree_repo_auto_lock_cleanup,
};

return &table;
Expand Down
17 changes: 17 additions & 0 deletions src/libostree/ostree-cmdprivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,34 @@
#pragma once

#include "ostree-types.h"
/* This is only needed to get the OstreeRepoLockType and OstreeRepoAutoLock
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a FIXME to this comment too. Similarly for other ‘remove this when no longer experimental’ comments throughout the rest of the PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will address after #1343.

* types. This should be removed once they're no longer experimental.
*/
#include "ostree-repo-private.h"

G_BEGIN_DECLS

gboolean _ostree_impl_system_generator (const char *ostree_cmdline, const char *normal_dir, const char *early_dir, const char *late_dir, GError **error);

/**
* OstreeCmdPrivateVTable: (skip)
*
* Function table to share private API between the OSTree commandline and the
* library. Don't use this.
*/
typedef struct {
gboolean (* ostree_system_generator) (const char *ostree_cmdline, const char *normal_dir, const char *early_dir, const char *late_dir, GError **error);
gboolean (* ostree_generate_grub2_config) (OstreeSysroot *sysroot, int bootversion, int target_fd, GCancellable *cancellable, GError **error);
gboolean (* ostree_static_delta_dump) (OstreeRepo *repo, const char *delta_id, GCancellable *cancellable, GError **error);
gboolean (* ostree_static_delta_query_exists) (OstreeRepo *repo, const char *delta_id, gboolean *out_exists, GCancellable *cancellable, GError **error);
gboolean (* ostree_static_delta_delete) (OstreeRepo *repo, const char *delta_id, GCancellable *cancellable, GError **error);
/* Remove this when ostree_repo_set_lock_timeout is no longer experimental */
gboolean (* ostree_repo_set_lock_timeout) (OstreeRepo *repo, gint timeout);
/* Remove these when ostree_repo_auto_lock_push and
* ostree_repo_auto_lock_cleanup are no longer experimental
*/
OstreeRepoAutoLock * (* ostree_repo_auto_lock_push) (OstreeRepo *repo, OstreeRepoLockType lock_type, GCancellable *cancellable, GError **error);
void (* ostree_repo_auto_lock_cleanup) (OstreeRepoAutoLock *lock);
} OstreeCmdPrivateVTable;

/* Note this not really "public", we just export the symbol, but not the header */
Expand Down
26 changes: 26 additions & 0 deletions src/libostree/ostree-repo-checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "ostree-sepolicy-private.h"
#include "ostree-core-private.h"
#include "ostree-repo-private.h"
#include "ostree-autocleanups.h"

#define WHITEOUT_PREFIX ".wh."

Expand Down Expand Up @@ -991,6 +992,16 @@ checkout_tree_at (OstreeRepo *self,
g_assert (options->force_copy);
}

/* Take a shared repo lock to try to ensure objects aren't deleted. If the
* repo is not writable, this will be a noop and we just hope for the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the repo is not writeable can we really expect objects to be deleted? Are you thinking of the situation where a repo is being operated on by processes belonging to two users: the user deleting objects has write permissions on the repository, and the user reading it does not?

We could perhaps fix this by sticking an inotify on the lock file if we only have read-only access to the repository, and bailing out if an exclusive lock is taken on the repository while we’re in a locked section. That might be overengineering things though. It would, however, be a general solution which could be implemented in the generic locking code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The case is simply an unprivileged user trying to checkout from /ostree/repo or /var/lib/flatpak/repo or something like that. They should be allowed to do a checkout as they were before.

Like you say, if the repo is not writable by the current user, then they're not going to be deleting any objects. But if, say, flatpak is pruning /var/lib/flatpak/repo while you're trying to do a checkout, bad things could happen.

But that's already the case, and I don't think we should optimize for unprivileged users. I'm really just trying to make sure that multiple processes running as the repo owner can't screw each other since that's by far the common case. Since the unprivileged user won't even be able to open the lock file (it's opened 600 particularly so that only repo owners can take exclusive locks), then I don't think they'll be able to get the lock state.

An inotify watch is interesting. I'm not sure inotify can tell you when the lock state changes, though, only if it's been opened.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, having done a bit of reading, I don’t think it’s possible to monitor the status of a file lock using inotify. The only way of doing it is to poll with fcntl(F_GETLK), but that’s horrific and racy.

The other approach I can think of is to use a world-writeable directory (somewhere in /run, probably), which can contain the locks for many repositories, each with a deterministic name based on the repository directory’s device ID and inode.

That said, allowing an unprivileged process to take a lock on a repository it can’t write to allows priority inversion attacks, since there’s no realistic way to stop it taking an exclusive lock.

Unless we put shared locks in /run and exclusive locks in $repo/.lock?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still believe the way this was written where only the repo owner can manage the lock is correct. I think any attempt to allow non-owners to take a lock would be very complex and likely mean that flock-type locking can't be used.

* best...
*/
g_autoptr(OstreeRepoAutoLock) lock = NULL;
lock = ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_SHARED,
cancellable, error);
if (!lock)
return FALSE;

/* Special case handling for subpath of a non-directory */
if (g_file_info_get_file_type (source_info) != G_FILE_TYPE_DIRECTORY)
{
Expand Down Expand Up @@ -1062,6 +1073,8 @@ canonicalize_options (OstreeRepo *self,
* physical filesystem. @source may be any subdirectory of a given
* commit. The @mode and @overwrite_mode allow control over how the
* files are checked out.
*
* This function takes a shared lock on the @self repository.
*/
gboolean
ostree_repo_checkout_tree (OstreeRepo *self,
Expand Down Expand Up @@ -1105,6 +1118,8 @@ ostree_repo_checkout_tree (OstreeRepo *self,
* default is not to use the repository-internal uncompressed objects
* cache.
*
* This function takes a shared lock on the @self repository.
*
* This function is deprecated. Use ostree_repo_checkout_at() instead.
*/
gboolean
Expand Down Expand Up @@ -1150,6 +1165,8 @@ ostree_repo_checkout_tree_at (OstreeRepo *self,
* Note in addition that unlike ostree_repo_checkout_tree(), the
* default is not to use the repository-internal uncompressed objects
* cache.
*
* This function takes a shared lock on the @self repository.
*/
gboolean
ostree_repo_checkout_at (OstreeRepo *self,
Expand Down Expand Up @@ -1273,12 +1290,21 @@ ostree_repo_devino_cache_new (void)
* Call this after finishing a succession of checkout operations; it
* will delete any currently-unused uncompressed objects from the
* cache.
*
* This function takes an exclusive lock on the @self repository.
*/
gboolean
ostree_repo_checkout_gc (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
{
/* Take an exclusive repo lock while deleting uncompressed objects */
g_autoptr(OstreeRepoAutoLock) lock = NULL;
lock = ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE,
cancellable, error);
if (!lock)
return FALSE;

g_autoptr(GHashTable) to_clean_dirs = NULL;

g_mutex_lock (&self->cache_lock);
Expand Down
49 changes: 41 additions & 8 deletions src/libostree/ostree-repo-commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -986,13 +986,16 @@ write_metadata_object (OstreeRepo *self,
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
{
GError *local_error = NULL;
/* If we are writing a commit, be sure there is no tombstone for it.
We may have deleted the commit and now we are trying to pull it again. */
if (!ostree_repo_delete_object (self,
OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT,
actual_checksum,
cancellable,
&local_error))
/* If we are writing a commit, be sure there is no tombstone for it. We
* may have deleted the commit and now we are trying to pull it again.
* Delete with a shared lock here so concurrent commits and pulls aren't
* prevented.
*/
if (!_ostree_repo_delete_object_shared (self,
OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT,
actual_checksum,
cancellable,
&local_error))
{
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
g_clear_error (&local_error);
Expand Down Expand Up @@ -1203,7 +1206,9 @@ ostree_repo_scan_hardlinks (OstreeRepo *self,
* ostree_repo_abort_transaction().
*
* Currently, transactions are not atomic, and aborting a transaction
* will not erase any data you write during the transaction.
* will not erase any data you write during the transaction.
*
* This function takes a shared lock on the @self repository.
*/
gboolean
ostree_repo_prepare_transaction (OstreeRepo *self,
Expand All @@ -1214,6 +1219,11 @@ ostree_repo_prepare_transaction (OstreeRepo *self,

g_return_val_if_fail (self->in_transaction == FALSE, FALSE);

self->txn_locked = ostree_repo_lock_push (self, OSTREE_REPO_LOCK_SHARED,
cancellable, error);
if (!self->txn_locked)
return FALSE;

memset (&self->txn_stats, 0, sizeof (OstreeRepoTransactionStats));

self->in_transaction = TRUE;
Expand Down Expand Up @@ -1699,6 +1709,13 @@ ostree_repo_commit_transaction (OstreeRepo *self,
if (!ot_ensure_unlinked_at (self->repo_dir_fd, "transaction", 0))
return FALSE;

if (self->txn_locked)
{
if (!ostree_repo_lock_pop (self, cancellable, error))
return FALSE;
self->txn_locked = FALSE;
}

if (out_stats)
*out_stats = self->txn_stats;

Expand Down Expand Up @@ -1739,6 +1756,13 @@ ostree_repo_abort_transaction (OstreeRepo *self,

self->in_transaction = FALSE;

if (self->txn_locked)
{
if (!ostree_repo_lock_pop (self, cancellable, error))
return FALSE;
self->txn_locked = FALSE;
}

return TRUE;
}

Expand Down Expand Up @@ -2355,6 +2379,8 @@ ostree_repo_read_commit_detached_metadata (OstreeRepo *self,
* Replace any existing metadata associated with commit referred to by
* @checksum with @metadata. If @metadata is %NULL, then existing
* data will be deleted.
*
* This function takes a shared lock on the @self repository.
*/
gboolean
ostree_repo_write_commit_detached_metadata (OstreeRepo *self,
Expand All @@ -2363,6 +2389,13 @@ ostree_repo_write_commit_detached_metadata (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
{
/* Take shared lock so associated commit doesn't get deleted */
g_autoptr(OstreeRepoAutoLock) lock = NULL;
lock = ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_SHARED,
cancellable, error);
if (!lock)
return FALSE;

int dest_dfd;
if (self->in_transaction)
dest_dfd = self->commit_stagedir.fd;
Expand Down
Loading