[libc-commits] [libc] [libc] Implement renameat in stdio (PR #225739)

via libc-commits libc-commits at lists.llvm.org
Wed Sep 23 04:56:53 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Aman Maurya (amanmaurya92)

<details>
<summary>Changes</summary>

Implement the standard POSIX.1-2008 / POSIX.1-2024 function renameat in <stdio.h>.

Fixes #<!-- -->225679

---
Full diff: https://github.com/llvm/llvm-project/pull/225739.diff


11 Files Affected:

- (modified) libc/config/linux/aarch64/entrypoints.txt (+1) 
- (modified) libc/config/linux/riscv/entrypoints.txt (+1) 
- (modified) libc/config/linux/x86_64/entrypoints.txt (+1) 
- (modified) libc/include/stdio.yaml (+9) 
- (modified) libc/src/__support/OSUtil/linux/syscall_wrappers/rename.h (+16) 
- (modified) libc/src/stdio/CMakeLists.txt (+7) 
- (modified) libc/src/stdio/linux/CMakeLists.txt (+11) 
- (added) libc/src/stdio/linux/renameat.cpp (+28) 
- (added) libc/src/stdio/renameat.h (+21) 
- (modified) libc/test/src/stdio/CMakeLists.txt (+23) 
- (added) libc/test/src/stdio/renameat_test.cpp (+94) 


``````````diff
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 479a70846714f0..6429fa8c451e0b 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -266,6 +266,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.printf
     libc.src.stdio.remove
     libc.src.stdio.rename
+    libc.src.stdio.renameat
     libc.src.stdio.scanf
     libc.src.stdio.vscanf
     libc.src.stdio.snprintf
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index c841b28d9e544e..c5987828efb67d 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -289,6 +289,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.printf
     libc.src.stdio.remove
     libc.src.stdio.rename
+    libc.src.stdio.renameat
     libc.src.stdio.scanf
     libc.src.stdio.vscanf
     libc.src.stdio.snprintf
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 786a1ea0743055..4e8f0790789502 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -289,6 +289,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.printf
     libc.src.stdio.remove
     libc.src.stdio.rename
+    libc.src.stdio.renameat
     libc.src.stdio.scanf
     libc.src.stdio.vscanf
     libc.src.stdio.snprintf
diff --git a/libc/include/stdio.yaml b/libc/include/stdio.yaml
index bb28dadd29b3ba..c01197955ef6e2 100644
--- a/libc/include/stdio.yaml
+++ b/libc/include/stdio.yaml
@@ -353,6 +353,15 @@ functions:
     arguments:
       - type: const char *
       - type: const char *
+  - name: renameat
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: int
+      - type: const char *
+      - type: int
+      - type: const char *
   - name: scanf
     standards:
       - stdc
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/rename.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/rename.h
index ec0be35b27ab8d..90a58cf9b331ef 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/rename.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/rename.h
@@ -24,6 +24,22 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace linux_syscalls {
 
+LIBC_INLINE ErrorOr<int> renameat(int olddirfd, const char *oldpath,
+                                  int newdirfd, const char *newpath) {
+#ifdef SYS_renameat2
+  int ret =
+      syscall_impl<int>(SYS_renameat2, olddirfd, oldpath, newdirfd, newpath, 0);
+#elif defined(SYS_renameat)
+  int ret =
+      syscall_impl<int>(SYS_renameat, olddirfd, oldpath, newdirfd, newpath);
+#else
+#error "renameat and renameat2 syscalls not available."
+#endif
+  if (ret < 0)
+    return Error(-ret);
+  return ret;
+}
+
 LIBC_INLINE ErrorOr<int> rename(const char *oldpath, const char *newpath) {
 #ifdef SYS_renameat2
   int ret =
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index a0d4e86674b956..d475ad5d7708cf 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -271,6 +271,13 @@ add_entrypoint_object(
     .${LIBC_TARGET_OS}.rename
 )
 
+add_entrypoint_object(
+  renameat
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.renameat
+)
+
 add_entrypoint_object(
   fdopen
   ALIAS
diff --git a/libc/src/stdio/linux/CMakeLists.txt b/libc/src/stdio/linux/CMakeLists.txt
index 6631f2ca4814a1..db715c2b2bd333 100644
--- a/libc/src/stdio/linux/CMakeLists.txt
+++ b/libc/src/stdio/linux/CMakeLists.txt
@@ -23,6 +23,17 @@ add_entrypoint_object(
     libc.src.errno.errno
 )
 
+add_entrypoint_object(
+  renameat
+  SRCS
+    renameat.cpp
+  HDRS
+    ../renameat.h
+  DEPENDS
+    libc.src.__support.OSUtil.linux.syscall_wrappers.rename
+    libc.src.errno.errno
+)
+
 add_entrypoint_object(
   fdopen
   SRCS
diff --git a/libc/src/stdio/linux/renameat.cpp b/libc/src/stdio/linux/renameat.cpp
new file mode 100644
index 00000000000000..e270d01fc776c6
--- /dev/null
+++ b/libc/src/stdio/linux/renameat.cpp
@@ -0,0 +1,28 @@
+//===-- Linux implementation of renameat ----------------------------------===//
+//
+// 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/stdio/renameat.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/rename.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, renameat,
+                   (int olddirfd, const char *oldpath, int newdirfd,
+                    const char *newpath)) {
+  auto result = linux_syscalls::renameat(olddirfd, oldpath, newdirfd, newpath);
+  if (!result) {
+    libc_errno = result.error();
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/renameat.h b/libc/src/stdio/renameat.h
new file mode 100644
index 00000000000000..154f3e6fe4b138
--- /dev/null
+++ b/libc/src/stdio/renameat.h
@@ -0,0 +1,21 @@
+//===-- Implementation header of renameat -----------------------*- 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_STDIO_RENAMEAT_H
+#define LLVM_LIBC_SRC_STDIO_RENAMEAT_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int renameat(int olddirfd, const char *oldpath, int newdirfd,
+             const char *newpath);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_RENAMEAT_H
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index e0ca1ab2ca162c..28b07cd984e9ea 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -546,6 +546,29 @@ if(${LIBC_TARGET_OS} STREQUAL "linux")
       libc.test.UnitTest.ErrnoSetterMatcher
   )
 
+  add_libc_test(
+    renameat_test
+    SUITE
+      libc_stdio_unittests
+    SRCS
+      renameat_test.cpp
+    DEPENDS
+      libc.hdr.errno_macros
+      libc.hdr.fcntl_macros
+      libc.hdr.sys_stat_macros
+      libc.hdr.unistd_macros
+      libc.src.__support.CPP.scope
+      libc.src.errno.errno
+      libc.src.fcntl.open
+      libc.src.stdio.renameat
+      libc.src.unistd.access
+      libc.src.unistd.close
+      libc.src.unistd.unlink
+      libc.test.UnitTest.ErrnoCheckingTest
+      libc.test.UnitTest.ErrnoSetterMatcher
+  )
+
+
   add_libc_test(
     fdopen_test
     SUITE
diff --git a/libc/test/src/stdio/renameat_test.cpp b/libc/test/src/stdio/renameat_test.cpp
new file mode 100644
index 00000000000000..be3fd118aec3f0
--- /dev/null
+++ b/libc/test/src/stdio/renameat_test.cpp
@@ -0,0 +1,94 @@
+//===-- Unittests for renameat --------------------------------------------===//
+//
+// 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 "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "hdr/sys_stat_macros.h"
+#include "hdr/unistd_macros.h"
+#include "src/__support/CPP/scope.h"
+#include "src/__support/libc_errno.h"
+#include "src/fcntl/open.h"
+#include "src/stdio/renameat.h"
+#include "src/unistd/access.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 LlvmLibcRenameatTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcRenameatTest, CreateAndRenameFileWithAtFdcwd) {
+  constexpr const char *FILENAME0 = "renameat.test.file0";
+  auto TEST_FILEPATH0 = libc_make_test_file_path(FILENAME0);
+  constexpr const char *FILENAME1 = "renameat.test.file1";
+  auto TEST_FILEPATH1 = libc_make_test_file_path(FILENAME1);
+
+  int fd = LIBC_NAMESPACE::open(TEST_FILEPATH0, O_WRONLY | O_CREAT, S_IRWXU);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_GT(fd, 0);
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+  LIBC_NAMESPACE::cpp::scope_exit cleanup_files([&] {
+    LIBC_NAMESPACE::unlink(TEST_FILEPATH0);
+    LIBC_NAMESPACE::unlink(TEST_FILEPATH1);
+    LIBC_NAMESPACE::libc_errno = 0;
+  });
+
+  ASSERT_THAT(LIBC_NAMESPACE::access(TEST_FILEPATH0, F_OK), Succeeds(0));
+  ASSERT_THAT(LIBC_NAMESPACE::renameat(AT_FDCWD, TEST_FILEPATH0, AT_FDCWD,
+                                       TEST_FILEPATH1),
+              Succeeds(0));
+  ASSERT_THAT(LIBC_NAMESPACE::access(TEST_FILEPATH1, F_OK), Succeeds(0));
+  ASSERT_THAT(LIBC_NAMESPACE::access(TEST_FILEPATH0, F_OK), Fails(ENOENT));
+}
+
+TEST_F(LlvmLibcRenameatTest, CreateAndRenameWithDirFd) {
+  auto TEST_DIR = libc_make_test_file_path("testdata");
+  constexpr const char *BASENAME0 = "renameat_dir0.test";
+  constexpr const char *BASENAME1 = "renameat_dir1.test";
+  auto PATH0 = libc_make_test_file_path("testdata/renameat_dir0.test");
+  auto PATH1 = libc_make_test_file_path("testdata/renameat_dir1.test");
+
+  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)); });
+
+  int fd = LIBC_NAMESPACE::open(PATH0, O_WRONLY | O_CREAT, S_IRWXU);
+  ASSERT_GT(fd, 0);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+  LIBC_NAMESPACE::cpp::scope_exit cleanup_files([&] {
+    LIBC_NAMESPACE::unlink(PATH0);
+    LIBC_NAMESPACE::unlink(PATH1);
+    LIBC_NAMESPACE::libc_errno = 0;
+  });
+
+  ASSERT_THAT(LIBC_NAMESPACE::access(PATH0, F_OK), Succeeds(0));
+  ASSERT_THAT(LIBC_NAMESPACE::renameat(dirfd, BASENAME0, dirfd, BASENAME1),
+              Succeeds(0));
+  ASSERT_THAT(LIBC_NAMESPACE::access(PATH1, F_OK), Succeeds(0));
+  ASSERT_THAT(LIBC_NAMESPACE::access(PATH0, F_OK), Fails(ENOENT));
+}
+
+TEST_F(LlvmLibcRenameatTest, BadDirFd) {
+  ASSERT_THAT(LIBC_NAMESPACE::renameat(-1, "some-file", -1, "other-file"),
+              Fails(EBADF));
+}
+
+TEST_F(LlvmLibcRenameatTest, RenameNonExistent) {
+  constexpr const char *FILENAME1 = "renameat.test.nonexistent";
+  auto TEST_FILEPATH1 = libc_make_test_file_path(FILENAME1);
+
+  ASSERT_THAT(LIBC_NAMESPACE::renameat(AT_FDCWD, "non-existent-source",
+                                       AT_FDCWD, TEST_FILEPATH1),
+              Fails(ENOENT));
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/225739


More information about the libc-commits mailing list