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 @@ -277,7 +277,8 @@ } def SysStatAPI : PublicAPI<"sys/stat.h"> { - let Types = ["mode_t"]; + let Types = ["mode_t", "dev_t", "ino_t", "nlink_t", "uid_t", "gid_t", "off_t", + "struct timespec", "blksize_t", "blkcnt_t", "struct stat"]; } def SysSendfileAPI : PublicAPI<"sys/sendfile.h"> { diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -113,8 +113,11 @@ libc.src.sys.stat.chmod libc.src.sys.stat.fchmod libc.src.sys.stat.fchmodat + libc.src.sys.stat.fstat + libc.src.sys.stat.lstat libc.src.sys.stat.mkdir libc.src.sys.stat.mkdirat + libc.src.sys.stat.stat # sys/utsname.h entrypoints libc.src.sys.utsname.uname diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -222,6 +222,7 @@ .llvm_libc_common_h .llvm-libc-macros.sys_stat_macros .llvm-libc-types.mode_t + .llvm-libc-types.struct_stat ) add_gen_header( diff --git a/libc/include/llvm-libc-macros/linux/fcntl-macros.h b/libc/include/llvm-libc-macros/linux/fcntl-macros.h --- a/libc/include/llvm-libc-macros/linux/fcntl-macros.h +++ b/libc/include/llvm-libc-macros/linux/fcntl-macros.h @@ -62,6 +62,15 @@ #define S_ISUID 04000 #define S_ISGID 02000 +// File type flags +#define S_IFMT 0170000 +#define S_IFDIR 0040000 +#define S_IFCHR 0020000 +#define S_IFBLK 0060000 +#define S_IFREG 0100000 +#define S_FIFO 0010000 +#define S_IFLNK 0120000 + // Special directory FD to indicate that the path argument to // openat is relative to the current directory. #define AT_FDCWD -100 @@ -70,6 +79,13 @@ // has to perform the equivalent of "rmdir" on the path argument. #define AT_REMOVEDIR 0x200 +// Special flag for functions like lstat to convey that symlinks +// should not be followed. +#define AT_SYMLINK_NOFOLLOW 0x100 + +// Allow empty relative pathname. +#define AT_EMPTY_PATH 0x1000 + // Values of SYS_fcntl commands. #define F_DUPFD 0 #define F_GETFD 1 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 @@ -9,10 +9,13 @@ add_header(__qsortcompare_t HDR __qsortcompare_t.h) add_header(__sighandler_t HDR __sighandler_t.h) add_header(__thread_type HDR __thread_type.h) +add_header(blkcnt_t HDR blkcnt_t.h) +add_header(blksize_t HDR blksize_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) add_header(double_t HDR double_t.h) add_header(DIR HDR DIR.h) +add_header(dev_t HDR dev_t.h) add_header(div_t HDR div_t.h) add_header(ldiv_t HDR ldiv_t.h) add_header(lldiv_t HDR lldiv_t.h) @@ -20,10 +23,13 @@ add_header(fenv_t HDR fenv_t.h) add_header(fexcept_t HDR fexcept_t.h) add_header(float_t HDR float_t.h) +add_header(gid_t HDR gid_t.h) +add_header(uid_t HDR uid_t.h) add_header(imaxdiv_t HDR imaxdiv_t.h) add_header(ino_t HDR ino_t.h) 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(pthread_attr_t HDR pthread_attr_t.h DEPENDS .size_t) @@ -36,11 +42,19 @@ add_header(ssize_t HDR ssize_t.h) add_header(struct_dirent HDR struct_dirent.h DEPENDS .ino_t .off_t) add_header(struct_sigaction HDR struct_sigaction.h) +add_header(time_t HDR time_t.h) +add_header(struct_timespec HDR struct_timespec.h DEPENDS .time_t) +add_header( + struct_stat + HDR struct_stat.h + DEPENDS + .dev_t .ino_t .mode_t .nlink_t .uid_t .gid_t .off_t .struct_timespec + .blksize_t .blkcnt_t +) add_header(struct_tm HDR struct_tm.h) add_header(struct_utsname HDR struct_utsname.h) add_header(thrd_start_t HDR thrd_start_t.h) add_header(thrd_t HDR thrd_t.h DEPENDS .__thread_type) -add_header(time_t HDR time_t.h) add_header(tss_t HDR tss_t.h) add_header(tss_dtor_t HDR tss_dtor_t.h) add_header(__atexithandler_t HDR __atexithandler_t.h) diff --git a/libc/include/llvm-libc-types/ino_t.h b/libc/include/llvm-libc-types/blkcnt_t.h copy from libc/include/llvm-libc-types/ino_t.h copy to libc/include/llvm-libc-types/blkcnt_t.h --- a/libc/include/llvm-libc-types/ino_t.h +++ b/libc/include/llvm-libc-types/blkcnt_t.h @@ -1,4 +1,4 @@ -//===-- Definition of type ino_t ------------------------------------------===// +//===-- Definition of blkcnt_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. @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#ifndef __LLVM_LIBC_TYPES_INO_T_H__ -#define __LLVM_LIBC_TYPES_INO_T_H__ +#ifndef __LLVM_LIBC_TYPES_BLKCNT_T_H__ +#define __LLVM_LIBC_TYPES_BLKCNT_T_H__ -typedef unsigned long ino_t; +typedef __INTPTR_TYPE__ blkcnt_t; -#endif // __LLVM_LIBC_TYPES_INO_T_H__ +#endif // __LLVM_LIBC_TYPES_BLKCNT_T_H__ diff --git a/libc/include/llvm-libc-types/ino_t.h b/libc/include/llvm-libc-types/blksize_t.h copy from libc/include/llvm-libc-types/ino_t.h copy to libc/include/llvm-libc-types/blksize_t.h --- a/libc/include/llvm-libc-types/ino_t.h +++ b/libc/include/llvm-libc-types/blksize_t.h @@ -1,4 +1,4 @@ -//===-- Definition of type ino_t ------------------------------------------===// +//===-- Definition of blksize_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. @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#ifndef __LLVM_LIBC_TYPES_INO_T_H__ -#define __LLVM_LIBC_TYPES_INO_T_H__ +#ifndef __LLVM_LIBC_TYPES_BLKSIZE_T_H__ +#define __LLVM_LIBC_TYPES_BLKSIZE_T_H__ -typedef unsigned long ino_t; +typedef __INTPTR_TYPE__ blksize_t; -#endif // __LLVM_LIBC_TYPES_INO_T_H__ +#endif // __LLVM_LIBC_TYPES_BLKSIZE_T_H__ diff --git a/libc/include/llvm-libc-types/ino_t.h b/libc/include/llvm-libc-types/dev_t.h copy from libc/include/llvm-libc-types/ino_t.h copy to libc/include/llvm-libc-types/dev_t.h --- a/libc/include/llvm-libc-types/ino_t.h +++ b/libc/include/llvm-libc-types/dev_t.h @@ -1,4 +1,4 @@ -//===-- Definition of type ino_t ------------------------------------------===// +//===-- Definition of dev_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. @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#ifndef __LLVM_LIBC_TYPES_INO_T_H__ -#define __LLVM_LIBC_TYPES_INO_T_H__ +#ifndef __LLVM_LIBC_TYPES_DEV_T_H__ +#define __LLVM_LIBC_TYPES_DEV_T_H__ -typedef unsigned long ino_t; +typedef __UINT64_TYPE__ dev_t; -#endif // __LLVM_LIBC_TYPES_INO_T_H__ +#endif // __LLVM_LIBC_TYPES_DEV_T_H__ diff --git a/libc/include/llvm-libc-types/ino_t.h b/libc/include/llvm-libc-types/gid_t.h copy from libc/include/llvm-libc-types/ino_t.h copy to libc/include/llvm-libc-types/gid_t.h --- a/libc/include/llvm-libc-types/ino_t.h +++ b/libc/include/llvm-libc-types/gid_t.h @@ -1,4 +1,4 @@ -//===-- Definition of type ino_t ------------------------------------------===// +//===-- Definition of gid_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. @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#ifndef __LLVM_LIBC_TYPES_INO_T_H__ -#define __LLVM_LIBC_TYPES_INO_T_H__ +#ifndef __LLVM_LIBC_TYPES_GID_T_H__ +#define __LLVM_LIBC_TYPES_GID_T_H__ -typedef unsigned long ino_t; +typedef __UINT32_TYPE__ gid_t; -#endif // __LLVM_LIBC_TYPES_INO_T_H__ +#endif // __LLVM_LIBC_TYPES_GID_T_H__ 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 @@ -1,4 +1,4 @@ -//===-- Definition of type ino_t ------------------------------------------===// +//===-- Definition of ino_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. @@ -9,6 +9,6 @@ #ifndef __LLVM_LIBC_TYPES_INO_T_H__ #define __LLVM_LIBC_TYPES_INO_T_H__ -typedef unsigned long ino_t; +typedef __UINTPTR_TYPE__ ino_t; #endif // __LLVM_LIBC_TYPES_INO_T_H__ diff --git a/libc/include/llvm-libc-types/ino_t.h b/libc/include/llvm-libc-types/nlink_t.h copy from libc/include/llvm-libc-types/ino_t.h copy to libc/include/llvm-libc-types/nlink_t.h --- a/libc/include/llvm-libc-types/ino_t.h +++ b/libc/include/llvm-libc-types/nlink_t.h @@ -1,4 +1,4 @@ -//===-- Definition of type ino_t ------------------------------------------===// +//===-- Definition of nlink_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. @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#ifndef __LLVM_LIBC_TYPES_INO_T_H__ -#define __LLVM_LIBC_TYPES_INO_T_H__ +#ifndef __LLVM_LIBC_TYPES_NLINK_T_H__ +#define __LLVM_LIBC_TYPES_NLINK_T_H__ -typedef unsigned long ino_t; +typedef __UINTPTR_TYPE__ nlink_t; -#endif // __LLVM_LIBC_TYPES_INO_T_H__ +#endif // __LLVM_LIBC_TYPES_NLINK_T_H__ diff --git a/libc/include/llvm-libc-types/struct_stat.h b/libc/include/llvm-libc-types/struct_stat.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-types/struct_stat.h @@ -0,0 +1,39 @@ +//===-- Definition of struct stat -----------------------------------------===// +// +// 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_STAT_H__ +#define __LLVM_LIBC_TYPES_STRUCT_STAT_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct stat { + dev_t st_dev; + ino_t st_ino; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + off_t st_size; + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; + blksize_t st_blksize; + blkcnt_t st_blocks; +}; + +#endif // __LLVM_LIBC_TYPES_STRUCT_STAT_H__ diff --git a/libc/include/llvm-libc-types/ino_t.h b/libc/include/llvm-libc-types/struct_timespec.h copy from libc/include/llvm-libc-types/ino_t.h copy to libc/include/llvm-libc-types/struct_timespec.h --- a/libc/include/llvm-libc-types/ino_t.h +++ b/libc/include/llvm-libc-types/struct_timespec.h @@ -1,4 +1,4 @@ -//===-- Definition of type ino_t ------------------------------------------===// +//===-- Definition of struct timespec -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,9 +6,14 @@ // //===----------------------------------------------------------------------===// -#ifndef __LLVM_LIBC_TYPES_INO_T_H__ -#define __LLVM_LIBC_TYPES_INO_T_H__ +#ifndef __LLVM_LIBC_TYPES_TIMESPEC_H__ +#define __LLVM_LIBC_TYPES_TIMESPEC_H__ -typedef unsigned long ino_t; +#include -#endif // __LLVM_LIBC_TYPES_INO_T_H__ +struct timespec { + time_t tv_sec; + long tv_nsec; +}; + +#endif // __LLVM_LIBC_TYPES_TIMESPEC_H__ diff --git a/libc/include/llvm-libc-types/time_t.h b/libc/include/llvm-libc-types/time_t.h --- a/libc/include/llvm-libc-types/time_t.h +++ b/libc/include/llvm-libc-types/time_t.h @@ -9,6 +9,6 @@ #ifndef __LLVM_LIBC_TYPES_TIME_T_H__ #define __LLVM_LIBC_TYPES_TIME_T_H__ -typedef long time_t; +typedef __INTPTR_TYPE__ time_t; #endif // __LLVM_LIBC_TYPES_TIME_T_H__ diff --git a/libc/include/llvm-libc-types/ino_t.h b/libc/include/llvm-libc-types/uid_t.h copy from libc/include/llvm-libc-types/ino_t.h copy to libc/include/llvm-libc-types/uid_t.h --- a/libc/include/llvm-libc-types/ino_t.h +++ b/libc/include/llvm-libc-types/uid_t.h @@ -1,4 +1,4 @@ -//===-- Definition of type ino_t ------------------------------------------===// +//===-- Definition of uid_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. @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#ifndef __LLVM_LIBC_TYPES_INO_T_H__ -#define __LLVM_LIBC_TYPES_INO_T_H__ +#ifndef __LLVM_LIBC_TYPES_UID_T_H__ +#define __LLVM_LIBC_TYPES_UID_T_H__ -typedef unsigned long ino_t; +typedef __UINT32_TYPE__ uid_t; -#endif // __LLVM_LIBC_TYPES_INO_T_H__ +#endif // __LLVM_LIBC_TYPES_UID_T_H__ diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -16,6 +16,18 @@ def PThreadKeyTPtr : PtrType; def InoT : NamedType<"ino_t">; +def UidT : NamedType<"uid_t">; +def GidT : NamedType<"gid_t">; +def DevT : NamedType<"dev_t">; +def BlkSizeT : NamedType<"blksize_t">; +def BlkCntT : NamedType<"blkcnt_t">; +def NLinkT : NamedType<"nlink_t">; +def TimeSpec : NamedType<"struct timespec">; + +def StatType : NamedType<"struct stat">; +def StatTypePtr : PtrType; +def RestrictedStatTypePtr : RestrictedPtrType; + def DIR : NamedType<"DIR">; def DIRPtr : PtrType; def DIRRestrictedPtr : RestrictedPtrType; @@ -491,7 +503,7 @@ HeaderSpec SysStat = HeaderSpec< "sys/stat.h", [], // Macros - [ModeTType], // Types + [ModeTType, DevT, InoT, UidT, GidT, TimeSpec, BlkSizeT, BlkCntT, OffTType, NLinkT, StatType], // Types [], // Enumerations [ FunctionSpec< @@ -509,6 +521,16 @@ RetValSpec, [ArgSpec, ArgSpec, ArgSpec, ArgSpec] >, + FunctionSpec< + "fstat", + RetValSpec, + [ArgSpec, ArgSpec] + >, + FunctionSpec< + "lstat", + RetValSpec, + [ArgSpec, ArgSpec] + >, FunctionSpec< "mkdir", RetValSpec, @@ -519,6 +541,11 @@ RetValSpec, [ArgSpec, ArgSpec, ArgSpec] >, + FunctionSpec< + "stat", + RetValSpec, + [ArgSpec, ArgSpec] + >, ] >; diff --git a/libc/src/sys/stat/CMakeLists.txt b/libc/src/sys/stat/CMakeLists.txt --- a/libc/src/sys/stat/CMakeLists.txt +++ b/libc/src/sys/stat/CMakeLists.txt @@ -23,6 +23,20 @@ .${LIBC_TARGET_OS}.fchmod ) +add_entrypoint_object( + fstat + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.fstat +) + +add_entrypoint_object( + lstat + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.lstat +) + add_entrypoint_object( mkdir ALIAS @@ -36,3 +50,10 @@ DEPENDS .${LIBC_TARGET_OS}.mkdirat ) + +add_entrypoint_object( + stat + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.stat +) diff --git a/libc/src/sys/stat/fstat.h b/libc/src/sys/stat/fstat.h new file mode 100644 --- /dev/null +++ b/libc/src/sys/stat/fstat.h @@ -0,0 +1,20 @@ +//===-- Implementation header for fstat -------------------------*- C++ -*-===// +// +// 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_SRC_SYS_STAT_FSTAT_H +#define LLVM_LIBC_SRC_SYS_STAT_FSTAT_H + +#include + +namespace __llvm_libc { + +int fstat(int fd, struct stat *statbuf); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SYS_STAT_FSTAT_H diff --git a/libc/src/sys/stat/linux/CMakeLists.txt b/libc/src/sys/stat/linux/CMakeLists.txt --- a/libc/src/sys/stat/linux/CMakeLists.txt +++ b/libc/src/sys/stat/linux/CMakeLists.txt @@ -64,3 +64,50 @@ libc.src.__support.OSUtil.osutil libc.src.errno.errno ) + +add_header_library( + kernel_statx + HDRS + kernel_statx.h + DEPENDS + libc.include.sys_stat + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + +add_entrypoint_object( + stat + SRCS + stat.cpp + HDRS + ../stat.h + DEPENDS + .kernel_statx + libc.include.fcntl + libc.include.sys_stat +) + +add_entrypoint_object( + lstat + SRCS + lstat.cpp + HDRS + ../lstat.h + DEPENDS + .kernel_statx + libc.include.fcntl + libc.include.sys_stat +) + +add_entrypoint_object( + fstat + SRCS + fstat.cpp + HDRS + ../fstat.h + DEPENDS + .kernel_statx + libc.include.fcntl + libc.include.sys_stat +) diff --git a/libc/src/sys/stat/linux/fstat.cpp b/libc/src/sys/stat/linux/fstat.cpp new file mode 100644 --- /dev/null +++ b/libc/src/sys/stat/linux/fstat.cpp @@ -0,0 +1,23 @@ +//===-- Linux implementation of fstat -------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/sys/stat/fstat.h" +#include "kernel_statx.h" + +#include "src/__support/common.h" + +#include +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, fstat, (int fd, struct stat *statbuf)) { + return statx(fd, "", AT_EMPTY_PATH, statbuf); +} + +} // namespace __llvm_libc diff --git a/libc/src/sys/stat/linux/kernel_statx.h b/libc/src/sys/stat/linux/kernel_statx.h new file mode 100644 --- /dev/null +++ b/libc/src/sys/stat/linux/kernel_statx.h @@ -0,0 +1,105 @@ +//===-- Wrapper over SYS_statx syscall ------------------------------------===// +// +// 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_SRC_SYS_STAT_LINUX_STATX_H +#define LLVM_LIBC_SRC_SYS_STAT_LINUX_STATX_H + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. + +#include +#include +#include +#include // For syscall numbers. + +// It is safe to include this kernel header as it is designed to be +// included from user programs without causing any name pollution. +#include + +namespace { + +// The type definitions in the internal namespace match kernel's definition of +// the statx_timestamp and statx types in linux/stat.h. We define equivalent +// types here instead of including that header file to avoid name mixup between +// linux/stat.h and the libc's stat.h. +struct statx_timestamp { + int64_t tv_sec; + uint32_t tv_nsec; + int32_t __reserved; +}; + +struct statx_buf { + uint32_t stx_mask; // What results were written + uint32_t stx_blksize; // Preferred general I/O size + uint64_t stx_attributes; // Flags conveying information about the file + uint32_t stx_nlink; // Number of hard links + uint32_t stx_uid; // User ID of owner + uint32_t stx_gid; // Group ID of owner + uint16_t stx_mode; // File mode + uint16_t __spare0[1]; + uint64_t stx_ino; // Inode number + uint64_t stx_size; // File size + uint64_t stx_blocks; // Number of 512-byte blocks allocated + uint64_t stx_attributes_mask; // Mask to show what's supported in + // stx_attributes + struct statx_timestamp stx_atime; // Last access time + struct statx_timestamp stx_btime; // File creation time + struct statx_timestamp stx_ctime; // Last attribute change time + struct statx_timestamp stx_mtime; // Last data modification time + uint32_t stx_rdev_major; // Device ID of special file + uint32_t stx_rdev_minor; + uint32_t stx_dev_major; // ID of device containing file + uint32_t stx_dev_minor; + uint64_t stx_mnt_id; + uint64_t __spare2; + uint64_t __spare3[12]; // Spare space for future expansion +}; + +// The below mask value is based on the definition of a similarly +// named macro in linux/stat.h. When this flag is passed for the +// mask argument to the statx syscall, all fields except the +// stx_btime field will be filled in. +constexpr unsigned int STATX_BASIC_STATS_MASK = 0x7FF; + +} // Anonymous namespace + +namespace __llvm_libc { + +inline int statx(int dirfd, const char *__restrict path, int flags, + struct stat *__restrict statbuf) { + // We make a statx syscall and copy out the result into the |statbuf|. + ::statx_buf xbuf; + long ret = + syscall(SYS_statx, dirfd, path, flags, ::STATX_BASIC_STATS_MASK, &xbuf); + if (ret < 0) { + errno = -ret; + return -1; + } + + statbuf->st_dev = MKDEV(xbuf.stx_dev_major, xbuf.stx_dev_minor); + statbuf->st_ino = xbuf.stx_ino; + statbuf->st_mode = xbuf.stx_mode; + statbuf->st_nlink = xbuf.stx_nlink; + statbuf->st_uid = xbuf.stx_uid; + statbuf->st_gid = xbuf.stx_gid; + statbuf->st_rdev = MKDEV(xbuf.stx_rdev_major, xbuf.stx_rdev_minor); + statbuf->st_size = xbuf.stx_size; + statbuf->st_atim.tv_sec = xbuf.stx_atime.tv_sec; + statbuf->st_atim.tv_nsec = xbuf.stx_atime.tv_nsec; + statbuf->st_mtim.tv_sec = xbuf.stx_mtime.tv_sec; + statbuf->st_mtim.tv_nsec = xbuf.stx_mtime.tv_nsec; + statbuf->st_ctim.tv_sec = xbuf.stx_ctime.tv_sec; + statbuf->st_ctim.tv_nsec = xbuf.stx_ctime.tv_nsec; + statbuf->st_blksize = xbuf.stx_blksize; + statbuf->st_blocks = xbuf.stx_blocks; + + return 0; +} + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SYS_STAT_LINUX_STATX_H diff --git a/libc/src/sys/stat/linux/lstat.cpp b/libc/src/sys/stat/linux/lstat.cpp new file mode 100644 --- /dev/null +++ b/libc/src/sys/stat/linux/lstat.cpp @@ -0,0 +1,26 @@ +//===-- Linux implementation of lstat -------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/sys/stat/lstat.h" +#include "kernel_statx.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, lstat, + (const char *__restrict path, + struct stat *__restrict statbuf)) { + return statx(AT_FDCWD, path, AT_SYMLINK_NOFOLLOW, statbuf); +} + +} // namespace __llvm_libc diff --git a/libc/src/sys/stat/linux/stat.cpp b/libc/src/sys/stat/linux/stat.cpp new file mode 100644 --- /dev/null +++ b/libc/src/sys/stat/linux/stat.cpp @@ -0,0 +1,25 @@ +//===-- Linux implementation of stat --------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/sys/stat/stat.h" +#include "kernel_statx.h" + +#include "src/__support/common.h" + +#include +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, stat, + (const char *__restrict path, + struct stat *__restrict statbuf)) { + return statx(AT_FDCWD, path, 0, statbuf); +} + +} // namespace __llvm_libc diff --git a/libc/src/sys/stat/lstat.h b/libc/src/sys/stat/lstat.h new file mode 100644 --- /dev/null +++ b/libc/src/sys/stat/lstat.h @@ -0,0 +1,20 @@ +//===-- Implementation header for lstat -------------------------*- C++ -*-===// +// +// 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_SRC_SYS_STAT_LSTAT_H +#define LLVM_LIBC_SRC_SYS_STAT_LSTAT_H + +#include + +namespace __llvm_libc { + +int lstat(const char *__restrict path, struct stat *__restrict statbuf); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SYS_STAT_LSTAT_H diff --git a/libc/src/sys/stat/stat.h b/libc/src/sys/stat/stat.h new file mode 100644 --- /dev/null +++ b/libc/src/sys/stat/stat.h @@ -0,0 +1,20 @@ +//===-- Implementation header for stat --------------------------*- C++ -*-===// +// +// 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_SRC_SYS_STAT_STAT_H +#define LLVM_LIBC_SRC_SYS_STAT_STAT_H + +#include + +namespace __llvm_libc { + +int stat(const char *__restrict path, struct stat *__restrict statbuf); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SYS_STAT_STAT_H diff --git a/libc/test/src/sys/stat/CMakeLists.txt b/libc/test/src/sys/stat/CMakeLists.txt --- a/libc/test/src/sys/stat/CMakeLists.txt +++ b/libc/test/src/sys/stat/CMakeLists.txt @@ -63,3 +63,51 @@ libc.src.sys.stat.mkdirat libc.src.unistd.rmdir ) + +add_libc_unittest( + stat_test + SUITE + libc_sys_stat_unittests + SRCS + stat_test.cpp + DEPENDS + libc.include.errno + libc.include.fcntl + libc.include.sys_stat + libc.src.sys.stat.stat + libc.src.fcntl.open + libc.src.unistd.close + libc.src.unistd.unlink +) + +add_libc_unittest( + lstat_test + SUITE + libc_sys_stat_unittests + SRCS + lstat_test.cpp + DEPENDS + libc.include.errno + libc.include.fcntl + libc.include.sys_stat + libc.src.sys.stat.lstat + libc.src.fcntl.open + libc.src.unistd.close + libc.src.unistd.unlink +) + +add_libc_unittest( + fstat_test + SUITE + libc_sys_stat_unittests + SRCS + fstat_test.cpp + DEPENDS + libc.include.errno + libc.include.fcntl + libc.include.sys_stat + libc.src.sys.stat.fstat + libc.src.fcntl.open + libc.src.unistd.close + libc.src.unistd.unlink +) diff --git a/libc/test/src/sys/stat/fstat_test.cpp b/libc/test/src/sys/stat/fstat_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/sys/stat/fstat_test.cpp @@ -0,0 +1,51 @@ +//===-- Unittests for fstat -----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/fcntl/open.h" +#include "src/sys/stat/fstat.h" +#include "src/unistd/close.h" +#include "src/unistd/unlink.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" +#include "utils/testutils/FDReader.h" + +#include +#include +#include + +TEST(LlvmLibcFStatTest, CreatAndReadMode) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + + // The test file is initially writable. We open it for writing and ensure + // that it indeed can be opened for writing. Next, we close the file and + // make it readonly using chmod. We test that chmod actually succeeded by + // trying to open the file for writing and failing. + constexpr const char *TEST_FILE = "testdata/fstat.test"; + errno = 0; + + int fd = __llvm_libc::open(TEST_FILE, O_CREAT | O_WRONLY, S_IRWXU); + ASSERT_GT(fd, 0); + ASSERT_EQ(errno, 0); + + struct stat statbuf; + ASSERT_THAT(__llvm_libc::fstat(fd, &statbuf), Succeeds(0)); + + ASSERT_EQ(int(statbuf.st_mode), int(S_IRWXU | S_IFREG)); + + ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0)); + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE), Succeeds(0)); +} + +TEST(LlvmLibcFStatTest, NonExistentFile) { + errno = 0; + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + struct stat statbuf; + ASSERT_THAT(__llvm_libc::fstat(-1, &statbuf), Fails(EBADF)); + errno = 0; +} diff --git a/libc/test/src/sys/stat/lstat_test.cpp b/libc/test/src/sys/stat/lstat_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/sys/stat/lstat_test.cpp @@ -0,0 +1,51 @@ +//===-- Unittests for lstat -----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/fcntl/open.h" +#include "src/sys/stat/lstat.h" +#include "src/unistd/close.h" +#include "src/unistd/unlink.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" +#include "utils/testutils/FDReader.h" + +#include +#include +#include + +TEST(LlvmLibcLStatTest, CreatAndReadMode) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + + // The test file is initially writable. We open it for writing and ensure + // that it indeed can be opened for writing. Next, we close the file and + // make it readonly using chmod. We test that chmod actually succeeded by + // trying to open the file for writing and failing. + constexpr const char *TEST_FILE = "testdata/lstat.test"; + errno = 0; + + int fd = __llvm_libc::open(TEST_FILE, O_CREAT | O_WRONLY, S_IRWXU); + ASSERT_GT(fd, 0); + ASSERT_EQ(errno, 0); + ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0)); + + struct stat statbuf; + ASSERT_THAT(__llvm_libc::lstat(TEST_FILE, &statbuf), Succeeds(0)); + + ASSERT_EQ(int(statbuf.st_mode), int(S_IRWXU | S_IFREG)); + + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE), Succeeds(0)); +} + +TEST(LlvmLibcLStatTest, NonExistentFile) { + errno = 0; + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + struct stat statbuf; + ASSERT_THAT(__llvm_libc::lstat("non-existent-file", &statbuf), Fails(ENOENT)); + errno = 0; +} diff --git a/libc/test/src/sys/stat/stat_test.cpp b/libc/test/src/sys/stat/stat_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/sys/stat/stat_test.cpp @@ -0,0 +1,51 @@ +//===-- Unittests for stat ------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/fcntl/open.h" +#include "src/sys/stat/stat.h" +#include "src/unistd/close.h" +#include "src/unistd/unlink.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" +#include "utils/testutils/FDReader.h" + +#include +#include +#include + +TEST(LlvmLibcStatTest, CreatAndReadMode) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + + // The test file is initially writable. We open it for writing and ensure + // that it indeed can be opened for writing. Next, we close the file and + // make it readonly using chmod. We test that chmod actually succeeded by + // trying to open the file for writing and failing. + constexpr const char *TEST_FILE = "testdata/stat.test"; + errno = 0; + + int fd = __llvm_libc::open(TEST_FILE, O_CREAT | O_WRONLY, S_IRWXU); + ASSERT_GT(fd, 0); + ASSERT_EQ(errno, 0); + ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0)); + + struct stat statbuf; + ASSERT_THAT(__llvm_libc::stat(TEST_FILE, &statbuf), Succeeds(0)); + + ASSERT_EQ(int(statbuf.st_mode), int(S_IRWXU | S_IFREG)); + + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE), Succeeds(0)); +} + +TEST(LlvmLibcStatTest, NonExistentFile) { + errno = 0; + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + struct stat statbuf; + ASSERT_THAT(__llvm_libc::stat("non-existent-file", &statbuf), Fails(ENOENT)); + errno = 0; +}