]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
tools/nolibc: add support for directory access
authorThomas Weißschuh <thomas.weissschuh@linutronix.de>
Sun, 9 Feb 2025 13:25:46 +0000 (14:25 +0100)
committerThomas Weißschuh <linux@weissschuh.net>
Sun, 9 Feb 2025 15:46:50 +0000 (16:46 +0100)
Add an implementation for directory access operations.
To keep nolibc itself allocation-free, a "DIR *" does not point to any
data, but directly encodes a filedescriptor number, equivalent to "FILE *".
Without any per-directory storage it is not possible to implement
readdir() POSIX confirming. Instead only readdir_r() is provided.
While readdir_r() is deprecated in glibc, the reasons for that are
not applicable to nolibc.

Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Link: https://lore.kernel.org/r/20250209-nolibc-dir-v2-2-57cc1da8558b@weissschuh.net
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
tools/include/nolibc/Makefile
tools/include/nolibc/dirent.h [new file with mode: 0644]
tools/include/nolibc/nolibc.h
tools/testing/selftests/nolibc/nolibc-test.c

index a1f55fb24bb38c1f49c653af5825e8bcc569a56d..dceec0e1a135119108d6f4dcb3d2ec57c002ffd3 100644 (file)
@@ -29,6 +29,7 @@ all_files := \
                compiler.h \
                crt.h \
                ctype.h \
+               dirent.h \
                errno.h \
                nolibc.h \
                signal.h \
diff --git a/tools/include/nolibc/dirent.h b/tools/include/nolibc/dirent.h
new file mode 100644 (file)
index 0000000..c5c30d0
--- /dev/null
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
+/*
+ * Directory access for NOLIBC
+ * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
+ */
+
+#ifndef _NOLIBC_DIRENT_H
+#define _NOLIBC_DIRENT_H
+
+#include "stdint.h"
+#include "types.h"
+
+#include <linux/limits.h>
+
+struct dirent {
+       ino_t   d_ino;
+       char    d_name[NAME_MAX + 1];
+};
+
+/* See comment of FILE in stdio.h */
+typedef struct {
+       char dummy[1];
+} DIR;
+
+static __attribute__((unused))
+DIR *fdopendir(int fd)
+{
+       if (fd < 0) {
+               SET_ERRNO(EBADF);
+               return NULL;
+       }
+       return (DIR *)(intptr_t)~fd;
+}
+
+static __attribute__((unused))
+DIR *opendir(const char *name)
+{
+       int fd;
+
+       fd = open(name, O_RDONLY);
+       if (fd == -1)
+               return NULL;
+       return fdopendir(fd);
+}
+
+static __attribute__((unused))
+int closedir(DIR *dirp)
+{
+       intptr_t i = (intptr_t)dirp;
+
+       if (i >= 0) {
+               SET_ERRNO(EBADF);
+               return -1;
+       }
+       return close(~i);
+}
+
+static __attribute__((unused))
+int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
+{
+       char buf[sizeof(struct linux_dirent64) + NAME_MAX + 1];
+       struct linux_dirent64 *ldir = (void *)buf;
+       intptr_t i = (intptr_t)dirp;
+       int fd, ret;
+
+       if (i >= 0)
+               return EBADF;
+
+       fd = ~i;
+
+       ret = sys_getdents64(fd, ldir, sizeof(buf));
+       if (ret < 0)
+               return -ret;
+       if (ret == 0) {
+               *result = NULL;
+               return 0;
+       }
+
+       /*
+        * getdents64() returns as many entries as fit the buffer.
+        * readdir() can only return one entry at a time.
+        * Make sure the non-returned ones are not skipped.
+        */
+       ret = lseek(fd, ldir->d_off, SEEK_SET);
+       if (ret == -1)
+               return errno;
+
+       entry->d_ino = ldir->d_ino;
+       /* the destination should always be big enough */
+       strlcpy(entry->d_name, ldir->d_name, sizeof(entry->d_name));
+       *result = entry;
+       return 0;
+}
+
+/* make sure to include all global symbols */
+#include "nolibc.h"
+
+#endif /* _NOLIBC_DIRENT_H */
index 92436b1e44413e659b3cca592930aad8b458cb74..05d92afedb7258f0e3c311bf6f12be68b25d6e9a 100644 (file)
 #include "string.h"
 #include "time.h"
 #include "stackprotector.h"
+#include "dirent.h"
 
 /* Used by programs to avoid std includes */
 #define NOLIBC
index f162793b162f9b1ec687098b9094a6d247a53e99..798fbdcd3ff8c36b514feb3fa1c7b8d7701cccd7 100644 (file)
@@ -769,6 +769,44 @@ int test_getdents64(const char *dir)
        return ret;
 }
 
+static int test_dirent(void)
+{
+       int comm = 0, cmdline = 0;
+       struct dirent dirent, *result;
+       DIR *dir;
+       int ret;
+
+       dir = opendir("/proc/self");
+       if (!dir)
+               return 1;
+
+       while (1) {
+               errno = 0;
+               ret = readdir_r(dir, &dirent, &result);
+               if (ret != 0)
+                       return 1;
+               if (!result)
+                       break;
+
+               if (strcmp(dirent.d_name, "comm") == 0)
+                       comm++;
+               else if (strcmp(dirent.d_name, "cmdline") == 0)
+                       cmdline++;
+       }
+
+       if (errno)
+               return 1;
+
+       ret = closedir(dir);
+       if (ret)
+               return 1;
+
+       if (comm != 1 || cmdline != 1)
+               return 1;
+
+       return 0;
+}
+
 int test_getpagesize(void)
 {
        int x = getpagesize();
@@ -1061,6 +1099,7 @@ int run_syscall(int min, int max)
                CASE_TEST(fork);              EXPECT_SYSZR(1, test_fork()); break;
                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(proc, test_dirent()); break;
                CASE_TEST(gettimeofday_tv);   EXPECT_SYSZR(1, gettimeofday(&tv, NULL)); break;
                CASE_TEST(gettimeofday_tv_tz);EXPECT_SYSZR(1, gettimeofday(&tv, &tz)); break;
                CASE_TEST(getpagesize);       EXPECT_SYSZR(1, test_getpagesize()); break;