diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -112,9 +112,15 @@ libc.src.unistd.close libc.src.unistd.fchdir libc.src.unistd.fsync + libc.src.unistd.link + libc.src.unistd.linkat libc.src.unistd.lseek libc.src.unistd.read + libc.src.unistd.readlink + libc.src.unistd.readlinkat libc.src.unistd.rmdir + libc.src.unistd.symlink + libc.src.unistd.symlinkat libc.src.unistd.unlink libc.src.unistd.unlinkat libc.src.unistd.write 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 @@ -112,9 +112,15 @@ libc.src.unistd.close libc.src.unistd.fchdir libc.src.unistd.fsync + libc.src.unistd.link + libc.src.unistd.linkat libc.src.unistd.lseek libc.src.unistd.read + libc.src.unistd.readlink + libc.src.unistd.readlinkat libc.src.unistd.rmdir + libc.src.unistd.symlink + libc.src.unistd.symlinkat libc.src.unistd.unlink libc.src.unistd.unlinkat libc.src.unistd.write diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -283,6 +283,16 @@ RetValSpec, [ArgSpec] >, + FunctionSpec< + "link", + RetValSpec, + [ArgSpec, ArgSpec] + >, + FunctionSpec< + "linkat", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec, ArgSpec, ArgSpec] + >, FunctionSpec< "lseek", RetValSpec, @@ -293,11 +303,31 @@ RetValSpec, [ArgSpec, ArgSpec, ArgSpec] >, + FunctionSpec< + "readlink", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] + >, + FunctionSpec< + "readlinkat", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] + >, FunctionSpec< "rmdir", RetValSpec, [ArgSpec] >, + FunctionSpec< + "symlink", + RetValSpec, + [ArgSpec, ArgSpec] + >, + FunctionSpec< + "symlinkat", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec, ArgSpec] + >, FunctionSpec< "unlink", RetValSpec, 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 @@ -30,6 +30,20 @@ .${LIBC_TARGET_OS}.fsync ) +add_entrypoint_object( + link + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.link +) + +add_entrypoint_object( + linkat + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.linkat +) + add_entrypoint_object( lseek ALIAS @@ -44,6 +58,20 @@ .${LIBC_TARGET_OS}.read ) +add_entrypoint_object( + readlink + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.readlink +) + +add_entrypoint_object( + readlinkat + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.readlinkat +) + add_entrypoint_object( rmdir ALIAS @@ -51,6 +79,20 @@ .${LIBC_TARGET_OS}.rmdir ) +add_entrypoint_object( + symlink + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.symlink +) + +add_entrypoint_object( + symlinkat + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.symlinkat +) + add_entrypoint_object( unlink ALIAS diff --git a/libc/src/unistd/link.h b/libc/src/unistd/link.h new file mode 100644 --- /dev/null +++ b/libc/src/unistd/link.h @@ -0,0 +1,20 @@ +//===-- Implementation header for link --------------------------*- 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_LINK_H +#define LLVM_LIBC_SRC_UNISTD_LINK_H + +#include + +namespace __llvm_libc { + +int link(const char *, const char *); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_LINK_H diff --git a/libc/src/unistd/linkat.h b/libc/src/unistd/linkat.h new file mode 100644 --- /dev/null +++ b/libc/src/unistd/linkat.h @@ -0,0 +1,18 @@ +//===-- Implementation header for linkat ------------------------*- 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_LINKAT_H +#define LLVM_LIBC_SRC_UNISTD_LINKAT_H + +namespace __llvm_libc { + +int linkat(int, const char *, int, const char *, int); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_LINKAT_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 @@ -50,6 +50,34 @@ libc.src.errno.errno ) +add_entrypoint_object( + link + SRCS + link.cpp + HDRS + ../link.h + DEPENDS + libc.include.fcntl + libc.include.unistd + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + +add_entrypoint_object( + linkat + SRCS + linkat.cpp + HDRS + ../linkat.h + DEPENDS + libc.include.fcntl + libc.include.unistd + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + add_entrypoint_object( lseek SRCS @@ -90,6 +118,62 @@ libc.src.errno.errno ) +add_entrypoint_object( + readlink + SRCS + readlink.cpp + HDRS + ../readlink.h + DEPENDS + libc.include.fcntl + libc.include.unistd + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + +add_entrypoint_object( + readlinkat + SRCS + readlinkat.cpp + HDRS + ../readlinkat.h + DEPENDS + libc.include.fcntl + libc.include.unistd + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + +add_entrypoint_object( + symlink + SRCS + symlink.cpp + HDRS + ../symlink.h + DEPENDS + libc.include.fcntl + libc.include.unistd + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + +add_entrypoint_object( + symlinkat + SRCS + symlinkat.cpp + HDRS + ../symlinkat.h + DEPENDS + libc.include.fcntl + libc.include.unistd + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + add_entrypoint_object( unlink SRCS diff --git a/libc/src/unistd/linux/link.cpp b/libc/src/unistd/linux/link.cpp new file mode 100644 --- /dev/null +++ b/libc/src/unistd/linux/link.cpp @@ -0,0 +1,36 @@ +//===-- Linux implementation of link --------------------------------------===// +// +// 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/link.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, link, (const char *path1, const char *path2)) { +#ifdef SYS_link + long ret = __llvm_libc::syscall(SYS_link, path1, path2); +#elif SYS_linkat + long ret = + __llvm_libc::syscall(SYS_linkat, AT_FDCWD, path1, AT_FDCWD, path2, 0); +#else +#error "SYS_link or SYS_linkat not available." +#endif + if (ret < 0) { + errno = -ret; + return -1; + } + return ret; +} + +} // namespace __llvm_libc diff --git a/libc/src/unistd/linux/linkat.cpp b/libc/src/unistd/linux/linkat.cpp new file mode 100644 --- /dev/null +++ b/libc/src/unistd/linux/linkat.cpp @@ -0,0 +1,31 @@ +//===-- Linux implementation of linkat ------------------------------------===// +// +// 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/linkat.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, linkat, + (int fd1, const char *path1, int fd2, const char *path2, + int flags)) { + long ret = __llvm_libc::syscall(SYS_linkat, fd1, path1, fd2, path2, flags); + if (ret < 0) { + errno = -ret; + return -1; + } + return ret; +} + +} // namespace __llvm_libc diff --git a/libc/src/unistd/linux/readlink.cpp b/libc/src/unistd/linux/readlink.cpp new file mode 100644 --- /dev/null +++ b/libc/src/unistd/linux/readlink.cpp @@ -0,0 +1,38 @@ +//===-- Linux implementation of readlink ----------------------------------===// +// +// 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/readlink.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(ssize_t, readlink, + (const char *__restrict path, char *__restrict buf, + size_t bufsize)) { +#ifdef SYS_readlink + ssize_t ret = __llvm_libc::syscall(SYS_readlink, path, buf, bufsize); +#elif SYS_readlinkat + ssize_t ret = + __llvm_libc::syscall(SYS_readlinkat, AT_FDCWD, path, buf, bufsize); +#else +#error "SYS_readlink or SYS_readlinkat not available." +#endif + if (ret < 0) { + errno = -ret; + return -1; + } + return ret; +} + +} // namespace __llvm_libc diff --git a/libc/src/unistd/linux/readlinkat.cpp b/libc/src/unistd/linux/readlinkat.cpp new file mode 100644 --- /dev/null +++ b/libc/src/unistd/linux/readlinkat.cpp @@ -0,0 +1,31 @@ +//===-- Linux implementation of readlinkat --------------------------------===// +// +// 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/readlinkat.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(ssize_t, readlinkat, + (int fd, const char *__restrict path, char *__restrict buf, + size_t bufsize)) { + ssize_t ret = __llvm_libc::syscall(SYS_readlinkat, fd, path, buf, bufsize); + if (ret < 0) { + errno = -ret; + return -1; + } + return ret; +} + +} // namespace __llvm_libc diff --git a/libc/src/unistd/linux/symlink.cpp b/libc/src/unistd/linux/symlink.cpp new file mode 100644 --- /dev/null +++ b/libc/src/unistd/linux/symlink.cpp @@ -0,0 +1,35 @@ +//===-- Linux implementation of symlink -----------------------------------===// +// +// 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/symlink.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, symlink, (const char *path1, const char *path2)) { +#ifdef SYS_symlink + long ret = __llvm_libc::syscall(SYS_symlink, path1, path2); +#elif SYS_symlinkat + long ret = __llvm_libc::syscall(SYS_symlinkat, path1, AT_FDCWD, path2); +#else +#error "SYS_symlink or SYS_symlinkat not available." +#endif + if (ret < 0) { + errno = -ret; + return -1; + } + return ret; +} + +} // namespace __llvm_libc diff --git a/libc/src/unistd/linux/symlinkat.cpp b/libc/src/unistd/linux/symlinkat.cpp new file mode 100644 --- /dev/null +++ b/libc/src/unistd/linux/symlinkat.cpp @@ -0,0 +1,30 @@ +//===-- Linux implementation of symlinkat ---------------------------------===// +// +// 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/symlinkat.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, symlinkat, + (const char *path1, int fd, const char *path2)) { + long ret = __llvm_libc::syscall(SYS_symlinkat, path1, fd, path2); + if (ret < 0) { + errno = -ret; + return -1; + } + return ret; +} + +} // namespace __llvm_libc diff --git a/libc/src/unistd/readlink.h b/libc/src/unistd/readlink.h new file mode 100644 --- /dev/null +++ b/libc/src/unistd/readlink.h @@ -0,0 +1,20 @@ +//===-- Implementation header for readlink ----------------------*- 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_READLINK_H +#define LLVM_LIBC_SRC_UNISTD_READLINK_H + +#include + +namespace __llvm_libc { + +ssize_t readlink(const char *__restrict, char *__restrict, size_t); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_READLINK_H diff --git a/libc/src/unistd/readlinkat.h b/libc/src/unistd/readlinkat.h new file mode 100644 --- /dev/null +++ b/libc/src/unistd/readlinkat.h @@ -0,0 +1,20 @@ +//===-- Implementation header for readlinkat --------------------*- 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_READLINKAT_H +#define LLVM_LIBC_SRC_UNISTD_READLINKAT_H + +#include + +namespace __llvm_libc { + +ssize_t readlinkat(int, const char *__restrict, char *__restrict, size_t); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_READLINKAT_H diff --git a/libc/src/unistd/symlink.h b/libc/src/unistd/symlink.h new file mode 100644 --- /dev/null +++ b/libc/src/unistd/symlink.h @@ -0,0 +1,20 @@ +//===-- Implementation header for symlink -----------------------*- 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_SYMLINK_H +#define LLVM_LIBC_SRC_UNISTD_SYMLINK_H + +#include + +namespace __llvm_libc { + +int symlink(const char *, const char *); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_SYMLINK_H diff --git a/libc/src/unistd/symlinkat.h b/libc/src/unistd/symlinkat.h new file mode 100644 --- /dev/null +++ b/libc/src/unistd/symlinkat.h @@ -0,0 +1,20 @@ +//===-- Implementation header for symlinkat ---------------------*- 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_SYMLINKAT_H +#define LLVM_LIBC_SRC_UNISTD_SYMLINKAT_H + +#include + +namespace __llvm_libc { + +int symlinkat(const char *, int, const char *); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_SYMLINKAT_H 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 @@ -49,6 +49,36 @@ libc.test.errno_setter_matcher ) +add_libc_unittest( + link_test + SUITE + libc_unistd_unittests + SRCS + link_test.cpp + DEPENDS + libc.include.errno + libc.include.unistd + libc.src.fcntl.open + libc.src.unistd.close + libc.src.unistd.link + libc.src.unistd.unlink +) + +add_libc_unittest( + linkat_test + SUITE + libc_unistd_unittests + SRCS + linkat_test.cpp + DEPENDS + libc.include.errno + libc.include.unistd + libc.src.fcntl.open + libc.src.unistd.close + libc.src.unistd.linkat + libc.src.unistd.unlink +) + add_libc_unittest( lseek_test SUITE @@ -78,6 +108,67 @@ libc.src.unistd.rmdir ) +add_libc_unittest( + readlink_test + SUITE + libc_unistd_unittests + SRCS + readlink_test.cpp + DEPENDS + libc.include.errno + libc.include.unistd + libc.src.unistd.readlink + libc.src.unistd.symlink + libc.src.unistd.unlink + libc.src.__support.CPP.string_view +) + +add_libc_unittest( + readlinkat_test + SUITE + libc_unistd_unittests + SRCS + readlinkat_test.cpp + DEPENDS + libc.include.errno + libc.include.fcntl + libc.include.unistd + libc.src.unistd.readlinkat + libc.src.unistd.symlink + libc.src.unistd.unlink + libc.src.__support.CPP.string_view +) + +add_libc_unittest( + symlink_test + SUITE + libc_unistd_unittests + SRCS + symlink_test.cpp + DEPENDS + libc.include.errno + libc.include.unistd + libc.src.fcntl.open + libc.src.unistd.close + libc.src.unistd.symlink + libc.src.unistd.unlink +) + +add_libc_unittest( + symlinkat_test + SUITE + libc_unistd_unittests + SRCS + symlinkat_test.cpp + DEPENDS + libc.include.errno + libc.include.unistd + libc.src.fcntl.open + libc.src.unistd.close + libc.src.unistd.symlinkat + libc.src.unistd.unlink +) + add_libc_unittest( unlink_test SUITE diff --git a/libc/test/src/unistd/link_test.cpp b/libc/test/src/unistd/link_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/unistd/link_test.cpp @@ -0,0 +1,49 @@ +//===-- Unittests for link ------------------------------------------------===// +// +// 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/link.h" +#include "src/unistd/unlink.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" + +#include + +TEST(LlvmLibcLinkTest, CreateAndUnlink) { + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + constexpr const char *TEST_FILE = "testdata/link.test"; + constexpr const char *TEST_FILE_LINK = "testdata/link.test.link"; + + // The test strategy is as follows: + // 1. Create a normal file + // 2. Create a link to that file. + // 3. Open the link to check that the link was created. + // 4. Cleanup the file and its link. + errno = 0; + int write_fd = __llvm_libc::open(TEST_FILE, O_WRONLY | O_CREAT, S_IRWXU); + ASSERT_EQ(errno, 0); + ASSERT_GT(write_fd, 0); + ASSERT_THAT(__llvm_libc::close(write_fd), Succeeds(0)); + ASSERT_THAT(__llvm_libc::link(TEST_FILE, TEST_FILE_LINK), Succeeds(0)); + + int link_fd = __llvm_libc::open(TEST_FILE_LINK, O_PATH); + ASSERT_GT(link_fd, 0); + ASSERT_EQ(errno, 0); + ASSERT_THAT(__llvm_libc::close(link_fd), Succeeds(0)); + + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE), Succeeds(0)); + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE_LINK), Succeeds(0)); +} + +TEST(LlvmLibcLinkTest, LinkToNonExistentFile) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + ASSERT_THAT( + __llvm_libc::link("testdata/non-existent-file", "testdata/bad-link"), + Fails(ENOENT)); +} diff --git a/libc/test/src/unistd/linkat_test.cpp b/libc/test/src/unistd/linkat_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/unistd/linkat_test.cpp @@ -0,0 +1,56 @@ +//===-- Unittests for linkat ----------------------------------------------===// +// +// 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/linkat.h" +#include "src/unistd/unlink.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" + +#include + +TEST(LlvmLibcLinkatTest, CreateAndUnlink) { + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + constexpr const char *TEST_DIR = "testdata"; + constexpr const char *TEST_FILE = "linkat.test"; + constexpr const char *TEST_FILE_PATH = "testdata/linkat.test"; + constexpr const char *TEST_FILE_LINK = "linkat.test.link"; + constexpr const char *TEST_FILE_LINK_PATH = "testdata/linkat.test.link"; + + // The test strategy is as follows: + // 1. Create a normal file + // 2. Create a link to that file. + // 3. Open the link to check that the link was created. + // 4. Cleanup the file and its link. + errno = 0; + int write_fd = __llvm_libc::open(TEST_FILE_PATH, O_WRONLY | O_CREAT, S_IRWXU); + ASSERT_EQ(errno, 0); + ASSERT_GT(write_fd, 0); + ASSERT_THAT(__llvm_libc::close(write_fd), Succeeds(0)); + + int dir_fd = __llvm_libc::open(TEST_DIR, O_DIRECTORY); + ASSERT_THAT(__llvm_libc::linkat(dir_fd, TEST_FILE, dir_fd, TEST_FILE_LINK, 0), + Succeeds(0)); + + int link_fd = __llvm_libc::open(TEST_FILE_LINK_PATH, O_PATH); + ASSERT_GT(link_fd, 0); + ASSERT_EQ(errno, 0); + ASSERT_THAT(__llvm_libc::close(link_fd), Succeeds(0)); + + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE_PATH), Succeeds(0)); + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE_LINK_PATH), Succeeds(0)); + ASSERT_THAT(__llvm_libc::close(dir_fd), Succeeds(0)); +} + +TEST(LlvmLibcLinkatTest, LinkToNonExistentFile) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + ASSERT_THAT(__llvm_libc::linkat(AT_FDCWD, "testdata/non-existent-file", + AT_FDCWD, "testdata/bad-link", 0), + Fails(ENOENT)); +} diff --git a/libc/test/src/unistd/readlink_test.cpp b/libc/test/src/unistd/readlink_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/unistd/readlink_test.cpp @@ -0,0 +1,45 @@ +//===-- Unittests for readlink --------------------------------------------===// +// +// 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/unistd/readlink.h" +#include "src/unistd/symlink.h" +#include "src/unistd/unlink.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" + +#include + +namespace cpp = __llvm_libc::cpp; + +TEST(LlvmLibcReadlinkTest, CreateAndUnlink) { + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + constexpr const char LINK_VAL[] = "readlink_test_value"; + constexpr const char LINK[] = "testdata/readlink.test.link"; + errno = 0; + + // The test strategy is as follows: + // 1. Create a symlink with value LINK_VAL. + // 2. Read the symlink with readlink. The link value read should be LINK_VAL + // 3. Cleanup the symlink created in step #1. + ASSERT_THAT(__llvm_libc::symlink(LINK_VAL, LINK), Succeeds(0)); + + char buf[sizeof(LINK_VAL)]; + ssize_t len = __llvm_libc::readlink(LINK, buf, sizeof(buf)); + ASSERT_EQ(errno, 0); + ASSERT_EQ(cpp::string_view(buf, len), cpp::string_view(LINK_VAL)); + + ASSERT_THAT(__llvm_libc::unlink(LINK), Succeeds(0)); +} + +TEST(LlvmLibcReadlinkTest, ReadlinkInNonExistentPath) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + char buf[8]; + ASSERT_THAT(__llvm_libc::readlink("non-existent-link", buf, sizeof(buf)), + Fails(ENOENT)); +} diff --git a/libc/test/src/unistd/readlinkat_test.cpp b/libc/test/src/unistd/readlinkat_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/unistd/readlinkat_test.cpp @@ -0,0 +1,47 @@ +//===-- Unittests for readlinkat ------------------------------------------===// +// +// 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/unistd/readlinkat.h" +#include "src/unistd/symlink.h" +#include "src/unistd/unlink.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" + +#include +#include + +namespace cpp = __llvm_libc::cpp; + +TEST(LlvmLibcReadlinkatTest, CreateAndUnlink) { + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + constexpr const char LINK_VAL[] = "readlinkat_test_value"; + constexpr const char LINK[] = "testdata/readlinkat.test.link"; + errno = 0; + + // The test strategy is as follows: + // 1. Create a symlink with value LINK_VAL. + // 2. Read the symlink with readlink. The link value read should be LINK_VAL + // 3. Cleanup the symlink created in step #1. + ASSERT_THAT(__llvm_libc::symlink(LINK_VAL, LINK), Succeeds(0)); + + char buf[sizeof(LINK_VAL)]; + ssize_t len = __llvm_libc::readlinkat(AT_FDCWD, LINK, buf, sizeof(buf)); + ASSERT_EQ(errno, 0); + ASSERT_EQ(cpp::string_view(buf, len), cpp::string_view(LINK_VAL)); + + ASSERT_THAT(__llvm_libc::unlink(LINK), Succeeds(0)); +} + +TEST(LlvmLibcReadlinkatTest, ReadlinkInNonExistentPath) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + char buf[8]; + ASSERT_THAT( + __llvm_libc::readlinkat(AT_FDCWD, "non-existent-link", buf, sizeof(buf)), + Fails(ENOENT)); +} diff --git a/libc/test/src/unistd/symlink_test.cpp b/libc/test/src/unistd/symlink_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/unistd/symlink_test.cpp @@ -0,0 +1,52 @@ +//===-- Unittests for symlink ---------------------------------------------===// +// +// 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/symlink.h" +#include "src/unistd/unlink.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" + +#include + +TEST(LlvmLibcSymlinkTest, CreateAndUnlink) { + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + constexpr const char *TEST_FILE_BASE = "symlink.test"; + constexpr const char *TEST_FILE = "testdata/symlink.test"; + constexpr const char *TEST_FILE_LINK = "testdata/symlink.test.symlink"; + + // The test strategy is as follows: + // 1. Create a normal file + // 2. Create a symlink to that file. + // 3. Open the symlink to check that the symlink was created. + // 4. Cleanup the file and its symlink. + errno = 0; + int write_fd = __llvm_libc::open(TEST_FILE, O_WRONLY | O_CREAT, S_IRWXU); + ASSERT_EQ(errno, 0); + ASSERT_GT(write_fd, 0); + ASSERT_THAT(__llvm_libc::close(write_fd), Succeeds(0)); + + ASSERT_THAT(__llvm_libc::symlink(TEST_FILE_BASE, TEST_FILE_LINK), + Succeeds(0)); + + int symlink_fd = __llvm_libc::open(TEST_FILE_LINK, O_PATH); + ASSERT_GT(symlink_fd, 0); + ASSERT_EQ(errno, 0); + ASSERT_THAT(__llvm_libc::close(symlink_fd), Succeeds(0)); + + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE), Succeeds(0)); + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE_LINK), Succeeds(0)); +} + +TEST(LlvmLibcSymlinkTest, SymlinkInNonExistentPath) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + ASSERT_THAT(__llvm_libc::symlink("non-existent-dir/non-existent-file", + "non-existent-dir/bad-symlink"), + Fails(ENOENT)); +} diff --git a/libc/test/src/unistd/symlinkat_test.cpp b/libc/test/src/unistd/symlinkat_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/unistd/symlinkat_test.cpp @@ -0,0 +1,56 @@ +//===-- Unittests for symlinkat -------------------------------------------===// +// +// 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/symlinkat.h" +#include "src/unistd/unlink.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" + +#include + +TEST(LlvmLibcSymlinkatTest, CreateAndUnlink) { + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + constexpr const char *TEST_DIR = "testdata"; + constexpr const char *TEST_FILE = "symlinkat.test"; + constexpr const char *TEST_FILE_PATH = "testdata/symlinkat.test"; + constexpr const char *TEST_FILE_LINK = "symlinkat.test.link"; + constexpr const char *TEST_FILE_LINK_PATH = "testdata/symlinkat.test.link"; + + // The test strategy is as follows: + // 1. Create a normal file + // 2. Create a link to that file. + // 3. Open the link to check that the link was created. + // 4. Cleanup the file and its link. + errno = 0; + int write_fd = __llvm_libc::open(TEST_FILE_PATH, O_WRONLY | O_CREAT, S_IRWXU); + ASSERT_EQ(errno, 0); + ASSERT_GT(write_fd, 0); + ASSERT_THAT(__llvm_libc::close(write_fd), Succeeds(0)); + + int dir_fd = __llvm_libc::open(TEST_DIR, O_DIRECTORY); + ASSERT_THAT(__llvm_libc::symlinkat(TEST_FILE, dir_fd, TEST_FILE_LINK), + Succeeds(0)); + + int link_fd = __llvm_libc::open(TEST_FILE_LINK_PATH, O_PATH); + ASSERT_GT(link_fd, 0); + ASSERT_EQ(errno, 0); + ASSERT_THAT(__llvm_libc::close(link_fd), Succeeds(0)); + + ASSERT_THAT(__llvm_libc::close(dir_fd), Succeeds(0)); + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE_LINK_PATH), Succeeds(0)); + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE_PATH), Succeeds(0)); +} + +TEST(LlvmLibcSymlinkatTest, SymlinkInNonExistentPath) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + ASSERT_THAT(__llvm_libc::symlinkat("non-existent-dir/non-existent-file", + AT_FDCWD, "non-existent-dir/bad-link"), + Fails(ENOENT)); +}