[libc-commits] [libc] 8b0e84a - [libc] Add implementation of the POSIX getcwd function.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Fri Oct 14 09:21:10 PDT 2022


Author: Siva Chandra Reddy
Date: 2022-10-14T16:20:59Z
New Revision: 8b0e84a6f6be46b3b9002df161d398e403d184f6

URL: https://github.com/llvm/llvm-project/commit/8b0e84a6f6be46b3b9002df161d398e403d184f6
DIFF: https://github.com/llvm/llvm-project/commit/8b0e84a6f6be46b3b9002df161d398e403d184f6.diff

LOG: [libc] Add implementation of the POSIX getcwd function.

Reviewed By: michaelrj

Differential Revision: https://reviews.llvm.org/D135905

Added: 
    libc/src/unistd/getcwd.h
    libc/src/unistd/linux/getcwd.cpp
    libc/test/integration/src/unistd/getcwd_test.cpp

Modified: 
    libc/config/linux/x86_64/entrypoints.txt
    libc/spec/posix.td
    libc/src/string/CMakeLists.txt
    libc/src/string/strdup.cpp
    libc/src/string/string_utils.h
    libc/src/unistd/CMakeLists.txt
    libc/src/unistd/linux/CMakeLists.txt
    libc/test/integration/src/unistd/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 3a7b142a70f0..6198b353eaa4 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -152,6 +152,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.unistd.fchdir
     libc.src.unistd.fsync
     libc.src.unistd.ftruncate
+    libc.src.unistd.getcwd
     libc.src.unistd.geteuid
     libc.src.unistd.getpid
     libc.src.unistd.getppid

