openat is useful to avoid needing to construct relative paths, so expose a wrapper for using it directly.
Signed-off-by: Louis Taylor louis@kragniz.eu --- tools/include/nolibc/sys.h | 29 ++++++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 22 +++++++++++++++ 2 files changed, 51 insertions(+)
diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h index 8f44c33b1213..e5ff34df4aee 100644 --- a/tools/include/nolibc/sys.h +++ b/tools/include/nolibc/sys.h @@ -765,6 +765,35 @@ int mount(const char *src, const char *tgt, return __sysret(sys_mount(src, tgt, fst, flags, data)); }
+/* + * int openat(int dirfd, const char *path, int flags[, mode_t mode]); + */ + +static __attribute__((unused)) +int sys_openat(int dirfd, const char *path, int flags, mode_t mode) +{ +#ifdef __NR_openat + return my_syscall4(__NR_openat, dirfd, path, flags, mode); +#else + return __nolibc_enosys(__func__, dirfd, path, flags, mode); +#endif +} + +static __attribute__((unused)) +int openat(int dirfd, const char *path, int flags, ...) +{ + mode_t mode = 0; + + if (flags & O_CREAT) { + va_list args; + + va_start(args, flags); + mode = va_arg(args, int); + va_end(args); + } + + return __sysret(sys_openat(dirfd, path, flags, mode)); +}
/* * int open(const char *path, int flags[, mode_t mode]); diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 79c3e6a845f3..97ded6c76f99 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1028,6 +1028,26 @@ int test_rlimit(void) return 0; }
+int test_openat(void) +{ + int dev; + int null; + + dev = openat(AT_FDCWD, "/dev", O_DIRECTORY); + if (dev < 0) + return -1; + + null = openat(dev, "null", 0); + if (null < 0) { + close(dev); + return -1; + } + + close(dev); + close(null); + + return 0; +}
/* Run syscall tests between IDs <min> and <max>. * Return 0 on success, non-zero on failure. @@ -1116,6 +1136,8 @@ int run_syscall(int min, int max) CASE_TEST(mmap_munmap_good); EXPECT_SYSZR(1, test_mmap_munmap()); break; CASE_TEST(open_tty); EXPECT_SYSNE(1, tmp = open("/dev/null", 0), -1); if (tmp != -1) close(tmp); break; CASE_TEST(open_blah); EXPECT_SYSER(1, tmp = open("/proc/self/blah", 0), -1, ENOENT); if (tmp != -1) close(tmp); break; + CASE_TEST(openat_fdcwd); EXPECT_SYSNE(1, tmp = openat(AT_FDCWD, "/dev/null", 0), -1); if (tmp != -1) close(tmp); break; + CASE_TEST(openat_dir); EXPECT_SYSNE(1, test_openat(), -1); break; CASE_TEST(pipe); EXPECT_SYSZR(1, test_pipe()); break; CASE_TEST(poll_null); EXPECT_SYSZR(1, poll(NULL, 0, 0)); break; CASE_TEST(poll_stdout); EXPECT_SYSNE(1, ({ struct pollfd fds = { 1, POLLOUT, 0}; poll(&fds, 1, 0); }), -1); break;
Hi Louis,
On 2025-03-02 20:25:23+0000, Louis Taylor wrote:
openat is useful to avoid needing to construct relative paths, so expose a wrapper for using it directly.
Can you say what you are using nolibc for? I'm curious :-)
Signed-off-by: Louis Taylor louis@kragniz.eu
tools/include/nolibc/sys.h | 29 ++++++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 22 +++++++++++++++ 2 files changed, 51 insertions(+)
diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h index 8f44c33b1213..e5ff34df4aee 100644 --- a/tools/include/nolibc/sys.h +++ b/tools/include/nolibc/sys.h @@ -765,6 +765,35 @@ int mount(const char *src, const char *tgt, return __sysret(sys_mount(src, tgt, fst, flags, data)); } +/*
- int openat(int dirfd, const char *path, int flags[, mode_t mode]);
- */
+static __attribute__((unused)) +int sys_openat(int dirfd, const char *path, int flags, mode_t mode) +{ +#ifdef __NR_openat
- return my_syscall4(__NR_openat, dirfd, path, flags, mode);
+#else
- return __nolibc_enosys(__func__, dirfd, path, flags, mode);
+#endif
All architectures support openat(), so the #else could be dropped.
+}
+static __attribute__((unused)) +int openat(int dirfd, const char *path, int flags, ...) +{
- mode_t mode = 0;
- if (flags & O_CREAT) {
va_list args;
va_start(args, flags);
mode = va_arg(args, int);
mode_t instead of int?
va_end(args);
- }
- return __sysret(sys_openat(dirfd, path, flags, mode));
+} /*
- int open(const char *path, int flags[, mode_t mode]);
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 79c3e6a845f3..97ded6c76f99 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1028,6 +1028,26 @@ int test_rlimit(void) return 0; } +int test_openat(void)
static.
+{
- int dev;
- int null;
- dev = openat(AT_FDCWD, "/dev", O_DIRECTORY);
- if (dev < 0)
return -1;
- null = openat(dev, "null", 0);
- if (null < 0) {
close(dev);
return -1;
- }
- close(dev);
- close(null);
- return 0;
+} /* Run syscall tests between IDs <min> and <max>.
- Return 0 on success, non-zero on failure.
@@ -1116,6 +1136,8 @@ int run_syscall(int min, int max) CASE_TEST(mmap_munmap_good); EXPECT_SYSZR(1, test_mmap_munmap()); break; CASE_TEST(open_tty); EXPECT_SYSNE(1, tmp = open("/dev/null", 0), -1); if (tmp != -1) close(tmp); break; CASE_TEST(open_blah); EXPECT_SYSER(1, tmp = open("/proc/self/blah", 0), -1, ENOENT); if (tmp != -1) close(tmp); break;
CASE_TEST(openat_fdcwd); EXPECT_SYSNE(1, tmp = openat(AT_FDCWD, "/dev/null", 0), -1); if (tmp != -1) close(tmp); break;
AT_FDCWD is already used in test_openat(). What additional value does the test above add?
CASE_TEST(pipe); EXPECT_SYSZR(1, test_pipe()); break; CASE_TEST(poll_null); EXPECT_SYSZR(1, poll(NULL, 0, 0)); break; CASE_TEST(poll_stdout); EXPECT_SYSNE(1, ({ struct pollfd fds = { 1, POLLOUT, 0}; poll(&fds, 1, 0); }), -1); break;CASE_TEST(openat_dir); EXPECT_SYSNE(1, test_openat(), -1); break;
-- 2.45.2
On Sun Mar 2, 2025 at 10:24 PM GMT, Thomas Weißschuh wrote:
Hi Louis,
On 2025-03-02 20:25:23+0000, Louis Taylor wrote:
openat is useful to avoid needing to construct relative paths, so expose a wrapper for using it directly.
Can you say what you are using nolibc for? I'm curious :-)
An incredibly dumb side project which involves creating a very small static binary. So far I've also needed a few other functions (for example clock_gettime) which I've just added local wrappers for, since I'm not sure if they're in scope for nolibc. If they are, I can also send patches for those as well.
Signed-off-by: Louis Taylor louis@kragniz.eu
tools/include/nolibc/sys.h | 29 ++++++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 22 +++++++++++++++ 2 files changed, 51 insertions(+)
diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h index 8f44c33b1213..e5ff34df4aee 100644 --- a/tools/include/nolibc/sys.h +++ b/tools/include/nolibc/sys.h @@ -765,6 +765,35 @@ int mount(const char *src, const char *tgt, return __sysret(sys_mount(src, tgt, fst, flags, data)); } +/*
- int openat(int dirfd, const char *path, int flags[, mode_t mode]);
- */
+static __attribute__((unused)) +int sys_openat(int dirfd, const char *path, int flags, mode_t mode) +{ +#ifdef __NR_openat
- return my_syscall4(__NR_openat, dirfd, path, flags, mode);
+#else
- return __nolibc_enosys(__func__, dirfd, path, flags, mode);
+#endif
All architectures support openat(), so the #else could be dropped.
I agree, but I followed the existing implementation for sys_open which only uses openat if it is defined. If openat can be assumed to always exist, that other #ifdef should be dropped (which I can do in another patch).
+}
+static __attribute__((unused)) +int openat(int dirfd, const char *path, int flags, ...) +{
- mode_t mode = 0;
- if (flags & O_CREAT) {
va_list args;
va_start(args, flags);
mode = va_arg(args, int);
mode_t instead of int?
This implementation is yoinked directly from open() below. I have no opinions, but if it should be changed it should be changed in both functions.
va_end(args);
- }
- return __sysret(sys_openat(dirfd, path, flags, mode));
+} /*
- int open(const char *path, int flags[, mode_t mode]);
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 79c3e6a845f3..97ded6c76f99 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1028,6 +1028,26 @@ int test_rlimit(void) return 0; } +int test_openat(void)
static.
I'll change this. Should the other test_ functions in this file also be static?
+{
- int dev;
- int null;
- dev = openat(AT_FDCWD, "/dev", O_DIRECTORY);
- if (dev < 0)
return -1;
- null = openat(dev, "null", 0);
- if (null < 0) {
close(dev);
return -1;
- }
- close(dev);
- close(null);
- return 0;
+} /* Run syscall tests between IDs <min> and <max>.
- Return 0 on success, non-zero on failure.
@@ -1116,6 +1136,8 @@ int run_syscall(int min, int max) CASE_TEST(mmap_munmap_good); EXPECT_SYSZR(1, test_mmap_munmap()); break; CASE_TEST(open_tty); EXPECT_SYSNE(1, tmp = open("/dev/null", 0), -1); if (tmp != -1) close(tmp); break; CASE_TEST(open_blah); EXPECT_SYSER(1, tmp = open("/proc/self/blah", 0), -1, ENOENT); if (tmp != -1) close(tmp); break;
CASE_TEST(openat_fdcwd); EXPECT_SYSNE(1, tmp = openat(AT_FDCWD, "/dev/null", 0), -1); if (tmp != -1) close(tmp); break;
AT_FDCWD is already used in test_openat(). What additional value does the test above add?
None, I just wrote that one first before thinking about how I'd test opening something relative to an fd. I'll drop it.
CASE_TEST(pipe); EXPECT_SYSZR(1, test_pipe()); break; CASE_TEST(poll_null); EXPECT_SYSZR(1, poll(NULL, 0, 0)); break; CASE_TEST(poll_stdout); EXPECT_SYSNE(1, ({ struct pollfd fds = { 1, POLLOUT, 0}; poll(&fds, 1, 0); }), -1); break;CASE_TEST(openat_dir); EXPECT_SYSNE(1, test_openat(), -1); break;
-- 2.45.2
On Mon Mar 3, 2025 at 8:49 AM GMT, Louis Taylor wrote:
+}
+static __attribute__((unused)) +int openat(int dirfd, const char *path, int flags, ...) +{
- mode_t mode = 0;
- if (flags & O_CREAT) {
va_list args;
va_start(args, flags);
mode = va_arg(args, int);
mode_t instead of int?
This implementation is yoinked directly from open() below. I have no opinions, but if it should be changed it should be changed in both functions.
Actually, maybe this openat function could just entirely drop the varargs since compatibility is less of an issue, effectively going back to the interface before a7604ba149e7 (tools/nolibc/sys: make open() take a vararg on the 3rd argument, 2022-02-07) rather than copying the current state.
linux-kselftest-mirror@lists.linaro.org