[libc-commits] [libc] 00e51f0 - [libc] Implement linux link, linkat, symlink, symlinkat, readlink, readlinkat.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Thu Aug 25 11:50:59 PDT 2022


Author: Siva Chandra Reddy
Date: 2022-08-25T18:50:39Z
New Revision: 00e51f04e8c10d0ad3e6089c9ab490394cd69a83

URL: https://github.com/llvm/llvm-project/commit/00e51f04e8c10d0ad3e6089c9ab490394cd69a83
DIFF: https://github.com/llvm/llvm-project/commit/00e51f04e8c10d0ad3e6089c9ab490394cd69a83.diff

LOG: [libc] Implement linux link, linkat, symlink, symlinkat, readlink, readlinkat.

Reviewed By: michaelrj

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

Added: 
    libc/src/unistd/link.h
    libc/src/unistd/linkat.h
    libc/src/unistd/linux/link.cpp
    libc/src/unistd/linux/linkat.cpp
    libc/src/unistd/linux/readlink.cpp
    libc/src/unistd/linux/readlinkat.cpp
    libc/src/unistd/linux/symlink.cpp
    libc/src/unistd/linux/symlinkat.cpp
    libc/src/unistd/readlink.h
    libc/src/unistd/readlinkat.h
    libc/src/unistd/symlink.h
    libc/src/unistd/symlinkat.h
    libc/test/src/unistd/link_test.cpp
    libc/test/src/unistd/linkat_test.cpp
    libc/test/src/unistd/readlink_test.cpp
    libc/test/src/unistd/readlinkat_test.cpp
    libc/test/src/unistd/symlink_test.cpp
    libc/test/src/unistd/symlinkat_test.cpp