diff  --git a/libc/spec/posix.td b/libc/spec/posix.td
index 53906d0d060e..29f8ed9801db 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -359,6 +359,11 @@ def POSIX : StandardSpec<"POSIX"> {
           RetValSpec<IntType>,
           [ArgSpec<IntType>]
         >,
+        FunctionSpec<
+          "getcwd",
+          RetValSpec<CharPtr>,
+          [ArgSpec<CharPtr>, ArgSpec<SizeTType>]
+        >,
         FunctionSpec<
           "close",
           RetValSpec<IntType>,

diff  --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt
index 3525e8724049..ab579afa4cfd 100644
--- a/libc/src/string/CMakeLists.txt
+++ b/libc/src/string/CMakeLists.txt
@@ -5,6 +5,7 @@ add_header_library(
   HDRS
     string_utils.h
   DEPENDS
+    libc.include.stdlib
     libc.src.__support.CPP.bitset
     .memory_utils.memcpy_implementation
     .memory_utils.bzero_implementation

diff  --git a/libc/src/string/strdup.cpp b/libc/src/string/strdup.cpp
index ee324b073663..d7adc85fc223 100644
--- a/libc/src/string/strdup.cpp
+++ b/libc/src/string/strdup.cpp
@@ -17,16 +17,7 @@
 namespace __llvm_libc {
 
 LLVM_LIBC_FUNCTION(char *, strdup, (const char *src)) {
-  if (src == nullptr) {
-    return nullptr;
-  }
-  size_t len = internal::string_length(src) + 1;
-  char *dest = reinterpret_cast<char *>(::malloc(len));
-  if (dest == nullptr) {
-    return nullptr;
-  }
-  inline_memcpy(dest, src, len);
-  return dest;
+  return internal::strdup(src);
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/string/string_utils.h b/libc/src/string/string_utils.h
index b1b434dbf172..da4ad8e4a270 100644
--- a/libc/src/string/string_utils.h
+++ b/libc/src/string/string_utils.h
@@ -11,9 +11,10 @@
 
 #include "src/__support/CPP/bitset.h"
 #include "src/__support/common.h"
-#include "src/string/memory_utils/memcpy_implementations.h"
 #include "src/string/memory_utils/bzero_implementations.h"
-#include <stddef.h> // size_t
+#include "src/string/memory_utils/memcpy_implementations.h"
+#include <stddef.h> // For size_t
+#include <stdlib.h> // For malloc and free
 
 namespace __llvm_libc {
 namespace internal {
@@ -98,6 +99,17 @@ static inline size_t strlcpy(char *__restrict dst, const char *__restrict src,
   return len;
 }
 
+inline char *strdup(const char *src) {
+  if (src == nullptr)
+    return nullptr;
+  size_t len = string_length(src) + 1;
+  char *newstr = reinterpret_cast<char *>(::malloc(len));
+  if (newstr == nullptr)
+    return nullptr;
+  inline_memcpy(newstr, src, len);
+  return newstr;
+}
+
 } // namespace internal
 } // namespace __llvm_libc
 

diff  --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt
index 3d4fa3f13b38..cd4002e35d7a 100644
--- a/libc/src/unistd/CMakeLists.txt
+++ b/libc/src/unistd/CMakeLists.txt
@@ -86,6 +86,13 @@ add_entrypoint_object(
     .${LIBC_TARGET_OS}.ftruncate
 )
 
+add_entrypoint_object(
+  getcwd
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.getcwd
+)
+
 add_entrypoint_object(
   getpid
   ALIAS

diff  --git a/libc/src/unistd/getcwd.h b/libc/src/unistd/getcwd.h
new file mode 100644
index 000000000000..8a6947f09a12
--- /dev/null
+++ b/libc/src/unistd/getcwd.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for getcwd ------------------------*- 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_UNISTD_GETCWD_H
+#define LLVM_LIBC_SRC_UNISTD_GETCWD_H
+
+#include <unistd.h>
+
+namespace __llvm_libc {
+
+char *getcwd(char *buf, size_t size);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_UNISTD_GETCWD_H

diff  --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt
index af4585457e09..47382d2490c1 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -159,6 +159,21 @@ add_entrypoint_object(
     libc.src.errno.errno
 )
 
+add_entrypoint_object(
+  getcwd
+  SRCS
+    getcwd.cpp
+  HDRS
+    ../getcwd.h
+  DEPENDS
+    libc.include.errno
+    libc.include.stdlib
+    libc.include.unistd
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.errno
+)
+
 add_entrypoint_object(
   geteuid
   SRCS

diff  --git a/libc/src/unistd/linux/getcwd.cpp b/libc/src/unistd/linux/getcwd.cpp
new file mode 100644
index 000000000000..7475f461d8b9
--- /dev/null
+++ b/libc/src/unistd/linux/getcwd.cpp
@@ -0,0 +1,66 @@
+//===-- Linux implementation of getcwd ------------------------------------===//
+//
+// 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/unistd/getcwd.h"
+
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+#include "src/string/string_utils.h" // For strdup.
+
+#include <errno.h>
+#include <linux/limits.h> // This is safe to include without any name pollution.
+#include <stdlib.h>
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+namespace {
+
+bool getcwd_syscall(char *buf, size_t size) {
+  int ret = __llvm_libc::syscall_impl(SYS_getcwd, buf, size);
+  if (ret < 0) {
+    errno = -ret;
+    return false;
+  } else if (ret == 0 || buf[0] != '/') {
+    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.
+    // We will allocate a static buffer of size PATH_MAX first and fetch the cwd
+    // into it. This way, if the syscall fails, we avoid unnecessary malloc
+    // and free.
+    char pathbuf[PATH_MAX];
+    if (!getcwd_syscall(pathbuf, PATH_MAX))
+      return nullptr;
+    char *cwd = internal::strdup(pathbuf);
+    if (cwd == nullptr) {
+      errno = ENOMEM;
+      return nullptr;
+    }
+    return cwd;
+  } else if (size == 0) {
+    errno = EINVAL;
+    return nullptr;
+  }
+
+  // TODO: When buf is not sufficient, evaluate the full cwd path using
+  // alternate approaches.
+
+  if (!getcwd_syscall(buf, size))
+    return nullptr;
+  return buf;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/test/integration/src/unistd/CMakeLists.txt b/libc/test/integration/src/unistd/CMakeLists.txt
index 509666cdf0ee..7631f5bd4a3a 100644
--- a/libc/test/integration/src/unistd/CMakeLists.txt
+++ b/libc/test/integration/src/unistd/CMakeLists.txt
@@ -1,6 +1,21 @@
 add_custom_target(unistd-integration-tests)
 add_dependencies(libc-integration-tests unistd-integration-tests)
 
+add_integration_test(
+  getcwd_test
+  SUITE
+    unistd-integration-tests
+  SRCS
+    getcwd_test.cpp
+  LOADER
+    libc.loader.linux.crt1
+  DEPENDS
+    libc.include.errno
+    libc.src.__support.CPP.string_view
+    libc.src.stdlib.getenv
+    libc.src.unistd.getcwd
+)
+
 add_integration_test(
   fork_test
   SUITE

diff  --git a/libc/test/integration/src/unistd/getcwd_test.cpp b/libc/test/integration/src/unistd/getcwd_test.cpp
new file mode 100644
index 000000000000..80b45efb97fb
--- /dev/null
+++ b/libc/test/integration/src/unistd/getcwd_test.cpp
@@ -0,0 +1,42 @@
+//===-- Unittests for getcwd ----------------------------------------------===//
+//
+// 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/__support/CPP/string_view.h"
+#include "src/stdlib/getenv.h"
+#include "src/unistd/getcwd.h"
+
+#include "utils/IntegrationTest/test.h"
+
+#include <errno.h>
+
+using __llvm_libc::cpp::string_view;
+
+TEST_MAIN(int argc, char **argv, char **envp) {
+  char buffer[1024];
+  ASSERT_TRUE(string_view(__llvm_libc::getenv("PWD")) ==
+              __llvm_libc::getcwd(buffer, 1024));
+
+  // nullptr buffer
+  char *cwd = __llvm_libc::getcwd(nullptr, 0);
+  ASSERT_TRUE(string_view(__llvm_libc::getenv("PWD")) == cwd);
+  free(cwd);
+
+  // Bad size
+  cwd = __llvm_libc::getcwd(buffer, 0);
+  ASSERT_TRUE(cwd == nullptr);
+  ASSERT_EQ(errno, EINVAL);
+  errno = 0;
+
+  // Insufficient size
+  cwd = __llvm_libc::getcwd(buffer, 2);
+  ASSERT_TRUE(cwd == nullptr);
+  int err = errno;
+  ASSERT_EQ(err, ERANGE);
+
+  return 0;
+}


        


More information about the libc-commits mailing list