]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fd-util: Add fd_is_writable() to check if FD is opened for writing
authorChris Down <chris@chrisdown.name>
Mon, 17 Nov 2025 03:05:09 +0000 (11:05 +0800)
committerChris Down <chris@chrisdown.name>
Wed, 19 Nov 2025 18:02:21 +0000 (02:02 +0800)
This checks whether a file descriptor is valid and opened in a mode that
allows writing (O_WRONLY or O_RDWR). This is useful when we want to
verify that inherited FDs can actually be used for output operations
before dup'ing them.

The helper explicitly handles O_PATH file descriptors, which cannot be
used for I/O operations and thus are never writable.

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

index e0dcbcecd18ea022deb296934f34e4928835f60e..834b162c47d74aa4ba5935adf99a358cd93eae26 100644 (file)
@@ -998,6 +998,21 @@ int fd_vet_accmode(int fd, int mode) {
         return -EPROTOTYPE;
 }
 
+int fd_is_writable(int fd) {
+        int r;
+
+        assert(fd >= 0);
+
+        r = fd_vet_accmode(fd, O_WRONLY);
+        if (r >= 0)
+                return true;
+
+        if (IN_SET(r, -EPROTOTYPE, -EBADFD, -EISDIR))
+                return false;
+
+        return r;
+}
+
 int fd_verify_safe_flags_full(int fd, int extra_flags) {
         int flags, unexpected_flags;
 
index baa81b6a66284daae9e7ae5646752cd59a6055b3..89741881940323dd9c7758d1917da0b3e053728b 100644 (file)
@@ -152,6 +152,7 @@ int fd_reopen_condition(int fd, int flags, int mask, int *ret_new_fd);
 
 int fd_is_opath(int fd);
 int fd_vet_accmode(int fd, int mode);
+int fd_is_writable(int fd);
 
 int fd_verify_safe_flags_full(int fd, int extra_flags);
 static inline int fd_verify_safe_flags(int fd) {
index a22c0e3119e51eb333d4877b1fa52bab2891bbb6..db869b5d11cd43afa58b679b22f24c9f468d8804 100644 (file)
@@ -903,4 +903,27 @@ TEST(fd_vet_accmode) {
         ASSERT_ERROR(fd_vet_accmode(fd_opath, O_RDWR), EBADFD);
 }
 
+TEST(fd_is_writable) {
+        _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-fd-writable.XXXXXX";
+        _cleanup_close_ int fd_ro = -EBADF, fd_wo = -EBADF, fd_rw = -EBADF, fd_path = -EBADF;
+
+        ASSERT_OK(fd_rw = mkostemp_safe(name));
+        ASSERT_OK_POSITIVE(fd_is_writable(fd_rw));
+
+        ASSERT_OK(fd_ro = open(name, O_RDONLY | O_CLOEXEC));
+        ASSERT_OK_ZERO(fd_is_writable(fd_ro));
+
+        ASSERT_OK(fd_wo = open(name, O_WRONLY | O_CLOEXEC));
+        ASSERT_OK_POSITIVE(fd_is_writable(fd_wo));
+
+        ASSERT_OK(fd_path = open(name, O_PATH | O_CLOEXEC));
+        ASSERT_OK_ZERO(fd_is_writable(fd_path));
+
+        ASSERT_SIGNAL(fd_is_writable(-1), SIGABRT);
+
+        safe_close(fd_ro);
+        ASSERT_ERROR(fd_is_writable(fd_ro), EBADF);
+        TAKE_FD(fd_ro);
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);