* [PATCH v3 1/3] tools/nolibc: Add internal __nolibc_dirfd() helper
2026-10-31 4:29 [PATCH v3 0/3] nolibc: Add dirfd() Daniel Palmer
@ 2026-10-31 4:29 ` Daniel Palmer
2026-10-31 4:29 ` [PATCH v3 2/3] tools/nolibc: Add dirfd() Daniel Palmer
2026-10-31 4:30 ` [PATCH v3 3/3] selftests/nolibc: Add test for dirfd() Daniel Palmer
2 siblings, 0 replies; 4+ messages in thread
From: Daniel Palmer @ 2026-10-31 4:29 UTC (permalink / raw)
To: w, linux; +Cc: linux-kernel, david.laight.linux, Daniel Palmer
closedir() and readdir_r() both convert a DIR pointer back into
an fd. Move that logic into a single helper.
The helper does not set errno so that it can be used by
readdir_r() which returns its own error.
Signed-off-by: Daniel Palmer <daniel@thingy.jp>
---
tools/include/nolibc/dirent.h | 24 +++++++++++++++++-------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/tools/include/nolibc/dirent.h b/tools/include/nolibc/dirent.h
index 19857369de2a..7666dc69506f 100644
--- a/tools/include/nolibc/dirent.h
+++ b/tools/include/nolibc/dirent.h
@@ -27,6 +27,18 @@ typedef struct {
char dummy[1];
} DIR;
+/* Internal dirfd() that does not set errno */
+static __attribute__((unused))
+int __nolibc_dirfd(DIR *dirp)
+{
+ intptr_t fd = ~(intptr_t)dirp;
+
+ if (fd < 0)
+ return -1;
+
+ return fd;
+}
+
static __attribute__((unused))
DIR *fdopendir(int fd)
{
@@ -65,13 +77,13 @@ DIR *opendir(const char *name)
static __attribute__((unused))
int closedir(DIR *dirp)
{
- intptr_t i = (intptr_t)dirp;
+ int fd = __nolibc_dirfd(dirp);
- if (i >= 0) {
+ if (fd < 0) {
SET_ERRNO(EBADF);
return -1;
}
- return close(~i);
+ return close(fd);
}
static __attribute__((unused))
@@ -79,15 +91,13 @@ int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
{
char buf[sizeof(struct linux_dirent64) + NAME_MAX + 1] __nolibc_aligned_as(struct linux_dirent64);
struct linux_dirent64 *ldir = (void *)buf;
- intptr_t i = (intptr_t)dirp;
int fd, ret;
off_t off;
- if (i >= 0)
+ fd = __nolibc_dirfd(dirp);
+ if (fd < 0)
return EBADF;
- fd = ~i;
-
ret = _sys_getdents64(fd, ldir, sizeof(buf));
if (ret < 0)
return -ret;
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v3 3/3] selftests/nolibc: Add test for dirfd()
2026-10-31 4:29 [PATCH v3 0/3] nolibc: Add dirfd() Daniel Palmer
2026-10-31 4:29 ` [PATCH v3 1/3] tools/nolibc: Add internal __nolibc_dirfd() helper Daniel Palmer
2026-10-31 4:29 ` [PATCH v3 2/3] tools/nolibc: Add dirfd() Daniel Palmer
@ 2026-10-31 4:30 ` Daniel Palmer
2 siblings, 0 replies; 4+ messages in thread
From: Daniel Palmer @ 2026-10-31 4:30 UTC (permalink / raw)
To: w, linux; +Cc: linux-kernel, david.laight.linux, Daniel Palmer
Test that open() and dirfd() use the same fd and then use dirfd()
with fstatat() to stat one of its children.
The test only runs for the nolibc build because it uses readdir_r()
which is overridden to force a failure in the libc build.
Signed-off-by: Daniel Palmer <daniel@thingy.jp>
---
tools/testing/selftests/nolibc/nolibc-test.c | 40 ++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index f3d0d27cdbe1..648a539fb42a 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -859,6 +859,45 @@ static int test_dirent(void)
return 0;
}
+static int test_dirfd(void)
+{
+ struct dirent dirent, *result;
+ int ret = 0, fd;
+ struct stat st;
+ DIR *dir;
+
+ fd = open("/", O_RDONLY | O_DIRECTORY);
+ if (fd < 0)
+ return __LINE__;
+
+ dir = fdopendir(fd);
+ if (!dir) {
+ close(fd);
+ return __LINE__;
+ }
+
+ /* Check that dirfd() gives the same fd as open() */
+ if (dirfd(dir) != fd) {
+ ret = __LINE__;
+ goto out;
+ }
+
+ /* Get the name of a child of dir */
+ if (readdir_r(dir, &dirent, &result) != 0 || !result) {
+ ret = __LINE__;
+ goto out;
+ }
+
+ /* stat the child relative to dir */
+ if (fstatat(dirfd(dir), dirent.d_name, &st, AT_SYMLINK_NOFOLLOW) != 0)
+ ret = __LINE__;
+
+out:
+ closedir(dir);
+
+ return ret;
+}
+
int test_getcwd(void)
{
char cwd_syscall[PATH_MAX];
@@ -1764,6 +1803,7 @@ int run_syscall(int min, int max)
CASE_TEST(getdents64_root); EXPECT_SYSNE(1, test_getdents64("/"), -1); break;
CASE_TEST(getdents64_null); EXPECT_SYSER(1, test_getdents64("/dev/null"), -1, ENOTDIR); break;
CASE_TEST(directories); EXPECT_SYSZR(is_nolibc && proc, test_dirent()); break;
+ CASE_TEST(dirfd); EXPECT_SYSZR(is_nolibc, test_dirfd()); break;
CASE_TEST(getrandom); EXPECT_SYSZR(1, test_getrandom()); break;
CASE_TEST(gettimeofday_tv); EXPECT_SYSZR(1, gettimeofday(&tv, NULL)); break;
CASE_TEST(gettimeofday_tv_tz);EXPECT_SYSZR(1, gettimeofday(&tv, &tz)); break;
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread