From: Chris Down Date: Mon, 17 Nov 2025 03:05:09 +0000 (+0800) Subject: fd-util: Add fd_is_writable() to check if FD is opened for writing X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=592c57e586fa5aac78145d689608c487344605db;p=thirdparty%2Fsystemd.git fd-util: Add fd_is_writable() to check if FD is opened for writing 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. --- diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index e0dcbcecd18..834b162c47d 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -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; diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index baa81b6a662..89741881940 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -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) { diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c index a22c0e3119e..db869b5d11c 100644 --- a/src/test/test-fd-util.c +++ b/src/test/test-fd-util.c @@ -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);