[libc-commits] [libc] [libc] Implement mknodat in sys/stat (PR #225512)
Aman Maurya via libc-commits
libc-commits at lists.llvm.org
Tue Sep 22 22:21:16 PDT 2026
https://github.com/amanmaurya92 updated https://github.com/llvm/llvm-project/pull/225512
>From 56fab5d756a7ef57aa581fddd0479d49155558e7 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Wed, 23 Sep 2026 01:55:55 +0530
Subject: [PATCH 1/2] [libc] Implement mknodat in sys/stat
Implement the standard POSIX.1-2008 / POSIX.1-2024 function mknodat in <sys/stat.h>.
Fixes #225506
Assisted by Antigravity and Gemini
---
libc/config/linux/aarch64/entrypoints.txt | 1 +
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/include/sys/stat.yaml | 7 ++
libc/src/sys/stat/CMakeLists.txt | 7 ++
libc/src/sys/stat/linux/CMakeLists.txt | 16 ++++
libc/src/sys/stat/linux/mknodat.cpp | 35 +++++++
libc/src/sys/stat/mknodat.h | 27 ++++++
libc/test/src/sys/stat/CMakeLists.txt | 25 +++++
libc/test/src/sys/stat/mknodat_test.cpp | 109 ++++++++++++++++++++++
10 files changed, 229 insertions(+)
create mode 100644 libc/src/sys/stat/linux/mknodat.cpp
create mode 100644 libc/src/sys/stat/mknodat.h
create mode 100644 libc/test/src/sys/stat/mknodat_test.cpp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 7ddb2d45e9771..8e0c5d4b7a7e1 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -366,6 +366,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.stat.mkdirat
libc.src.sys.stat.mkfifo
libc.src.sys.stat.mkfifoat
+ libc.src.sys.stat.mknodat
libc.src.sys.stat.stat
libc.src.sys.stat.umask
libc.src.sys.stat.utimensat
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 7c01c536ed73a..26330b9c9569b 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -397,6 +397,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.stat.mkdirat
libc.src.sys.stat.mkfifo
libc.src.sys.stat.mkfifoat
+ libc.src.sys.stat.mknodat
libc.src.sys.stat.stat
libc.src.sys.stat.umask
libc.src.sys.stat.utimensat
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index e9761d6da1434..658fb33e467eb 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -402,6 +402,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.stat.mkdirat
libc.src.sys.stat.mkfifo
libc.src.sys.stat.mkfifoat
+ libc.src.sys.stat.mknodat
libc.src.sys.stat.stat
libc.src.sys.stat.umask
libc.src.sys.stat.utimensat
diff --git a/libc/include/sys/stat.yaml b/libc/include/sys/stat.yaml
index 4e4f197a7f19c..217e55f6e97bb 100644
--- a/libc/include/sys/stat.yaml
+++ b/libc/include/sys/stat.yaml
@@ -76,6 +76,13 @@ functions:
- type: int
- type: const char *
- type: mode_t
+ - name: mknodat
+ return_type: int
+ arguments:
+ - type: int
+ - type: const char *
+ - type: mode_t
+ - type: dev_t
- name: stat
return_type: int
arguments:
diff --git a/libc/src/sys/stat/CMakeLists.txt b/libc/src/sys/stat/CMakeLists.txt
index d08db2d9a3e08..7ff6effa56898 100644
--- a/libc/src/sys/stat/CMakeLists.txt
+++ b/libc/src/sys/stat/CMakeLists.txt
@@ -72,6 +72,13 @@ add_entrypoint_object(
.${LIBC_TARGET_OS}.mkfifoat
)
+add_entrypoint_object(
+ mknodat
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.mknodat
+)
+
add_entrypoint_object(
stat
ALIAS
diff --git a/libc/src/sys/stat/linux/CMakeLists.txt b/libc/src/sys/stat/linux/CMakeLists.txt
index 14b200c2ad49a..d763a0008af6a 100644
--- a/libc/src/sys/stat/linux/CMakeLists.txt
+++ b/libc/src/sys/stat/linux/CMakeLists.txt
@@ -88,6 +88,22 @@ add_entrypoint_object(
libc.src.errno.errno
)
+add_entrypoint_object(
+ mknodat
+ SRCS
+ mknodat.cpp
+ HDRS
+ ../mknodat.h
+ DEPENDS
+ libc.hdr.types.dev_t
+ libc.hdr.types.mode_t
+ libc.src.__support.OSUtil.linux.syscall_wrappers.mknodat
+ libc.src.__support.common
+ libc.src.__support.libc_errno
+ libc.src.__support.macros.config
+ libc.src.errno.errno
+)
+
add_entrypoint_object(
stat
SRCS
diff --git a/libc/src/sys/stat/linux/mknodat.cpp b/libc/src/sys/stat/linux/mknodat.cpp
new file mode 100644
index 0000000000000..ccbe484e73e1e
--- /dev/null
+++ b/libc/src/sys/stat/linux/mknodat.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mknodat.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/stat/mknodat.h"
+
+#include "hdr/types/dev_t.h"
+#include "hdr/types/mode_t.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/mknodat.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, mknodat,
+ (int dirfd, const char *path, mode_t mode, dev_t dev)) {
+ auto result = linux_syscalls::mknodat(dirfd, path, mode, dev);
+ if (!result) {
+ libc_errno = result.error();
+ return -1;
+ }
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/stat/mknodat.h b/libc/src/sys/stat/mknodat.h
new file mode 100644
index 0000000000000..136f8690e3d15
--- /dev/null
+++ b/libc/src/sys/stat/mknodat.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 mknodat.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_STAT_MKNODAT_H
+#define LLVM_LIBC_SRC_SYS_STAT_MKNODAT_H
+
+#include "hdr/types/dev_t.h"
+#include "hdr/types/mode_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_STAT_MKNODAT_H
diff --git a/libc/test/src/sys/stat/CMakeLists.txt b/libc/test/src/sys/stat/CMakeLists.txt
index d5972c1357fee..42b5309d1c345 100644
--- a/libc/test/src/sys/stat/CMakeLists.txt
+++ b/libc/test/src/sys/stat/CMakeLists.txt
@@ -120,6 +120,31 @@ add_libc_test(
libc.test.UnitTest.ErrnoSetterMatcher
)
+add_libc_test(
+ mknodat_test
+ SUITE
+ libc_sys_stat_unittests
+ SRCS
+ mknodat_test.cpp
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.fcntl_macros
+ libc.hdr.sys_stat_macros
+ libc.hdr.types.dev_t
+ libc.hdr.types.mode_t
+ libc.hdr.types.struct_stat
+ libc.src.__support.CPP.scope
+ libc.src.errno.errno
+ libc.src.fcntl.open
+ libc.src.sys.stat.mknodat
+ libc.src.sys.stat.stat
+ libc.src.sys.stat.umask
+ libc.src.unistd.close
+ libc.src.unistd.unlink
+ libc.test.UnitTest.ErrnoCheckingTest
+ libc.test.UnitTest.ErrnoSetterMatcher
+)
+
add_libc_test(
stat_test
SUITE
diff --git a/libc/test/src/sys/stat/mknodat_test.cpp b/libc/test/src/sys/stat/mknodat_test.cpp
new file mode 100644
index 0000000000000..d891ee4b701b5
--- /dev/null
+++ b/libc/test/src/sys/stat/mknodat_test.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mknodat.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "hdr/sys_stat_macros.h"
+#include "hdr/types/dev_t.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/mknodat.h"
+#include "src/sys/stat/stat.h"
+#include "src/sys/stat/umask.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 LlvmLibcMknodatTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcMknodatTest, CreateAndRemoveRegularFileWithAtFdcwd) {
+ constexpr const char *TEST_FILE = "testdata/mknodat_reg.test";
+ constexpr mode_t FILE_MODE = S_IRUSR | S_IWUSR;
+
+ mode_t old_mask = LIBC_NAMESPACE::umask(0);
+ ASSERT_THAT(
+ LIBC_NAMESPACE::mknodat(AT_FDCWD, TEST_FILE, S_IFREG | FILE_MODE, 0),
+ Succeeds(0));
+ LIBC_NAMESPACE::umask(old_mask);
+
+ LIBC_NAMESPACE::cpp::scope_exit cleanup(
+ [&] { EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0)); });
+
+ struct stat statbuf;
+ ASSERT_THAT(LIBC_NAMESPACE::stat(TEST_FILE, &statbuf), Succeeds(0));
+ ASSERT_TRUE(S_ISREG(statbuf.st_mode));
+ ASSERT_EQ(statbuf.st_mode & 07777, static_cast<mode_t>(FILE_MODE));
+}
+
+TEST_F(LlvmLibcMknodatTest, CreateAndRemoveFifoWithAtFdcwd) {
+ constexpr const char *TEST_FIFO = "testdata/mknodat_fifo.test";
+ constexpr mode_t FIFO_MODE = S_IRUSR | S_IWUSR;
+
+ mode_t old_mask = LIBC_NAMESPACE::umask(0);
+ ASSERT_THAT(
+ LIBC_NAMESPACE::mknodat(AT_FDCWD, TEST_FIFO, S_IFIFO | FIFO_MODE, 0),
+ Succeeds(0));
+ LIBC_NAMESPACE::umask(old_mask);
+
+ LIBC_NAMESPACE::cpp::scope_exit cleanup(
+ [&] { EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FIFO), Succeeds(0)); });
+
+ struct stat statbuf;
+ ASSERT_THAT(LIBC_NAMESPACE::stat(TEST_FIFO, &statbuf), Succeeds(0));
+ ASSERT_TRUE(S_ISFIFO(statbuf.st_mode));
+ ASSERT_EQ(statbuf.st_mode & 07777, static_cast<mode_t>(FIFO_MODE));
+}
+
+TEST_F(LlvmLibcMknodatTest, CreateAndRemoveWithDirFd) {
+ constexpr const char *TEST_DIR = "testdata";
+ constexpr const char *TEST_FILE_BASENAME = "mknodat_dir.test";
+ constexpr const char *TEST_FILE_PATH = "testdata/mknodat_dir.test";
+ constexpr mode_t FILE_MODE = S_IRUSR | S_IWUSR;
+
+ int dirfd = LIBC_NAMESPACE::open(TEST_DIR, O_DIRECTORY);
+ ASSERT_GT(dirfd, 0);
+ LIBC_NAMESPACE::cpp::scope_exit cleanup_dir(
+ [&] { EXPECT_THAT(LIBC_NAMESPACE::close(dirfd), Succeeds(0)); });
+
+ mode_t old_mask = LIBC_NAMESPACE::umask(0);
+ ASSERT_THAT(LIBC_NAMESPACE::mknodat(dirfd, TEST_FILE_BASENAME,
+ S_IFREG | FILE_MODE, 0),
+ Succeeds(0));
+ LIBC_NAMESPACE::umask(old_mask);
+
+ LIBC_NAMESPACE::cpp::scope_exit cleanup_file([&] {
+ EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_PATH), Succeeds(0));
+ });
+
+ struct stat statbuf;
+ ASSERT_THAT(LIBC_NAMESPACE::stat(TEST_FILE_PATH, &statbuf), Succeeds(0));
+ ASSERT_TRUE(S_ISREG(statbuf.st_mode));
+ ASSERT_EQ(statbuf.st_mode & 07777, static_cast<mode_t>(FILE_MODE));
+}
+
+TEST_F(LlvmLibcMknodatTest, BadDirFd) {
+ ASSERT_THAT(LIBC_NAMESPACE::mknodat(-1, "some-file", S_IFREG | 0644, 0),
+ Fails(EBADF));
+}
+
+TEST_F(LlvmLibcMknodatTest, NonExistentPath) {
+ ASSERT_THAT(LIBC_NAMESPACE::mknodat(AT_FDCWD,
+ "testdata/non-existent-dir/mknodat.test",
+ S_IFREG | 0644, 0),
+ Fails(ENOENT));
+}
>From 55320bf1768a407cbac521cd5038a3dc7255e766 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Wed, 23 Sep 2026 10:43:18 +0530
Subject: [PATCH 2/2] [libc] Use libc_make_test_file_path in mknodat tests
---
libc/test/src/sys/stat/mknodat_test.cpp | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/libc/test/src/sys/stat/mknodat_test.cpp b/libc/test/src/sys/stat/mknodat_test.cpp
index d891ee4b701b5..82549cf19cec7 100644
--- a/libc/test/src/sys/stat/mknodat_test.cpp
+++ b/libc/test/src/sys/stat/mknodat_test.cpp
@@ -32,7 +32,7 @@ using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
using LlvmLibcMknodatTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
TEST_F(LlvmLibcMknodatTest, CreateAndRemoveRegularFileWithAtFdcwd) {
- constexpr const char *TEST_FILE = "testdata/mknodat_reg.test";
+ auto TEST_FILE = libc_make_test_file_path("mknodat_reg.test");
constexpr mode_t FILE_MODE = S_IRUSR | S_IWUSR;
mode_t old_mask = LIBC_NAMESPACE::umask(0);
@@ -51,7 +51,7 @@ TEST_F(LlvmLibcMknodatTest, CreateAndRemoveRegularFileWithAtFdcwd) {
}
TEST_F(LlvmLibcMknodatTest, CreateAndRemoveFifoWithAtFdcwd) {
- constexpr const char *TEST_FIFO = "testdata/mknodat_fifo.test";
+ auto TEST_FIFO = libc_make_test_file_path("mknodat_fifo.test");
constexpr mode_t FIFO_MODE = S_IRUSR | S_IWUSR;
mode_t old_mask = LIBC_NAMESPACE::umask(0);
@@ -70,9 +70,9 @@ TEST_F(LlvmLibcMknodatTest, CreateAndRemoveFifoWithAtFdcwd) {
}
TEST_F(LlvmLibcMknodatTest, CreateAndRemoveWithDirFd) {
- constexpr const char *TEST_DIR = "testdata";
+ auto TEST_DIR = libc_make_test_file_path("testdata");
constexpr const char *TEST_FILE_BASENAME = "mknodat_dir.test";
- constexpr const char *TEST_FILE_PATH = "testdata/mknodat_dir.test";
+ auto TEST_FILE_PATH = libc_make_test_file_path("testdata/mknodat_dir.test");
constexpr mode_t FILE_MODE = S_IRUSR | S_IWUSR;
int dirfd = LIBC_NAMESPACE::open(TEST_DIR, O_DIRECTORY);
@@ -102,8 +102,7 @@ TEST_F(LlvmLibcMknodatTest, BadDirFd) {
}
TEST_F(LlvmLibcMknodatTest, NonExistentPath) {
- ASSERT_THAT(LIBC_NAMESPACE::mknodat(AT_FDCWD,
- "testdata/non-existent-dir/mknodat.test",
- S_IFREG | 0644, 0),
+ auto BAD_PATH = libc_make_test_file_path("non-existent-dir/mknodat.test");
+ ASSERT_THAT(LIBC_NAMESPACE::mknodat(AT_FDCWD, BAD_PATH, S_IFREG | 0644, 0),
Fails(ENOENT));
}
More information about the libc-commits
mailing list