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

Jeff Bailey via libc-commits libc-commits at lists.llvm.org
Wed Sep 9 07:08:57 PDT 2026


================
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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);
+  char *orig = LIBC_NAMESPACE::strdup(tmpl);
+  ASSERT_NE(orig, nullptr);
+  auto cleanup = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::rmdir(tmpl);
+    ::free(tmpl);
+    ::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"));
+  char *tmpl2 = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+  ASSERT_NE(tmpl1, nullptr);
+  ASSERT_NE(tmpl2, nullptr);
+  auto cleanup1 = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::rmdir(tmpl1);
+    ::free(tmpl1);
+  });
+  auto cleanup2 = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::rmdir(tmpl2);
+    ::free(tmpl2);
+  });
----------------
kaladron wrote:

I have them separate because the cleanup is scheduled immediately after a resource is booked.  If I do both first and then do the cleanup after, if the second strdup fails then the test will fail before the cleanup has been scheduled.

https://github.com/llvm/llvm-project/pull/222106


More information about the libc-commits mailing list