[libc-commits] [libc] 0553228 - [libc] Add Linux implementations of POSIX chdir, fchdir, chmod and fchmod.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Tue Aug 23 10:17:18 PDT 2022


Author: Siva Chandra Reddy
Date: 2022-08-23T17:16:49Z
New Revision: 055322891ce9df72116da23529f999658b258450

URL: https://github.com/llvm/llvm-project/commit/055322891ce9df72116da23529f999658b258450
DIFF: https://github.com/llvm/llvm-project/commit/055322891ce9df72116da23529f999658b258450.diff

LOG: [libc] Add Linux implementations of POSIX chdir, fchdir, chmod and fchmod.

Reviewed By: michaelrj

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

Added: 
    libc/src/sys/stat/chmod.h
    libc/src/sys/stat/fchmod.h
    libc/src/sys/stat/linux/chmod.cpp
    libc/src/sys/stat/linux/fchmod.cpp
    libc/src/unistd/chdir.h
    libc/src/unistd/fchdir.h
    libc/src/unistd/linux/chdir.cpp
    libc/src/unistd/linux/fchdir.cpp
    libc/test/src/sys/stat/chmod_test.cpp
    libc/test/src/sys/stat/fchmod_test.cpp
    libc/test/src/unistd/chdir_test.cpp
    libc/test/src/unistd/fchdir_test.cpp

Modified: 
    libc/config/linux/x86_64/entrypoints.txt
    libc/include/llvm-libc-macros/linux/fcntl-macros.h
    libc/spec/posix.td
    libc/src/sys/stat/CMakeLists.txt
    libc/src/sys/stat/linux/CMakeLists.txt
    libc/src/unistd/CMakeLists.txt
    libc/src/unistd/linux/CMakeLists.txt
    libc/test/src/sys/stat/CMakeLists.txt
    libc/test/src/sys/stat/testdata/CMakeLists.txt
    libc/test/src/unistd/CMakeLists.txt
    libc/test/src/unistd/testdata/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index c8d3c8370e98a..4429f38b21342 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -101,11 +101,15 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.sys.mman.munmap
 
     # sys/stat.h entrypoints
+    libc.src.sys.stat.chmod
+    libc.src.sys.stat.fchmod
     libc.src.sys.stat.mkdir
     libc.src.sys.stat.mkdirat
 
     # unistd.h entrypoints
+    libc.src.unistd.chdir
     libc.src.unistd.close
+    libc.src.unistd.fchdir
     libc.src.unistd.fsync
     libc.src.unistd.lseek
     libc.src.unistd.read

diff  --git a/libc/include/llvm-libc-macros/linux/fcntl-macros.h b/libc/include/llvm-libc-macros/linux/fcntl-macros.h
index 48cb5f3ac450a..6f30330f3a457 100644
--- a/libc/include/llvm-libc-macros/linux/fcntl-macros.h
+++ b/libc/include/llvm-libc-macros/linux/fcntl-macros.h
@@ -12,6 +12,7 @@
 // File creation flags
 #define O_CLOEXEC 02000000
 #define O_CREAT 00000100
+#define O_PATH 010000000
 
 #ifdef __aarch64__
 #define O_DIRECTORY 040000

