* [PATCH v2 0/2] nolibc: Add dirfd()
@ 2026-10-07 5:29 Daniel Palmer
2026-10-07 5:29 ` [PATCH v2 1/2] tools/nolibc: " Daniel Palmer
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Daniel Palmer @ 2026-10-07 5:29 UTC (permalink / raw)
To: w, linux; +Cc: linux-kernel, Daniel Palmer
Add dirfd() to convert a DIR back to an fd for functions like fstatat()
that take an fd.
Change log:
v2 - Fix the name of the dirfd helper, I debated over removing the helper
as suggested by Thomas but it seems right to have the logic in a single
place.
Add test a basic test to make sure dirfd() actually does convert back
to the expected fd and using dirfd() with fstatat().
Daniel Palmer (2):
tools/nolibc: Add dirfd()
selftests/nolibc: Add test for dirfd()
tools/include/nolibc/dirent.h | 38 ++++++++++++----
tools/testing/selftests/nolibc/nolibc-test.c | 47 ++++++++++++++++++++
2 files changed, 77 insertions(+), 8 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v2 1/2] tools/nolibc: Add dirfd() 2026-10-07 5:29 [PATCH v2 0/2] nolibc: Add dirfd() Daniel Palmer @ 2026-10-07 5:29 ` Daniel Palmer 2026-10-07 8:03 ` Thomas Weißschuh 2026-10-07 5:29 ` [PATCH v2 2/2] selftests/nolibc: Add test for dirfd() Daniel Palmer 2026-10-07 6:43 ` [PATCH v2 0/2] nolibc: Add dirfd() Willy Tarreau 2 siblings, 1 reply; 6+ messages in thread From: Daniel Palmer @ 2026-10-07 5:29 UTC (permalink / raw) To: w, linux; +Cc: linux-kernel, Daniel Palmer This is useful for things like fstatat() on children of a directory opened with opendir(). At the same time use the new internal helper to replace the two places that are converting the DIR pointer back into an fd. Signed-off-by: Daniel Palmer <daniel@thingy.jp> --- tools/include/nolibc/dirent.h | 38 +++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/tools/include/nolibc/dirent.h b/tools/include/nolibc/dirent.h index 4e02ef25e72d..d282ea8eb5d3 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 i = (intptr_t)dirp; + + if (i >= 0) + return -1; + + return ~i; +} + static __attribute__((unused)) DIR *fdopendir(int fd) { @@ -49,15 +61,27 @@ DIR *opendir(const char *name) } static __attribute__((unused)) -int closedir(DIR *dirp) +int dirfd(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 fd; +} + +static __attribute__((unused)) +int closedir(DIR *dirp) +{ + int fd = dirfd(dirp); + + if (fd < 0) + return -1; + + return close(fd); } static __attribute__((unused)) @@ -65,14 +89,12 @@ 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; - 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] 6+ messages in thread
* Re: [PATCH v2 1/2] tools/nolibc: Add dirfd() 2026-10-07 5:29 ` [PATCH v2 1/2] tools/nolibc: " Daniel Palmer @ 2026-10-07 8:03 ` Thomas Weißschuh 0 siblings, 0 replies; 6+ messages in thread From: Thomas Weißschuh @ 2026-10-07 8:03 UTC (permalink / raw) To: Daniel Palmer; +Cc: w, linux-kernel On 2026-10-07 14:29:12+0900, Daniel Palmer wrote: > This is useful for things like fstatat() on children of a directory > opened with opendir(). > > At the same time use the new internal helper to replace the two places > that are converting the DIR pointer back into an fd. > > Signed-off-by: Daniel Palmer <daniel@thingy.jp> > --- > tools/include/nolibc/dirent.h | 38 +++++++++++++++++++++++++++-------- > 1 file changed, 30 insertions(+), 8 deletions(-) > > diff --git a/tools/include/nolibc/dirent.h b/tools/include/nolibc/dirent.h > index 4e02ef25e72d..d282ea8eb5d3 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 i = (intptr_t)dirp; > + > + if (i >= 0) > + return -1; > + > + return ~i; > +} I'm fine with keeping __nolibc_dirfd(), but the diffs would be nicer if it was introduced in a dedicated commit. > + > static __attribute__((unused)) > DIR *fdopendir(int fd) > { > @@ -49,15 +61,27 @@ DIR *opendir(const char *name) > } > > static __attribute__((unused)) > -int closedir(DIR *dirp) > +int dirfd(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 fd; > +} (...) ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 2/2] selftests/nolibc: Add test for dirfd() 2026-10-07 5:29 [PATCH v2 0/2] nolibc: Add dirfd() Daniel Palmer 2026-10-07 5:29 ` [PATCH v2 1/2] tools/nolibc: " Daniel Palmer @ 2026-10-07 5:29 ` Daniel Palmer 2026-10-07 8:09 ` Thomas Weißschuh 2026-10-07 6:43 ` [PATCH v2 0/2] nolibc: Add dirfd() Willy Tarreau 2 siblings, 1 reply; 6+ messages in thread From: Daniel Palmer @ 2026-10-07 5:29 UTC (permalink / raw) To: w, linux; +Cc: linux-kernel, Daniel Palmer Test that open() and dirfd() use the same fd and then use dirfd() with fstatat() to stat each of its children. Signed-off-by: Daniel Palmer <daniel@thingy.jp> --- tools/testing/selftests/nolibc/nolibc-test.c | 47 ++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index ed860b0a15a1..82616f2c1ba4 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -858,6 +858,52 @@ 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; + } + + /* Loop through the children of the directory calling fstatat() on each */ + while (1) { + if (readdir_r(dir, &dirent, &result) != 0) { + ret = __LINE__; + goto out; + } + + if (!result) + break; + + if (fstatat(dirfd(dir), dirent.d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) { + ret = __LINE__; + goto out; + + } + } + +out: + closedir(dir); + + return ret; +} + int test_getcwd(void) { char cwd_syscall[PATH_MAX]; @@ -1651,6 +1697,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] 6+ messages in thread
* Re: [PATCH v2 2/2] selftests/nolibc: Add test for dirfd() 2026-10-07 5:29 ` [PATCH v2 2/2] selftests/nolibc: Add test for dirfd() Daniel Palmer @ 2026-10-07 8:09 ` Thomas Weißschuh 0 siblings, 0 replies; 6+ messages in thread From: Thomas Weißschuh @ 2026-10-07 8:09 UTC (permalink / raw) To: Daniel Palmer; +Cc: w, linux-kernel Hi Daniel, On 2026-10-07 14:29:13+0900, Daniel Palmer wrote: > Test that open() and dirfd() use the same fd and then use > dirfd() with fstatat() to stat each of its children. In general the series looks good. I have some minor requests, but can also implement these myself if you prefer. Also I'd like to push the patches into the next development cycles. > Signed-off-by: Daniel Palmer <daniel@thingy.jp> > --- > tools/testing/selftests/nolibc/nolibc-test.c | 47 ++++++++++++++++++++ > 1 file changed, 47 insertions(+) > > diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c > index ed860b0a15a1..82616f2c1ba4 100644 > --- a/tools/testing/selftests/nolibc/nolibc-test.c > +++ b/tools/testing/selftests/nolibc/nolibc-test.c > @@ -858,6 +858,52 @@ 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; > + } > + > + /* Loop through the children of the directory calling fstatat() on each */ What is the advantage of reading all entries here? We already do something similar in test_dirent(). Reading a single entry should be enough. > + while (1) { > + if (readdir_r(dir, &dirent, &result) != 0) { > + ret = __LINE__; > + goto out; > + } > + > + if (!result) > + break; > + > + if (fstatat(dirfd(dir), dirent.d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) { > + ret = __LINE__; > + goto out; > + > + } > + } > + > +out: > + closedir(dir); > + > + return ret; > +} > + > int test_getcwd(void) > { > char cwd_syscall[PATH_MAX]; > @@ -1651,6 +1697,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; Could you mention in the commit message why this is specific to nolibc? > 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] 6+ messages in thread
* Re: [PATCH v2 0/2] nolibc: Add dirfd() 2026-10-07 5:29 [PATCH v2 0/2] nolibc: Add dirfd() Daniel Palmer 2026-10-07 5:29 ` [PATCH v2 1/2] tools/nolibc: " Daniel Palmer 2026-10-07 5:29 ` [PATCH v2 2/2] selftests/nolibc: Add test for dirfd() Daniel Palmer @ 2026-10-07 6:43 ` Willy Tarreau 2 siblings, 0 replies; 6+ messages in thread From: Willy Tarreau @ 2026-10-07 6:43 UTC (permalink / raw) To: Daniel Palmer; +Cc: linux, linux-kernel Hi Daniel, On Wed, Oct 07, 2026 at 02:29:11PM +0900, Daniel Palmer wrote: > Add dirfd() to convert a DIR back to an fd for functions like fstatat() > that take an fd. > > Change log: > v2 - Fix the name of the dirfd helper, I debated over removing the helper > as suggested by Thomas but it seems right to have the logic in a single > place. > Add test a basic test to make sure dirfd() actually does convert back > to the expected fd and using dirfd() with fstatat(). The series looks good to me: Reviewed-by: Willy Tarreau <w@1wt.eu> Let's wait for Thomas in case he spots anything subtle. Thanks! Willy ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-07 8:09 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-07 5:29 [PATCH v2 0/2] nolibc: Add dirfd() Daniel Palmer 2026-10-07 5:29 ` [PATCH v2 1/2] tools/nolibc: " Daniel Palmer 2026-10-07 8:03 ` Thomas Weißschuh 2026-10-07 5:29 ` [PATCH v2 2/2] selftests/nolibc: Add test for dirfd() Daniel Palmer 2026-10-07 8:09 ` Thomas Weißschuh 2026-10-07 6:43 ` [PATCH v2 0/2] nolibc: Add dirfd() Willy Tarreau
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®