[libc] Add implementations of POSIX mkdir, mkdirat, rmdir, unlink and unlinkat.
authorSiva Chandra Reddy <sivachandra@google.com>
Mon, 31 Jan 2022 17:32:07 +0000 (17:32 +0000)
committerSiva Chandra Reddy <sivachandra@google.com>
Tue, 1 Feb 2022 05:17:10 +0000 (05:17 +0000)
Reviewed By: michaelrj

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

30 files changed:
libc/config/linux/api.td
libc/config/linux/x86_64/entrypoints.txt
libc/include/CMakeLists.txt
libc/include/llvm-libc-macros/linux/fcntl-macros.h
libc/include/sys/stat.h.def [new file with mode: 0644]
libc/spec/posix.td
libc/src/sys/CMakeLists.txt
libc/src/sys/stat/CMakeLists.txt [new file with mode: 0644]
libc/src/sys/stat/linux/CMakeLists.txt [new file with mode: 0644]
libc/src/sys/stat/linux/mkdir.cpp [new file with mode: 0644]
libc/src/sys/stat/linux/mkdirat.cpp [new file with mode: 0644]
libc/src/sys/stat/mkdir.h [new file with mode: 0644]
libc/src/sys/stat/mkdirat.h [new file with mode: 0644]
libc/src/unistd/CMakeLists.txt
libc/src/unistd/linux/CMakeLists.txt
libc/src/unistd/linux/rmdir.cpp [new file with mode: 0644]
libc/src/unistd/linux/unlink.cpp [new file with mode: 0644]
libc/src/unistd/linux/unlinkat.cpp [new file with mode: 0644]
libc/src/unistd/rmdir.h [new file with mode: 0644]
libc/src/unistd/unlink.h [new file with mode: 0644]
libc/src/unistd/unlinkat.h [new file with mode: 0644]
libc/test/src/sys/CMakeLists.txt
libc/test/src/sys/stat/CMakeLists.txt [new file with mode: 0644]
libc/test/src/sys/stat/mkdirat_test.cpp [new file with mode: 0644]
libc/test/src/sys/stat/testdata/CMakeLists.txt [new file with mode: 0644]
libc/test/src/unistd/CMakeLists.txt
libc/test/src/unistd/rmdir_test.cpp [new file with mode: 0644]
libc/test/src/unistd/testdata/CMakeLists.txt [new file with mode: 0644]
libc/test/src/unistd/unlink_test.cpp [new file with mode: 0644]
libc/test/src/unistd/unlinkat_test.cpp [new file with mode: 0644]

index 7bfa621..69c663f 100644 (file)
@@ -242,3 +242,7 @@ def ThreadsAPI : PublicAPI<"threads.h"> {
 def UniStdAPI : PublicAPI<"unistd.h"> {
   let Types = ["size_t", "ssize_t"];
 }
+
+def SysStatAPI : PublicAPI<"sys/stat.h"> {
+  let Types = ["mode_t"];
+}
index d230dec..aa3d69a 100644 (file)
@@ -85,10 +85,17 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.sys.mman.mmap
     libc.src.sys.mman.munmap
 
+    # sys/stat.h entrypoints
+    libc.src.sys.stat.mkdir
+    libc.src.sys.stat.mkdirat
+
     # unistd.h entrypoints
     libc.src.unistd.close
     libc.src.unistd.fsync
     libc.src.unistd.read
+    libc.src.unistd.rmdir
+    libc.src.unistd.unlink
+    libc.src.unistd.unlinkat
     libc.src.unistd.write
 )
 
