diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -101,11 +101,15 @@ 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 --- 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 --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -263,6 +263,16 @@ ], [], // Enumerations [ + FunctionSpec< + "chdir", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "fchdir", + RetValSpec, + [ArgSpec] + >, FunctionSpec< "close", RetValSpec, @@ -393,6 +403,16 @@ [ModeTType], // Types [], // Enumerations [ + FunctionSpec< + "chmod", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "fchmod", + RetValSpec, + [ArgSpec] + >, FunctionSpec< "mkdir", RetValSpec, diff --git a/libc/src/sys/stat/CMakeLists.txt b/libc/src/sys/stat/CMakeLists.txt --- a/libc/src/sys/stat/CMakeLists.txt +++ b/libc/src/sys/stat/CMakeLists.txt @@ -2,6 +2,20 @@ 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 --- /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 + +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 --- /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 + +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 --- 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 --- /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 +#include +#include +#include // 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 --- /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 +#include +#include +#include // 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 --- a/libc/src/unistd/CMakeLists.txt +++ b/libc/src/unistd/CMakeLists.txt @@ -2,6 +2,13 @@ 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 @@ .${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 --- /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 --- /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 --- 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 @@ 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 --- /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 +#include // 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 --- /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 +#include // 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 --- a/libc/test/src/sys/stat/CMakeLists.txt +++ b/libc/test/src/sys/stat/CMakeLists.txt @@ -2,6 +2,38 @@ 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 --- /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 +#include +#include + +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 --- /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 +#include +#include + +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 --- 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 --- a/libc/test/src/unistd/CMakeLists.txt +++ b/libc/test/src/unistd/CMakeLists.txt @@ -2,6 +2,36 @@ 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 --- /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 +#include + +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 --- /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 +#include + +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 --- 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")