[libc-commits] [libc] [libc] Implement mkostemp (PR #224112)

via libc-commits libc-commits at lists.llvm.org
Wed Sep 16 12:05:19 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Jeff Bailey (kaladron)

<details>
<summary>Changes</summary>

Added the POSIX mkostemp entrypoint for Linux targets (aarch64, riscv, x86_64) following the shared mktemp_core helper pattern from commit 1b48a14a3608.

Validated open flags against the supported POSIX Issue 8 set (O_APPEND, O_CLOEXEC, O_DSYNC, O_SYNC) before invoking internal::mktemp_core.

Added unit tests in mkostemp_test.cpp covering flag combinations, append semantics, template modification, and error conditions.

Assisted-by: Automated tooling, human reviewed.

---
Full diff: https://github.com/llvm/llvm-project/pull/224112.diff


10 Files Affected:

- (modified) libc/config/linux/aarch64/entrypoints.txt (+1) 
- (modified) libc/config/linux/riscv/entrypoints.txt (+1) 
- (modified) libc/config/linux/x86_64/entrypoints.txt (+1) 
- (modified) libc/include/stdlib.yaml (+7) 
- (modified) libc/src/stdlib/CMakeLists.txt (+7) 
- (modified) libc/src/stdlib/linux/CMakeLists.txt (+17) 
- (added) libc/src/stdlib/linux/mkostemp.cpp (+54) 
- (added) libc/src/stdlib/mkostemp.h (+33) 
- (modified) libc/test/src/stdlib/CMakeLists.txt (+33) 
- (added) libc/test/src/stdlib/mkostemp_test.cpp (+337) 


``````````diff
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index bc76076582fc1..91f040400a8db 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -221,6 +221,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdlib.lldiv
     libc.src.stdlib.memalignment
     libc.src.stdlib.mkdtemp
+    libc.src.stdlib.mkostemp
     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 77c06949c1425..f1d4e5d7debab 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -245,6 +245,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdlib.lldiv
     libc.src.stdlib.memalignment
     libc.src.stdlib.mkdtemp
+    libc.src.stdlib.mkostemp
     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 ff7b7099d28c6..dcab91033fb73 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -245,6 +245,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdlib.lldiv
     libc.src.stdlib.memalignment
     libc.src.stdlib.mkdtemp
+    libc.src.stdlib.mkostemp
     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 eb0ca6ce42eef..aabb9dd106af5 100644
--- a/libc/include/stdlib.yaml
+++ b/libc/include/stdlib.yaml
@@ -184,6 +184,13 @@ functions:
     return_type: char *
     arguments:
       - type: char *
+  - name: mkostemp
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: char *
+      - type: int
   - name: mkstemp
     standards:
       - posix
diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index 45f27c6da3619..2b115114d0385 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -605,6 +605,13 @@ add_entrypoint_object(
     .${LIBC_TARGET_OS}.mkdtemp
 )
 
+add_entrypoint_object(
+  mkostemp
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.mkostemp
+)
+
 add_entrypoint_object(
   mkstemp
   ALIAS
diff --git a/libc/src/stdlib/linux/CMakeLists.txt b/libc/src/stdlib/linux/CMakeLists.txt
index a996d131fb580..d65952985cec1 100644
--- a/libc/src/stdlib/linux/CMakeLists.txt
+++ b/libc/src/stdlib/linux/CMakeLists.txt
@@ -153,6 +153,23 @@ add_entrypoint_object(
     libc.src.__support.macros.null_check
 )
 
+add_entrypoint_object(
+  mkostemp
+  SRCS
+    mkostemp.cpp
+  HDRS
+    ../mkostemp.h
+  DEPENDS
+    .mktemp_util
+    libc.hdr.errno_macros
+    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
+)
+
 add_entrypoint_object(
   mkstemp
   SRCS
diff --git a/libc/src/stdlib/linux/mkostemp.cpp b/libc/src/stdlib/linux/mkostemp.cpp
new file mode 100644
index 0000000000000..d640a62123143
--- /dev/null
+++ b/libc/src/stdlib/linux/mkostemp.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mkostemp, a POSIX function that creates a unique temporary
+/// file from a template string ending in at least six 'X' characters with
+/// additional open flags.
+///
+/// Replaces the trailing X's with random characters from the POSIX portable
+/// filename character set, opens the file exclusively with the specified flags,
+/// and returns an open file descriptor, retrying automatically on name
+/// collision. See:
+/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/mkostemp.h"
+#include "hdr/errno_macros.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, mkostemp, (char *tmpl, int flags)) {
+  LIBC_CRASH_ON_NULLPTR(tmpl);
+
+  // POSIX.1-2024 restricts flags to the following open constants.
+  constexpr int ALLOWED_FLAGS = O_APPEND | O_CLOEXEC | O_DSYNC | O_SYNC;
+  if ((flags & ~ALLOWED_FLAGS) != 0) {
+    libc_errno = EINVAL;
+    return -1;
+  }
+
+  auto res = internal::mktemp_core(tmpl, [flags](const char *path) {
+    return linux_syscalls::open(path, O_RDWR | O_CREAT | O_EXCL | flags, 0600);
+  });
+  if (!res.has_value()) {
+    libc_errno = res.error();
+    return -1;
+  }
+  return res.value();
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/mkostemp.h b/libc/src/stdlib/mkostemp.h
new file mode 100644
index 0000000000000..23ffd58239858
--- /dev/null
+++ b/libc/src/stdlib/mkostemp.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mkostemp, a POSIX function that creates a unique temporary
+/// file from a template string ending in at least six 'X' characters with
+/// additional open flags.
+///
+/// Replaces the trailing X's with random characters from the POSIX portable
+/// filename character set, opens the file exclusively with the specified flags,
+/// and returns an open file descriptor, retrying automatically on name
+/// collision. See:
+/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDLIB_MKOSTEMP_H
+#define LLVM_LIBC_SRC_STDLIB_MKOSTEMP_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int mkostemp(char *tmpl, int flags);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDLIB_MKOSTEMP_H
diff --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt
index c8912ea348cb1..40167c1638cc7 100644
--- a/libc/test/src/stdlib/CMakeLists.txt
+++ b/libc/test/src/stdlib/CMakeLists.txt
@@ -440,6 +440,39 @@ add_libc_test(
     libc.test.UnitTest.ErrnoSetterMatcher
 )
 
+add_libc_test(
+  mkostemp_test
+  SUITE
+    libc-stdlib-tests
+  SRCS
+    mkostemp_test.cpp
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fcntl_macros
+    libc.hdr.func.free
+    libc.hdr.signal_macros
+    libc.hdr.stdio_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.fcntl.fcntl
+    libc.src.stdlib.mkostemp
+    libc.src.string.strdup
+    libc.src.string.strlen
+    libc.src.sys.stat.stat
+    libc.src.unistd.access
+    libc.src.unistd.close
+    libc.src.unistd.lseek
+    libc.src.unistd.read
+    libc.src.unistd.unlink
+    libc.src.unistd.write
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+    libc.test.UnitTest.MemoryMatcher
+)
+
 add_libc_test(
   mkstemp_test
   SUITE
diff --git a/libc/test/src/stdlib/mkostemp_test.cpp b/libc/test/src/stdlib/mkostemp_test.cpp
new file mode 100644
index 0000000000000..e910367c57b01
--- /dev/null
+++ b/libc/test/src/stdlib/mkostemp_test.cpp
@@ -0,0 +1,337 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mkostemp
+/// See: https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "hdr/func/free.h"
+#include "hdr/signal_macros.h"
+#include "hdr/stdio_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/fcntl/fcntl.h"
+#include "src/stdlib/mkostemp.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/close.h"
+#include "src/unistd/lseek.h"
+#include "src/unistd/read.h"
+#include "src/unistd/unlink.h"
+#include "src/unistd/write.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/MemoryMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LIBC_NAMESPACE::cpp::string_view;
+using LIBC_NAMESPACE::testing::MemoryView;
+using LlvmLibcMkostempTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcMkostempTest, ValidTemplateDefaultFlags) {
+  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::unlink(tmpl);
+    ::free(tmpl);
+  });
+
+  int fd = -1;
+  ASSERT_THAT(fd = LIBC_NAMESPACE::mkostemp(tmpl, 0),
+              returns(GE(0)).with_errno(EQ(0)));
+  auto cleanup_fd =
+      LIBC_NAMESPACE::cpp::scope_exit([&] { LIBC_NAMESPACE::close(fd); });
+
+  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_IFREG));
+  EXPECT_EQ(st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO),
+            static_cast<mode_t>(S_IRUSR | S_IWUSR));
+
+  ASSERT_THAT(LIBC_NAMESPACE::write(fd, "llvm", 4),
+              Succeeds(static_cast<ssize_t>(4)));
+}
+
+TEST_F(LlvmLibcMkostempTest, 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::unlink(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);
+  int fd = -1;
+  ASSERT_THAT(fd = LIBC_NAMESPACE::mkostemp(tmpl, 0),
+              returns(GE(0)).with_errno(EQ(0)));
+  auto cleanup_fd =
+      LIBC_NAMESPACE::cpp::scope_exit([&] { LIBC_NAMESPACE::close(fd); });
+
+  EXPECT_EQ(string_view(tmpl, len - 6), string_view(orig, len - 6));
+  EXPECT_NE(string_view(tmpl, len).substr(len - 6), string_view("XXXXXX"));
+}
+
+TEST_F(LlvmLibcMkostempTest, AllCharactersInCharset) {
+  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::unlink(tmpl);
+    ::free(tmpl);
+  });
+
+  size_t len = LIBC_NAMESPACE::strlen(tmpl);
+  int fd = -1;
+  ASSERT_THAT(fd = LIBC_NAMESPACE::mkostemp(tmpl, 0),
+              returns(GE(0)).with_errno(EQ(0)));
+  auto cleanup_fd =
+      LIBC_NAMESPACE::cpp::scope_exit([&] { LIBC_NAMESPACE::close(fd); });
+
+  // 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).substr(len - 6))
+    EXPECT_NE(CHARSET.find_first_of(c), string_view::npos);
+}
+
+TEST_F(LlvmLibcMkostempTest, Uniqueness) {
+  char *tmpl1 = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+  ASSERT_NE(tmpl1, nullptr);
+  auto cleanup_tmpl1 = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::unlink(tmpl1);
+    ::free(tmpl1);
+  });
+
+  char *tmpl2 = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+  ASSERT_NE(tmpl2, nullptr);
+  auto cleanup_tmpl2 = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::unlink(tmpl2);
+    ::free(tmpl2);
+  });
+
+  int fd1 = -1;
+  ASSERT_THAT(fd1 = LIBC_NAMESPACE::mkostemp(tmpl1, 0),
+              returns(GE(0)).with_errno(EQ(0)));
+  auto cleanup_fd1 =
+      LIBC_NAMESPACE::cpp::scope_exit([&] { LIBC_NAMESPACE::close(fd1); });
+
+  int fd2 = -1;
+  ASSERT_THAT(fd2 = LIBC_NAMESPACE::mkostemp(tmpl2, 0),
+              returns(GE(0)).with_errno(EQ(0)));
+  auto cleanup_fd2 =
+      LIBC_NAMESPACE::cpp::scope_exit([&] { LIBC_NAMESPACE::close(fd2); });
+
+  EXPECT_STRNE(tmpl1, tmpl2);
+}
+
+TEST_F(LlvmLibcMkostempTest, FlagCloexec) {
+  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::unlink(tmpl);
+    ::free(tmpl);
+  });
+
+  int fd = -1;
+  ASSERT_THAT(fd = LIBC_NAMESPACE::mkostemp(tmpl, O_CLOEXEC),
+              returns(GE(0)).with_errno(EQ(0)));
+  auto cleanup_fd =
+      LIBC_NAMESPACE::cpp::scope_exit([&] { LIBC_NAMESPACE::close(fd); });
+
+  int fd_flags = LIBC_NAMESPACE::fcntl(fd, F_GETFD);
+  ASSERT_GE(fd_flags, 0);
+  EXPECT_EQ(fd_flags & FD_CLOEXEC, FD_CLOEXEC);
+}
+
+TEST_F(LlvmLibcMkostempTest, FlagAppend) {
+  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::unlink(tmpl);
+    ::free(tmpl);
+  });
+
+  int fd = -1;
+  ASSERT_THAT(fd = LIBC_NAMESPACE::mkostemp(tmpl, O_APPEND),
+              returns(GE(0)).with_errno(EQ(0)));
+  auto cleanup_fd =
+      LIBC_NAMESPACE::cpp::scope_exit([&] { LIBC_NAMESPACE::close(fd); });
+
+  int fl_flags = LIBC_NAMESPACE::fcntl(fd, F_GETFL);
+  ASSERT_GE(fl_flags, 0);
+  EXPECT_EQ(fl_flags & O_APPEND, O_APPEND);
+
+  ASSERT_THAT(LIBC_NAMESPACE::write(fd, "abc", 3),
+              Succeeds(static_cast<ssize_t>(3)));
+  ASSERT_THAT(LIBC_NAMESPACE::lseek(fd, 0, SEEK_SET),
+              Succeeds(static_cast<off_t>(0)));
+  // With O_APPEND, writes must always append to the end of the file.
+  ASSERT_THAT(LIBC_NAMESPACE::write(fd, "def", 3),
+              Succeeds(static_cast<ssize_t>(3)));
+  ASSERT_THAT(LIBC_NAMESPACE::lseek(fd, 0, SEEK_SET),
+              Succeeds(static_cast<off_t>(0)));
+
+  char buf[6];
+  ASSERT_THAT(LIBC_NAMESPACE::read(fd, buf, 6),
+              Succeeds(static_cast<ssize_t>(6)));
+  EXPECT_MEM_EQ(MemoryView("abcdef", 6), MemoryView(buf, 6));
+}
+
+TEST_F(LlvmLibcMkostempTest, FlagSyncVariants) {
+  constexpr int SYNC_FLAGS[] = {O_SYNC, O_DSYNC};
+  for (int sync_flag : SYNC_FLAGS) {
+    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::unlink(tmpl);
+      ::free(tmpl);
+    });
+
+    int fd = -1;
+    ASSERT_THAT(fd = LIBC_NAMESPACE::mkostemp(tmpl, sync_flag),
+                returns(GE(0)).with_errno(EQ(0)));
+    auto cleanup_fd =
+        LIBC_NAMESPACE::cpp::scope_exit([&] { LIBC_NAMESPACE::close(fd); });
+    int fl = LIBC_NAMESPACE::fcntl(fd, F_GETFL);
+    ASSERT_GE(fl, 0);
+    EXPECT_EQ(fl & sync_flag, sync_flag);
+  }
+}
+
+TEST_F(LlvmLibcMkostempTest, CombinedFlags) {
+  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::unlink(tmpl);
+    ::free(tmpl);
+  });
+
+  int fd = -1;
+  ASSERT_THAT(fd = LIBC_NAMESPACE::mkostemp(tmpl, O_CLOEXEC | O_APPEND),
+              returns(GE(0)).with_errno(EQ(0)));
+  auto cleanup_fd =
+      LIBC_NAMESPACE::cpp::scope_exit([&] { LIBC_NAMESPACE::close(fd); });
+
+  int fd_flags = LIBC_NAMESPACE::fcntl(fd, F_GETFD);
+  ASSERT_GE(fd_flags, 0);
+  EXPECT_EQ(fd_flags & FD_CLOEXEC, FD_CLOEXEC);
+
+  int fl_flags = LIBC_NAMESPACE::fcntl(fd, F_GETFL);
+  ASSERT_GE(fl_flags, 0);
+  EXPECT_EQ(fl_flags & O_APPEND, O_APPEND);
+}
+
+TEST_F(LlvmLibcMkostempTest, SixXsNoPrefix) {
+  char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("XXXXXX"));
+  ASSERT_NE(tmpl, nullptr);
+  auto cleanup_tmpl = LIBC_NAMESPACE::cpp::scope_exit([&] {
+    LIBC_NAMESPACE::unlink(tmpl);
+    ::free(tmpl);
+  });
+
+  int fd = -1;
+  ASSERT_THAT(fd = LIBC_NAMESPACE::mkostemp(tmpl, 0),
+              returns(GE(0)).with_errno(EQ(0)));
+  auto cleanup_fd =
+      LIBC_NAMESPACE::cpp::scope_exit([&] { LIBC_NAMESPACE::close(fd); });
+
+  EXPECT_THAT(LIBC_NAMESPACE::access(tmpl, F_OK), Succeeds(0));
+}
+
+TEST_F(LlvmLibcMkostempTest, 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::unlink(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);
+  int fd = -1;
+  ASSERT_THAT(fd = LIBC_NAMESPACE::mkostemp(tmpl, 0),
+              returns(GE(0)).with_errno(EQ(0)));
+  auto cleanup_fd =
+      LIBC_NAMESPACE::cpp::scope_exit([&] { LIBC_NAMESPACE::close(fd); });
+
+  EXPECT_EQ(string_view(tmpl, len - 10), string_view(orig, len - 10));
+  EXPECT_NE(string_view(tmpl, len).substr(len - 10), string_view("XXXXXXXXXX"));
+  EXPECT_THAT(LIBC_NAMESPACE::access(tmpl, F_OK), Succeeds(0));
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+TEST_F(LlvmLibcMkostempTest, NullPointer) {
+  ASSERT_DEATH([] { LIBC_NAMESPACE::mkostemp(nullptr, 0); }, WITH_SIGNAL(-1));
+}
+#endif
+
+TEST_F(LlvmLibcMkostempTest, InvalidFlags) {
+  char tmpl[] = "tmp_XXXXXX";
+  EXPECT_THAT(LIBC_NAMESPACE::mkostemp(tmpl, O_CREAT), Fails(EINVAL));
+  EXPECT_THAT(LIBC_NAMESPACE::mkostemp(tmpl, O_EXCL), Fails(EINVAL));
+  EXPECT_THAT(LIBC_NAMESPACE::mkostemp(tmpl, O_RDWR), Fails(EINVAL));
+  EXPECT_THAT(LIBC_NAMESPACE::mkostemp(tmpl, O_WRONLY), Fails(EINVAL));
+  EXPECT_THAT(LIBC_NAMESPACE::mkostemp(tmpl, O_TRUNC), Fails(EINVAL));
+  EXPECT_THAT(LIBC_NAMESPACE::mkostemp(tmpl, -1), Fails(EINVAL));
+  EXPECT_STREQ(tmpl, "tmp_XXXXXX");
+}
+
+TEST_F(LlvmLibcMkostempTest, TemplateTooShort) {
+  char tmpl[] = "XXXXX";
+  EXPECT_THAT(LIBC_NAMESPACE::mkostemp(tmpl, 0), Fails(EINVAL));
+}
+
+TEST_F(LlvmLibcMkostempTest, DoesNotEndInXs) {
+  char tmpl[] = "tmp_XXXXXY";
+  EXPECT_THAT(LIBC_NAMESPACE::mkostemp(tmpl, 0), Fails(EINVAL));
+}
+
+TEST_F(LlvmLibcMkostempTest, XsNotAtEnd) {
+  char tmpl[] = "XXXXXXtmp";
+  EXPECT_THAT(LIBC_NAMESPACE::mkostemp(tmpl, 0), Fails(EINVAL));
+}
+
+TEST_F(LlvmLibcMkostempTest, FiveXsAtEnd) {
+  char tmpl[] = "tmp_XXXXX";
+  EXPECT_THAT(LIBC_NAMESPACE::mkostemp(tmpl, 0), Fails(EINVAL));
+}
+
+TEST_F(LlvmLibcMkostempTest, EmptyString) {
+  char tmpl[] = "";
+  EXPECT_THAT(LIBC_NAMESPACE::mkostemp(tmpl, 0), Fails(EINVAL));
+}
+
+TEST_F(LlvmLibcMkostempTest, 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::mkostemp(tmpl, 0), Fails(ENOENT));
+}

``````````

</details>


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


More information about the libc-commits mailing list