diff  --git a/libc/spec/posix.td b/libc/spec/posix.td
index fd5c0a9853597..0acbb63f2c678 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -263,6 +263,16 @@ def POSIX : StandardSpec<"POSIX"> {
     ],
     [], // Enumerations
     [
+        FunctionSpec<
+          "chdir",
+          RetValSpec<IntType>,
+          [ArgSpec<ConstCharPtr>]
+        >,
+        FunctionSpec<
+          "fchdir",
+          RetValSpec<IntType>,
+          [ArgSpec<IntType>]
+        >,
         FunctionSpec<
           "close",
           RetValSpec<IntType>,
@@ -393,6 +403,16 @@ def POSIX : StandardSpec<"POSIX"> {
     [ModeTType], // Types
     [], // Enumerations
     [
+        FunctionSpec<
+          "chmod",
+          RetValSpec<IntType>,
+          [ArgSpec<ConstCharPtr>]
+        >,
+        FunctionSpec<
+          "fchmod",
+          RetValSpec<IntType>,
+          [ArgSpec<IntType>]
+        >,
         FunctionSpec<
             "mkdir",
             RetValSpec<IntType>,

diff  --git a/libc/src/sys/stat/CMakeLists.txt b/libc/src/sys/stat/CMakeLists.txt
index 44bc48a8af5d6..b6ca26de9d350 100644
--- a/libc/src/sys/stat/CMakeLists.txt
+++ b/libc/src/sys/stat/CMakeLists.txt
@@ -2,6 +2,20 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
   add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
 endif()
 
+add_entrypoint_object(
+  chmod
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.chmod
+)
+
+add_entrypoint_object(
+  fchmod
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.fchmod
+)
+
 add_entrypoint_object(
   mkdir
   ALIAS

diff  --git a/libc/src/sys/stat/chmod.h b/libc/src/sys/stat/chmod.h
new file mode 100644
index 0000000000000..bfeaa9dbd721e
--- /dev/null
+++ b/libc/src/sys/stat/chmod.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for chmod -------------------------*- 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_SYS_STAT_CHMOD_H
+#define LLVM_LIBC_SRC_SYS_STAT_CHMOD_H
+
+#include <sys/stat.h>
+
+namespace __llvm_libc {
+
+int chmod(const char *path, mode_t mode);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SYS_STAT_CHMOD_H

diff  --git a/libc/src/sys/stat/fchmod.h b/libc/src/sys/stat/fchmod.h
new file mode 100644
index 0000000000000..34c167067c08a
--- /dev/null
+++ b/libc/src/sys/stat/fchmod.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for fchmod ------------------------*- 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_SYS_STAT_FCHMOD_H
+#define LLVM_LIBC_SRC_SYS_STAT_FCHMOD_H
+
+#include <sys/stat.h>
+
+namespace __llvm_libc {
+
+int fchmod(int fd, mode_t mode);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SYS_STAT_FCHMOD_H

diff  --git a/libc/src/sys/stat/linux/CMakeLists.txt b/libc/src/sys/stat/linux/CMakeLists.txt
index 88d50622e98e0..1c86a8782d09d 100644
--- a/libc/src/sys/stat/linux/CMakeLists.txt
+++ b/libc/src/sys/stat/linux/CMakeLists.txt
@@ -1,3 +1,30 @@
+add_entrypoint_object(
+  chmod
+  SRCS
+    chmod.cpp
+  HDRS
+    ../chmod.h
+  DEPENDS
+    libc.include.fcntl
+    libc.include.sys_stat
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  fchmod
+  SRCS
+    fchmod.cpp
+  HDRS
+    ../fchmod.h
+  DEPENDS
+    libc.include.sys_stat
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.errno
+)
+
 add_entrypoint_object(
   mkdir
   SRCS

diff  --git a/libc/src/sys/stat/linux/chmod.cpp b/libc/src/sys/stat/linux/chmod.cpp
new file mode 100644
index 0000000000000..6297e5cd051c1
--- /dev/null
+++ b/libc/src/sys/stat/linux/chmod.cpp
@@ -0,0 +1,37 @@
+//===-- Linux implementation of chmod -------------------------------------===//
+//
+// 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/sys/stat/chmod.h"
+
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, chmod, (const char *path, mode_t mode)) {
+#ifdef SYS_chmod
+  long ret = __llvm_libc::syscall(SYS_chmod, path, mode);
+#elif defined(SYS_chmodat)
+  long ret = __llvm_libc::syscall(SYS_chmodat, AT_FDCWD, path, mode);
+#else
+#error "chmod and chmodat syscalls not available."
+#endif
+
+  if (ret < 0) {
+    errno = -ret;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/sys/stat/linux/fchmod.cpp b/libc/src/sys/stat/linux/fchmod.cpp
new file mode 100644
index 0000000000000..5edf3a51aa2d7
--- /dev/null
+++ b/libc/src/sys/stat/linux/fchmod.cpp
@@ -0,0 +1,30 @@
+//===-- Linux implementation of fchmod ------------------------------------===//
+//
+// 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/sys/stat/fchmod.h"
+
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, fchmod, (int fd, mode_t mode)) {
+  long ret = __llvm_libc::syscall(SYS_fchmod, fd, mode);
+  if (ret < 0) {
+    errno = -ret;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt
index d67b0b33d179a..306fdd4a37dfc 100644
--- a/libc/src/unistd/CMakeLists.txt
+++ b/libc/src/unistd/CMakeLists.txt
@@ -2,6 +2,13 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
   add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
 endif()
 
+add_entrypoint_object(
+  chdir
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.chdir
+)
+
 add_entrypoint_object(
   close
   ALIAS
@@ -9,6 +16,13 @@ add_entrypoint_object(
     .${LIBC_TARGET_OS}.close
 )
 
+add_entrypoint_object(
+  fchdir
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.fchdir
+)
+
 add_entrypoint_object(
   fsync
   ALIAS

diff  --git a/libc/src/unistd/chdir.h b/libc/src/unistd/chdir.h
new file mode 100644
index 0000000000000..228b78fc79924
--- /dev/null
+++ b/libc/src/unistd/chdir.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for chdir -------------------------*- 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_CHDIR_H
+#define LLVM_LIBC_SRC_UNISTD_CHDIR_H
+
+namespace __llvm_libc {
+
+int chdir(const char *path);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_UNISTD_CHDIR_H

diff  --git a/libc/src/unistd/fchdir.h b/libc/src/unistd/fchdir.h
new file mode 100644
index 0000000000000..3d402e62f4066
--- /dev/null
+++ b/libc/src/unistd/fchdir.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for fchdir ------------------------*- 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_FCHDIR_H
+#define LLVM_LIBC_SRC_UNISTD_FCHDIR_H
+
+namespace __llvm_libc {
+
+int fchdir(int fd);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_UNISTD_FCHDIR_H

diff  --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt
index 29a2cd629a4bd..3889f6171c1c2 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -1,3 +1,16 @@
+add_entrypoint_object(
+  chdir
+  SRCS
+    chdir.cpp
+  HDRS
+    ../chdir.h
+  DEPENDS
+    libc.include.unistd
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.errno
+)
+
 add_entrypoint_object(
   close
   SRCS
@@ -11,6 +24,19 @@ add_entrypoint_object(
     libc.src.errno.errno
 )
 
+add_entrypoint_object(
+  fchdir
+  SRCS
+    fchdir.cpp
+  HDRS
+    ../fchdir.h
+  DEPENDS
+    libc.include.unistd
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.errno
+)
+
 add_entrypoint_object(
   fsync
   SRCS

diff  --git a/libc/src/unistd/linux/chdir.cpp b/libc/src/unistd/linux/chdir.cpp
new file mode 100644
index 0000000000000..93ae33861c98a
--- /dev/null
+++ b/libc/src/unistd/linux/chdir.cpp
@@ -0,0 +1,28 @@
+//===-- Linux implementation of chdir -------------------------------------===//
+//
+// 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/chdir.h"
+
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+
+#include <errno.h>
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, chdir, (const char *path)) {
+  long ret = __llvm_libc::syscall(SYS_chdir, path);
+  if (ret < 0) {
+    errno = -ret;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/unistd/linux/fchdir.cpp b/libc/src/unistd/linux/fchdir.cpp
new file mode 100644
index 0000000000000..86af84d4ebfd6
--- /dev/null
+++ b/libc/src/unistd/linux/fchdir.cpp
@@ -0,0 +1,28 @@
+//===-- Linux implementation of fchdir ------------------------------------===//
+//
+// 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/fchdir.h"
+
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+
+#include <errno.h>
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, fchdir, (int fd)) {
+  long ret = __llvm_libc::syscall(SYS_fchdir, fd);
+  if (ret < 0) {
+    errno = -ret;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/test/src/sys/stat/CMakeLists.txt b/libc/test/src/sys/stat/CMakeLists.txt
index eec671a77af4a..c67ba395ee783 100644
--- a/libc/test/src/sys/stat/CMakeLists.txt
+++ b/libc/test/src/sys/stat/CMakeLists.txt
@@ -2,6 +2,38 @@ add_libc_testsuite(libc_sys_stat_unittests)
 
 add_subdirectory(testdata)
 
+add_libc_unittest(
+  chmod_test
+  SUITE
+    libc_sys_stat_unittests
+  SRCS
+    chmod_test.cpp
+  DEPENDS
+    libc.include.errno
+    libc.include.fcntl
+    libc.include.sys_stat
+    libc.src.fcntl.open
+    libc.src.sys.stat.chmod
+    libc.src.unistd.close
+    libc.src.unistd.write
+)
+
+add_libc_unittest(
+  fchmod_test
+  SUITE
+    libc_sys_stat_unittests
+  SRCS
+    fchmod_test.cpp
+  DEPENDS
+    libc.include.errno
+    libc.include.fcntl
+    libc.include.sys_stat
+    libc.src.fcntl.open
+    libc.src.sys.stat.fchmod
+    libc.src.unistd.close
+    libc.src.unistd.write
+)
+
 add_libc_unittest(
   mkdirat_test
   SUITE

diff  --git a/libc/test/src/sys/stat/chmod_test.cpp b/libc/test/src/sys/stat/chmod_test.cpp
new file mode 100644
index 0000000000000..e6841bbec81e9
--- /dev/null
+++ b/libc/test/src/sys/stat/chmod_test.cpp
@@ -0,0 +1,64 @@
+//===-- Unittests for chmod -----------------------------------------------===//
+//
+// 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/fcntl/open.h"
+#include "src/sys/stat/chmod.h"
+#include "src/unistd/close.h"
+#include "src/unistd/write.h"
+#include "test/ErrnoSetterMatcher.h"
+#include "utils/UnitTest/Test.h"
+#include "utils/testutils/FDReader.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+TEST(LlvmLibcChmodTest, ChangeAndOpen) {
+  using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+  using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+
+  // The test file is initially writable. We open it for writing and ensure
+  // that it indeed can be opened for writing. Next, we close the file and
+  // make it readonly using chmod. We test that chmod actually succeeded by
+  // trying to open the file for writing and failing.
+  constexpr const char *TEST_FILE = "testdata/chmod.test";
+  const char WRITE_DATA[] = "test data";
+  constexpr ssize_t WRITE_SIZE = ssize_t(sizeof(WRITE_DATA));
+  errno = 0;
+
+  int fd = __llvm_libc::open(TEST_FILE, O_APPEND | O_WRONLY);
+  ASSERT_GT(fd, 0);
+  ASSERT_EQ(errno, 0);
+  ASSERT_EQ(__llvm_libc::write(fd, WRITE_DATA, sizeof(WRITE_DATA)), WRITE_SIZE);
+  ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0));
+
+  fd = __llvm_libc::open(TEST_FILE, O_PATH);
+  ASSERT_GT(fd, 0);
+  ASSERT_EQ(errno, 0);
+  ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0));
+  EXPECT_THAT(__llvm_libc::chmod(TEST_FILE, S_IRUSR), Succeeds(0));
+
+  // Opening for writing should fail.
+  EXPECT_EQ(__llvm_libc::open(TEST_FILE, O_APPEND | O_WRONLY), -1);
+  EXPECT_NE(errno, 0);
+  errno = 0;
+  // But opening for reading should succeed.
+  fd = __llvm_libc::open(TEST_FILE, O_APPEND | O_RDONLY);
+  EXPECT_GT(fd, 0);
+  EXPECT_EQ(errno, 0);
+
+  EXPECT_THAT(__llvm_libc::close(fd), Succeeds(0));
+  EXPECT_THAT(__llvm_libc::chmod(TEST_FILE, S_IRWXU), Succeeds(0));
+}
+
+TEST(LlvmLibcChmodTest, NonExistentFile) {
+  errno = 0;
+  using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+  ASSERT_THAT(__llvm_libc::chmod("non-existent-file", S_IRUSR), Fails(ENOENT));
+  errno = 0;
+}

diff  --git a/libc/test/src/sys/stat/fchmod_test.cpp b/libc/test/src/sys/stat/fchmod_test.cpp
new file mode 100644
index 0000000000000..74b528a7ea86e
--- /dev/null
+++ b/libc/test/src/sys/stat/fchmod_test.cpp
@@ -0,0 +1,64 @@
+//===-- Unittests for fchmod ----------------------------------------------===//
+//
+// 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/fcntl/open.h"
+#include "src/sys/stat/fchmod.h"
+#include "src/unistd/close.h"
+#include "src/unistd/write.h"
+#include "test/ErrnoSetterMatcher.h"
+#include "utils/UnitTest/Test.h"
+#include "utils/testutils/FDReader.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+TEST(LlvmLibcChmodTest, ChangeAndOpen) {
+  using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+  using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+
+  // The test file is initially writable. We open it for writing and ensure
+  // that it indeed can be opened for writing. Next, we close the file and
+  // make it readonly using chmod. We test that chmod actually succeeded by
+  // trying to open the file for writing and failing.
+  constexpr const char *TEST_FILE = "testdata/fchmod.test";
+  const char WRITE_DATA[] = "test data";
+  constexpr ssize_t WRITE_SIZE = ssize_t(sizeof(WRITE_DATA));
+  errno = 0;
+
+  int fd = __llvm_libc::open(TEST_FILE, O_APPEND | O_WRONLY);
+  ASSERT_GT(fd, 0);
+  ASSERT_EQ(errno, 0);
+  ASSERT_EQ(__llvm_libc::write(fd, WRITE_DATA, sizeof(WRITE_DATA)), WRITE_SIZE);
+  ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0));
+
+  fd = __llvm_libc::open(TEST_FILE, O_APPEND | O_WRONLY);
+  ASSERT_GT(fd, 0);
+  ASSERT_EQ(errno, 0);
+  EXPECT_THAT(__llvm_libc::fchmod(fd, S_IRUSR), Succeeds(0));
+  ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0));
+
+  // Opening for writing should fail.
+  EXPECT_EQ(__llvm_libc::open(TEST_FILE, O_APPEND | O_WRONLY), -1);
+  EXPECT_NE(errno, 0);
+  errno = 0;
+  // But opening for reading should succeed.
+  fd = __llvm_libc::open(TEST_FILE, O_APPEND | O_RDONLY);
+  EXPECT_GT(fd, 0);
+  EXPECT_EQ(errno, 0);
+
+  EXPECT_THAT(__llvm_libc::fchmod(fd, S_IRWXU), Succeeds(0));
+  EXPECT_THAT(__llvm_libc::close(fd), Succeeds(0));
+}
+
+TEST(LlvmLibcChmodTest, NonExistentFile) {
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::fchmod(-1, S_IRUSR), -1);
+  ASSERT_NE(errno, 0);
+  errno = 0;
+}

diff  --git a/libc/test/src/sys/stat/testdata/CMakeLists.txt b/libc/test/src/sys/stat/testdata/CMakeLists.txt
index 25d6247c1d975..5a5c5c6754896 100644
--- a/libc/test/src/sys/stat/testdata/CMakeLists.txt
+++ b/libc/test/src/sys/stat/testdata/CMakeLists.txt
@@ -1,2 +1,4 @@
-# This directory will be used to create test files and delete them
-# from tests.
+# This directory will be used to create test files.
+
+file(GENERATE OUTPUT chmod.test CONTENT "chmod test")
+file(GENERATE OUTPUT fchmod.test CONTENT "fchmod test")

diff  --git a/libc/test/src/unistd/CMakeLists.txt b/libc/test/src/unistd/CMakeLists.txt
index a60318a98fc9b..30f7b370eb9ad 100644
--- a/libc/test/src/unistd/CMakeLists.txt
+++ b/libc/test/src/unistd/CMakeLists.txt
@@ -2,6 +2,36 @@ add_libc_testsuite(libc_unistd_unittests)
 
 add_subdirectory(testdata)
 
+add_libc_unittest(
+  chdir_test
+  SUITE
+    libc_unistd_unittests
+  SRCS
+    chdir_test.cpp
+  DEPENDS
+    libc.include.errno
+    libc.include.unistd
+    libc.src.fcntl.open
+    libc.src.unistd.chdir
+    libc.src.unistd.close
+    libc.test.errno_setter_matcher
+)
+
+add_libc_unittest(
+  fchdir_test
+  SUITE
+    libc_unistd_unittests
+  SRCS
+    fchdir_test.cpp
+  DEPENDS
+    libc.include.errno
+    libc.include.unistd
+    libc.src.fcntl.open
+    libc.src.unistd.fchdir
+    libc.src.unistd.close
+    libc.test.errno_setter_matcher
+)
+
 add_libc_unittest(
   read_write_test
   SUITE

diff  --git a/libc/test/src/unistd/chdir_test.cpp b/libc/test/src/unistd/chdir_test.cpp
new file mode 100644
index 0000000000000..4e24444ea6637
--- /dev/null
+++ b/libc/test/src/unistd/chdir_test.cpp
@@ -0,0 +1,47 @@
+//===-- Unittests for chdir -----------------------------------------------===//
+//
+// 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/fcntl/open.h"
+#include "src/unistd/chdir.h"
+#include "src/unistd/close.h"
+#include "test/ErrnoSetterMatcher.h"
+#include "utils/UnitTest/Test.h"
+#include "utils/testutils/FDReader.h"
+
+#include <errno.h>
+#include <fcntl.h>
+
+TEST(LlvmLibcChdirTest, ChangeAndOpen) {
+  // The idea of this test is that we will first open an existing test file
+  // without changing the directory to make sure it exists. Next, we change
+  // directory and open the same file to make sure that the "chdir" operation
+  // succeeded.
+  using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+  constexpr const char *TEST_DIR = "testdata";
+  constexpr const char *TEST_FILE = "testdata/chdir.test";
+  constexpr const char *TEST_FILE_BASE = "chdir.test";
+  errno = 0;
+
+  int fd = __llvm_libc::open(TEST_FILE, O_PATH);
+  ASSERT_GT(fd, 0);
+  ASSERT_EQ(errno, 0);
+  ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0));
+
+  ASSERT_THAT(__llvm_libc::chdir(TEST_DIR), Succeeds(0));
+  fd = __llvm_libc::open(TEST_FILE_BASE, O_PATH);
+  ASSERT_GT(fd, 0);
+  ASSERT_EQ(errno, 0);
+  ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0));
+}
+
+TEST(LlvmLibcChdirTest, ChangeToNonExistentDir) {
+  errno = 0;
+  using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+  ASSERT_THAT(__llvm_libc::chdir("non-existent-dir"), Fails(ENOENT));
+  errno = 0;
+}

diff  --git a/libc/test/src/unistd/fchdir_test.cpp b/libc/test/src/unistd/fchdir_test.cpp
new file mode 100644
index 0000000000000..e44b6b0657642
--- /dev/null
+++ b/libc/test/src/unistd/fchdir_test.cpp
@@ -0,0 +1,52 @@
+//===-- Unittests for fchdir ----------------------------------------------===//
+//
+// 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/fcntl/open.h"
+#include "src/unistd/close.h"
+#include "src/unistd/fchdir.h"
+#include "test/ErrnoSetterMatcher.h"
+#include "utils/UnitTest/Test.h"
+#include "utils/testutils/FDReader.h"
+
+#include <errno.h>
+#include <fcntl.h>
+
+TEST(LlvmLibcChdirTest, ChangeAndOpen) {
+  // The idea of this test is that we will first open an existing test file
+  // without changing the directory to make sure it exists. Next, we change
+  // directory and open the same file to make sure that the "fchdir" operation
+  // succeeded.
+  using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+  constexpr const char *TEST_DIR = "testdata";
+  constexpr const char *TEST_FILE = "testdata/fchdir.test";
+  constexpr const char *TEST_FILE_BASE = "fchdir.test";
+  errno = 0;
+
+  int dir_fd = __llvm_libc::open(TEST_DIR, O_DIRECTORY);
+  ASSERT_GT(dir_fd, 0);
+  ASSERT_EQ(errno, 0);
+  int file_fd = __llvm_libc::open(TEST_FILE, O_PATH);
+  ASSERT_GT(file_fd, 0);
+  ASSERT_EQ(errno, 0);
+  ASSERT_THAT(__llvm_libc::close(file_fd), Succeeds(0));
+
+  ASSERT_THAT(__llvm_libc::fchdir(dir_fd), Succeeds(0));
+  file_fd = __llvm_libc::open(TEST_FILE_BASE, O_PATH);
+  ASSERT_GT(file_fd, 0);
+  ASSERT_EQ(errno, 0);
+  ASSERT_THAT(__llvm_libc::close(file_fd), Succeeds(0));
+  ASSERT_THAT(__llvm_libc::close(dir_fd), Succeeds(0));
+}
+
+TEST(LlvmLibcChdirTest, ChangeToNonExistentDir) {
+  using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::fchdir(0), -1);
+  ASSERT_NE(errno, 0);
+  errno = 0;
+}

diff  --git a/libc/test/src/unistd/testdata/CMakeLists.txt b/libc/test/src/unistd/testdata/CMakeLists.txt
index f302be251143c..56d712d417028 100644
--- a/libc/test/src/unistd/testdata/CMakeLists.txt
+++ b/libc/test/src/unistd/testdata/CMakeLists.txt
@@ -1,4 +1,5 @@
-# This directory will be used to create test files and delete them
-# from tests.
+# This directory will be used to create test files.
 
 file(GENERATE OUTPUT lseek.test CONTENT "lseek test")
+file(GENERATE OUTPUT chdir.test CONTENT "chdir test")
+file(GENERATE OUTPUT fchdir.test CONTENT "fchdir test")


        


More information about the libc-commits mailing list