mirror of
https://github.com/torvalds/linux.git
synced 2025-04-12 16:47:42 +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
@ -29,6 +29,7 @@ all_files := \
|
||||
compiler.h \
|
||||
crt.h \
|
||||
ctype.h \
|
||||
dirent.h \
|
||||
errno.h \
|
||||
nolibc.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 "time.h"
|
||||
#include "stackprotector.h"
|
||||
#include "dirent.h"
|
||||
|
||||
/* Used by programs to avoid std includes */
|
||||
#define NOLIBC
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user