]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/basic/fs-util.c
Merge pull request #26944 from aafeijoo-suse/systemd-network-generator-initrd-fix
[thirdparty/systemd.git] / src / basic / fs-util.c
index b942abd477e1f00fb8979e3f27f31a950f48b6c4..1e1413dc80eca339ace82b939030f4e2b8569a4d 100644 (file)
@@ -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"
@@ -781,12 +783,23 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
         return 0;
 }
 
-int open_parent(const char *path, int flags, mode_t mode) {
+int open_parent_at(int dir_fd, const char *path, int flags, mode_t mode) {
         _cleanup_free_ char *parent = NULL;
         int r;
 
+        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+        assert(path);
+
         r = path_extract_directory(path, &parent);
-        if (r < 0)
+        if (r == -EDESTADDRREQ) {
+                parent = strdup(".");
+                if (!parent)
+                        return -ENOMEM;
+        } else if (r == -EADDRNOTAVAIL) {
+                parent = strdup(path);
+                if (!parent)
+                        return -ENOMEM;
+        } else if (r < 0)
                 return r;
 
         /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an
@@ -797,7 +810,7 @@ int open_parent(const char *path, int flags, mode_t mode) {
         else if (!FLAGS_SET(flags, O_TMPFILE))
                 flags |= O_DIRECTORY|O_RDONLY;
 
-        return RET_NERRNO(open(parent, flags, mode));
+        return RET_NERRNO(openat(dir_fd, parent, flags, mode));
 }
 
 int conservative_renameat(
@@ -811,7 +824,7 @@ int conservative_renameat(
          * have the exact same contents and basic file attributes already. In that case remove the new file
          * instead. This call is useful for reducing inotify wakeups on files that are updated but don't
          * actually change. This function is written in a style that we rather rename too often than suppress
-         * too much. i.e. whenever we are in doubt we rather rename than fail. After all reducing inotify
+         * too much. I.e. whenever we are in doubt, we rather rename than fail. After all reducing inotify
          * events is an optimization only, not more. */
 
         old_fd = openat(olddirfd, oldpath, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_NOFOLLOW);
@@ -987,7 +1000,7 @@ int parse_cifs_service(
 
 int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
         _cleanup_close_ int fd = -EBADF, parent_fd = -EBADF;
-        _cleanup_free_ char *fname = NULL;
+        _cleanup_free_ char *fname = NULL, *parent = NULL;
         int r;
 
         /* Creates a directory with mkdirat() and then opens it, in the "most atomic" fashion we can
@@ -1002,19 +1015,13 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
         /* Note that O_DIRECTORY|O_NOFOLLOW is implied, but we allow specifying it anyway. The following
          * flags actually make sense to specify: O_CLOEXEC, O_EXCL, O_NOATIME, O_PATH */
 
-        if (isempty(path))
-                return -EINVAL;
-
-        if (!filename_is_valid(path)) {
-                _cleanup_free_ char *parent = NULL;
-
-                /* If this is not a valid filename, it's a path. Let's open the parent directory then, so
-                 * that we can pin it, and operate below it. */
-
-                r = path_extract_directory(path, &parent);
-                if (r < 0)
+        /* If this is not a valid filename, it's a path. Let's open the parent directory then, so
+         * that we can pin it, and operate below it. */
+        r = path_extract_directory(path, &parent);
+        if (r < 0) {
+                if (!IN_SET(r, -EDESTADDRREQ, -EADDRNOTAVAIL))
                         return r;
-
+        } else {
                 r = path_extract_filename(path, &fname);
                 if (r < 0)
                         return r;
@@ -1088,6 +1095,14 @@ 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 (isempty(path)) {
+                assert(!FLAGS_SET(flags, O_CREAT|O_EXCL));
+                return fd_reopen(dir_fd, flags & ~O_NOFOLLOW);
+        }
+
         if (FLAGS_SET(flags, O_DIRECTORY|O_CREAT)) {
                 r = RET_NERRNO(mkdirat(dir_fd, path, mode));
                 if (r == -EEXIST) {
@@ -1124,3 +1139,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);
+}