blob: af9856510cdd0226606193d8109a17f6d62208c3 [file] [edit]
//===----------------------------------------------------------------------===//
//
// 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