From 4ad272015e30345daefdb302969b9f46918e0b03 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Mon, 1 Dec 2025 04:21:05 +0300 Subject: [PATCH] libutil: Implement createAnonymousTempFile There are a lot of cases where we don't care about having the temporary file linked anywhere at all -- just a descriptor is more than enough. --- src/libutil-tests/file-system.cc | 17 +++++++++++++++++ src/libutil/file-system.cc | 20 ++++++++++++++++++++ src/libutil/include/nix/util/file-system.hh | 6 ++++++ 3 files changed, 43 insertions(+) diff --git a/src/libutil-tests/file-system.cc b/src/libutil-tests/file-system.cc index 8ea081c51..9d319518f 100644 --- a/src/libutil-tests/file-system.cc +++ b/src/libutil-tests/file-system.cc @@ -381,4 +381,21 @@ TEST(openFileEnsureBeneathNoSymlinks, works) #endif +/* ---------------------------------------------------------------------------- + * createAnonymousTempFile + * --------------------------------------------------------------------------*/ + +TEST(createAnonymousTempFile, works) +{ + auto fd = createAnonymousTempFile(); + writeFull(fd.get(), "test"); + lseek(fd.get(), 0, SEEK_SET); + FdSource source{fd.get()}; + EXPECT_EQ(source.drain(), "test"); + lseek(fd.get(), 0, SEEK_END); + writeFull(fd.get(), "test"); + lseek(fd.get(), 0, SEEK_SET); + EXPECT_EQ(source.drain(), "testtest"); +} + } // namespace nix diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 6b145b343..4502df792 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -713,11 +713,31 @@ std::filesystem::path createTempDir(const std::filesystem::path & tmpRoot, const } } +AutoCloseFD createAnonymousTempFile() +{ + AutoCloseFD fd; +#ifdef O_TMPFILE + fd = ::open(defaultTempDir().c_str(), O_TMPFILE | O_CLOEXEC | O_RDWR, S_IWUSR | S_IRUSR); + if (!fd) + throw SysError("creating anonymous temporary file"); +#else + auto [fd2, path] = createTempFile("nix-anonymous"); + if (!fd2) + throw SysError("creating temporary file '%s'", path); + fd = std::move(fd2); +# ifndef _WIN32 + unlink(requireCString(path)); /* We only care about the file descriptor. */ +# endif +#endif + return fd; +} + std::pair createTempFile(const Path & prefix) { Path tmpl(defaultTempDir() + "/" + prefix + ".XXXXXX"); // Strictly speaking, this is UB, but who cares... // FIXME: use O_TMPFILE. + // FIXME: Windows should use FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE AutoCloseFD fd = toDescriptor(mkstemp((char *) tmpl.c_str())); if (!fd) throw SysError("creating temporary file '%s'", tmpl); diff --git a/src/libutil/include/nix/util/file-system.hh b/src/libutil/include/nix/util/file-system.hh index 7673c24fd..73270aeb2 100644 --- a/src/libutil/include/nix/util/file-system.hh +++ b/src/libutil/include/nix/util/file-system.hh @@ -337,6 +337,12 @@ typedef std::unique_ptr AutoCloseDir; std::filesystem::path createTempDir(const std::filesystem::path & tmpRoot = "", const std::string & prefix = "nix", mode_t mode = 0755); +/** + * Create an anonymous readable/writable temporary file, returning a file handle. + * On UNIX there resulting file isn't linked to any path on the filesystem. + */ +AutoCloseFD createAnonymousTempFile(); + /** * Create a temporary file, returning a file handle and its path. */