[libc-commits] [libc] [libc][getcwd] Refactor getcwd to use the syscall wrapper pattern (PR #204000)
Jackson Stogel via libc-commits
libc-commits at lists.llvm.org
Mon Jun 15 23:02:20 PDT 2026
https://github.com/jtstogel updated https://github.com/llvm/llvm-project/pull/204000
>From 99e87c6cfc0289e7f35eec95b635b1c78fe4fa89 Mon Sep 17 00:00:00 2001
From: jtstogel <jtstogel at google.com>
Date: Mon, 15 Jun 2026 14:29:21 -0700
Subject: [PATCH 1/3] [libc][getcwd] Migrate getcwd to the linux syscall
wrapper pattern
Allows for re-use by other entrypoints. This PR keeps the error handling
for unreachable paths in `syscall_wrappers::getcwd` since I imagine most
usages of getcwd would rather error than receive `"(unreachable) /..."`.
---
.../linux/syscall_wrappers/CMakeLists.txt | 14 ++++++
.../OSUtil/linux/syscall_wrappers/getcwd.h | 48 +++++++++++++++++++
libc/src/unistd/linux/CMakeLists.txt | 6 ++-
libc/src/unistd/linux/getcwd.cpp | 44 ++++++++---------
4 files changed, 87 insertions(+), 25 deletions(-)
create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/getcwd.h
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 97848b1848660..36af7ffa898f1 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -542,6 +542,20 @@ add_header_library(
libc.include.sys_syscall
)
+add_header_library(
+ getcwd
+ HDRS
+ getcwd.h
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.types.ssize_t
+ libc.include.sys_syscall
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.src.__support.OSUtil.osutil
+)
+
add_header_library(
link
HDRS
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/getcwd.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/getcwd.h
new file mode 100644
index 0000000000000..df7cf70ae21ba
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/getcwd.h
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Syscall wrapper for getcwd.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETCWD_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETCWD_H
+
+#include "hdr/errno_macros.h"
+#include "hdr/types/ssize_t.h"
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+
+#include <sys/syscall.h> // For syscall numbers
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+ErrorOr<ssize_t> getcwd(char *buf, size_t size) {
+ ssize_t ret = syscall_impl<ssize_t>(SYS_getcwd, buf, size);
+ if (ret < 0) {
+ return Error(static_cast<int>(-ret));
+ }
+
+ // Return ENOENT for unreachable paths. This can occur, for example,
+ // when getcwd is called in a directory after `chroot` has switched
+ // the filesystem root.
+ if (ret == 0 || buf[0] != '/') {
+ return Error(ENOENT);
+ }
+
+ return ret;
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETCWD_H
diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt
index be057b4f80972..bf99fd4ccb389 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -223,9 +223,11 @@ add_entrypoint_object(
../getcwd.h
DEPENDS
libc.hdr.types.size_t
- libc.hdr.fcntl_macros
- libc.include.unistd
+ libc.hdr.types.ssize_t
libc.include.sys_syscall
+ libc.include.unistd
+ libc.src.__support.CPP.optional
+ libc.src.__support.OSUtil.linux.syscall_wrappers.getcwd
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)
diff --git a/libc/src/unistd/linux/getcwd.cpp b/libc/src/unistd/linux/getcwd.cpp
index c0e475dd3e8ff..6ac166a5c0611 100644
--- a/libc/src/unistd/linux/getcwd.cpp
+++ b/libc/src/unistd/linux/getcwd.cpp
@@ -8,33 +8,21 @@
#include "src/unistd/getcwd.h"
+#include "hdr/types/size_t.h"
+#include "hdr/types/ssize_t.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/getcwd.h"
#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/string/allocating_string_utils.h" // For strdup.
-#include "src/__support/libc_errno.h"
#include <linux/limits.h> // This is safe to include without any name pollution.
-#include <sys/syscall.h> // For syscall numbers.
+#include <sys/syscall.h> // For syscall numbers.
namespace LIBC_NAMESPACE_DECL {
-namespace {
-
-bool getcwd_syscall(char *buf, size_t size) {
- int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_getcwd, buf, size);
- if (ret < 0) {
- libc_errno = -ret;
- return false;
- } else if (ret == 0 || buf[0] != '/') {
- libc_errno = ENOENT;
- return false;
- }
- return true;
-}
-
-} // anonymous namespace
-
LLVM_LIBC_FUNCTION(char *, getcwd, (char *buf, size_t size)) {
if (buf == nullptr) {
// We match glibc's behavior here and return the cwd in a malloc-ed buffer.
@@ -42,24 +30,34 @@ LLVM_LIBC_FUNCTION(char *, getcwd, (char *buf, size_t size)) {
// into it. This way, if the syscall fails, we avoid unnecessary malloc
// and free.
char pathbuf[PATH_MAX];
- if (!getcwd_syscall(pathbuf, PATH_MAX))
+
+ ErrorOr<ssize_t> bytes_written = linux_syscalls::getcwd(pathbuf, PATH_MAX);
+ if (!bytes_written) {
+ libc_errno = bytes_written.error();
return nullptr;
- auto cwd = internal::strdup(pathbuf);
+ }
+
+ cpp::optional<char *> cwd = internal::strdup(pathbuf);
if (!cwd) {
libc_errno = ENOMEM;
return nullptr;
}
return *cwd;
- } else if (size == 0) {
+ }
+
+ if (size == 0) {
libc_errno = EINVAL;
return nullptr;
}
// TODO: When buf is not sufficient, evaluate the full cwd path using
// alternate approaches.
-
- if (!getcwd_syscall(buf, size))
+ ErrorOr<ssize_t> bytes_written = linux_syscalls::getcwd(buf, size);
+ if (!bytes_written) {
+ libc_errno = bytes_written.error();
return nullptr;
+ }
+
return buf;
}
>From 50f1fb245e518784639bfa6f238cf2e613b4c251 Mon Sep 17 00:00:00 2001
From: jtstogel <jtstogel at google.com>
Date: Mon, 15 Jun 2026 19:49:34 -0700
Subject: [PATCH 2/3] Add inline
---
libc/src/__support/OSUtil/linux/syscall_wrappers/getcwd.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/getcwd.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/getcwd.h
index df7cf70ae21ba..f0aa9cb3fbab8 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/getcwd.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/getcwd.h
@@ -26,7 +26,7 @@
namespace LIBC_NAMESPACE_DECL {
namespace linux_syscalls {
-ErrorOr<ssize_t> getcwd(char *buf, size_t size) {
+LIBC_INLINE ErrorOr<ssize_t> getcwd(char *buf, size_t size) {
ssize_t ret = syscall_impl<ssize_t>(SYS_getcwd, buf, size);
if (ret < 0) {
return Error(static_cast<int>(-ret));
>From 93c5a7610e1e5ecc121df2edf555263db421a253 Mon Sep 17 00:00:00 2001
From: jtstogel <jtstogel at google.com>
Date: Mon, 15 Jun 2026 23:02:01 -0700
Subject: [PATCH 3/3] Fix includes and deps
---
libc/src/unistd/linux/CMakeLists.txt | 6 +++---
libc/src/unistd/linux/getcwd.cpp | 2 --
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt
index bf99fd4ccb389..592349f1039fe 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -224,12 +224,12 @@ add_entrypoint_object(
DEPENDS
libc.hdr.types.size_t
libc.hdr.types.ssize_t
- libc.include.sys_syscall
- libc.include.unistd
+ libc.src.__support.common
libc.src.__support.CPP.optional
+ libc.src.__support.macros.config
libc.src.__support.OSUtil.linux.syscall_wrappers.getcwd
- libc.src.__support.OSUtil.osutil
libc.src.errno.errno
+ libc.src.string.allocating_string_utils
)
add_entrypoint_object(
diff --git a/libc/src/unistd/linux/getcwd.cpp b/libc/src/unistd/linux/getcwd.cpp
index 6ac166a5c0611..4a4461ff6ca9a 100644
--- a/libc/src/unistd/linux/getcwd.cpp
+++ b/libc/src/unistd/linux/getcwd.cpp
@@ -12,14 +12,12 @@
#include "hdr/types/ssize_t.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/getcwd.h"
-#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/string/allocating_string_utils.h" // For strdup.
#include <linux/limits.h> // This is safe to include without any name pollution.
-#include <sys/syscall.h> // For syscall numbers.
namespace LIBC_NAMESPACE_DECL {
More information about the libc-commits
mailing list