* [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
* [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 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
* 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
* 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
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®