31 changes: 31 additions & 0 deletions libc/src/unistd/linux/readlinkat.cpp
Original file line number Diff line number Diff line change
@@ -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 <errno.h>
#include <fcntl.h>
#include <sys/syscall.h> // 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
35 changes: 35 additions & 0 deletions libc/src/unistd/linux/symlink.cpp
Original file line number Diff line number Diff line change
@@ -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 <errno.h>
#include <fcntl.h>
#include <sys/syscall.h> // 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 defined(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
30 changes: 30 additions & 0 deletions libc/src/unistd/linux/symlinkat.cpp
Original file line number Diff line number Diff line change
@@ -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 <errno.h>
#include <fcntl.h>
#include <sys/syscall.h> // 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
20 changes: 20 additions & 0 deletions libc/src/unistd/readlink.h
Original file line number Diff line number Diff line change
@@ -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 <unistd.h>

namespace __llvm_libc {

ssize_t readlink(const char *__restrict, char *__restrict, size_t);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_UNISTD_READLINK_H
20 changes: 20 additions & 0 deletions libc/src/unistd/readlinkat.h
Original file line number Diff line number Diff line change
@@ -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 <unistd.h>

namespace __llvm_libc {

ssize_t readlinkat(int, const char *__restrict, char *__restrict, size_t);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_UNISTD_READLINKAT_H
20 changes: 20 additions & 0 deletions libc/src/unistd/symlink.h
Original file line number Diff line number Diff line change
@@ -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 <unistd.h>

namespace __llvm_libc {

int symlink(const char *, const char *);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_UNISTD_SYMLINK_H
20 changes: 20 additions & 0 deletions libc/src/unistd/symlinkat.h
Original file line number Diff line number Diff line change
@@ -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 <unistd.h>

namespace __llvm_libc {

int symlinkat(const char *, int, const char *);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_UNISTD_SYMLINKAT_H
91 changes: 91 additions & 0 deletions libc/test/src/unistd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,36 @@ add_libc_unittest(
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
Expand Down Expand Up @@ -78,6 +108,67 @@ add_libc_unittest(
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
Expand Down
49 changes: 49 additions & 0 deletions libc/test/src/unistd/link_test.cpp
Original file line number Diff line number Diff line change
@@ -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 <errno.h>

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));
}
56 changes: 56 additions & 0 deletions libc/test/src/unistd/linkat_test.cpp
Original file line number Diff line number Diff line change
@@ -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 <errno.h>

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));
}
45 changes: 45 additions & 0 deletions libc/test/src/unistd/readlink_test.cpp
Original file line number Diff line number Diff line change
@@ -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 <errno.h>

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));
}
47 changes: 47 additions & 0 deletions libc/test/src/unistd/readlinkat_test.cpp
Original file line number Diff line number Diff line change
@@ -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 <errno.h>
#include <fcntl.h>

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));
}
52 changes: 52 additions & 0 deletions libc/test/src/unistd/symlink_test.cpp
Original file line number Diff line number Diff line change
@@ -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 <errno.h>

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));
}
56 changes: 56 additions & 0 deletions libc/test/src/unistd/symlinkat_test.cpp
Original file line number Diff line number Diff line change
@@ -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 <errno.h>

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));
}