index d208f17..bb71381 100644 (file)
@@ -168,6 +168,15 @@ add_gen_header(
 )
 
 add_gen_header(
+  sys_stat
+  DEF_FILE sys/stat.h.def
+  GEN_HDR sys/stat.h
+  DEPENDS
+    .llvm_libc_common_h
+    .llvm-libc-types.mode_t
+)
+
+add_gen_header(
   sys_syscall
   DEF_FILE sys/syscall.h.def
   GEN_HDR sys/syscall.h
index 007f780..46655c2 100644 (file)
@@ -53,4 +53,8 @@
 // openat is relative to the current directory.
 #define AT_FDCWD -100
 
+// Special flag to the function unlinkat to indicate that it
+// has to perform the equivalent of "rmdir" on the path argument.
+#define AT_REMOVEDIR 0x200
+
 #endif // __LLVM_LIBC_MACROS_LINUX_FCNTL_MACROS_H
diff --git a/libc/include/sys/stat.h.def b/libc/include/sys/stat.h.def
new file mode 100644 (file)
index 0000000..f925208
--- /dev/null
@@ -0,0 +1,16 @@
+//===-- POSIX header stat.h -----------------------------------------------===//
+//
+// 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_SYS_STAT_H
+#define LLVM_LIBC_SYS_STAT_H
+
+#include <__llvm-libc-common.h>
+
+%%public_api()
+
+#endif // LLVM_LIBC_SYS_STAT_H
index de3e2d5..dc9a3ee 100644 (file)
@@ -243,6 +243,21 @@ def POSIX : StandardSpec<"POSIX"> {
           [ArgSpec<IntType>, ArgSpec<VoidPtr>, ArgSpec<SizeTType>]
         >,
         FunctionSpec<
+          "rmdir",
+          RetValSpec<IntType>,
+          [ArgSpec<ConstCharPtr>]
+        >,
+        FunctionSpec<
+          "unlink",
+          RetValSpec<IntType>,
+          [ArgSpec<ConstCharPtr>]
+        >,
+        FunctionSpec<
+          "unlinkat",
+          RetValSpec<IntType>,
+          [ArgSpec<IntType>, ArgSpec<ConstCharPtr>, ArgSpec<IntType>]
+        >,
+        FunctionSpec<
           "write",
           RetValSpec<SSizeTType>,
           [ArgSpec<IntType>, ArgSpec<ConstVoidPtr>, ArgSpec<SizeTType>]
@@ -317,12 +332,32 @@ def POSIX : StandardSpec<"POSIX"> {
     ]
   >;
 
+  HeaderSpec SysStat = HeaderSpec<
+    "sys/stat.h",
+    [], // Macros
+    [ModeTType], // Types
+    [], // Enumerations
+    [
+        FunctionSpec<
+            "mkdir",
+            RetValSpec<IntType>,
+            [ArgSpec<ConstCharPtr>, ArgSpec<ModeTType>]
+        >,
+        FunctionSpec<
+            "mkdirat",
+            RetValSpec<IntType>,
+            [ArgSpec<IntType>, ArgSpec<ConstCharPtr>, ArgSpec<ModeTType>]
+        >,
+    ]
+  >;
+
   let Headers = [
     CType,
     Errno,
     FCntl,
-    SysMMan,
     Signal,
+    SysMMan,
+    SysStat,
     UniStd,
     String
   ];
index 03c59bf..4c7e820 100644 (file)
@@ -1 +1,2 @@
 add_subdirectory(mman)
+add_subdirectory(stat)
diff --git a/libc/src/sys/stat/CMakeLists.txt b/libc/src/sys/stat/CMakeLists.txt
new file mode 100644 (file)
index 0000000..44bc48a
--- /dev/null
@@ -0,0 +1,17 @@
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+  add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+endif()
+
+add_entrypoint_object(
+  mkdir
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.mkdir
+)
+
+add_entrypoint_object(
+  mkdirat
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.mkdirat
+)
diff --git a/libc/src/sys/stat/linux/CMakeLists.txt b/libc/src/sys/stat/linux/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5a81883
--- /dev/null
@@ -0,0 +1,25 @@
+add_entrypoint_object(
+  mkdir
+  SRCS
+    mkdir.cpp
+  HDRS
+    ../mkdir.h
+  DEPENDS
+    libc.include.sys_stat
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.__errno_location
+)
+
+add_entrypoint_object(
+  mkdirat
+  SRCS
+    mkdirat.cpp
+  HDRS
+    ../mkdirat.h
+  DEPENDS
+    libc.include.sys_stat
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.__errno_location
+)
diff --git a/libc/src/sys/stat/linux/mkdir.cpp b/libc/src/sys/stat/linux/mkdir.cpp
new file mode 100644 (file)
index 0000000..3477e1a
--- /dev/null
@@ -0,0 +1,36 @@
+//===-- Linux implementation of mkdir -------------------------------------===//
+//
+// 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/mkdir.h"
+
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, mkdir, (const char *path, mode_t mode)) {
+#ifdef SYS_mkdir
+  long ret = __llvm_libc::syscall(SYS_mkdir, path, mode);
+#elif defined(SYS_unlinkat)
+  long ret = __llvm_libc::syscall(SYS_mkdirat, AT_FDCWD, path, mode);
+#else
+#error "mkdir and mkdirat syscalls not available."
+#endif
+
+  if (ret < 0) {
+    errno = -ret;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/sys/stat/linux/mkdirat.cpp b/libc/src/sys/stat/linux/mkdirat.cpp
new file mode 100644 (file)
index 0000000..07cd337
--- /dev/null
@@ -0,0 +1,34 @@
+//===-- Linux implementation of mkdirat -----------------------------------===//
+//
+// 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/mkdirat.h"
+
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, mkdirat, (int dfd, const char *path, mode_t mode)) {
+#ifdef SYS_unlinkat
+  long ret = __llvm_libc::syscall(SYS_mkdirat, dfd, path, mode);
+#else
+#error "mkdirat syscalls not available."
+#endif
+
+  if (ret < 0) {
+    errno = -ret;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/sys/stat/mkdir.h b/libc/src/sys/stat/mkdir.h
new file mode 100644 (file)
index 0000000..535423a
--- /dev/null
@@ -0,0 +1,20 @@
+//===-- Implementation header for mkdir -------------------------*- 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_MKDIR_H
+#define LLVM_LIBC_SRC_SYS_STAT_MKDIR_H
+
+#include <sys/stat.h>
+
+namespace __llvm_libc {
+
+int mkdir(const char *path, mode_t mode);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SYS_STAT_MKDIR_H
diff --git a/libc/src/sys/stat/mkdirat.h b/libc/src/sys/stat/mkdirat.h
new file mode 100644 (file)
index 0000000..883709c
--- /dev/null
@@ -0,0 +1,20 @@
+//===-- Implementation header for mkdirat -----------------------*- 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_MKDIRAT_H
+#define LLVM_LIBC_SRC_SYS_STAT_MKDIRAT_H
+
+#include <sys/stat.h>
+
+namespace __llvm_libc {
+
+int mkdirat(int dfd, const char *path, mode_t mode);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SYS_STAT_MKDIRAT_H
index 1d0ef0e..70210eb 100644 (file)
@@ -24,6 +24,27 @@ add_entrypoint_object(
 )
 
 add_entrypoint_object(
+  rmdir
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.rmdir
+)
+
+add_entrypoint_object(
+  unlink
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.unlink
+)
+
+add_entrypoint_object(
+  unlinkat
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.unlinkat
+)
+
+add_entrypoint_object(
   write
   ALIAS
   DEPENDS
index a9c429d..c08bd18 100644 (file)
@@ -38,6 +38,45 @@ add_entrypoint_object(
 )
 
 add_entrypoint_object(
+  rmdir
+  SRCS
+    rmdir.cpp
+  HDRS
+    ../rmdir.h
+  DEPENDS
+    libc.include.unistd
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.__errno_location
+)
+
+add_entrypoint_object(
+  unlink
+  SRCS
+    unlink.cpp
+  HDRS
+    ../unlink.h
+  DEPENDS
+    libc.include.unistd
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.__errno_location
+)
+
+add_entrypoint_object(
+  unlinkat
+  SRCS
+    unlinkat.cpp
+  HDRS
+    ../unlinkat.h
+  DEPENDS
+    libc.include.unistd
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.__errno_location
+)
+
+add_entrypoint_object(
   write
   SRCS
     write.cpp
diff --git a/libc/src/unistd/linux/rmdir.cpp b/libc/src/unistd/linux/rmdir.cpp
new file mode 100644 (file)
index 0000000..220330a
--- /dev/null
@@ -0,0 +1,35 @@
+//===-- Linux implementation of rmdir -------------------------------------===//
+//
+// 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/rmdir.h"
+
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+
+#include <errno.h>
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, rmdir, (const char *path)) {
+#ifdef SYS_rmdir
+  long ret = __llvm_libc::syscall(SYS_rmdir, path);
+#elif defined(SYS_unlinkat)
+  long ret = __llvm_libc::syscall(SYS_unlinkat, AT_FDCWD, path, 0);
+#else
+#error "rmdir and unlinkat syscalls not available."
+#endif
+
+  if (ret < 0) {
+    errno = -ret;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/unistd/linux/unlink.cpp b/libc/src/unistd/linux/unlink.cpp
new file mode 100644 (file)
index 0000000..f02f745
--- /dev/null
@@ -0,0 +1,35 @@
+//===-- Linux implementation of unlink ------------------------------------===//
+//
+// 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/unlink.h"
+
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+
+#include <errno.h>
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, unlink, (const char *path)) {
+#ifdef SYS_unlink
+  long ret = __llvm_libc::syscall(SYS_unlink, path);
+#elif defined(SYS_unlinkat)
+  long ret = __llvm_libc::syscall(SYS_unlinkat, AT_FDCWD, path, 0);
+#else
+#error "Unlink syscalls not available."
+#endif
+
+  if (ret < 0) {
+    errno = -ret;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/unistd/linux/unlinkat.cpp b/libc/src/unistd/linux/unlinkat.cpp
new file mode 100644 (file)
index 0000000..54d3c10
--- /dev/null
@@ -0,0 +1,33 @@
+//===-- Linux implementation of unlinkat ----------------------------------===//
+//
+// 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/unlinkat.h"
+
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+
+#include <errno.h>
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, unlinkat, (int dfd, const char *path, int flags)) {
+#ifdef SYS_unlinkat
+  long ret = __llvm_libc::syscall(SYS_unlinkat, dfd, path, flags);
+#else
+#error "unlinkat syscall not available."
+#endif
+
+  if (ret < 0) {
+    errno = -ret;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/unistd/rmdir.h b/libc/src/unistd/rmdir.h
new file mode 100644 (file)
index 0000000..5fe7fac
--- /dev/null
@@ -0,0 +1,18 @@
+//===-- Implementation header for rmdir -------------------------*- 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_RMDIR_H
+#define LLVM_LIBC_SRC_UNISTD_RMDIR_H
+
+namespace __llvm_libc {
+
+int rmdir(const char *path);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_UNISTD_RMDIR_H
diff --git a/libc/src/unistd/unlink.h b/libc/src/unistd/unlink.h
new file mode 100644 (file)
index 0000000..1119bfa
--- /dev/null
@@ -0,0 +1,18 @@
+//===-- Implementation header for unlink ------------------------*- 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_UNLINK_H
+#define LLVM_LIBC_SRC_UNISTD_UNLINK_H
+
+namespace __llvm_libc {
+
+int unlink(const char *path);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_UNISTD_UNLINK_H
diff --git a/libc/src/unistd/unlinkat.h b/libc/src/unistd/unlinkat.h
new file mode 100644 (file)
index 0000000..b32d7b5
--- /dev/null
@@ -0,0 +1,18 @@
+//===-- Implementation header for unlinkat ----------------------*- 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_UNLINKAT_H
+#define LLVM_LIBC_SRC_UNISTD_UNLINKAT_H
+
+namespace __llvm_libc {
+
+int unlinkat(int dfd, const char *path, int flags);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_UNISTD_UNLINKAT_H
index 03c59bf..4c7e820 100644 (file)
@@ -1 +1,2 @@
 add_subdirectory(mman)
+add_subdirectory(stat)
diff --git a/libc/test/src/sys/stat/CMakeLists.txt b/libc/test/src/sys/stat/CMakeLists.txt
new file mode 100644 (file)
index 0000000..eec671a
--- /dev/null
@@ -0,0 +1,17 @@
+add_libc_testsuite(libc_sys_stat_unittests)
+
+add_subdirectory(testdata)
+
+add_libc_unittest(
+  mkdirat_test
+  SUITE
+    libc_sys_stat_unittests
+  SRCS
+    mkdirat_test.cpp
+  DEPENDS
+    libc.include.errno
+    libc.include.fcntl
+    libc.include.sys_stat
+    libc.src.sys.stat.mkdirat
+    libc.src.unistd.rmdir
+)
diff --git a/libc/test/src/sys/stat/mkdirat_test.cpp b/libc/test/src/sys/stat/mkdirat_test.cpp
new file mode 100644 (file)
index 0000000..4a45fe2
--- /dev/null
@@ -0,0 +1,29 @@
+//===-- Unittests for mkdirat ---------------------------------------------===//
+//
+// 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/mkdirat.h"
+#include "src/unistd/rmdir.h"
+#include "test/ErrnoSetterMatcher.h"
+#include "utils/UnitTest/Test.h"
+#include "utils/testutils/FDReader.h"
+
+#include <errno.h>
+#include <fcntl.h>
+
+TEST(LlvmLibcMkdiratTest, CreateAndRemove) {
+  using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+  constexpr const char *TEST_DIR = "testdata/rmdir.testdir";
+  ASSERT_THAT(__llvm_libc::mkdirat(AT_FDCWD, TEST_DIR, S_IRWXU), Succeeds(0));
+  ASSERT_THAT(__llvm_libc::rmdir(TEST_DIR), Succeeds(0));
+}
+
+TEST(LlvmLibcMkdiratTest, BadPath) {
+  using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+  ASSERT_THAT(__llvm_libc::mkdirat(AT_FDCWD, "non-existent-dir/test", S_IRWXU),
+              Fails(ENOENT));
+}
diff --git a/libc/test/src/sys/stat/testdata/CMakeLists.txt b/libc/test/src/sys/stat/testdata/CMakeLists.txt
new file mode 100644 (file)
index 0000000..25d6247
--- /dev/null
@@ -0,0 +1,2 @@
+# This directory will be used to create test files and delete them
+# from tests.
index cba400b..abaec09 100644 (file)
@@ -1,5 +1,7 @@
 add_libc_testsuite(libc_unistd_unittests)
 
+add_subdirectory(testdata)
+
 add_libc_unittest(
   read_write_test
   SUITE
@@ -16,3 +18,45 @@ add_libc_unittest(
     libc.src.unistd.write
     libc.test.errno_setter_matcher
 )
+
+add_libc_unittest(
+  rmdir_test
+  SUITE
+    libc_unistd_unittests
+  SRCS
+    rmdir_test.cpp
+  DEPENDS
+    libc.include.errno
+    libc.include.fcntl
+    libc.src.sys.stat.mkdir
+    libc.src.unistd.rmdir
+)
+
+add_libc_unittest(
+  unlink_test
+  SUITE
+    libc_unistd_unittests
+  SRCS
+    unlink_test.cpp
+  DEPENDS
+    libc.include.errno
+    libc.include.unistd
+    libc.src.fcntl.open
+    libc.src.unistd.close
+    libc.src.unistd.unlink
+)
+
+add_libc_unittest(
+  unlinkat_test
+  SUITE
+    libc_unistd_unittests
+  SRCS
+    unlinkat_test.cpp
+  DEPENDS
+    libc.include.errno
+    libc.include.unistd
+    libc.src.fcntl.open
+    libc.src.fcntl.openat
+    libc.src.unistd.close
+    libc.src.unistd.unlinkat
+)
diff --git a/libc/test/src/unistd/rmdir_test.cpp b/libc/test/src/unistd/rmdir_test.cpp
new file mode 100644 (file)
index 0000000..5e07849
--- /dev/null
@@ -0,0 +1,28 @@
+//===-- Unittests for rmdir -----------------------------------------------===//
+//
+// 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/mkdir.h"
+#include "src/unistd/rmdir.h"
+#include "test/ErrnoSetterMatcher.h"
+#include "utils/UnitTest/Test.h"
+#include "utils/testutils/FDReader.h"
+
+#include <errno.h>
+#include <fcntl.h>
+
+TEST(LlvmLibcRmdirTest, CreateAndRemove) {
+  using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+  constexpr const char *TEST_DIR = "testdata/rmdir.testdir";
+  ASSERT_THAT(__llvm_libc::mkdir(TEST_DIR, S_IRWXU), Succeeds(0));
+  ASSERT_THAT(__llvm_libc::rmdir(TEST_DIR), Succeeds(0));
+}
+
+TEST(LlvmLibcRmdirTest, RemoveNonExistentDir) {
+  using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+  ASSERT_THAT(__llvm_libc::rmdir("testdata/non-existent-dir"), Fails(ENOENT));
+}
diff --git a/libc/test/src/unistd/testdata/CMakeLists.txt b/libc/test/src/unistd/testdata/CMakeLists.txt
new file mode 100644 (file)
index 0000000..25d6247
--- /dev/null
@@ -0,0 +1,2 @@
+# This directory will be used to create test files and delete them
+# from tests.
diff --git a/libc/test/src/unistd/unlink_test.cpp b/libc/test/src/unistd/unlink_test.cpp
new file mode 100644 (file)
index 0000000..be358cf
--- /dev/null
@@ -0,0 +1,31 @@
+//===-- Unittests for unlink ----------------------------------------------===//
+//
+// 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/unlink.h"
+#include "test/ErrnoSetterMatcher.h"
+#include "utils/UnitTest/Test.h"
+#include "utils/testutils/FDReader.h"
+
+#include <errno.h>
+
+TEST(LlvmLibcUnlinkTest, CreateAndUnlink) {
+  using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+  constexpr const char *TEST_FILE = "testdata/unlink.test";
+  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::unlink(TEST_FILE), Succeeds(0));
+}
+
+TEST(LlvmLibcUnlinkTest, UnlinkNonExistentFile) {
+  using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+  ASSERT_THAT(__llvm_libc::unlink("testdata/non-existent-file"), Fails(ENOENT));
+}
diff --git a/libc/test/src/unistd/unlinkat_test.cpp b/libc/test/src/unistd/unlinkat_test.cpp
new file mode 100644 (file)
index 0000000..8ce10c9
--- /dev/null
@@ -0,0 +1,45 @@
+//===-- Unittests for unlinkat --------------------------------------------===//
+//
+// 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/fcntl/openat.h"
+#include "src/unistd/close.h"
+#include "src/unistd/unlinkat.h"
+#include "test/ErrnoSetterMatcher.h"
+#include "utils/UnitTest/Test.h"
+#include "utils/testutils/FDReader.h"
+
+#include <errno.h>
+
+TEST(LlvmLibcUnlinkatTest, CreateAndDeleteTest) {
+  using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+  constexpr const char *TEST_DIR = "testdata";
+  constexpr const char *TEST_FILE = "openat.test";
+  int dir_fd = __llvm_libc::open(TEST_DIR, O_DIRECTORY);
+  ASSERT_EQ(errno, 0);
+  ASSERT_GT(dir_fd, 0);
+  int write_fd =
+      __llvm_libc::openat(dir_fd, 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::unlinkat(dir_fd, TEST_FILE, 0), Succeeds(0));
+  ASSERT_THAT(__llvm_libc::close(dir_fd), Succeeds(0));
+}
+
+TEST(LlvmLibcUnlinkatTest, UnlinkatNonExistentFile) {
+  constexpr const char *TEST_DIR = "testdata";
+  int dir_fd = __llvm_libc::open(TEST_DIR, O_DIRECTORY);
+  ASSERT_EQ(errno, 0);
+  ASSERT_GT(dir_fd, 0);
+  using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+  using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+  ASSERT_THAT(__llvm_libc::unlinkat(dir_fd, "non-existent-file", 0),
+              Fails(ENOENT));
+  ASSERT_THAT(__llvm_libc::close(dir_fd), Succeeds(0));
+}