Modified: 
    libc/config/linux/aarch64/entrypoints.txt
    libc/config/linux/x86_64/entrypoints.txt
    libc/spec/posix.td
    libc/src/unistd/CMakeLists.txt
    libc/src/unistd/linux/CMakeLists.txt
    libc/test/src/unistd/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 95d6fcb2d986d..13ca094e5cee0 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -112,9 +112,15 @@ set(TARGET_LIBC_ENTRYPOINTS
     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
index 787f22b12eeac..15bf3b13dc66e 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -112,9 +112,15 @@ set(TARGET_LIBC_ENTRYPOINTS
     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
index c4ab8efc4dda1..27fb7c23d45ce 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -283,6 +283,16 @@ def POSIX : StandardSpec<"POSIX"> {
           RetValSpec<IntType>,
           [ArgSpec<IntType>]
         >,
+        FunctionSpec<
+          "link",
+          RetValSpec<IntType>,
+          [ArgSpec<ConstCharPtr>, ArgSpec<ConstCharPtr>]
+        >,
+        FunctionSpec<
+          "linkat",
+          RetValSpec<IntType>,
+          [ArgSpec<IntType>, ArgSpec<ConstCharPtr>, ArgSpec<IntType>, ArgSpec<ConstCharPtr>, ArgSpec<IntType>]
+        >,
         FunctionSpec<
           "lseek",
           RetValSpec<OffTType>,
@@ -293,11 +303,31 @@ def POSIX : StandardSpec<"POSIX"> {
           RetValSpec<SSizeTType>,
           [ArgSpec<IntType>, ArgSpec<VoidPtr>, ArgSpec<SizeTType>]
         >,
+        FunctionSpec<
+          "readlink",
+          RetValSpec<SSizeTType>,
+          [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtr>, ArgSpec<SizeTType>]
+        >,
+        FunctionSpec<
+          "readlinkat",
+          RetValSpec<SSizeTType>,
+          [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtr>, ArgSpec<SizeTType>]
+        >,
         FunctionSpec<
           "rmdir",
           RetValSpec<IntType>,
           [ArgSpec<ConstCharPtr>]
         >,
+        FunctionSpec<
+          "symlink",
+          RetValSpec<IntType>,
+          [ArgSpec<ConstCharPtr>, ArgSpec<ConstCharPtr>]
+        >,
+        FunctionSpec<
+          "symlinkat",
+          RetValSpec<IntType>,
+          [ArgSpec<IntType>, ArgSpec<ConstCharPtr>, ArgSpec<IntType>, ArgSpec<ConstCharPtr>]
+        >,
         FunctionSpec<
           "unlink",
           RetValSpec<IntType>,

diff  --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt
index 306fdd4a37dfc..849b83e6d23a6 100644
--- a/libc/src/unistd/CMakeLists.txt
+++ b/libc/src/unistd/CMakeLists.txt
@@ -30,6 +30,20 @@ add_entrypoint_object(
     .${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 @@ add_entrypoint_object(
     .${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 @@ add_entrypoint_object(
     .${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
index 0000000000000..492274286e044
--- /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 <unistd.h>
+
+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
index 0000000000000..7bc161952a866
--- /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
index 3889f6171c1c2..132fd7b78cb75 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -50,6 +50,34 @@ add_entrypoint_object(
     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 @@ add_entrypoint_object(
     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
index 0000000000000..9f6f915d16135
--- /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 <errno.h>
+#include <fcntl.h>
+#include <sys/syscall.h> // 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 defined(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
index 0000000000000..ef5761173dd0a
--- /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 <errno.h>
+#include <fcntl.h>
+#include <sys/syscall.h> // 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
index 0000000000000..354cdb2dc4e70
--- /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 <errno.h>
+#include <fcntl.h>
+#include <sys/syscall.h> // 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 defined(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
index 0000000000000..ceb4061a84cd2
--- /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 <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

diff  --git a/libc/src/unistd/linux/symlink.cpp b/libc/src/unistd/linux/symlink.cpp
new file mode 100644
index 0000000000000..add7b95f756dc
--- /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 <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

diff  --git a/libc/src/unistd/linux/symlinkat.cpp b/libc/src/unistd/linux/symlinkat.cpp
new file mode 100644
index 0000000000000..e0edfc842dd68
--- /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 <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

diff  --git a/libc/src/unistd/readlink.h b/libc/src/unistd/readlink.h
new file mode 100644
index 0000000000000..ba246bf91b8a8
--- /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 <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

diff  --git a/libc/src/unistd/readlinkat.h b/libc/src/unistd/readlinkat.h
new file mode 100644
index 0000000000000..54346ee1132cb
--- /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 <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

diff  --git a/libc/src/unistd/symlink.h b/libc/src/unistd/symlink.h
new file mode 100644
index 0000000000000..a495db8238bc0
--- /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 <unistd.h>
+
+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
index 0000000000000..ff77f0a52f6d0
--- /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 <unistd.h>
+
+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
index 30f7b370eb9ad..2c9369cbd51ed 100644
--- a/libc/test/src/unistd/CMakeLists.txt
+++ b/libc/test/src/unistd/CMakeLists.txt
@@ -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
@@ -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

diff  --git a/libc/test/src/unistd/link_test.cpp b/libc/test/src/unistd/link_test.cpp
new file mode 100644
index 0000000000000..e54005261d2d1
--- /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 <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));
+}

diff  --git a/libc/test/src/unistd/linkat_test.cpp b/libc/test/src/unistd/linkat_test.cpp
new file mode 100644
index 0000000000000..8e3266ff26bd7
--- /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 <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));
+}

diff  --git a/libc/test/src/unistd/readlink_test.cpp b/libc/test/src/unistd/readlink_test.cpp
new file mode 100644
index 0000000000000..ea9e164144bd3
--- /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 <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));
+}

diff  --git a/libc/test/src/unistd/readlinkat_test.cpp b/libc/test/src/unistd/readlinkat_test.cpp
new file mode 100644
index 0000000000000..942b6290288a1
--- /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 <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));
+}

diff  --git a/libc/test/src/unistd/symlink_test.cpp b/libc/test/src/unistd/symlink_test.cpp
new file mode 100644
index 0000000000000..52d666262200a
--- /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 <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));
+}

diff  --git a/libc/test/src/unistd/symlinkat_test.cpp b/libc/test/src/unistd/symlinkat_test.cpp
new file mode 100644
index 0000000000000..a1f416a403e61
--- /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 <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));
+}


        


More information about the libc-commits mailing list