[libc-commits] [libc] a173863 - [libc] Implement mkdtemp (#222106)

via libc-commits libc-commits at lists.llvm.org
Wed Sep 9 10:54:12 PDT 2026


Author: Jeff Bailey
Date: 2026-09-09T18:54:07+01:00
New Revision: a173863e63415ae54b5d0abf1bc5897c61604e5f

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

LOG: [libc] Implement mkdtemp (#222106)

Add the POSIX mkdtemp entrypoint for Linux targets (aarch64, riscv,
x86_64).

Extract the shared template replacement and random character retry loop
into linux/mktemp_util.h, relocating mkstemp to linux/mkstemp.cpp and
reusing the helper across both entrypoints.

Add comprehensive unit tests covering directory creation, permissions,
uniqueness, and invalid template handling.

Assisted-by: Automated tooling, human reviewed.

Added: 
    libc/src/stdlib/linux/mkdtemp.cpp
    libc/src/stdlib/linux/mkstemp.cpp
    libc/src/stdlib/linux/mktemp_util.h
    libc/src/stdlib/mkdtemp.h
    libc/test/src/stdlib/mkdtemp_test.cpp

Modified: 
    libc/config/linux/aarch64/entrypoints.txt
    libc/config/linux/riscv/entrypoints.txt
    libc/config/linux/x86_64/entrypoints.txt
    libc/include/stdlib.yaml
    libc/src/stdlib/CMakeLists.txt
    libc/src/stdlib/linux/CMakeLists.txt
    libc/src/stdlib/mkstemp.h
    libc/test/src/stdlib/CMakeLists.txt

Removed: 
    libc/src/stdlib/mkstemp.cpp


################################################################################
diff  --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index ea1ca820f6ec8..6167a80f27363 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -219,6 +219,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdlib.llabs
     libc.src.stdlib.lldiv
     libc.src.stdlib.memalignment
+    libc.src.stdlib.mkdtemp
     libc.src.stdlib.mkstemp
     libc.src.stdlib.qsort
     libc.src.stdlib.qsort_r

diff  --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 48cc1323b28fc..f3118f9657dcd 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -243,6 +243,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdlib.llabs
     libc.src.stdlib.lldiv
     libc.src.stdlib.memalignment
+    libc.src.stdlib.mkdtemp
     libc.src.stdlib.mkstemp
     libc.src.stdlib.qsort
     libc.src.stdlib.qsort_r

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 46b5efd13dfda..09c8043767383 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -243,6 +243,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdlib.llabs
     libc.src.stdlib.lldiv
     libc.src.stdlib.memalignment
+    libc.src.stdlib.mkdtemp
     libc.src.stdlib.mkstemp
     libc.src.stdlib.qsort
     libc.src.stdlib.qsort_r

diff  --git a/libc/include/stdlib.yaml b/libc/include/stdlib.yaml
index 784c52c7bdcf7..eb0ca6ce42eef 100644
--- a/libc/include/stdlib.yaml
+++ b/libc/include/stdlib.yaml
@@ -178,6 +178,12 @@ functions:
     return_type: size_t
     arguments:
       - type: const void *
+  - name: mkdtemp
+    standards:
+      - posix
+    return_type: char *
+    arguments:
+      - type: char *
   - name: mkstemp
     standards:
       - posix

diff  --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index 6f5ec7811c7bf..45f27c6da3619 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -411,23 +411,6 @@ add_entrypoint_object(
     libc.hdr.types.size_t
 )
 
-add_entrypoint_object(
-  mkstemp
-  SRCS
-    mkstemp.cpp
-  HDRS
-    mkstemp.h
-  DEPENDS
-    libc.hdr.errno_macros
-    libc.hdr.fcntl_macros
-    libc.src.__support.OSUtil.linux.syscall_wrappers.getrandom
-    libc.src.__support.OSUtil.linux.syscall_wrappers.open
-    libc.src.__support.common
-    libc.src.__support.libc_errno
-    libc.src.__support.macros.config
-    libc.src.__support.macros.null_check
-)
-
 add_entrypoint_object(
   mbtowc
   SRCS
@@ -615,6 +598,20 @@ add_entrypoint_object(
     .${LIBC_TARGET_OS}.getloadavg
 )
 
+add_entrypoint_object(
+  mkdtemp
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.mkdtemp
+)
+
+add_entrypoint_object(
+  mkstemp
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.mkstemp
+)
+
 if(NOT LLVM_LIBC_FULL_BUILD)
   return()
 endif()

diff  --git a/libc/src/stdlib/linux/CMakeLists.txt b/libc/src/stdlib/linux/CMakeLists.txt
index f4af7075cbad5..a996d131fb580 100644
--- a/libc/src/stdlib/linux/CMakeLists.txt
+++ b/libc/src/stdlib/linux/CMakeLists.txt
@@ -119,3 +119,53 @@ add_entrypoint_object(
     libc.src.__support.macros.config
 )
 
+add_header_library(
+  mktemp_util
+  HDRS
+    mktemp_util.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.stdint_proxy
+    libc.hdr.types.size_t
+    libc.src.__support.CPP.array
+    libc.src.__support.CPP.span
+    libc.src.__support.CPP.string_view
+    libc.src.__support.OSUtil.linux.syscall_wrappers.getrandom
+    libc.src.__support.common
+    libc.src.__support.error_or
+    libc.src.__support.macros.attributes
+    libc.src.__support.macros.config
+)
+
+add_entrypoint_object(
+  mkdtemp
+  SRCS
+    mkdtemp.cpp
+  HDRS
+    ../mkdtemp.h
+  DEPENDS
+    .mktemp_util
+    libc.hdr.sys_stat_macros
+    libc.src.__support.OSUtil.linux.syscall_wrappers.mkdir
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
+)
+
+add_entrypoint_object(
+  mkstemp
+  SRCS
+    mkstemp.cpp
+  HDRS
+    ../mkstemp.h
+  DEPENDS
+    .mktemp_util
+    libc.hdr.fcntl_macros
+    libc.src.__support.OSUtil.linux.syscall_wrappers.open
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
+)
+

diff  --git a/libc/src/stdlib/linux/mkdtemp.cpp b/libc/src/stdlib/linux/mkdtemp.cpp
new file mode 100644
index 0000000000000..8a7f1bc4c5175
--- /dev/null
+++ b/libc/src/stdlib/linux/mkdtemp.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of mkdtemp, a POSIX function that creates a unique temporary
+/// directory from a template string ending in at least six 'X' characters.
+///
+/// Replaces the trailing X's with random characters from the POSIX portable
+/// filename character set, creates the directory with 0700 permissions,
+/// and returns the pathname, retrying automatically on name collision. See:
+/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/mkdtemp.h"
+#include "hdr/sys_stat_macros.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/mkdir.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+#include "src/stdlib/linux/mktemp_util.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(char *, mkdtemp, (char *tmpl)) {
+  LIBC_CRASH_ON_NULLPTR(tmpl);
+
+  auto res = internal::mktemp_core(tmpl, [](const char *path) {
+    return linux_syscalls::mkdir(path, S_IRWXU);
+  });
+  if (!res.has_value()) {
+    libc_errno = res.error();
+    return nullptr;
+  }
+  return tmpl;
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/stdlib/linux/mkstemp.cpp b/libc/src/stdlib/linux/mkstemp.cpp
new file mode 100644
index 0000000000000..c1546958de891
--- /dev/null
+++ b/libc/src/stdlib/linux/mkstemp.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of mkstemp, a POSIX function that creates a unique temporary
+/// file from a template string ending in at least six 'X' characters.
+///
+/// Replaces the trailing X's with random characters from the POSIX portable
+/// filename character set, opens the file exclusively, and returns an open
+/// file descriptor, retrying automatically on name collision. See:
+/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkstemp.html
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/mkstemp.h"
+#include "hdr/fcntl_macros.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/open.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+#include "src/stdlib/linux/mktemp_util.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, mkstemp, (char *tmpl)) {
+  LIBC_CRASH_ON_NULLPTR(tmpl);
+
+  auto res = internal::mktemp_core(tmpl, [](const char *path) {
+    return linux_syscalls::open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
+  });
+  if (!res.has_value()) {
+    libc_errno = res.error();
+    return -1;
+  }
+  return res.value();
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/stdlib/linux/mktemp_util.h b/libc/src/stdlib/linux/mktemp_util.h
new file mode 100644
index 0000000000000..af9856510cdd0
--- /dev/null
+++ b/libc/src/stdlib/linux/mktemp_util.h
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Shared helper functions for creating unique temporary files and directories.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDLIB_LINUX_MKTEMP_UTIL_H
+#define LLVM_LIBC_SRC_STDLIB_LINUX_MKTEMP_UTIL_H
+
+#include "hdr/errno_macros.h"
+#include "hdr/stdint_proxy.h"
+#include "hdr/types/size_t.h"
+#include "src/__support/CPP/array.h"
+#include "src/__support/CPP/span.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/getrandom.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace internal {
+
+// POSIX portable filename character set, sorted by ASCII value.
+// See
+// https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_265
+LIBC_INLINE_VAR constexpr cpp::string_view MKTEMP_CHARSET =
+    "-._0123456789"
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "abcdefghijklmnopqrstuvwxyz";
+
+// Minimum number of trailing 'X' characters required by POSIX.
+LIBC_INLINE_VAR constexpr size_t MIN_MKTEMP_SUFFIX = 6;
+
+/// Core helper function for creating temporary files and directories.
+///
+/// \param tmpl Template string ending in at least six 'X' characters.
+/// \param create_fn Callable taking `const char *path` and returning
+/// `ErrorOr<int>`.
+/// \return `ErrorOr<int>` with the result of `create_fn` on success, or Error
+/// on failure.
+template <typename CreateFn>
+LIBC_INLINE ErrorOr<int> mktemp_core(char *tmpl, CreateFn create_fn) {
+  cpp::string_view str_view(tmpl);
+  size_t len = str_view.size();
+  if (len < MIN_MKTEMP_SUFFIX)
+    return Error(EINVAL);
+
+  size_t pos = str_view.find_last_not_of('X');
+  size_t count = (pos == cpp::string_view::npos) ? len : len - pos - 1;
+
+  if (count < MIN_MKTEMP_SUFFIX)
+    return Error(EINVAL);
+
+  cpp::span<char> suffix(tmpl + (len - count), count);
+
+  // Maximum collision retry attempts before returning EEXIST per POSIX.
+  constexpr size_t MAX_ATTEMPTS = 10000;
+  // Read random bytes in batches to minimize getrandom syscall overhead.
+  constexpr size_t BATCH_SIZE = 64;
+  cpp::array<uint8_t, BATCH_SIZE> rand_buf;
+
+  for (size_t attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
+    for (size_t offset = 0; offset < count;) {
+      size_t chunk =
+          (count - offset < BATCH_SIZE) ? (count - offset) : BATCH_SIZE;
+      auto ret = linux_syscalls::getrandom(rand_buf.data(), chunk, 0);
+      if (!ret.has_value())
+        return Error(ret.error());
+      if (ret.value() == 0)
+        return Error(EIO);
+      for (size_t j = 0; j < static_cast<size_t>(ret.value()); ++j) {
+        suffix[offset + j] =
+            MKTEMP_CHARSET[rand_buf[j] % MKTEMP_CHARSET.size()];
+      }
+      offset += static_cast<size_t>(ret.value());
+    }
+
+    auto result = create_fn(tmpl);
+    if (!result.has_value() && result.error() == EEXIST)
+      continue;
+    return result;
+  }
+  return Error(EEXIST);
+}
+
+} // namespace internal
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDLIB_LINUX_MKTEMP_UTIL_H

diff  --git a/libc/src/stdlib/mkdtemp.h b/libc/src/stdlib/mkdtemp.h
new file mode 100644
index 0000000000000..322aabf54ea55
--- /dev/null
+++ b/libc/src/stdlib/mkdtemp.h
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Declaration of mkdtemp, a POSIX function that creates a unique temporary
+/// directory from a template string ending in at least six 'X' characters.
+///
+/// Replaces the trailing X's with random characters from the POSIX portable
+/// filename character set, creates the directory with 0700 permissions,
+/// and returns the pathname, retrying automatically on name collision. See:
+/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDLIB_MKDTEMP_H
+#define LLVM_LIBC_SRC_STDLIB_MKDTEMP_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+/// Create a unique temporary directory from a template string.
+///
+/// \param tmpl Template string ending in at least six 'X' characters.
+/// \return Pointer to the modified template string on success, nullptr on
+/// error.
+char *mkdtemp(char *tmpl);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDLIB_MKDTEMP_H

diff  --git a/libc/src/stdlib/mkstemp.cpp b/libc/src/stdlib/mkstemp.cpp
deleted file mode 100644
index 3fb0d902160c2..0000000000000
--- a/libc/src/stdlib/mkstemp.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Implementation of mkstemp, a POSIX function that creates a unique temporary
-/// file from a template string ending in at least six 'X' characters.
-///
-/// Replaces the trailing X's with random characters from the POSIX portable
-/// filename character set, opens the file exclusively, and returns an open
-/// file descriptor, retrying automatically on name collision. See:
-/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html
-///
-//===----------------------------------------------------------------------===//
-
-#include "src/stdlib/mkstemp.h"
-#include "hdr/errno_macros.h"
-#include "hdr/fcntl_macros.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/linux/syscall_wrappers/getrandom.h"
-#include "src/__support/OSUtil/linux/syscall_wrappers/open.h"
-#include "src/__support/common.h"
-#include "src/__support/libc_errno.h"
-#include "src/__support/macros/config.h"
-#include "src/__support/macros/null_check.h"
-
-namespace LIBC_NAMESPACE_DECL {
-
-LLVM_LIBC_FUNCTION(int, mkstemp, (char *tmpl)) {
-  LIBC_CRASH_ON_NULLPTR(tmpl);
-
-  cpp::string_view str_view(tmpl);
-  size_t count = 0;
-  size_t len = str_view.size();
-
-  for (size_t i = len; i > 0; i--) {
-    if (str_view[i - 1] != 'X')
-      break;
-    count++;
-  }
-
-  if (count < 6) {
-    libc_errno = EINVAL;
-    return -1;
-  }
-
-  char *suffix = tmpl + len - count;
-
-  // POSIX portable filename character set, sorted by ASCII value.
-  // See
-  // https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_265
-  const char charset[] = "-._0123456789"
-                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                         "abcdefghijklmnopqrstuvwxyz";
-
-  int result = -1;
-  bool file_created = false;
-  while (!file_created) {
-    for (size_t i = 0; i < count; i++) {
-      uint8_t rand_byte;
-      auto ret = linux_syscalls::getrandom(&rand_byte, 1, 0);
-      if (!ret.has_value()) {
-        libc_errno = ret.error();
-        return -1;
-      }
-      // sizeof(charset) - 1 to account for the null terminator
-      suffix[i] = charset[rand_byte % (sizeof(charset) - 1)];
-    }
-
-    auto fd = linux_syscalls::open(tmpl, O_RDWR | O_CREAT | O_EXCL, 0600);
-    if (!fd.has_value()) {
-      if (fd.error() == EEXIST)
-        continue;
-      libc_errno = fd.error();
-      return -1;
-    }
-    result = fd.value();
-    file_created = true;
-  }
-  return result;
-}
-
-} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/stdlib/mkstemp.h b/libc/src/stdlib/mkstemp.h
index 4e113780a72dc..1b3503d1beac5 100644
--- a/libc/src/stdlib/mkstemp.h
+++ b/libc/src/stdlib/mkstemp.h
@@ -13,7 +13,7 @@
 /// Replaces the trailing X's with random characters from the POSIX portable
 /// filename character set, opens the file exclusively, and returns an open
 /// file descriptor, retrying automatically on name collision. See:
-/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html
+/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkstemp.html
 ///
 //===----------------------------------------------------------------------===//
 

diff  --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt
index 1bcffea447ed6..c8912ea348cb1 100644
--- a/libc/test/src/stdlib/CMakeLists.txt
+++ b/libc/test/src/stdlib/CMakeLists.txt
@@ -417,6 +417,29 @@ add_libc_test(
     libc.src.stdlib.memalignment
 )
 
+add_libc_test(
+  mkdtemp_test
+  SUITE
+    libc-stdlib-tests
+  SRCS
+    mkdtemp_test.cpp
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.sys_stat_macros
+    libc.hdr.types.struct_stat
+    libc.hdr.unistd_macros
+    libc.src.__support.CPP.scope
+    libc.src.__support.CPP.string_view
+    libc.src.stdlib.mkdtemp
+    libc.src.string.strdup
+    libc.src.string.strlen
+    libc.src.sys.stat.stat
+    libc.src.unistd.access
+    libc.src.unistd.rmdir
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
 add_libc_test(
   mkstemp_test
   SUITE

diff  --git a/libc/test/src/stdlib/mkdtemp_test.cpp b/libc/test/src/stdlib/mkdtemp_test.cpp
new file mode 100644
index 0000000000000..306fd2073dd3e
--- /dev/null
+++ b/libc/test/src/stdlib/mkdtemp_test.cpp
@@ -0,0 +1,184 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Tests for mkdtemp
+/// See: https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/sys_stat_macros.h"
+#include "hdr/types/struct_stat.h"
+#include "hdr/unistd_macros.h"
+#include "src/__support/CPP/scope.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/stdlib/mkdtemp.h"
+#include "src/string/strdup.h"
+#include "src/string/strlen.h"
+#include "src/sys/stat/stat.h"
+#include "src/unistd/access.h"
+#include "src/unistd/rmdir.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LIBC_NAMESPACE::cpp::string_view;
+using LlvmLibcMkdtempTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcMkdtempTest, ValidTemplate) {
+  char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+  ASSERT_NE(tmpl, nullptr);
+  auto cleanup = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::rmdir(tmpl);
+    ::free(tmpl);
+  });
+
+  ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl));
+  EXPECT_THAT(LIBC_NAMESPACE::access(tmpl, F_OK), Succeeds(0));
+
+  struct stat st;
+  ASSERT_THAT(LIBC_NAMESPACE::stat(tmpl, &st), Succeeds(0));
+  EXPECT_EQ(st.st_mode & S_IFMT, static_cast<mode_t>(S_IFDIR));
+  EXPECT_EQ(st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO),
+            static_cast<mode_t>(S_IRWXU));
+}
+
+TEST_F(LlvmLibcMkdtempTest, TemplateModifiedInPlace) {
+  char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+  ASSERT_NE(tmpl, nullptr);
+  auto cleanup_tmpl = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::rmdir(tmpl);
+    ::free(tmpl);
+  });
+
+  char *orig = LIBC_NAMESPACE::strdup(tmpl);
+  ASSERT_NE(orig, nullptr);
+  auto cleanup_orig = LIBC_NAMESPACE::cpp::scope_exit([&] { ::free(orig); });
+
+  size_t len = LIBC_NAMESPACE::strlen(tmpl);
+  ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl));
+
+  EXPECT_EQ(string_view(tmpl, len - 6), string_view(orig, len - 6));
+  EXPECT_NE(string_view(tmpl + len - 6, 6), string_view("XXXXXX"));
+}
+
+TEST_F(LlvmLibcMkdtempTest, AllCharactersInCharset) {
+  char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+  ASSERT_NE(tmpl, nullptr);
+  auto cleanup = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::rmdir(tmpl);
+    ::free(tmpl);
+  });
+
+  size_t len = LIBC_NAMESPACE::strlen(tmpl);
+  ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl));
+
+  // POSIX portable filename character set, sorted by ASCII value.
+  // See
+  // https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_265
+  constexpr string_view CHARSET = "-._0123456789"
+                                  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                                  "abcdefghijklmnopqrstuvwxyz";
+  for (char c : string_view(tmpl + len - 6, 6))
+    EXPECT_NE(CHARSET.find_first_of(c), string_view::npos);
+}
+
+TEST_F(LlvmLibcMkdtempTest, Uniqueness) {
+  char *tmpl1 = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+  ASSERT_NE(tmpl1, nullptr);
+  auto cleanup1 = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::rmdir(tmpl1);
+    ::free(tmpl1);
+  });
+
+  char *tmpl2 = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+  ASSERT_NE(tmpl2, nullptr);
+  auto cleanup2 = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::rmdir(tmpl2);
+    ::free(tmpl2);
+  });
+
+  ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl1), Succeeds(tmpl1));
+  ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl2), Succeeds(tmpl2));
+
+  EXPECT_STRNE(tmpl1, tmpl2);
+}
+
+TEST_F(LlvmLibcMkdtempTest, SixXsNoPrefix) {
+  char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("XXXXXX"));
+  ASSERT_NE(tmpl, nullptr);
+  auto cleanup = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::rmdir(tmpl);
+    ::free(tmpl);
+  });
+
+  ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl));
+  EXPECT_THAT(LIBC_NAMESPACE::access(tmpl, F_OK), Succeeds(0));
+}
+
+TEST_F(LlvmLibcMkdtempTest, MoreThanSixXs) {
+  char *tmpl =
+      LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXXXXXX"));
+  ASSERT_NE(tmpl, nullptr);
+  auto cleanup_tmpl = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::rmdir(tmpl);
+    ::free(tmpl);
+  });
+
+  char *orig = LIBC_NAMESPACE::strdup(tmpl);
+  ASSERT_NE(orig, nullptr);
+  auto cleanup_orig = LIBC_NAMESPACE::cpp::scope_exit([&] { ::free(orig); });
+
+  size_t len = LIBC_NAMESPACE::strlen(tmpl);
+  ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl));
+
+  EXPECT_EQ(string_view(tmpl, len - 10), string_view(orig, len - 10));
+  EXPECT_NE(string_view(tmpl + len - 10, 10), string_view("XXXXXXXXXX"));
+  EXPECT_THAT(LIBC_NAMESPACE::access(tmpl, F_OK), Succeeds(0));
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+TEST_F(LlvmLibcMkdtempTest, NullPointer) {
+  ASSERT_DEATH([] { LIBC_NAMESPACE::mkdtemp(nullptr); }, WITH_SIGNAL(-1));
+}
+#endif
+
+TEST_F(LlvmLibcMkdtempTest, TemplateTooShort) {
+  char tmpl[] = "XXXXX";
+  EXPECT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Fails<char *>(EINVAL, nullptr));
+}
+
+TEST_F(LlvmLibcMkdtempTest, DoesNotEndInXs) {
+  char tmpl[] = "tmp_XXXXXY";
+  EXPECT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Fails<char *>(EINVAL, nullptr));
+}
+
+TEST_F(LlvmLibcMkdtempTest, XsNotAtEnd) {
+  char tmpl[] = "XXXXXXtmp";
+  EXPECT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Fails<char *>(EINVAL, nullptr));
+}
+
+TEST_F(LlvmLibcMkdtempTest, FiveXsAtEnd) {
+  char tmpl[] = "tmp_XXXXX";
+  EXPECT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Fails<char *>(EINVAL, nullptr));
+}
+
+TEST_F(LlvmLibcMkdtempTest, EmptyString) {
+  char tmpl[] = "";
+  EXPECT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Fails<char *>(EINVAL, nullptr));
+}
+
+TEST_F(LlvmLibcMkdtempTest, NonExistentParentDirectory) {
+  char *tmpl = LIBC_NAMESPACE::strdup(
+      libc_make_test_file_path("non_existent_dir/tmp_XXXXXX"));
+  ASSERT_NE(tmpl, nullptr);
+  auto cleanup = LIBC_NAMESPACE::cpp::scope_exit([&] { ::free(tmpl); });
+  EXPECT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Fails<char *>(ENOENT, nullptr));
+}


        


More information about the libc-commits mailing list