[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