summaryrefslogtreecommitdiff
path: root/src/basic/fs-util.c
diff options
context:
space:
mode:
authorDaan De Meyer <daan.j.demeyer@gmail.com>2023-03-22 17:04:36 +0100
committerDaan De Meyer <daan.j.demeyer@gmail.com>2023-03-22 21:54:20 +0100
commit2646b86dd6a84f2e7ebc625203d44e6d1923c5bd (patch)
tree835cc1aa3aa41a91508a33e2dde76de745735a06 /src/basic/fs-util.c
parenta3b00f91bb985fa10bc33db5c7883e33dbdf83d0 (diff)
downloadsystemd-2646b86dd6a84f2e7ebc625203d44e6d1923c5bd.tar.gz
fs-util: Add xopenat_lock()
open/create a file/directory and lock it using the given lock type.
Diffstat (limited to 'src/basic/fs-util.c')
-rw-r--r--src/basic/fs-util.c44
1 files changed, 44 insertions, 0 deletions
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 19ada73af5..06bd5705f2 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -3,6 +3,7 @@
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
+#include <sys/file.h>
#include <linux/falloc.h>
#include <linux/magic.h>
#include <unistd.h>
@@ -13,6 +14,7 @@
#include "fileio.h"
#include "fs-util.h"
#include "hostname-util.h"
+#include "lock-util.h"
#include "log.h"
#include "macro.h"
#include "missing_fcntl.h"
@@ -1099,6 +1101,9 @@ int xopenat(int dir_fd, const char *path, int flags, mode_t mode) {
bool made = false;
int r;
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ assert(path);
+
if (FLAGS_SET(flags, O_DIRECTORY|O_CREAT)) {
r = RET_NERRNO(mkdirat(dir_fd, path, mode));
if (r == -EEXIST) {
@@ -1135,3 +1140,42 @@ int xopenat(int dir_fd, const char *path, int flags, mode_t mode) {
return TAKE_FD(fd);
}
+
+int xopenat_lock(int dir_fd, const char *path, int flags, mode_t mode, LockType locktype, int operation) {
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ assert(path);
+ assert(IN_SET(operation & ~LOCK_NB, LOCK_EX, LOCK_SH));
+
+ /* POSIX/UNPOSIX locks don't work on directories (errno is set to -EBADF so let's return early with
+ * the same error here). */
+ if (FLAGS_SET(flags, O_DIRECTORY) && locktype != LOCK_BSD)
+ return -EBADF;
+
+ for (;;) {
+ struct stat st;
+
+ fd = xopenat(dir_fd, path, flags, mode);
+ if (fd < 0)
+ return fd;
+
+ r = lock_generic(fd, locktype, operation);
+ if (r < 0)
+ return r;
+
+ /* If we acquired the lock, let's check if the file/directory still exists in the file
+ * system. If not, then the previous exclusive owner removed it and then closed it. In such a
+ * case our acquired lock is worthless, hence try again. */
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+ if (st.st_nlink > 0)
+ break;
+
+ fd = safe_close(fd);
+ }
+
+ return TAKE_FD(fd);
+}