- Implemented opendir, readdir, closedir, and related directory functions - Added fcntl system call support with proper flag handling - Integrated compiler-rt builtins for 128-bit integer operations - Updated build system to fetch and compile compiler-rt dependencies - Refactored ls command to use standard POSIX directory APIs - Added directory file descriptor management in kernel - Implemented getdents64master
parent
ee69e75f4f
commit
78d6d2b67b
34 changed files with 945 additions and 144 deletions
@ -0,0 +1,15 @@ |
|||||||
|
1a8f1058753f1ba890de984e48f0242a3a5c29a6a8f2ed9fd813f36985387e8d LICENSE.TXT |
||||||
|
ec5175561075845b7b16a92ad5ecdfd42a284de2736dd07e4ca7011b75c37709 lib/builtins/int_endianness.h |
||||||
|
a5735bf1bfacecd6cf5785ed19ae62a981ff2f0923c38caa99f4af47f2be57b5 lib/builtins/int_lib.h |
||||||
|
198552b25faf8a75b077ea145591cd7f6762b7f2712321b18f5e38fee8cc2a1e lib/builtins/int_types.h |
||||||
|
63d06198ab720a75c20bfd84be895c2746581b129168bae6201e7384bbb0473b lib/builtins/int_util.h |
||||||
|
833906405d418f0f421d6155acc5abf79d9902ed37a44903d01c95ff55d3a010 lib/builtins/int_div_impl.inc |
||||||
|
39afba4710d9b24c9c27cccdc8e18a18db10ce3616dc60cdcfa494f0a49a3a60 lib/builtins/ashlti3.c |
||||||
|
a6da2dc32167fbf369bbb0d185daeaf1b23aa13fda72f832e2097926c9fb2d74 lib/builtins/ashrti3.c |
||||||
|
89da91e31e33c3c62128c32ed1c740856a413c17ac1e7cd75702da37d1cf9fa4 lib/builtins/lshrti3.c |
||||||
|
b8392be2b070cda2a01bc33c7912e06ab83ce9a1ec7995fe0acbe44540cf92ae lib/builtins/divti3.c |
||||||
|
23b5f5af57a8a3ccdab785eec6e486bffef932cee87ff1152b3beedaa6296414 lib/builtins/modti3.c |
||||||
|
b2e133764668e78119f7c76facbb11f0b4b7c0c6b279fb0b97a589da11d1d060 lib/builtins/multi3.c |
||||||
|
c0b20f06f6e0e8a6b5423ca6674cf0c57219d07554f6f35c9ecc36bf28915355 lib/builtins/udivti3.c |
||||||
|
2fb3eb297e006000935452cb34622b389c5ce39af6e1416b4a11398b78142251 lib/builtins/udivmodti4.c |
||||||
|
3d88088419d87b486858a4bf54ff08209944ae526c77b8dc5e0c0b6b8001c1e9 lib/builtins/umodti3.c |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
#!/usr/bin/env sh |
||||||
|
|
||||||
|
set -eu |
||||||
|
|
||||||
|
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) |
||||||
|
version=23.1.1 |
||||||
|
tag="llvmorg-${version}" |
||||||
|
target="${project_dir}/thirdparty/compiler-rt" |
||||||
|
marker="${target}/.typephp-source-${version}" |
||||||
|
manifest="${project_dir}/tools/compiler-rt-builtins-files.sha256" |
||||||
|
base_url="https://raw.githubusercontent.com/llvm/llvm-project/${tag}/compiler-rt" |
||||||
|
|
||||||
|
if [ -f "${marker}" ] && [ -f "${target}/lib/builtins/udivti3.c" ]; then |
||||||
|
exit 0 |
||||||
|
fi |
||||||
|
|
||||||
|
temporary=$(mktemp -d "${TMPDIR:-/tmp}/typephp-os-compiler-rt.XXXXXX") |
||||||
|
trap 'rm -rf "${temporary}"' EXIT HUP INT TERM |
||||||
|
staging="${temporary}/installed" |
||||||
|
|
||||||
|
echo "Downloading compiler-rt builtins ${version}" |
||||||
|
while read -r expected relative; do |
||||||
|
[ -n "${relative}" ] || continue |
||||||
|
destination="${staging}/${relative}" |
||||||
|
mkdir -p "$(dirname -- "${destination}")" |
||||||
|
curl -L --fail --retry 3 -o "${destination}" "${base_url}/${relative}" |
||||||
|
printf '%s %s\n' "${expected}" "${destination}" | sha256sum -c - |
||||||
|
done < "${manifest}" |
||||||
|
|
||||||
|
mkdir -p "${project_dir}/thirdparty" |
||||||
|
rm -rf "${target}" |
||||||
|
mv "${staging}" "${target}" |
||||||
|
printf '%s\n' "${tag}" > "${marker}" |
||||||
|
echo "Installed compiler-rt builtins ${version} in ${target}" |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
#!/usr/bin/env sh |
||||||
|
|
||||||
|
set -eu |
||||||
|
|
||||||
|
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) |
||||||
|
version=7.0.0 |
||||||
|
commit=7c2a178142625cc9852e59a1a090468c61a62d3b |
||||||
|
archive_sha256=56d62366a6a15bc3fd8741bebcb311ca543553f7b734b1e780aeae33691bd621 |
||||||
|
target="${project_dir}/thirdparty/mlibc" |
||||||
|
marker="${target}/.typephp-source-${version}" |
||||||
|
|
||||||
|
if [ -f "${marker}" ] && [ -f "${target}/meson.build" ]; then |
||||||
|
exit 0 |
||||||
|
fi |
||||||
|
|
||||||
|
temporary=$(mktemp -d "${TMPDIR:-/tmp}/typephp-os-mlibc.XXXXXX") |
||||||
|
trap 'rm -rf "${temporary}"' EXIT HUP INT TERM |
||||||
|
archive="${temporary}/mlibc.tar.gz" |
||||||
|
staging="${temporary}/installed" |
||||||
|
|
||||||
|
echo "Downloading mlibc v${version} (${commit})" |
||||||
|
curl -L --fail --retry 3 -o "${archive}" \ |
||||||
|
"https://github.com/managarm/mlibc/archive/refs/tags/v${version}.tar.gz" |
||||||
|
printf '%s %s\n' "${archive_sha256}" "${archive}" | sha256sum -c - |
||||||
|
mkdir -p "${staging}" |
||||||
|
tar -xzf "${archive}" -C "${staging}" --strip-components=1 |
||||||
|
test -f "${staging}/LICENSE" |
||||||
|
|
||||||
|
mkdir -p "${project_dir}/thirdparty" |
||||||
|
rm -rf "${target}" |
||||||
|
mv "${staging}" "${target}" |
||||||
|
printf '%s\n' "${commit}" > "${marker}" |
||||||
|
echo "Installed mlibc v${version} in ${target}" |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
#!/usr/bin/env sh |
||||||
|
|
||||||
|
set -eu |
||||||
|
|
||||||
|
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) |
||||||
|
version=0.8.7 |
||||||
|
archive_sha256=e328a1d59b94748b111e022bca6a9d2fc0481fb57d23c87d90f394b559d4f062 |
||||||
|
target="${project_dir}/thirdparty/openlibm" |
||||||
|
marker="${target}/.typephp-source-${version}" |
||||||
|
manifest="${project_dir}/tools/openlibm-files.txt" |
||||||
|
|
||||||
|
if [ -f "${marker}" ] && [ -f "${target}/src/e_sqrt.c" ]; then |
||||||
|
exit 0 |
||||||
|
fi |
||||||
|
|
||||||
|
temporary=$(mktemp -d "${TMPDIR:-/tmp}/typephp-os-openlibm.XXXXXX") |
||||||
|
trap 'rm -rf "${temporary}"' EXIT HUP INT TERM |
||||||
|
archive="${temporary}/openlibm.tar.gz" |
||||||
|
source_dir="${temporary}/openlibm-${version}" |
||||||
|
staging="${temporary}/installed" |
||||||
|
|
||||||
|
echo "Downloading OpenLibm v${version}" |
||||||
|
curl -L --fail --retry 3 \ |
||||||
|
-o "${archive}" \ |
||||||
|
"https://github.com/JuliaMath/openlibm/archive/refs/tags/v${version}.tar.gz" |
||||||
|
echo "${archive_sha256} ${archive}" | sha256sum -c - |
||||||
|
tar -xzf "${archive}" -C "${temporary}" |
||||||
|
|
||||||
|
while IFS= read -r relative; do |
||||||
|
[ -n "${relative}" ] || continue |
||||||
|
mkdir -p "${staging}/$(dirname -- "${relative}")" |
||||||
|
cp "${source_dir}/${relative}" "${staging}/${relative}" |
||||||
|
done < "${manifest}" |
||||||
|
|
||||||
|
mkdir -p "${project_dir}/thirdparty" |
||||||
|
rm -rf "${target}" |
||||||
|
mv "${staging}" "${target}" |
||||||
|
printf '%s\n' "${archive_sha256}" > "${marker}" |
||||||
|
echo "Installed OpenLibm v${version} in ${target}" |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
#!/usr/bin/env sh |
||||||
|
|
||||||
|
set -eu |
||||||
|
|
||||||
|
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) |
||||||
|
version=0.8.14 |
||||||
|
commit=b7ec52ac35e075caffca5d330995d44e8dbfc8c3 |
||||||
|
archive_sha256=827e4cdfd69f5da973e00e2a59b30b3c9857fb7fae74c362fd0b4f96be7929b0 |
||||||
|
target="${project_dir}/thirdparty/toybox" |
||||||
|
marker="${target}/.typephp-source-${version}" |
||||||
|
|
||||||
|
if [ -f "${marker}" ] && [ -f "${target}/Makefile" ]; then |
||||||
|
exit 0 |
||||||
|
fi |
||||||
|
|
||||||
|
temporary=$(mktemp -d "${TMPDIR:-/tmp}/typephp-os-toybox.XXXXXX") |
||||||
|
trap 'rm -rf "${temporary}"' EXIT HUP INT TERM |
||||||
|
archive="${temporary}/toybox.tar.gz" |
||||||
|
staging="${temporary}/installed" |
||||||
|
|
||||||
|
echo "Downloading Toybox ${version} (${commit})" |
||||||
|
curl -L --fail --retry 3 -o "${archive}" \ |
||||||
|
"https://landley.net/toybox/downloads/toybox-${version}.tar.gz" |
||||||
|
printf '%s %s\n' "${archive_sha256}" "${archive}" | sha256sum -c - |
||||||
|
mkdir -p "${staging}" |
||||||
|
tar -xzf "${archive}" -C "${staging}" --strip-components=1 |
||||||
|
test -f "${staging}/LICENSE" |
||||||
|
|
||||||
|
mkdir -p "${project_dir}/thirdparty" |
||||||
|
rm -rf "${target}" |
||||||
|
mv "${staging}" "${target}" |
||||||
|
printf '%s\n' "${commit}" > "${marker}" |
||||||
|
echo "Installed Toybox ${version} in ${target}" |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
[binaries] |
||||||
|
c = 'gcc' |
||||||
|
cpp = 'g++' |
||||||
|
ar = 'ar' |
||||||
|
strip = 'strip' |
||||||
|
|
||||||
|
[host_machine] |
||||||
|
system = 'demo' |
||||||
|
cpu_family = 'x86_64' |
||||||
|
cpu = 'x86_64' |
||||||
|
endian = 'little' |
||||||
|
|
||||||
|
[properties] |
||||||
|
needs_exe_wrapper = true |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
#include <dirent.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <sys/mman.h> |
||||||
|
#include <sys/stat.h> |
||||||
|
#include <sys/utsname.h> |
||||||
|
#include <time.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
_Static_assert(sizeof(uintptr_t) == 8, "TypePHP-OS requires a 64-bit mlibc ABI"); |
||||||
|
|
||||||
|
static void typecheck(void) |
||||||
|
{ |
||||||
|
struct dirent entry; |
||||||
|
struct stat status; |
||||||
|
struct timespec timestamp; |
||||||
|
struct utsname identity; |
||||||
|
|
||||||
|
(void) entry; |
||||||
|
(void) status; |
||||||
|
(void) timestamp; |
||||||
|
(void) identity; |
||||||
|
} |
||||||
@ -0,0 +1,74 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
|
||||||
|
set -euo pipefail |
||||||
|
|
||||||
|
project_dir=$(cd -- "$(dirname -- "$0")/.." && pwd) |
||||||
|
build_dir="${project_dir}/build/thirdparty-smoke" |
||||||
|
meson_command=${MESON:-meson} |
||||||
|
|
||||||
|
if [[ ! -f "${project_dir}/build/libcompiler-rt-builtins.a" ]]; then |
||||||
|
echo "compiler-rt archive is missing; run make compiler-rt-builtins first" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
for symbol in __udivti3 __udivmodti4 __divti3 __modti3 __multi3; do |
||||||
|
if ! nm -g --defined-only "${project_dir}/build/libcompiler-rt-builtins.a" \ |
||||||
|
| grep -qE "[[:space:]]${symbol}$"; then |
||||||
|
echo "compiler-rt archive does not define ${symbol}" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
done |
||||||
|
echo "compiler-rt builtins archive: OK" |
||||||
|
|
||||||
|
if ! command -v "${meson_command}" >/dev/null 2>&1; then |
||||||
|
echo "Meson 1.3 or newer is required for the mlibc headers probe" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
mlibc_build="${build_dir}/mlibc-build" |
||||||
|
mlibc_sysroot="${build_dir}/mlibc-sysroot" |
||||||
|
rm -rf "${mlibc_build}" "${mlibc_sysroot}" |
||||||
|
"${meson_command}" setup \ |
||||||
|
"${mlibc_build}" "${project_dir}/thirdparty/mlibc" \ |
||||||
|
--cross-file "${project_dir}/tools/mlibc-demo-cross.ini" \ |
||||||
|
--prefix=/usr \ |
||||||
|
-Dheaders_only=true \ |
||||||
|
-Dposix_option=enabled \ |
||||||
|
-Dlinux_option=disabled \ |
||||||
|
-Dglibc_option=disabled \ |
||||||
|
-Dbsd_option=disabled |
||||||
|
DESTDIR="${mlibc_sysroot}" "${meson_command}" install -C "${mlibc_build}" |
||||||
|
compiler_headers=$(gcc -print-file-name=include) |
||||||
|
gcc -std=c11 -ffreestanding -fsyntax-only -nostdinc \ |
||||||
|
-isystem "${mlibc_sysroot}/usr/include" \ |
||||||
|
-isystem "${compiler_headers}" \ |
||||||
|
"${project_dir}/tools/mlibc-headers-smoke.c" |
||||||
|
echo "mlibc public-header ABI probe: OK" |
||||||
|
|
||||||
|
toybox_source="${project_dir}/thirdparty/toybox" |
||||||
|
toybox_config="${build_dir}/toybox.config" |
||||||
|
mkdir -p "${build_dir}" |
||||||
|
KCONFIG_ALLCONFIG="${project_dir}/tools/toybox-miniconfig" \ |
||||||
|
KCONFIG_CONFIG="${toybox_config}" \ |
||||||
|
CFLAGS="-O2 -fno-stack-protector -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0" \ |
||||||
|
make -C "${toybox_source}" allnoconfig toybox |
||||||
|
|
||||||
|
test "$("${toybox_source}/toybox" | tr ' ' '\n' | sed '/^$/d' | sort | tr '\n' ' ')" \ |
||||||
|
= "cat date echo pwd uname " |
||||||
|
test "$("${toybox_source}/toybox" echo 'Toybox smoke: OK')" = "Toybox smoke: OK" |
||||||
|
"${toybox_source}/toybox" date -u +%Y-%m-%d >/dev/null |
||||||
|
"${toybox_source}/toybox" uname -s >/dev/null |
||||||
|
|
||||||
|
toybox_symbols="${build_dir}/toybox-undefined-symbols.txt" |
||||||
|
nm -u "${toybox_source}/generated/unstripped/toybox" \ |
||||||
|
| awk '{ print $NF }' | sed 's/@.*//' | sort -u > "${toybox_symbols}" |
||||||
|
if grep -Eq '^(fork|vfork|execv|execve|execvp|posix_spawn)$' "${toybox_symbols}"; then |
||||||
|
echo "the selected Toybox applets unexpectedly require process creation" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
echo "Toybox selected applets build: OK" |
||||||
|
if grep -qx ioctl "${toybox_symbols}"; then |
||||||
|
echo "Toybox install: deferred (the common runtime still references unsupported ioctl)" |
||||||
|
else |
||||||
|
echo "Toybox install: ABI audit no longer reports ioctl; reassess target linking" |
||||||
|
fi |
||||||
|
echo "Toybox ABI report: ${toybox_symbols}" |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
CONFIG_TOYBOX=y |
||||||
|
CONFIG_TOYBOX_HELP=y |
||||||
|
# CONFIG_TOYBOX_FORK is not set |
||||||
|
CONFIG_CAT=y |
||||||
|
CONFIG_DATE=y |
||||||
|
CONFIG_ECHO=y |
||||||
|
CONFIG_PWD=y |
||||||
|
CONFIG_UNAME=y |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
#include <stddef.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
typedef unsigned __int128 uint128_t; |
||||||
|
typedef __int128 int128_t; |
||||||
|
|
||||||
|
static int fail(void) |
||||||
|
{ |
||||||
|
static const char message[] = "compiler-rt builtins: failed\n"; |
||||||
|
(void) write(STDERR_FILENO, message, sizeof(message) - 1); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char **argv) |
||||||
|
{ |
||||||
|
volatile uint128_t unsigned_numerator = ((uint128_t) 1 << 100) + 123456789u; |
||||||
|
volatile uint128_t unsigned_divisor = ((uint128_t) 1 << 37) + 19u; |
||||||
|
volatile int128_t signed_numerator = -(((int128_t) 1 << 99) + 7654321); |
||||||
|
volatile int128_t signed_divisor = ((int128_t) 1 << 35) + 11; |
||||||
|
uint128_t quotient; |
||||||
|
uint128_t remainder; |
||||||
|
int128_t signed_quotient; |
||||||
|
int128_t signed_remainder; |
||||||
|
(void) argc; |
||||||
|
(void) argv; |
||||||
|
|
||||||
|
quotient = unsigned_numerator / unsigned_divisor; |
||||||
|
remainder = unsigned_numerator % unsigned_divisor; |
||||||
|
if (quotient * unsigned_divisor + remainder != unsigned_numerator |
||||||
|
|| remainder >= unsigned_divisor) { |
||||||
|
return fail(); |
||||||
|
} |
||||||
|
|
||||||
|
signed_quotient = signed_numerator / signed_divisor; |
||||||
|
signed_remainder = signed_numerator % signed_divisor; |
||||||
|
if (signed_quotient * signed_divisor + signed_remainder != signed_numerator |
||||||
|
|| signed_remainder > 0) { |
||||||
|
return fail(); |
||||||
|
} |
||||||
|
|
||||||
|
(void) write(STDOUT_FILENO, "compiler-rt builtins: OK\n", |
||||||
|
sizeof("compiler-rt builtins: OK\n") - 1); |
||||||
|
return 0; |
||||||
|
} |
||||||
@ -0,0 +1,171 @@ |
|||||||
|
#include <dirent.h> |
||||||
|
#include <errno.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <stddef.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <sys/stat.h> |
||||||
|
#include <sys/syscall.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
/* glibc hides this GNU extension when php-nano requests POSIX.1-2008 only. */ |
||||||
|
long syscall(long number, ...); |
||||||
|
|
||||||
|
#ifndef O_DIRECTORY |
||||||
|
#define O_DIRECTORY 65536 |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef O_CLOEXEC |
||||||
|
#define O_CLOEXEC 524288 |
||||||
|
#endif |
||||||
|
|
||||||
|
enum { |
||||||
|
TYPEPHP_DIRECTORY_STREAMS = 4, |
||||||
|
TYPEPHP_DIRECTORY_BUFFER_SIZE = 1024, |
||||||
|
}; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
int used; |
||||||
|
int fd; |
||||||
|
size_t offset; |
||||||
|
size_t length; |
||||||
|
unsigned char buffer[TYPEPHP_DIRECTORY_BUFFER_SIZE]; |
||||||
|
struct dirent entry; |
||||||
|
} typephp_directory_stream; |
||||||
|
|
||||||
|
static typephp_directory_stream directory_streams[TYPEPHP_DIRECTORY_STREAMS]; |
||||||
|
|
||||||
|
static typephp_directory_stream *directory_state(DIR *directory) |
||||||
|
{ |
||||||
|
const uintptr_t address = (uintptr_t) directory; |
||||||
|
const uintptr_t begin = (uintptr_t) directory_streams; |
||||||
|
const uintptr_t end = (uintptr_t) (directory_streams |
||||||
|
+ TYPEPHP_DIRECTORY_STREAMS); |
||||||
|
typephp_directory_stream *state; |
||||||
|
if (address < begin || address >= end |
||||||
|
|| (address - begin) % sizeof(typephp_directory_stream) != 0) { |
||||||
|
errno = EBADF; |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
state = (typephp_directory_stream *) directory; |
||||||
|
if (!state->used) { |
||||||
|
errno = EBADF; |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
return state; |
||||||
|
} |
||||||
|
|
||||||
|
DIR *fdopendir(int fd) |
||||||
|
{ |
||||||
|
struct stat status; |
||||||
|
size_t index; |
||||||
|
if (fstat(fd, &status) != 0) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
if (!S_ISDIR(status.st_mode)) { |
||||||
|
errno = ENOTDIR; |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
for (index = 0; index < TYPEPHP_DIRECTORY_STREAMS; ++index) { |
||||||
|
typephp_directory_stream *state = &directory_streams[index]; |
||||||
|
if (!state->used) { |
||||||
|
state->used = 1; |
||||||
|
state->fd = fd; |
||||||
|
state->offset = 0; |
||||||
|
state->length = 0; |
||||||
|
return (DIR *) state; |
||||||
|
} |
||||||
|
} |
||||||
|
errno = EMFILE; |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
DIR *opendir(const char *path) |
||||||
|
{ |
||||||
|
int fd = open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC); |
||||||
|
DIR *directory; |
||||||
|
if (fd < 0) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
directory = fdopendir(fd); |
||||||
|
if (directory == NULL) { |
||||||
|
int saved_errno = errno; |
||||||
|
(void) close(fd); |
||||||
|
errno = saved_errno; |
||||||
|
} |
||||||
|
return directory; |
||||||
|
} |
||||||
|
|
||||||
|
struct dirent *readdir(DIR *directory) |
||||||
|
{ |
||||||
|
typephp_directory_stream *state = directory_state(directory); |
||||||
|
struct dirent *record; |
||||||
|
size_t name_capacity; |
||||||
|
size_t name_length = 0; |
||||||
|
if (state == NULL) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
if (state->offset == state->length) { |
||||||
|
long length = syscall(SYS_getdents64, state->fd, |
||||||
|
state->buffer, sizeof(state->buffer)); |
||||||
|
if (length <= 0) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
state->offset = 0; |
||||||
|
state->length = (size_t) length; |
||||||
|
} |
||||||
|
record = (struct dirent *) (state->buffer + state->offset); |
||||||
|
if (record->d_reclen < offsetof(struct dirent, d_name) + 1 |
||||||
|
|| record->d_reclen > state->length - state->offset) { |
||||||
|
errno = EIO; |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
name_capacity = record->d_reclen - offsetof(struct dirent, d_name); |
||||||
|
while (name_length < name_capacity |
||||||
|
&& record->d_name[name_length] != '\0') { |
||||||
|
++name_length; |
||||||
|
} |
||||||
|
if (name_length == name_capacity |
||||||
|
|| name_length >= sizeof(state->entry.d_name)) { |
||||||
|
errno = EIO; |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
state->entry.d_ino = record->d_ino; |
||||||
|
state->entry.d_off = record->d_off; |
||||||
|
state->entry.d_reclen = record->d_reclen; |
||||||
|
state->entry.d_type = record->d_type; |
||||||
|
for (size_t index = 0; index <= name_length; ++index) { |
||||||
|
state->entry.d_name[index] = record->d_name[index]; |
||||||
|
} |
||||||
|
state->offset += record->d_reclen; |
||||||
|
return &state->entry; |
||||||
|
} |
||||||
|
|
||||||
|
int closedir(DIR *directory) |
||||||
|
{ |
||||||
|
typephp_directory_stream *state = directory_state(directory); |
||||||
|
int result; |
||||||
|
if (state == NULL) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
result = close(state->fd); |
||||||
|
state->used = 0; |
||||||
|
state->fd = -1; |
||||||
|
state->offset = 0; |
||||||
|
state->length = 0; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
void rewinddir(DIR *directory) |
||||||
|
{ |
||||||
|
typephp_directory_stream *state = directory_state(directory); |
||||||
|
if (state != NULL && lseek(state->fd, 0, SEEK_SET) >= 0) { |
||||||
|
state->offset = 0; |
||||||
|
state->length = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int dirfd(DIR *directory) |
||||||
|
{ |
||||||
|
typephp_directory_stream *state = directory_state(directory); |
||||||
|
return state == NULL ? -1 : state->fd; |
||||||
|
} |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
#include <fcntl.h> |
||||||
|
#include <stdarg.h> |
||||||
|
#include <sys/syscall.h> |
||||||
|
|
||||||
|
/* glibc hides this GNU extension when php-nano requests POSIX.1-2008 only. */ |
||||||
|
long syscall(long number, ...); |
||||||
|
|
||||||
|
int fcntl(int fd, int command, ...) |
||||||
|
{ |
||||||
|
long argument = 0; |
||||||
|
if (command == F_SETFD || command == F_SETFL) { |
||||||
|
va_list arguments; |
||||||
|
va_start(arguments, command); |
||||||
|
argument = va_arg(arguments, int); |
||||||
|
va_end(arguments); |
||||||
|
} |
||||||
|
return (int) syscall(SYS_fcntl, fd, command, argument); |
||||||
|
} |
||||||
@ -0,0 +1,35 @@ |
|||||||
|
#ifndef TYPEPHP_OS_USER_DIRENT_H |
||||||
|
#define TYPEPHP_OS_USER_DIRENT_H |
||||||
|
|
||||||
|
#include <sys/types.h> |
||||||
|
|
||||||
|
/* Linux x86-64 dirent64 layout. getdents64() returns variable-length records;
|
||||||
|
* readdir() exposes the same prefix through this conventional fixed-size |
||||||
|
* userspace view. */ |
||||||
|
struct dirent { |
||||||
|
ino_t d_ino; |
||||||
|
off_t d_off; |
||||||
|
unsigned short d_reclen; |
||||||
|
unsigned char d_type; |
||||||
|
char d_name[256]; |
||||||
|
}; |
||||||
|
|
||||||
|
#define DT_UNKNOWN 0 |
||||||
|
#define DT_FIFO 1 |
||||||
|
#define DT_CHR 2 |
||||||
|
#define DT_DIR 4 |
||||||
|
#define DT_BLK 6 |
||||||
|
#define DT_REG 8 |
||||||
|
#define DT_LNK 10 |
||||||
|
#define DT_SOCK 12 |
||||||
|
|
||||||
|
typedef struct __typephp_dir_stream DIR; |
||||||
|
|
||||||
|
DIR *opendir(const char *path); |
||||||
|
DIR *fdopendir(int fd); |
||||||
|
struct dirent *readdir(DIR *directory); |
||||||
|
int closedir(DIR *directory); |
||||||
|
void rewinddir(DIR *directory); |
||||||
|
int dirfd(DIR *directory); |
||||||
|
|
||||||
|
#endif |
||||||
@ -1,16 +1,27 @@ |
|||||||
#include <sys/syscall.h> |
#include <dirent.h> |
||||||
|
#include <stddef.h> |
||||||
#include <unistd.h> |
#include <unistd.h> |
||||||
|
|
||||||
int main(int argc, char **argv) |
int main(int argc, char **argv) |
||||||
{ |
{ |
||||||
char entries[512]; |
const char *path = argc > 1 ? argv[1] : "."; |
||||||
const char *path = argc > 1 ? argv[1] : 0; |
DIR *directory = opendir(path); |
||||||
long length = syscall(SYS_typephp_listdir, path, entries, sizeof(entries)); |
struct dirent *entry; |
||||||
if (length < 0) { |
if (directory == NULL) { |
||||||
(void) write(STDERR_FILENO, "ls: cannot read directory\n", |
(void) write(STDERR_FILENO, "ls: cannot read directory\n", |
||||||
sizeof("ls: cannot read directory\n") - 1); |
sizeof("ls: cannot read directory\n") - 1); |
||||||
return 1; |
return 1; |
||||||
} |
} |
||||||
(void) write(STDOUT_FILENO, entries, (size_t) length); |
while ((entry = readdir(directory)) != NULL) { |
||||||
|
size_t length = 0; |
||||||
|
while (entry->d_name[length] != '\0') { |
||||||
|
++length; |
||||||
|
} |
||||||
|
(void) write(STDOUT_FILENO, entry->d_name, length); |
||||||
|
(void) write(STDOUT_FILENO, "\n", 1); |
||||||
|
} |
||||||
|
if (closedir(directory) != 0) { |
||||||
|
return 1; |
||||||
|
} |
||||||
return 0; |
return 0; |
||||||
} |
} |
||||||
|
|||||||
Loading…
Reference in new issue