diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -170,6 +170,7 @@ "ino_t", "DIR", "struct dirent", + "struct linux_dirent", ]; } 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 @@ -334,6 +334,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/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -27,6 +27,7 @@ .llvm-libc-types.ino_t .llvm-libc-types.DIR .llvm-libc-types.struct_dirent + .llvm-libc-types.struct_linux_dirent ) add_gen_header( 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,4 @@ -add_header(off64_t HDR off64_t.h) +add_header(off_t HDR off_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) @@ -20,7 +20,7 @@ add_header(clock_t HDR clock_t.h) add_header(clockid_t HDR clockid_t.h) add_header(cnd_t HDR cnd_t.h) -add_header(cookie_io_functions_t HDR cookie_io_functions_t.h DEPENDS .off64_t .ssize_t) +add_header(cookie_io_functions_t HDR cookie_io_functions_t.h DEPENDS .off_t .ssize_t) add_header(cpu_set_t HDR cpu_set_t.h) add_header(double_t HDR double_t.h) add_header(DIR HDR DIR.h) @@ -41,7 +41,6 @@ add_header(mode_t HDR mode_t.h) add_header(mtx_t HDR mtx_t.h DEPENDS .__futex_word .__mutex_type) add_header(nlink_t HDR nlink_t.h) -add_header(off_t HDR off_t.h) add_header(once_flag HDR once_flag.h DEPENDS .__futex_word) add_header(pid_t HDR pid_t.h) add_header(posix_spawn_file_actions_t HDR posix_spawn_file_actions_t.h) @@ -60,6 +59,7 @@ 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_linux_dirent HDR struct_linux_dirent.h DEPENDS .ino_t .off_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/cookie_io_functions_t.h b/libc/include/llvm-libc-types/cookie_io_functions_t.h --- a/libc/include/llvm-libc-types/cookie_io_functions_t.h +++ b/libc/include/llvm-libc-types/cookie_io_functions_t.h @@ -9,13 +9,13 @@ #ifndef __LLVM_LIBC_TYPES_COOKIE_IO_FUNCTIONS_T_H #define __LLVM_LIBC_TYPES_COOKIE_IO_FUNCTIONS_T_H -#include +#include #include #include typedef ssize_t cookie_read_function_t(void *, char *, size_t); typedef ssize_t cookie_write_function_t(void *, const char *, size_t); -typedef int cookie_seek_function_t(void *, off64_t *, int); +typedef int cookie_seek_function_t(void *, off_t *, int); typedef int cookie_close_function_t(void *); typedef struct { diff --git a/libc/include/llvm-libc-types/ino_t.h b/libc/include/llvm-libc-types/ino_t.h --- a/libc/include/llvm-libc-types/ino_t.h +++ b/libc/include/llvm-libc-types/ino_t.h @@ -9,6 +9,6 @@ #ifndef __LLVM_LIBC_TYPES_INO_T_H__ #define __LLVM_LIBC_TYPES_INO_T_H__ -typedef __UINTPTR_TYPE__ ino_t; +typedef __UINT64_TYPE__ ino_t; #endif // __LLVM_LIBC_TYPES_INO_T_H__ diff --git a/libc/include/llvm-libc-types/off64_t.h b/libc/include/llvm-libc-types/off64_t.h deleted file mode 100644 --- a/libc/include/llvm-libc-types/off64_t.h +++ /dev/null @@ -1,14 +0,0 @@ -//===-- Definition of off64_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_OFF64_T_H__ -#define __LLVM_LIBC_TYPES_OFF64_T_H__ - -typedef __INT64_TYPE__ off64_t; - -#endif // __LLVM_LIBC_TYPES_OFF64_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 @@ -18,6 +18,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/include/llvm-libc-types/struct_linux_dirent.h b/libc/include/llvm-libc-types/struct_linux_dirent.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-types/struct_linux_dirent.h @@ -0,0 +1,32 @@ +//===-- Definition of type struct linux dirent ----------------------------===// +// +// 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_STRUCT_LINUX_DIRENT_H__ +#define __LLVM_LIBC_TYPES_STRUCT_LINUX_DIRENT_H__ + +#include +#include + +// Only used by SYS_getdents +struct linux_dirent { + unsigned long d_ino; +#ifdef __unix__ + unsigned long d_off; + unsigned short d_reclen; +#endif + // This is the minimum width of the string. The user should use strlen + // to find the length of the string. + 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) + */ +}; + +#endif // __LLVM_LIBC_TYPES_STRUCT_LINUX_DIRENT_H__ diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -38,6 +38,7 @@ def DIRPtr : PtrType; def DIRRestrictedPtr : RestrictedPtrType; def StructDirent : NamedType<"struct dirent">; +def StructLinuxDirent : NamedType<"struct linux_dirent">; def StructDirentPtr : PtrType; def StructDirentPtrPtr : PtrType; def ConstStructDirentPtrPtr : ConstType; @@ -1088,7 +1089,7 @@ HeaderSpec Dirent = HeaderSpec< "dirent.h", [], // Macros - [InoT, StructDirent, DIR], // Types + [InoT, StructDirent, DIR, StructLinuxDirent], // Types [], // Enumerations [ FunctionSpec< 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,44 @@ if (fillsize == 0) return nullptr; +#ifdef SYS_getdents64 struct ::dirent *d = reinterpret_cast(buffer + readptr); +#elif defined(SYS_getdents) + 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); + memmove(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)); } diff --git a/libc/src/stdio/fopencookie.cpp b/libc/src/stdio/fopencookie.cpp --- a/libc/src/stdio/fopencookie.cpp +++ b/libc/src/stdio/fopencookie.cpp @@ -59,7 +59,7 @@ if (cookie_file->ops.seek == nullptr) { return Error(EINVAL); } - off64_t offset64 = offset; + off_t offset64 = offset; int result = cookie_file->ops.seek(cookie_file->cookie, &offset64, whence); if (result == 0) return offset64; diff --git a/libc/test/src/stdio/fopencookie_test.cpp b/libc/test/src/stdio/fopencookie_test.cpp --- a/libc/test/src/stdio/fopencookie_test.cpp +++ b/libc/test/src/stdio/fopencookie_test.cpp @@ -57,9 +57,9 @@ return copysize; } -int seek_ss(void *cookie, off64_t *offset, int whence) { +int seek_ss(void *cookie, off_t *offset, int whence) { auto *ss = reinterpret_cast(cookie); - off64_t new_offset; + off_t new_offset; if (whence == SEEK_SET) { new_offset = *offset; } else if (whence == SEEK_CUR) {