mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
tools/nolibc: add support for directory access
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>
This commit is contained in:
parent
dde5625d4d
commit
665fa8dea9
4 changed files with 139 additions and 0 deletions
|
@ -29,6 +29,7 @@ all_files := \
|
||||||
compiler.h \
|
compiler.h \
|
||||||
crt.h \
|
crt.h \
|
||||||
ctype.h \
|
ctype.h \
|
||||||
|
dirent.h \
|
||||||
errno.h \
|
errno.h \
|
||||||
nolibc.h \
|
nolibc.h \
|
||||||
signal.h \
|
signal.h \
|
||||||
|
|
98
tools/include/nolibc/dirent.h
Normal file
98
tools/include/nolibc/dirent.h
Normal file
|
@ -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 */
|
|
@ -105,6 +105,7 @@
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include "time.h"
|
#include "time.h"
|
||||||
#include "stackprotector.h"
|
#include "stackprotector.h"
|
||||||
|
#include "dirent.h"
|
||||||
|
|
||||||
/* Used by programs to avoid std includes */
|
/* Used by programs to avoid std includes */
|
||||||
#define NOLIBC
|
#define NOLIBC
|
||||||
|
|
|
@ -769,6 +769,44 @@ int test_getdents64(const char *dir)
|
||||||
return ret;
|
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 test_getpagesize(void)
|
||||||
{
|
{
|
||||||
int x = getpagesize();
|
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(fork); EXPECT_SYSZR(1, test_fork()); break;
|
||||||
CASE_TEST(getdents64_root); EXPECT_SYSNE(1, test_getdents64("/"), -1); 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(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); EXPECT_SYSZR(1, gettimeofday(&tv, NULL)); break;
|
||||||
CASE_TEST(gettimeofday_tv_tz);EXPECT_SYSZR(1, gettimeofday(&tv, &tz)); break;
|
CASE_TEST(gettimeofday_tv_tz);EXPECT_SYSZR(1, gettimeofday(&tv, &tz)); break;
|
||||||
CASE_TEST(getpagesize); EXPECT_SYSZR(1, test_getpagesize()); break;
|
CASE_TEST(getpagesize); EXPECT_SYSZR(1, test_getpagesize()); break;
|
||||||
|
|
Loading…
Add table
Reference in a new issue