diff --git a/libc/config/linux/riscv64/entrypoints.txt b/libc/config/linux/riscv64/entrypoints.txt --- a/libc/config/linux/riscv64/entrypoints.txt +++ b/libc/config/linux/riscv64/entrypoints.txt @@ -326,6 +326,12 @@ # assert.h entrypoints libc.src.assert.__assert_fail + # dirent.h entrypoints + libc.src.dirent.closedir + libc.src.dirent.dirfd + libc.src.dirent.opendir + libc.src.dirent.readdir + # network.h entrypoints libc.src.network.htonl libc.src.network.htons diff --git a/libc/config/linux/riscv64/headers.txt b/libc/config/linux/riscv64/headers.txt --- a/libc/config/linux/riscv64/headers.txt +++ b/libc/config/linux/riscv64/headers.txt @@ -1,6 +1,7 @@ set(TARGET_PUBLIC_HEADERS libc.include.assert libc.include.ctype + libc.include.dirent libc.include.errno libc.include.fcntl libc.include.fenv diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -1,4 +1,5 @@ add_header(off64_t HDR off64_t.h) +add_header(ino64_t HDR ino64_t.h) add_header(size_t HDR size_t.h) add_header(ssize_t HDR ssize_t.h) add_header(__atfork_callback_t HDR __atfork_callback_t.h) @@ -59,7 +60,7 @@ add_header(struct_timeval HDR struct_timeval.h DEPENDS .suseconds_t .time_t) add_header(struct_rlimit HDR struct_rlimit.h DEPENDS .rlim_t) add_header(struct_rusage HDR struct_rusage.h DEPENDS .struct_timeval) -add_header(struct_dirent HDR struct_dirent.h DEPENDS .ino_t .off_t) +add_header(struct_dirent HDR struct_dirent.h DEPENDS .ino_t .off_t .ino64_t .off64_t) add_header(union_sigval HDR union_sigval.h) add_header(siginfo_t HDR siginfo_t.h DEPENDS .union_sigval .pid_t .uid_t) add_header(sig_atomic_t HDR sig_atomic_t.h) diff --git a/libc/include/llvm-libc-types/ino64_t.h b/libc/include/llvm-libc-types/ino64_t.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-types/ino64_t.h @@ -0,0 +1,14 @@ +//===-- Definition of ino64_t type ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __LLVM_LIBC_TYPES_INO_T_H__ +#define __LLVM_LIBC_TYPES_INO_T_H__ + +typedef __UINTPTR_TYPE__ ino64_t; + +#endif // __LLVM_LIBC_TYPES_INO_T_H__ diff --git a/libc/include/llvm-libc-types/struct_dirent.h b/libc/include/llvm-libc-types/struct_dirent.h --- a/libc/include/llvm-libc-types/struct_dirent.h +++ b/libc/include/llvm-libc-types/struct_dirent.h @@ -9,8 +9,20 @@ #ifndef __LLVM_LIBC_TYPES_STRUCT_DIRENT_H__ #define __LLVM_LIBC_TYPES_STRUCT_DIRENT_H__ +#include // For syscall definitions. + +#ifdef SYS_getdents64 +#include +#include +#else #include #include +#endif + +#ifdef SYS_getdents64 +typedef ino64_t ino_t; +typedef off64_t off_t; +#endif struct dirent { ino_t d_ino; @@ -18,6 +30,7 @@ off_t d_off; unsigned short d_reclen; #endif + unsigned char d_type; // The user code should use strlen to determine actual the size of d_name. // Likewise, it is incorrect and prohibited by the POSIX standard to detemine // the size of struct dirent type using sizeof. The size should be got using diff --git a/libc/src/__support/File/dir.cpp b/libc/src/__support/File/dir.cpp --- a/libc/src/__support/File/dir.cpp +++ b/libc/src/__support/File/dir.cpp @@ -10,9 +10,11 @@ #include "src/__support/CPP/new.h" #include "src/__support/error_or.h" +#include "src/__support/libc_assert.h" #include "src/errno/libc_errno.h" // For error macros #include +#include // For strlen namespace __llvm_libc { @@ -40,7 +42,60 @@ if (fillsize == 0) return nullptr; +#ifdef SYS_getdents64 struct ::dirent *d = reinterpret_cast(buffer + readptr); +#elif defined(SYS_getdents) + // from https://man7.org/linux/man-pages/man2/getdents.2.html, SYS_getdents + // returns a slightly different dirent struct: + struct linux_dirent { + unsigned long d_ino; +#ifdef __unix__ + unsigned long d_off; + unsigned short d_reclen; +#endif + char d_name[1]; + /* + char pad; // Zero padding byte + char d_type; // File type (only since Linux + // 2.6.4); offset is (d_reclen - 1) + */ + }; + + struct linux_dirent *ld = + reinterpret_cast(buffer + readptr); + + size_t struct_size = + sizeof(ino_t) + sizeof(unsigned char) + strlen(ld->d_name); +#ifdef __unix__ + struct_size += sizeof(off_t) + sizeof(unsigned short); +#endif + const size_t aligned_struct_size = + ((struct_size + (alignof(linux_dirent) - 1)) & + ~(alignof(linux_dirent) - 1)); + +#ifdef __unix__ + LIBC_ASSERT(aligned_struct_size == ld->d_reclen); +#endif + + // d_type is a byte at the end of the structure that indicates the file + // type. + unsigned char d_type = buffer[readptr + aligned_struct_size - 1]; + + // Shift buffer by one + size_t d_name_offset = offsetof(linux_dirent, d_name); + memcpy(buffer + readptr + d_name_offset + 1, buffer + readptr + d_name_offset, + strlen(ld->d_name)); + + // set d_type and clear its previous location + memset(readptr + buffer + d_name_offset, d_type, sizeof(unsigned char)); + memset(readptr + buffer + aligned_struct_size - 1, 0, sizeof(unsigned char)); + + struct ::dirent *d = reinterpret_cast(buffer + readptr); +#else +#error \ + "SYS_getdents and SYS_getdents64 syscalls not available to perform a read operation." +#endif + #ifdef __unix__ // The d_reclen field is available on Linux but not required by POSIX. readptr += d->d_reclen; diff --git a/libc/src/__support/File/linux_dir.cpp b/libc/src/__support/File/linux_dir.cpp --- a/libc/src/__support/File/linux_dir.cpp +++ b/libc/src/__support/File/linux_dir.cpp @@ -34,8 +34,17 @@ } ErrorOr platform_fetch_dirents(int fd, cpp::span buffer) { +#ifdef SYS_getdents64 + long size = __llvm_libc::syscall_impl(SYS_getdents64, fd, buffer.data(), + buffer.size()); +#elif defined(SYS_getdents) long size = __llvm_libc::syscall_impl(SYS_getdents, fd, buffer.data(), buffer.size()); +#else +#error \ + "SYS_getdents and SYS_getdents64 syscalls not available to perform a fetch dirents operation." +#endif + if (size < 0) { return __llvm_libc::Error(static_cast(-size)); }