Skip to content
Snippets Groups Projects
Commit 970fc860 authored by Simon McVittie's avatar Simon McVittie
Browse files

bwrap-lock: Add at-fd-based path resolution


This lets us hold a fd to the parent directory open and resolve paths
relative to it (see openat(2)).

Signed-off-by: default avatarSimon McVittie <smcv@collabora.com>
parent 2377fcbf
No related branches found
No related tags found
No related merge requests found
...@@ -55,6 +55,8 @@ struct _PvBwrapLock ...@@ -55,6 +55,8 @@ struct _PvBwrapLock
/** /**
* pv_bwrap_lock_new: * pv_bwrap_lock_new:
* @at_fd: If not `AT_FDCWD` or -1, look up @path relative to this
* directory fd instead of the current working directory, as per `openat(2)`
* @path: File to lock * @path: File to lock
* @flags: Flags affecting how we lock the file * @flags: Flags affecting how we lock the file
* @error: Used to raise an error on failure * @error: Used to raise an error on failure
...@@ -74,7 +76,8 @@ struct _PvBwrapLock ...@@ -74,7 +76,8 @@ struct _PvBwrapLock
* or %NULL. * or %NULL.
*/ */
PvBwrapLock * PvBwrapLock *
pv_bwrap_lock_new (const gchar *path, pv_bwrap_lock_new (int at_fd,
const gchar *path,
PvBwrapLockFlags flags, PvBwrapLockFlags flags,
GError **error) GError **error)
{ {
...@@ -95,7 +98,8 @@ pv_bwrap_lock_new (const gchar *path, ...@@ -95,7 +98,8 @@ pv_bwrap_lock_new (const gchar *path,
else else
open_flags |= O_RDONLY; open_flags |= O_RDONLY;
fd = TEMP_FAILURE_RETRY (openat (AT_FDCWD, path, open_flags, 0644)); at_fd = glnx_dirfd_canonicalize (at_fd);
fd = TEMP_FAILURE_RETRY (openat (at_fd, path, open_flags, 0644));
if (fd < 0) if (fd < 0)
{ {
......
...@@ -56,7 +56,8 @@ typedef enum ...@@ -56,7 +56,8 @@ typedef enum
typedef struct _PvBwrapLock PvBwrapLock; typedef struct _PvBwrapLock PvBwrapLock;
PvBwrapLock *pv_bwrap_lock_new (const gchar *path, PvBwrapLock *pv_bwrap_lock_new (int at_fd,
const gchar *path,
PvBwrapLockFlags flags, PvBwrapLockFlags flags,
GError **error); GError **error);
PvBwrapLock *pv_bwrap_lock_new_take (int fd, PvBwrapLock *pv_bwrap_lock_new_take (int fd,
......
...@@ -314,7 +314,7 @@ pv_runtime_initable_init (GInitable *initable, ...@@ -314,7 +314,7 @@ pv_runtime_initable_init (GInitable *initable,
/* Take a lock on the runtime until we're finished with setup, /* Take a lock on the runtime until we're finished with setup,
* to make sure it doesn't get deleted. */ * to make sure it doesn't get deleted. */
files_ref = g_build_filename (self->source_files, ".ref", NULL); files_ref = g_build_filename (self->source_files, ".ref", NULL);
self->runtime_lock = pv_bwrap_lock_new (files_ref, self->runtime_lock = pv_bwrap_lock_new (AT_FDCWD, files_ref,
PV_BWRAP_LOCK_FLAGS_CREATE, PV_BWRAP_LOCK_FLAGS_CREATE,
error); error);
......
...@@ -117,7 +117,7 @@ opt_lock_file_cb (const char *name, ...@@ -117,7 +117,7 @@ opt_lock_file_cb (const char *name,
if (opt_wait) if (opt_wait)
flags |= PV_BWRAP_LOCK_FLAGS_WAIT; flags |= PV_BWRAP_LOCK_FLAGS_WAIT;
lock = pv_bwrap_lock_new (value, flags, error); lock = pv_bwrap_lock_new (AT_FDCWD, value, flags, error);
if (lock == NULL) if (lock == NULL)
return FALSE; return FALSE;
......
...@@ -78,12 +78,14 @@ test_locks (Fixture *f, ...@@ -78,12 +78,14 @@ test_locks (Fixture *f,
lock = g_build_filename (tmpdir.path, "lockfile", NULL); lock = g_build_filename (tmpdir.path, "lockfile", NULL);
/* Take a shared (read) lock */ /* Take a shared (read) lock */
read_lock1 = pv_bwrap_lock_new (lock, PV_BWRAP_LOCK_FLAGS_CREATE, &error); read_lock1 = pv_bwrap_lock_new (AT_FDCWD, lock, PV_BWRAP_LOCK_FLAGS_CREATE,
&error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert_nonnull (read_lock1); g_assert_nonnull (read_lock1);
/* We cannot take an exclusive (write) lock at the same time */ /* We cannot take an exclusive (write) lock at the same time */
write_lock1 = pv_bwrap_lock_new (lock, PV_BWRAP_LOCK_FLAGS_WRITE, &error); write_lock1 = pv_bwrap_lock_new (AT_FDCWD, lock, PV_BWRAP_LOCK_FLAGS_WRITE,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY);
g_assert_null (write_lock1); g_assert_null (write_lock1);
g_clear_error (&error); g_clear_error (&error);
...@@ -92,7 +94,8 @@ test_locks (Fixture *f, ...@@ -92,7 +94,8 @@ test_locks (Fixture *f,
is_ofd = pv_bwrap_lock_is_ofd (read_lock1); is_ofd = pv_bwrap_lock_is_ofd (read_lock1);
fd = pv_bwrap_lock_steal_fd (read_lock1); fd = pv_bwrap_lock_steal_fd (read_lock1);
g_assert_cmpint (fd, >=, 0); g_assert_cmpint (fd, >=, 0);
write_lock1 = pv_bwrap_lock_new (lock, PV_BWRAP_LOCK_FLAGS_WRITE, &error); write_lock1 = pv_bwrap_lock_new (AT_FDCWD, lock, PV_BWRAP_LOCK_FLAGS_WRITE,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY);
g_assert_null (write_lock1); g_assert_null (write_lock1);
g_clear_error (&error); g_clear_error (&error);
...@@ -101,7 +104,8 @@ test_locks (Fixture *f, ...@@ -101,7 +104,8 @@ test_locks (Fixture *f,
/* The lock is held even after we free the original lock abstraction */ /* The lock is held even after we free the original lock abstraction */
g_clear_pointer (&read_lock1, pv_bwrap_lock_free); g_clear_pointer (&read_lock1, pv_bwrap_lock_free);
write_lock1 = pv_bwrap_lock_new (lock, PV_BWRAP_LOCK_FLAGS_WRITE, &error); write_lock1 = pv_bwrap_lock_new (AT_FDCWD, lock, PV_BWRAP_LOCK_FLAGS_WRITE,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY);
g_assert_null (write_lock1); g_assert_null (write_lock1);
g_clear_error (&error); g_clear_error (&error);
...@@ -109,35 +113,41 @@ test_locks (Fixture *f, ...@@ -109,35 +113,41 @@ test_locks (Fixture *f,
/* We can make a new lock from an existing one */ /* We can make a new lock from an existing one */
read_lock1 = pv_bwrap_lock_new_take (glnx_steal_fd (&fd), is_ofd); read_lock1 = pv_bwrap_lock_new_take (glnx_steal_fd (&fd), is_ofd);
g_assert_nonnull (read_lock1); g_assert_nonnull (read_lock1);
write_lock1 = pv_bwrap_lock_new (lock, PV_BWRAP_LOCK_FLAGS_WRITE, &error); write_lock1 = pv_bwrap_lock_new (AT_FDCWD, lock, PV_BWRAP_LOCK_FLAGS_WRITE,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY);
g_assert_null (write_lock1); g_assert_null (write_lock1);
g_clear_error (&error); g_clear_error (&error);
/* We can take a second read lock at the same time */ /* We can take a second read lock at the same time */
read_lock2 = pv_bwrap_lock_new (lock, PV_BWRAP_LOCK_FLAGS_CREATE, &error); read_lock2 = pv_bwrap_lock_new (AT_FDCWD, lock, PV_BWRAP_LOCK_FLAGS_CREATE,
&error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert_nonnull (read_lock2); g_assert_nonnull (read_lock2);
/* Releasing one read lock is not enough */ /* Releasing one read lock is not enough */
g_clear_pointer (&read_lock1, pv_bwrap_lock_free); g_clear_pointer (&read_lock1, pv_bwrap_lock_free);
write_lock1 = pv_bwrap_lock_new (lock, PV_BWRAP_LOCK_FLAGS_WRITE, &error); write_lock1 = pv_bwrap_lock_new (AT_FDCWD, lock, PV_BWRAP_LOCK_FLAGS_WRITE,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY);
g_assert_null (write_lock1); g_assert_null (write_lock1);
g_clear_error (&error); g_clear_error (&error);
/* Releasing both read locks is enough to allow a write lock */ /* Releasing both read locks is enough to allow a write lock. This
* incidentally also tests the normalization of -1 to AT_FDCWD. */
g_clear_pointer (&read_lock2, pv_bwrap_lock_free); g_clear_pointer (&read_lock2, pv_bwrap_lock_free);
write_lock1 = pv_bwrap_lock_new (lock, PV_BWRAP_LOCK_FLAGS_WRITE, &error); write_lock1 = pv_bwrap_lock_new (-1, lock, PV_BWRAP_LOCK_FLAGS_WRITE, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert_nonnull (write_lock1); g_assert_nonnull (write_lock1);
/* We cannot take read or write locks while this lock is held */ /* We cannot take read or write locks while this lock is held.
write_lock2 = pv_bwrap_lock_new (lock, PV_BWRAP_LOCK_FLAGS_WRITE, &error); * The second part here also exercises a non-trivial at_fd. */
write_lock2 = pv_bwrap_lock_new (-1, lock, PV_BWRAP_LOCK_FLAGS_WRITE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY);
g_assert_null (write_lock2); g_assert_null (write_lock2);
g_clear_error (&error); g_clear_error (&error);
read_lock1 = pv_bwrap_lock_new (lock, PV_BWRAP_LOCK_FLAGS_NONE, &error); read_lock1 = pv_bwrap_lock_new (tmpdir.fd, "lockfile",
PV_BWRAP_LOCK_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BUSY);
g_assert_null (read_lock1); g_assert_null (read_lock1);
g_clear_error (&error); g_clear_error (&error);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment