* [PATCH 1/3] tools/nolibc: unistd: Add getcwd()
2026-05-02 7:25 [PATCH 0/3] nolibc: Add getcwd() and readlink() Daniel Palmer
@ 2026-05-02 7:25 ` Daniel Palmer
2026-05-02 13:41 ` Thomas Weißschuh
2026-05-02 7:25 ` [PATCH 2/3] tools/nolibc: unistd: Add readlink() Daniel Palmer
2026-05-02 7:25 ` [PATCH 3/3] selftests/nolibc: Add test for getcwd() and readlink() Daniel Palmer
2 siblings, 1 reply; 8+ messages in thread
From: Daniel Palmer @ 2026-05-02 7:25 UTC (permalink / raw)
To: w, linux; +Cc: linux-kernel, Daniel Palmer
Add getcwd() for getting the current working directory.
Signed-off-by: Daniel Palmer <daniel@thingy.jp>
---
tools/include/nolibc/unistd.h | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/tools/include/nolibc/unistd.h b/tools/include/nolibc/unistd.h
index 5882a6862066..c7f89c56bc9b 100644
--- a/tools/include/nolibc/unistd.h
+++ b/tools/include/nolibc/unistd.h
@@ -48,6 +48,28 @@ int access(const char *path, int amode)
return faccessat(AT_FDCWD, path, amode, 0);
}
+/*
+ * char *getcwd(char *buf, size_t size);
+ */
+
+static __attribute__((unused))
+int _sys_getcwd(char *buf, size_t size)
+{
+ return __nolibc_syscall2(__NR_getcwd, buf, size);
+}
+
+static __attribute__((unused))
+char *getcwd(char *buf, size_t size)
+{
+ /* If successful we get the number of bytes written into
+ * the buffer, so check the result is positive.
+ */
+ if (__sysret(_sys_getcwd(buf, size)) > 0)
+ return buf;
+
+ /* On error return NULL, __sysret() above will have set errno */
+ return NULL;
+}
static __attribute__((unused))
int msleep(unsigned int msecs)
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH 1/3] tools/nolibc: unistd: Add getcwd()
2026-05-02 7:25 ` [PATCH 1/3] tools/nolibc: unistd: Add getcwd() Daniel Palmer
@ 2026-05-02 13:41 ` Thomas Weißschuh
2026-05-05 9:07 ` Daniel Palmer
0 siblings, 1 reply; 8+ messages in thread
From: Thomas Weißschuh @ 2026-05-02 13:41 UTC (permalink / raw)
To: Daniel Palmer; +Cc: w, linux-kernel
May 2, 2026 09:25:43 Daniel Palmer <daniel@thingy.jp>:
> Add getcwd() for getting the current working directory.
>
> Signed-off-by: Daniel Palmer <daniel@thingy.jp>
> ---
> tools/include/nolibc/unistd.h | 22 ++++++++++++++++++++++
> 1 file changed, 22 insertions(+)
>
> diff --git a/tools/include/nolibc/unistd.h b/tools/include/nolibc/unistd.h
> index 5882a6862066..c7f89c56bc9b 100644
> --- a/tools/include/nolibc/unistd.h
> +++ b/tools/include/nolibc/unistd.h
> @@ -48,6 +48,28 @@ int access(const char *path, int amode)
> return faccessat(AT_FDCWD, path, amode, 0);
> }
>
> +/*
> + * char *getcwd(char *buf, size_t size);
> + */
> +
> +static __attribute__((unused))
> +int _sys_getcwd(char *buf, size_t size)
> +{
> + return __nolibc_syscall2(__NR_getcwd, buf, size);
> +}
> +
> +static __attribute__((unused))
> +char *getcwd(char *buf, size_t size)
> +{
> + /* If successful we get the number of bytes written into
> + * the buffer, so check the result is positive.
> + */
> + if (__sysret(_sys_getcwd(buf, size)) > 0)
According to the comment before the system call implementation this should be '>= 0'.
> + return buf;
> +
> + /* On error return NULL, __sysret() above will have set errno */
> + return NULL;
> +}
>
> static __attribute__((unused))
> int msleep(unsigned int msecs)
> --
> 2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH 1/3] tools/nolibc: unistd: Add getcwd()
2026-05-02 13:41 ` Thomas Weißschuh
@ 2026-05-05 9:07 ` Daniel Palmer
0 siblings, 0 replies; 8+ messages in thread
From: Daniel Palmer @ 2026-05-05 9:07 UTC (permalink / raw)
To: Thomas Weißschuh; +Cc: w, linux-kernel
Hi Thomas,
On Sat, 2 May 2026 at 22:41, Thomas Weißschuh <linux@weissschuh.net> wrote:
> > +{
> > + /* If successful we get the number of bytes written into
> > + * the buffer, so check the result is positive.
> > + */
> > + if (__sysret(_sys_getcwd(buf, size)) > 0)
>
> According to the comment before the system call implementation this should be '>= 0'.
I think 0 is an error and the comment I wrote is a bit off. I think it
should be "result is greater than 0".
Anyhow, I wondered what musl and glibc were doing around this as I
couldn't work it out from the manpage.
Long story short I have adjusted this so the logic follows what musl
is doing and I'll send a v2.
Thanks,
Daniel.
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/3] tools/nolibc: unistd: Add readlink()
2026-05-02 7:25 [PATCH 0/3] nolibc: Add getcwd() and readlink() Daniel Palmer
2026-05-02 7:25 ` [PATCH 1/3] tools/nolibc: unistd: Add getcwd() Daniel Palmer
@ 2026-05-02 7:25 ` Daniel Palmer
2026-05-02 13:42 ` Thomas Weißschuh
2026-05-02 7:25 ` [PATCH 3/3] selftests/nolibc: Add test for getcwd() and readlink() Daniel Palmer
2 siblings, 1 reply; 8+ messages in thread
From: Daniel Palmer @ 2026-05-02 7:25 UTC (permalink / raw)
To: w, linux; +Cc: linux-kernel, Daniel Palmer
Add readlink(). This is needed to test getcwd();
Signed-off-by: Daniel Palmer <daniel@thingy.jp>
---
tools/include/nolibc/unistd.h | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/tools/include/nolibc/unistd.h b/tools/include/nolibc/unistd.h
index c7f89c56bc9b..309a4e0e65d2 100644
--- a/tools/include/nolibc/unistd.h
+++ b/tools/include/nolibc/unistd.h
@@ -84,6 +84,26 @@ int msleep(unsigned int msecs)
return 0;
}
+/*
+ * ssize_t readlink(const char *path, char *buf, size_t bufsiz);
+ */
+
+static __attribute__((unused))
+ssize_t _sys_readlink(const char *path, char *buf, size_t bufsiz)
+{
+#ifdef __NR_readlink
+ return __nolibc_syscall3(__NR_readlink, path, buf, bufsiz);
+#else
+ return __nolibc_syscall4(__NR_readlinkat, AT_FDCWD, path, buf, bufsiz);
+#endif
+}
+
+static __attribute__((unused))
+ssize_t readlink(const char *path, char *buf, size_t bufsiz)
+{
+ return __sysret(_sys_readlink(path, buf, bufsiz));
+}
+
static __attribute__((unused))
unsigned int sleep(unsigned int seconds)
{
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH 2/3] tools/nolibc: unistd: Add readlink()
2026-05-02 7:25 ` [PATCH 2/3] tools/nolibc: unistd: Add readlink() Daniel Palmer
@ 2026-05-02 13:42 ` Thomas Weißschuh
0 siblings, 0 replies; 8+ messages in thread
From: Thomas Weißschuh @ 2026-05-02 13:42 UTC (permalink / raw)
To: Daniel Palmer; +Cc: w, linux-kernel
May 2, 2026 09:25:44 Daniel Palmer <daniel@thingy.jp>:
> Add readlink(). This is needed to test getcwd();
>
> Signed-off-by: Daniel Palmer <daniel@thingy.jp>
> ---
> tools/include/nolibc/unistd.h | 20 ++++++++++++++++++++
> 1 file changed, 20 insertions(+)
>
> diff --git a/tools/include/nolibc/unistd.h b/tools/include/nolibc/unistd.h
> index c7f89c56bc9b..309a4e0e65d2 100644
> --- a/tools/include/nolibc/unistd.h
> +++ b/tools/include/nolibc/unistd.h
> @@ -84,6 +84,26 @@ int msleep(unsigned int msecs)
> return 0;
> }
>
> +/*
> + * ssize_t readlink(const char *path, char *buf, size_t bufsiz);
> + */
> +
> +static __attribute__((unused))
> +ssize_t _sys_readlink(const char *path, char *buf, size_t bufsiz)
> +{
> +#ifdef __NR_readlink
> + return __nolibc_syscall3(__NR_readlink, path, buf, bufsiz);
> +#else
> + return __nolibc_syscall4(__NR_readlinkat, AT_FDCWD, path, buf, bufsiz);
> +#endif
Every architecture should have readli kat, no?
Then I think we can drop the fallback to regular readlink.
> +}
> +
> +static __attribute__((unused))
> +ssize_t readlink(const char *path, char *buf, size_t bufsiz)
> +{
> + return __sysret(_sys_readlink(path, buf, bufsiz));
> +}
> +
> static __attribute__((unused))
> unsigned int sleep(unsigned int seconds)
> {
> --
> 2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 3/3] selftests/nolibc: Add test for getcwd() and readlink()
2026-05-02 7:25 [PATCH 0/3] nolibc: Add getcwd() and readlink() Daniel Palmer
2026-05-02 7:25 ` [PATCH 1/3] tools/nolibc: unistd: Add getcwd() Daniel Palmer
2026-05-02 7:25 ` [PATCH 2/3] tools/nolibc: unistd: Add readlink() Daniel Palmer
@ 2026-05-02 7:25 ` Daniel Palmer
2026-05-02 13:43 ` Thomas Weißschuh
2 siblings, 1 reply; 8+ messages in thread
From: Daniel Palmer @ 2026-05-02 7:25 UTC (permalink / raw)
To: w, linux; +Cc: linux-kernel, Daniel Palmer
Add a test for getcwd() that uses readlink() so tests that
at the same time.
This is very basic, just getting what should be the same
string via getcwd() and readlink() and then checking they
are the same.
Signed-off-by: Daniel Palmer <daniel@thingy.jp>
---
tools/testing/selftests/nolibc/nolibc-test.c | 26 ++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index 08610cacf030..54513fb9a16a 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -849,6 +849,31 @@ static int test_dirent(void)
return 0;
}
+static int test_getcwd(void)
+{
+ char cwd_syscall[1024];
+ char cwd_proc[1024];
+ ssize_t len;
+
+ /* Read where the link /proc/self/cwd points */
+ len = readlink("/proc/self/cwd", cwd_proc, sizeof(cwd_proc) - 1);
+ if (len <= 0)
+ return -1;
+
+ /* Terminate the string from readlink() */
+ cwd_proc[len] = '\0';
+
+ /* Get the cwd via syscall */
+ if (getcwd(cwd_syscall, sizeof(cwd_syscall)) == NULL)
+ return -1;
+
+ /* Fail if they aren't the same */
+ if (strcmp(cwd_proc, cwd_syscall) != 0)
+ return -1;
+
+ return 0;
+}
+
int test_getrandom(void)
{
uint64_t rng = 0;
@@ -1477,6 +1502,7 @@ int run_syscall(int min, int max)
CASE_TEST(clock_getres); EXPECT_SYSZR(1, clock_getres(CLOCK_MONOTONIC, &ts)); break;
CASE_TEST(clock_gettime); EXPECT_SYSZR(1, clock_gettime(CLOCK_MONOTONIC, &ts)); break;
CASE_TEST(clock_settime); EXPECT_SYSER(1, clock_settime(CLOCK_MONOTONIC, &ts), -1, EINVAL); break;
+ CASE_TEST(getcwd); EXPECT_SYSZR(1, test_getcwd()); break;
CASE_TEST(getpid); EXPECT_SYSNE(1, getpid(), -1); break;
CASE_TEST(getppid); EXPECT_SYSNE(1, getppid(), -1); break;
CASE_TEST(gettid); EXPECT_SYSNE(has_gettid, gettid(), -1); break;
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH 3/3] selftests/nolibc: Add test for getcwd() and readlink()
2026-05-02 7:25 ` [PATCH 3/3] selftests/nolibc: Add test for getcwd() and readlink() Daniel Palmer
@ 2026-05-02 13:43 ` Thomas Weißschuh
0 siblings, 0 replies; 8+ messages in thread
From: Thomas Weißschuh @ 2026-05-02 13:43 UTC (permalink / raw)
To: Daniel Palmer; +Cc: w, linux-kernel
May 2, 2026 09:25:45 Daniel Palmer <daniel@thingy.jp>:
> Add a test for getcwd() that uses readlink() so tests that
> at the same time.
>
> This is very basic, just getting what should be the same
> string via getcwd() and readlink() and then checking they
> are the same.
>
> Signed-off-by: Daniel Palmer <daniel@thingy.jp>
> ---
> tools/testing/selftests/nolibc/nolibc-test.c | 26 ++++++++++++++++++++
> 1 file changed, 26 insertions(+)
>
> diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
> index 08610cacf030..54513fb9a16a 100644
> --- a/tools/testing/selftests/nolibc/nolibc-test.c
> +++ b/tools/testing/selftests/nolibc/nolibc-test.c
> @@ -849,6 +849,31 @@ static int test_dirent(void)
> return 0;
> }
>
> +static int test_getcwd(void)
> +{
> + char cwd_syscall[1024];
> + char cwd_proc[1024];
> + ssize_t len;
> +
> + /* Read where the link /proc/self/cwd points */
> + len = readlink("/proc/self/cwd", cwd_proc, sizeof(cwd_proc) - 1);
> + if (len <= 0)
> + return -1;
> +
> + /* Terminate the string from readlink() */
> + cwd_proc[len] = '\0';
> +
> + /* Get the cwd via syscall */
> + if (getcwd(cwd_syscall, sizeof(cwd_syscall)) == NULL)
> + return -1;
> +
> + /* Fail if they aren't the same */
> + if (strcmp(cwd_proc, cwd_syscall) != 0)
> + return -1;
> +
> + return 0;
> +}
> +
> int test_getrandom(void)
> {
> uint64_t rng = 0;
> @@ -1477,6 +1502,7 @@ int run_syscall(int min, int max)
> CASE_TEST(clock_getres); EXPECT_SYSZR(1, clock_getres(CLOCK_MONOTONIC, &ts)); break;
> CASE_TEST(clock_gettime); EXPECT_SYSZR(1, clock_gettime(CLOCK_MONOTONIC, &ts)); break;
> CASE_TEST(clock_settime); EXPECT_SYSER(1, clock_settime(CLOCK_MONOTONIC, &ts), -1, EINVAL); break;
> + CASE_TEST(getcwd); EXPECT_SYSZR(1, test_getcwd()); break;
EXPECT_SYSZR(proc, ...)
> CASE_TEST(getpid); EXPECT_SYSNE(1, getpid(), -1); break;
> CASE_TEST(getppid); EXPECT_SYSNE(1, getppid(), -1); break;
> CASE_TEST(gettid); EXPECT_SYSNE(has_gettid, gettid(), -1); break;
> --
> 2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread