[libc-commits] [libc] b4a444a - [libc][realpath] Support relative paths (#209374)
via libc-commits
libc-commits at lists.llvm.org
Fri Jul 17 14:51:09 PDT 2026
Author: Jackson Stogel
Date: 2026-07-17T14:51:04-07:00
New Revision: b4a444ad635af70fe32f58d46fe0c08221fbcf5a
URL: https://github.com/llvm/llvm-project/commit/b4a444ad635af70fe32f58d46fe0c08221fbcf5a
DIFF: https://github.com/llvm/llvm-project/commit/b4a444ad635af70fe32f58d46fe0c08221fbcf5a.diff
LOG: [libc][realpath] Support relative paths (#209374)
Updates realpath to call the `getcwd` linux syscall wrapper to resolve
cwd-relative queries.
Added:
Modified:
libc/src/stdlib/linux/CMakeLists.txt
libc/src/stdlib/linux/realpath.cpp
libc/test/src/stdlib/CMakeLists.txt
libc/test/src/stdlib/realpath_test.cpp
Removed:
################################################################################
diff --git a/libc/src/stdlib/linux/CMakeLists.txt b/libc/src/stdlib/linux/CMakeLists.txt
index 9189c985d9a27..5c91de4135c84 100644
--- a/libc/src/stdlib/linux/CMakeLists.txt
+++ b/libc/src/stdlib/linux/CMakeLists.txt
@@ -26,6 +26,7 @@ add_entrypoint_object(
libc.src.__support.error_or
libc.src.__support.libc_errno
libc.src.__support.macros.config
+ libc.src.__support.OSUtil.linux.syscall_wrappers.getcwd
libc.src.__support.OSUtil.path
libc.src.string.memory_utils.inline_memcpy
)
diff --git a/libc/src/stdlib/linux/realpath.cpp b/libc/src/stdlib/linux/realpath.cpp
index 63e9676ad3cae..b91033f9b9f60 100644
--- a/libc/src/stdlib/linux/realpath.cpp
+++ b/libc/src/stdlib/linux/realpath.cpp
@@ -18,6 +18,7 @@
#include "src/__support/CPP/optional.h"
#include "src/__support/CPP/string.h"
#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/getcwd.h"
#include "src/__support/OSUtil/path.h"
#include "src/__support/common.h"
#include "src/__support/error_or.h"
@@ -41,7 +42,21 @@ class ResolvedPath {
void set_to_root() { path_ = path::SEPARATOR; }
- cpp::optional<Error> set_to_cwd() { return Error(ENOSYS); }
+ cpp::optional<Error> set_to_cwd() {
+ char buf[PATH_MAX];
+ ErrorOr<ssize_t> ret = linux_syscalls::getcwd(buf, PATH_MAX);
+ if (!ret) {
+ if (ret.error() == ERANGE)
+ return Error(ENAMETOOLONG);
+ return Error(ret.error());
+ }
+
+ if (*ret <= 0)
+ return Error(EIO);
+
+ path_ = cpp::string_view(buf, *ret - 1);
+ return cpp::nullopt;
+ }
// Removes the trailing path component.
void set_to_parent() {
diff --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt
index 55f63d82bea46..761aa4f792e29 100644
--- a/libc/test/src/stdlib/CMakeLists.txt
+++ b/libc/test/src/stdlib/CMakeLists.txt
@@ -401,7 +401,9 @@ add_libc_test(
libc.src.string.strdup
libc.src.sys.random.getrandom
libc.src.sys.stat.mkdirat
+ libc.src.unistd.chdir
libc.src.unistd.close
+ libc.src.unistd.getcwd
libc.src.unistd.getpid
libc.src.unistd.unlinkat
libc.test.UnitTest.ErrnoCheckingTest
diff --git a/libc/test/src/stdlib/realpath_test.cpp b/libc/test/src/stdlib/realpath_test.cpp
index d95126ccef099..5e4a8931acc04 100644
--- a/libc/test/src/stdlib/realpath_test.cpp
+++ b/libc/test/src/stdlib/realpath_test.cpp
@@ -28,7 +28,9 @@
#include "src/string/strdup.h"
#include "src/sys/random/getrandom.h"
#include "src/sys/stat/mkdirat.h"
+#include "src/unistd/chdir.h"
#include "src/unistd/close.h"
+#include "src/unistd/getcwd.h"
#include "src/unistd/getpid.h"
#include "src/unistd/unlinkat.h"
#include "test/UnitTest/ErrnoCheckingTest.h"
@@ -37,6 +39,7 @@
namespace cpp = LIBC_NAMESPACE::cpp;
namespace path = LIBC_NAMESPACE::path;
using LIBC_NAMESPACE::FixedVector;
+using LIBC_NAMESPACE::testing::ErrnoCheckingTest;
using LIBC_NAMESPACE::testing::tlog;
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
@@ -170,10 +173,23 @@ cpp::string unique_id() {
return id;
}
-class LlvmLibcRealpathTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {
+class LlvmLibcRealpathTest : public ErrnoCheckingTest {
public:
+ void SetUp() override {
+ ErrnoCheckingTest::SetUp();
+
+ (void)LIBC_NAMESPACE::getcwd(start_dir, sizeof(start_dir));
+ ASSERT_ERRNO_SUCCESS();
+ }
+
+ void TearDown() override {
+ ASSERT_THAT(LIBC_NAMESPACE::chdir(start_dir), Succeeds());
+
+ ErrnoCheckingTest::TearDown();
+ }
+
char *realpath_buffered(const char *path) {
- return LIBC_NAMESPACE::realpath(path, buf_);
+ return LIBC_NAMESPACE::realpath(path, realpath_buf);
}
char *realpath_buffered(const cpp::string &path) {
@@ -206,7 +222,11 @@ class LlvmLibcRealpathTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {
}
private:
- char buf_[PATH_MAX];
+ // Buffer for storing realpath output.
+ char realpath_buf[PATH_MAX];
+
+ // Current working dir at test SetUp.
+ char start_dir[PATH_MAX];
};
TEST_F(LlvmLibcRealpathTest, ErrorsWithInvalidArgIfNullPath) {
@@ -352,3 +372,64 @@ TEST_F(LlvmLibcRealpathTest, AllocatesResultWhenBufferIsNull) {
ASSERT_STREQ(result, "/");
::free(result);
}
+
+TEST_F(LlvmLibcRealpathTest, RelativePathResolvesToCurrentWorkingDir) {
+ TestDir test_dir;
+ ASSERT_TRUE(
+ create_test_dir("RelativePathResolvesToCurrentWorkingDir", test_dir));
+
+ ASSERT_THAT(LIBC_NAMESPACE::chdir(test_dir.c_str()), Succeeds());
+ ASSERT_STREQ(realpath_buffered("."), test_dir.c_str());
+
+ ASSERT_THAT(test_dir.mkdir("a"), Succeeds());
+ ASSERT_STREQ(realpath_buffered("a"), test_dir.absolute_path("a").c_str());
+}
+
+// Creates a directory with the desired_size and then chdir's into it.
+// Returns true on success.
+[[nodiscard]] bool chdir_to_absolute_path_with_size(TestDir &test_dir,
+ size_t desired_size,
+ cpp::string &out) {
+ if (!create_absolute_path_with_size(test_dir, desired_size, out))
+ return false;
+
+ // Convert the directory to be relative to test_dir.
+ const char *test_dir_relative_path =
+ out.c_str() + test_dir.view().size() + PATH_SEP_SIZE;
+
+ // Change directories into the path iteratively.
+ // This allows us to chdir into paths longer than PATH_MAX, as long as each
+ // of test_dir and test_dir_relative_path are shorter than PATH_MAX.
+ if (LIBC_NAMESPACE::chdir(test_dir.c_str()))
+ return false;
+ if (LIBC_NAMESPACE::chdir(test_dir_relative_path))
+ return false;
+ return true;
+}
+
+TEST_F(LlvmLibcRealpathTest, RelativeRealpathAcceptsPathExactlyMaxSize) {
+ TestDir test_dir;
+ ASSERT_TRUE(
+ create_test_dir("RelativeRealpathAcceptsPathExactlyMaxSize", test_dir));
+
+ cpp::string path;
+ ASSERT_TRUE(chdir_to_absolute_path_with_size(test_dir, PATH_MAX - 1, path));
+
+ ASSERT_STREQ(realpath_buffered("."), path.c_str());
+}
+
+TEST_F(LlvmLibcRealpathTest, RelativeRealpathRejectsPathExceedingMaxSize) {
+ TestDir test_dir;
+ ASSERT_TRUE(
+ create_test_dir("RelativeRealpathRejectsPathExceedingMaxSize", test_dir));
+
+ cpp::string path;
+ if (!chdir_to_absolute_path_with_size(test_dir, PATH_MAX, path)) {
+ // Skip the test if the system didn't allow creating the path.
+ ASSERT_ERRNO_EQ(ENAMETOOLONG);
+ return;
+ }
+
+ ASSERT_EQ(realpath_buffered("."), nullptr);
+ ASSERT_ERRNO_EQ(ENAMETOOLONG);
+}
More information about the libc-commits
mailing list