lock-util: Add make_lock_file_at()

This commit is contained in:
Daan De Meyer
2023-03-09 12:59:09 +01:00
parent 2415a0c7aa
commit aea3ca3613
4 changed files with 60 additions and 6 deletions

View File

@@ -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;
}

View 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);

View 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',

37
src/test/test-lock-util.c Normal file
View File

@@ -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);