]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fd-util: add new helper fd_reopen_conditional()
authorLennart Poettering <lennart@poettering.net>
Tue, 29 Nov 2022 14:29:25 +0000 (15:29 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 29 Nov 2022 14:32:47 +0000 (15:32 +0100)
This is a wrapper around fd_reopen() that will reopen an fd if the
F_GETFL flags indicate this is necessary, and otherwise not.

This is useful for various utility calls that shall be able to operate
on O_PATH and without it, and might need to convert between the two
depending on what's passed in.

src/basic/fd-util.c
src/basic/fd-util.h
src/test/test-fd-util.c

index ec33a61588e9f8cb6eca7236c38e5a88fe4bdb89..01a45e4384daacc7b8efd5b05a0f0d21cb1c1a66 100644 (file)
@@ -780,6 +780,37 @@ int fd_reopen(int fd, int flags) {
         return new_fd;
 }
 
+int fd_reopen_condition(
+                int fd,
+                int flags,
+                int mask,
+                int *ret_new_fd) {
+
+        int r, new_fd;
+
+        assert(fd >= 0);
+
+        /* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified
+         * flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if
+         * needed, but only then. */
+
+        r = fcntl(fd, F_GETFL);
+        if (r < 0)
+                return -errno;
+
+        if ((r & mask) == (flags & mask)) {
+                *ret_new_fd = -1;
+                return fd;
+        }
+
+        new_fd = fd_reopen(fd, flags);
+        if (new_fd < 0)
+                return new_fd;
+
+        *ret_new_fd = new_fd;
+        return new_fd;
+}
+
 int read_nr_open(void) {
         _cleanup_free_ char *nr_open = NULL;
         int r;
index 29c7d86f27ddbb6b0f17ae1426cff33825fb3499..fbaa45861342fa51a5119f658bd5ec6c3a28a5a9 100644 (file)
@@ -108,6 +108,7 @@ static inline int make_null_stdio(void) {
         })
 
 int fd_reopen(int fd, int flags);
+int fd_reopen_condition(int fd, int flags, int mask, int *ret_new_fd);
 int read_nr_open(void);
 int fd_get_diskseq(int fd, uint64_t *ret);
 
index 2b85ceab822d521805f41fef92e722aa7007c579..df6ca13785faa5a1c5138ba8b5f49962488511a9 100644 (file)
@@ -483,6 +483,53 @@ TEST(fd_reopen) {
         fd1 = -1;
 }
 
+TEST(fd_reopen_condition) {
+        _cleanup_close_ int fd1 = -1, fd3 = -1;
+        int fd2, fl;
+
+        /* Open without O_PATH */
+        fd1 = open("/usr/", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+        assert_se(fd1 >= 0);
+
+        fl = fcntl(fd1, F_GETFL);
+        assert_se(FLAGS_SET(fl, O_DIRECTORY));
+        assert_se(!FLAGS_SET(fl, O_PATH));
+
+        fd2 = fd_reopen_condition(fd1, O_DIRECTORY, O_DIRECTORY|O_PATH, &fd3);
+        assert_se(fd2 == fd1);
+        assert_se(fd3 < 0);
+
+        /* Switch on O_PATH */
+        fd2 = fd_reopen_condition(fd1, O_DIRECTORY|O_PATH, O_DIRECTORY|O_PATH, &fd3);
+        assert_se(fd2 != fd1);
+        assert_se(fd3 == fd2);
+
+        fl = fcntl(fd2, F_GETFL);
+        assert_se(FLAGS_SET(fl, O_DIRECTORY));
+        assert_se(FLAGS_SET(fl, O_PATH));
+
+        close_and_replace(fd1, fd3);
+
+        fd2 = fd_reopen_condition(fd1, O_DIRECTORY|O_PATH, O_DIRECTORY|O_PATH, &fd3);
+        assert_se(fd2 == fd1);
+        assert_se(fd3 < 0);
+
+        /* Switch off O_PATH again */
+        fd2 = fd_reopen_condition(fd1, O_DIRECTORY, O_DIRECTORY|O_PATH, &fd3);
+        assert_se(fd2 != fd1);
+        assert_se(fd3 == fd2);
+
+        fl = fcntl(fd2, F_GETFL);
+        assert_se(FLAGS_SET(fl, O_DIRECTORY));
+        assert_se(!FLAGS_SET(fl, O_PATH));
+
+        close_and_replace(fd1, fd3);
+
+        fd2 = fd_reopen_condition(fd1, O_DIRECTORY, O_DIRECTORY|O_PATH, &fd3);
+        assert_se(fd2 == fd1);
+        assert_se(fd3 < 0);
+}
+
 TEST(take_fd) {
         _cleanup_close_ int fd1 = -1, fd2 = -1;
         int array[2] = { -1, -1 }, i = 0;