libutil: use OS‐provided entropy for temporary filenames

Relax the constraints on keeping the exact same filename format to
provide a more robust source of entropy with a simpler interface
(as previously suggested by eldritch horrors). Using 128 bits of
OS‐provided entropy ensures global uniqueness and allows us to
skip any thought of gracefully handling the case where these files
already exist.

My microbenchmark that repeatedly constructed paths like this and
printed them out showed that this takes about 1.23× the time of
the previous implementation, both taking on the order of a couple
microseconds for one iteration. Since everything that uses it is doing
things more expensive than printing to standard output, the actual
performance delta is likely to be lost in the noise. If it somehow
becomes a bottleneck, it can be optimized without sacrificing the
guarantees by reading from the system RNG only to seed a thread‐local
CSPRNG like [ChaCha8Rand], but I think that’s very unlikely.

We also tweak the recommended way of creating a temporary file inside
a directory in anticipation of later changes, and rename the `suffix`
parameter to `prefix` (it’s a prefix to the random characters and
a suffix to the root, but this way is more consistent).

[ChaCha8Rand]: https://c2sp.org/chacha8rand

Change-Id: I6a6a69648502c746d13d8c3bd2768cbbf1b90466
This commit is contained in:
Emily
2025-11-18 11:08:31 +01:00
committed by Qyriad
parent fc22163c57
commit 1df3d8c79d
2 changed files with 13 additions and 8 deletions
+9 -4
View File
@@ -3,6 +3,8 @@
#include <filesystem>
#include <atomic>
#include <random>
#include <ranges>
#include <sys/xattr.h>
#include "lix/libutil/environment-variables.hh"
#include "lix/libutil/file-descriptor.hh"
@@ -682,11 +684,14 @@ Path createTempSubdir(const Path & parent, const Path & prefix,
}
}
Path makeTempPath(const Path & root, const Path & suffix)
Path makeTempPath(const Path & root, const Path & prefix)
{
// start the counter at a random value to minimize issues with preexisting temp paths
static std::atomic_uint_fast32_t counter(std::random_device{}());
return fmt("%1%%2%-%3%-%4%", root, suffix, getpid(), counter.fetch_add(1, std::memory_order_relaxed));
static thread_local std::random_device generator{};
std::uniform_int_distribution<uint64_t> uniform_dist{};
const uint64_t entropy[2] = {uniform_dist(generator), uniform_dist(generator)};
auto unique = base32Encode(std::as_bytes(std::span(entropy)));
return fmt("%s%s-%s", root, prefix, unique);
}
Path makeTempSiblingPath(const Path & path)
+4 -4
View File
@@ -305,12 +305,12 @@ Path createTempSubdir(const Path & parent, const Path & prefix = "nix",
bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755);
/**
* Return temporary path constructed by appending a suffix to a root path.
* Return temporary path constructed by appending to a root path.
*
* The constructed path looks like `<root><suffix>-<pid>-<unique>`. To create a
* path nested in a directory, provide a suffix starting with `/`.
* The constructed path looks like `<root><prefix>-<unique>`. To create a
* path nested in a directory, provide a root ending with `/`.
*/
Path makeTempPath(const Path & root, const Path & suffix = ".tmp");
Path makeTempPath(const Path & root, const Path & prefix = ".tmp");
/**
* Return temporary path in the same directory as a given path.