[libc-commits] [libc] 24cc35c - [libc] Implement fstatat in sys/stat (#224334)
via libc-commits
libc-commits at lists.llvm.org
Tue Sep 22 02:34:31 PDT 2026
Author: Aman Maurya
Date: 2026-09-22T11:34:25+02:00
New Revision: 24cc35cc98486cc156170505ff1952d54713374f
URL: https://github.com/llvm/llvm-project/commit/24cc35cc98486cc156170505ff1952d54713374f
DIFF: https://github.com/llvm/llvm-project/commit/24cc35cc98486cc156170505ff1952d54713374f.diff
LOG: [libc] Implement fstatat in sys/stat (#224334)
### Summary
Implement the standard POSIX.1-2008 function `fstatat` in
`<sys/stat.h>`.
Fixes #224327
### Changes
- Added `fstatat` function specification to
`libc/include/sys/stat.yaml`.
- Added internal header `libc/src/sys/stat/fstatat.h`.
- Implemented `fstatat` in `libc/src/sys/stat/linux/fstatat.cpp` using
the existing `internal::stat_via_statx`.
- Registered `fstatat` entrypoint in `libc/src/sys/stat/CMakeLists.txt`
and `libc/src/sys/stat/linux/CMakeLists.txt`.
- Added entrypoint across Linux targets (`x86_64`, `aarch64`, `riscv`).
- Added hermetic unit tests in
`libc/test/src/sys/stat/fstatat_test.cpp`.
### Testing
- Built and passed hermetic unit test
`libc.test.src.sys.stat.fstatat_test` (5/5 tests passed).
- Verified stat-family suite (`stat_test`, `lstat_test`, `fstat_test`,
`fstatat_test`).
- Ran `generate-libc-headers` and verified generated `sys/stat.h`.
- Verified `docgen.py` output marks `fstatat` as implemented.
- Ran `clang-format` on all touched C++ files.
---------
Co-authored-by: Pavel Labath <pavel at labath.sk>
Added:
libc/src/sys/stat/fstatat.h
libc/src/sys/stat/linux/fstatat.cpp
libc/test/src/sys/stat/fstatat_test.cpp
Modified:
libc/config/linux/aarch64/entrypoints.txt
libc/config/linux/riscv/entrypoints.txt
libc/config/linux/x86_64/entrypoints.txt
libc/include/sys/stat.yaml
libc/src/sys/stat/CMakeLists.txt
libc/src/sys/stat/linux/CMakeLists.txt
libc/test/src/sys/stat/CMakeLists.txt
Removed:
################################################################################
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index f05ce0c10198a..c6e8dc4e69fb4 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -358,6 +358,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.stat.fchmod
libc.src.sys.stat.fchmodat
libc.src.sys.stat.fstat
+ libc.src.sys.stat.fstatat
libc.src.sys.stat.futimens
libc.src.sys.stat.lstat
libc.src.sys.stat.mkdir
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 9b8b496507a00..c56fbf7974ade 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -389,6 +389,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.stat.fchmod
libc.src.sys.stat.fchmodat
libc.src.sys.stat.fstat
+ libc.src.sys.stat.fstatat
libc.src.sys.stat.futimens
libc.src.sys.stat.lstat
libc.src.sys.stat.mkdir
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 5bd21b018dc18..b7c05bc6bc6c5 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -394,6 +394,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.stat.fchmod
libc.src.sys.stat.fchmodat
libc.src.sys.stat.fstat
+ libc.src.sys.stat.fstatat
libc.src.sys.stat.futimens
libc.src.sys.stat.lstat
libc.src.sys.stat.mkdir
diff --git a/libc/include/sys/stat.yaml b/libc/include/sys/stat.yaml
index 2c85429cfef17..4e4f197a7f19c 100644
--- a/libc/include/sys/stat.yaml
+++ b/libc/include/sys/stat.yaml
@@ -42,6 +42,13 @@ functions:
arguments:
- type: int
- type: struct stat *
+ - name: fstatat
+ return_type: int
+ arguments:
+ - type: int
+ - type: const char *__restrict
+ - type: struct stat *__restrict
+ - type: int
- name: lstat
return_type: int
arguments:
diff --git a/libc/src/sys/stat/CMakeLists.txt b/libc/src/sys/stat/CMakeLists.txt
index 8481434bc2847..d08db2d9a3e08 100644
--- a/libc/src/sys/stat/CMakeLists.txt
+++ b/libc/src/sys/stat/CMakeLists.txt
@@ -30,6 +30,13 @@ add_entrypoint_object(
.${LIBC_TARGET_OS}.fstat
)
+add_entrypoint_object(
+ fstatat
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.fstatat
+)
+
add_entrypoint_object(
lstat
ALIAS
diff --git a/libc/src/sys/stat/fstatat.h b/libc/src/sys/stat/fstatat.h
new file mode 100644
index 0000000000000..d8f3129d9edc6
--- /dev/null
+++ b/libc/src/sys/stat/fstatat.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for fstatat.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_STAT_FSTATAT_H
+#define LLVM_LIBC_SRC_SYS_STAT_FSTATAT_H
+
+#include "hdr/types/struct_stat.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int fstatat(int dirfd, const char *__restrict path,
+ struct stat *__restrict statbuf, int flags);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_STAT_FSTATAT_H
diff --git a/libc/src/sys/stat/linux/CMakeLists.txt b/libc/src/sys/stat/linux/CMakeLists.txt
index f53189bb0c5cc..14b200c2ad49a 100644
--- a/libc/src/sys/stat/linux/CMakeLists.txt
+++ b/libc/src/sys/stat/linux/CMakeLists.txt
@@ -130,6 +130,20 @@ add_entrypoint_object(
libc.src.__support.OSUtil.linux.stat.stat_via_statx
)
+add_entrypoint_object(
+ fstatat
+ SRCS
+ fstatat.cpp
+ HDRS
+ ../fstatat.h
+ DEPENDS
+ libc.hdr.fcntl_macros
+ libc.hdr.types.struct_stat
+ libc.src.__support.error_or
+ libc.src.__support.libc_errno
+ libc.src.__support.OSUtil.linux.stat.stat_via_statx
+)
+
add_entrypoint_object(
utimensat
SRCS
diff --git a/libc/src/sys/stat/linux/fstatat.cpp b/libc/src/sys/stat/linux/fstatat.cpp
new file mode 100644
index 0000000000000..826d5bcc6a6b3
--- /dev/null
+++ b/libc/src/sys/stat/linux/fstatat.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of fstatat.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/stat/fstatat.h"
+#include "hdr/fcntl_macros.h"
+#include "hdr/types/struct_stat.h"
+#include "src/__support/OSUtil/linux/stat/stat_via_statx.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, fstatat,
+ (int dirfd, const char *__restrict path,
+ struct stat *__restrict statbuf, int flags)) {
+ ErrorOr<void> result = internal::stat_via_statx(dirfd, path, flags, statbuf);
+ if (!result) {
+ libc_errno = result.error();
+ return -1;
+ }
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/sys/stat/CMakeLists.txt b/libc/test/src/sys/stat/CMakeLists.txt
index 3c9842d0f7f5a..d5972c1357fee 100644
--- a/libc/test/src/sys/stat/CMakeLists.txt
+++ b/libc/test/src/sys/stat/CMakeLists.txt
@@ -177,6 +177,27 @@ add_libc_test(
libc.test.UnitTest.ErrnoSetterMatcher
)
+add_libc_test(
+ fstatat_test
+ SUITE
+ libc_sys_stat_unittests
+ SRCS
+ fstatat_test.cpp
+ DEPENDS
+ libc.hdr.fcntl_macros
+ libc.hdr.sys_stat_macros
+ libc.hdr.types.mode_t
+ libc.hdr.types.struct_stat
+ libc.src.__support.CPP.scope
+ libc.src.errno.errno
+ libc.src.sys.stat.fstatat
+ libc.src.fcntl.open
+ libc.src.unistd.close
+ libc.src.unistd.unlink
+ libc.test.UnitTest.ErrnoCheckingTest
+ libc.test.UnitTest.ErrnoSetterMatcher
+)
+
add_libc_test(
utimensat_test
SUITE
diff --git a/libc/test/src/sys/stat/fstatat_test.cpp b/libc/test/src/sys/stat/fstatat_test.cpp
new file mode 100644
index 0000000000000..70911b8dfa1ca
--- /dev/null
+++ b/libc/test/src/sys/stat/fstatat_test.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for fstatat.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/fcntl_macros.h"
+#include "hdr/sys_stat_macros.h"
+#include "hdr/types/mode_t.h"
+#include "hdr/types/struct_stat.h"
+#include "src/__support/CPP/scope.h"
+#include "src/fcntl/open.h"
+#include "src/sys/stat/fstatat.h"
+#include "src/unistd/close.h"
+#include "src/unistd/unlink.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LlvmLibcFstatatTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcFstatatTest, StatWithAtFdcwd) {
+ constexpr const char *TEST_FILE = "testdata/fstatat.test";
+
+ int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_WRONLY, S_IRWXU);
+ ASSERT_GT(fd, 0);
+ ASSERT_ERRNO_SUCCESS();
+ LIBC_NAMESPACE::cpp::scope_exit cleanup(
+ [&] { EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0)); });
+ ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+ struct stat statbuf;
+ ASSERT_THAT(LIBC_NAMESPACE::fstatat(AT_FDCWD, TEST_FILE, &statbuf, 0),
+ Succeeds(0));
+
+ ASSERT_EQ(statbuf.st_mode, static_cast<mode_t>(S_IRWXU | S_IFREG));
+}
+
+TEST_F(LlvmLibcFstatatTest, StatWithDirFd) {
+ constexpr const char *TEST_DIR = "testdata";
+ constexpr const char *TEST_FILE = "testdata/fstatat_dir.test";
+ constexpr const char *TEST_FILE_BASENAME = "fstatat_dir.test";
+
+ int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_WRONLY, S_IRWXU);
+ ASSERT_GT(fd, 0);
+ ASSERT_ERRNO_SUCCESS();
+ LIBC_NAMESPACE::cpp::scope_exit cleanup_file(
+ [&] { EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0)); });
+ ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+ int dirfd = LIBC_NAMESPACE::open(TEST_DIR, O_DIRECTORY);
+ ASSERT_GT(dirfd, 0);
+ ASSERT_ERRNO_SUCCESS();
+ LIBC_NAMESPACE::cpp::scope_exit cleanup_dir(
+ [&] { EXPECT_THAT(LIBC_NAMESPACE::close(dirfd), Succeeds(0)); });
+
+ struct stat statbuf;
+ ASSERT_THAT(LIBC_NAMESPACE::fstatat(dirfd, TEST_FILE_BASENAME, &statbuf, 0),
+ Succeeds(0));
+
+ ASSERT_EQ(statbuf.st_mode, static_cast<mode_t>(S_IRWXU | S_IFREG));
+}
+
+TEST_F(LlvmLibcFstatatTest, StatEmptyPath) {
+ constexpr const char *TEST_FILE = "testdata/fstatat_empty.test";
+
+ int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_WRONLY, S_IRWXU);
+ ASSERT_GT(fd, 0);
+ ASSERT_ERRNO_SUCCESS();
+ LIBC_NAMESPACE::cpp::scope_exit cleanup([&] {
+ EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+ EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
+ });
+
+ struct stat statbuf;
+ ASSERT_THAT(LIBC_NAMESPACE::fstatat(fd, "", &statbuf, AT_EMPTY_PATH),
+ Succeeds(0));
+
+ ASSERT_EQ(statbuf.st_mode, static_cast<mode_t>(S_IRWXU | S_IFREG));
+}
+
+TEST_F(LlvmLibcFstatatTest, NonExistentFile) {
+ struct stat statbuf;
+ ASSERT_THAT(
+ LIBC_NAMESPACE::fstatat(AT_FDCWD, "non-existent-file", &statbuf, 0),
+ Fails(ENOENT));
+}
+
+TEST_F(LlvmLibcFstatatTest, BadDirFd) {
+ struct stat statbuf;
+ ASSERT_THAT(LIBC_NAMESPACE::fstatat(-1, "some-file", &statbuf, 0),
+ Fails(EBADF));
+}
More information about the libc-commits
mailing list