]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
lock-util: Add make_lock_file_at()
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 9 Mar 2023 11:59:09 +0000 (12:59 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 21 Mar 2023 14:19:33 +0000 (15:19 +0100)
src/basic/lock-util.c
src/basic/lock-util.h
src/test/meson.build
src/test/test-lock-util.c [new file with mode: 0644]

index 13e4c12237a42d0adc43456a777dc092be13b727..46391bb5ccee7c3d366e4a1ad1af03ddda1e0a65 100644 (file)
 #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;
 }
index 6eebd0914350570d1a701d52db4db6bce555a638..3659f2b3a0d625dc427e33f78913529d32b624d1 100644 (file)
@@ -1,17 +1,23 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <fcntl.h>
+
 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);
index e709314152282fd3b643bd7d5ee8e1aca6fc2740..8d815edea5ac83feb75fdfe1702c4a16ae3e0538 100644 (file)
@@ -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 (file)
index 0000000..a9a1b43
--- /dev/null
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <unistd.h>
+
+#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);