[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