From aea3ca36135aeb74ea38e7538c710d92b37f479d Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mar 21 2023 14:19:33 +0000 Subject: lock-util: Add make_lock_file_at() --- diff --git a/src/basic/lock-util.c b/src/basic/lock-util.c index 13e4c12..46391bb 100644 --- a/src/basic/lock-util.c +++ b/src/basic/lock-util.c @@ -15,17 +15,25 @@ #include "missing_fcntl.h" #include "path-util.h" -int make_lock_file(const char *p, int operation, LockFile *ret) { - _cleanup_close_ int fd = -EBADF; +int make_lock_file_at(int dir_fd, const char *p, int operation, LockFile *ret) { + _cleanup_close_ int fd = -EBADF, dfd = -EBADF; _cleanup_free_ char *t = NULL; int r; + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); assert(p); assert(IN_SET(operation & ~LOCK_NB, LOCK_EX, LOCK_SH)); assert(ret); + if (isempty(p)) + return -EINVAL; + /* We use UNPOSIX locks as they have nice semantics, and are mostly compatible with NFS. */ + dfd = fd_reopen(dir_fd, O_CLOEXEC|O_PATH|O_DIRECTORY); + if (dfd < 0) + return dfd; + t = strdup(p); if (!t) return -ENOMEM; @@ -33,7 +41,7 @@ int make_lock_file(const char *p, int operation, LockFile *ret) { for (;;) { struct stat st; - fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); + fd = openat(dfd, p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); if (fd < 0) return -errno; @@ -54,6 +62,7 @@ int make_lock_file(const char *p, int operation, LockFile *ret) { } *ret = (LockFile) { + .dir_fd = TAKE_FD(dfd), .path = TAKE_PTR(t), .fd = TAKE_FD(fd), .operation = operation, @@ -100,11 +109,12 @@ void release_lock_file(LockFile *f) { f->operation = LOCK_EX|LOCK_NB; if ((f->operation & ~LOCK_NB) == LOCK_EX) - (void) unlink(f->path); + (void) unlinkat(f->dir_fd, f->path, 0); f->path = mfree(f->path); } + f->dir_fd = safe_close(f->dir_fd); f->fd = safe_close(f->fd); f->operation = 0; } diff --git a/src/basic/lock-util.h b/src/basic/lock-util.h index 6eebd09..3659f2b 100644 --- a/src/basic/lock-util.h +++ b/src/basic/lock-util.h @@ -1,17 +1,23 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include + typedef struct LockFile { + int dir_fd; char *path; int fd; int operation; } LockFile; -int make_lock_file(const char *p, int operation, LockFile *ret); +int make_lock_file_at(int dir_fd, const char *p, int operation, LockFile *ret); +static inline int make_lock_file(const char *p, int operation, LockFile *ret) { + return make_lock_file_at(AT_FDCWD, p, operation, ret); +} int make_lock_file_for(const char *p, int operation, LockFile *ret); void release_lock_file(LockFile *f); -#define LOCK_FILE_INIT { .fd = -EBADF, .path = NULL } +#define LOCK_FILE_INIT { .dir_fd = -EBADF, .fd = -EBADF } /* POSIX locks with the same interface as flock(). */ int posix_lock(int fd, int operation); diff --git a/src/test/meson.build b/src/test/meson.build index e709314..8d815ed 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -106,6 +106,7 @@ simple_tests += files( 'test-list.c', 'test-local-addresses.c', 'test-locale-util.c', + 'test-lock-util.c', 'test-log.c', 'test-logarithm.c', 'test-macro.c', diff --git a/src/test/test-lock-util.c b/src/test/test-lock-util.c new file mode 100644 index 0000000..a9a1b43 --- /dev/null +++ b/src/test/test-lock-util.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "fd-util.h" +#include "lock-util.h" +#include "rm-rf.h" +#include "tests.h" +#include "tmpfile-util.h" + +TEST(make_lock_file) { + _cleanup_(rm_rf_physical_and_freep) char *t = NULL; + _cleanup_close_ int tfd = -EBADF; + _cleanup_(release_lock_file) LockFile lock1 = LOCK_FILE_INIT, lock2 = LOCK_FILE_INIT; + + assert_se((tfd = mkdtemp_open(NULL, 0, &t)) >= 0); + + assert_se(make_lock_file_at(tfd, "lock", LOCK_EX, &lock1) >= 0); + assert_se(faccessat(tfd, "lock", F_OK, 0) >= 0); + assert_se(make_lock_file_at(tfd, "lock", LOCK_EX|LOCK_NB, &lock2) == -EBUSY); + release_lock_file(&lock1); + assert_se(RET_NERRNO(faccessat(tfd, "lock", F_OK, 0)) == -ENOENT); + assert_se(make_lock_file_at(tfd, "lock", LOCK_EX, &lock2) >= 0); + release_lock_file(&lock2); + assert_se(make_lock_file_at(tfd, "lock", LOCK_SH, &lock1) >= 0); + assert_se(faccessat(tfd, "lock", F_OK, 0) >= 0); + assert_se(make_lock_file_at(tfd, "lock", LOCK_SH, &lock2) >= 0); + release_lock_file(&lock1); + assert_se(faccessat(tfd, "lock", F_OK, 0) >= 0); + release_lock_file(&lock2); + + assert_se(fchdir(tfd) >= 0); + assert_se(make_lock_file_at(tfd, "lock", LOCK_EX, &lock1) >= 0); + assert_se(make_lock_file("lock", LOCK_EX|LOCK_NB, &lock2) == -EBUSY); +} + +DEFINE_TEST_MAIN(LOG_INFO);