feat: Add temp-dir setting

This adds a new temp-dir setting for controlling the temporary directory
without having to change the TMPDIR env var. This can be used to e.g.
use a path on a case-sensitive store on macOS for temporary files
without changing the TMPDIR var used by interactive shells or commands
invoked with `nix run`.

This also stops unsetting `TMPDIR` on darwin when the env var value
starts with `/var/folders/`, preferring instead to just do the check
when reading `TMPDIR`. This way the inherited `TMPDIR` env var is
preserved for child processes (such as interactive shells).

As a side effect this changes the behavior of `nix-build -o ''` to act
like `nix-build --no-out-link` instead of failing with an error caused
by trying to create a symlink at the cwd.

Fixes: https://git.lix.systems/lix-project/lix/issues/253
Fixes: https://git.lix.systems/lix-project/lix/issues/112
Change-Id: I9ee826323f2deca62854715a77ca7a373a948a29
This commit is contained in:
Lily Ballard
2024-11-06 18:11:47 -08:00
parent 72cce7be3f
commit 3c8096e5cb
24 changed files with 182 additions and 47 deletions
+8 -7
View File
@@ -25,6 +25,7 @@
#include "legacy.hh"
#include "shlex.hh"
#include "nix-build.hh"
#include "temporary-dir.hh"
extern char * * environ __attribute__((weak)); // Man what even is this
@@ -55,8 +56,6 @@ static void main_nix_build(int argc, char * * argv)
std::string script;
std::vector<std::string> savedArgs;
AutoDelete tmpDir(createTempDir("", myName));
std::string outLink = "./result";
// List of environment variables kept for --pure
@@ -102,7 +101,6 @@ static void main_nix_build(int argc, char * * argv)
MyArgs myArgs(myName, [&](Strings::iterator & arg, const Strings::iterator & end) {
if (*arg == "--help") {
deletePath(tmpDir);
showManPage(myName);
}
@@ -113,7 +111,7 @@ static void main_nix_build(int argc, char * * argv)
; // obsolete
else if (*arg == "--no-out-link" || *arg == "--no-link")
outLink = (Path) tmpDir + "/result";
outLink = "";
else if (*arg == "--attr" || *arg == "-A")
attrPaths.push_back(getArg(*arg, arg, end));
@@ -198,6 +196,10 @@ static void main_nix_build(int argc, char * * argv)
if (packages && fromArgs)
throw UsageError("'-p' and '-E' are mutually exclusive");
AutoDelete tmpDir(createTempDir("", myName));
if (outLink.empty())
outLink = (Path) tmpDir + "/result";
auto store = openStore();
auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store;
@@ -416,8 +418,6 @@ static void main_nix_build(int argc, char * * argv)
// Set the environment.
auto env = getEnv();
auto tmp = defaultTempDir();
if (pure) {
decltype(env) newEnv;
for (auto & i : env)
@@ -428,7 +428,8 @@ static void main_nix_build(int argc, char * * argv)
env["__ETC_PROFILE_SOURCED"] = "1";
}
env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmp;
// Don't use defaultTempDir() here! We want to preserve the user's TMPDIR for the shell
env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = getEnvNonEmpty("TMPDIR").value_or("/tmp");
env["NIX_STORE"] = store->storeDir;
env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores);
+1
View File
@@ -6,6 +6,7 @@
#include "legacy.hh"
#include "fetchers.hh"
#include "eval-settings.hh" // for defexpr
#include "temporary-dir.hh"
#include "users.hh"
#include "nix-channel.hh"
+1
View File
@@ -6,6 +6,7 @@
#include "logging.hh"
#include "names.hh"
#include "store-api.hh"
#include "temporary-dir.hh"
#include "url-name.hh"
namespace nix
+1
View File
@@ -5,6 +5,7 @@
#include "processes.hh"
#include "tarfile.hh"
#include "store-api.hh"
#include "temporary-dir.hh"
#include "url-parts.hh"
#include "pathlocks.hh"
#include "users.hh"
+1
View File
@@ -2,6 +2,7 @@
#include "cache.hh"
#include "processes.hh"
#include "store-api.hh"
#include "temporary-dir.hh"
#include "url-parts.hh"
#include "users.hh"
+1
View File
@@ -5,6 +5,7 @@
#include "store-api.hh"
#include "archive.hh"
#include "tarfile.hh"
#include "temporary-dir.hh"
#include "types.hh"
#include "split.hh"
+1
View File
@@ -8,6 +8,7 @@
#include "remote-fs-accessor.hh"
#include "nar-info-disk-cache.hh" // IWYU pragma: keep
#include "nar-accessor.hh"
#include "temporary-dir.hh"
#include "thread-pool.hh"
#include "signals.hh"
#include "strings.hh"
@@ -3,6 +3,7 @@
#include "indirect-root-store.hh"
#include "machines.hh"
#include "store-api.hh"
#include "temporary-dir.hh"
#include "worker.hh"
#include "builtins.hh"
#include "builtins/buildenv.hh"
-5
View File
@@ -468,11 +468,6 @@ void initLibStore() {
[1] https://github.com/apple-oss-distributions/objc4/blob/01edf1705fbc3ff78a423cd21e03dfc21eb4d780/runtime/objc-initialize.mm#L614-L636
*/
curl_global_init(CURL_GLOBAL_ALL);
/* On macOS, don't use the per-session TMPDIR (as set e.g. by
sshd). This breaks build users because they don't have access
to the TMPDIR, in particular in nix-store --serve. */
if (defaultTempDir().starts_with("/var/folders/"))
unsetenv("TMPDIR");
#endif
registerStoreImplementations();
+14 -2
View File
@@ -615,11 +615,11 @@ public:
)"};
#endif
Setting<std::optional<Path>> buildDir{this, std::nullopt, "build-dir",
PathsSetting<std::optional<Path>> buildDir{this, std::nullopt, "build-dir",
R"(
The directory on the host, in which derivations' temporary build directories are created.
If not set, Nix will use the system temporary directory indicated by the `TMPDIR` environment variable.
If not set, Nix will use the [`temp-dir`](#conf-temp-dir) setting if set, otherwise the system temporary directory indicated by the `TMPDIR` environment variable.
Note that builds are often performed by the Nix daemon, so its `TMPDIR` is used, and not that of the Nix command line interface.
This is also the location where [`--keep-failed`](@docroot@/command-ref/opt-common.md#opt-keep-failed) leaves its files.
@@ -627,6 +627,18 @@ public:
If Nix runs without sandbox, or if the platform does not support sandboxing with bind mounts (e.g. macOS), then the [`builder`](@docroot@/language/derivations.md#attr-builder)'s environment will contain this directory, instead of the virtual location [`sandbox-build-dir`](#conf-sandbox-build-dir).
)"};
PathsSetting<std::optional<Path>> tempDir{this, std::nullopt, "temp-dir",
R"(
The directory on the host used as the default temporary directory.
If not set, Nix will use the system temporary directory indicated by the `TMPDIR` environment variable.
This will be used for anything that would otherwise fall back to `TMPDIR`, and the inherited `TMPDIR` value will be preserved for child processes to use.
If [`build-dir`](#conf-build-dir) is set, that takes precedence over this where it applies.
If set, the value must be a path that exists and is accessible to all users.
)"};
Setting<PathSet> allowedImpureHostPrefixes{this, {}, "allowed-impure-host-deps",
"Which prefixes to allow derivations to ask for access to (primarily for Darwin)."};
+1
View File
@@ -2,6 +2,7 @@
#include "globals.hh"
#include "archive.hh"
#include "pathlocks.hh"
#include "temporary-dir.hh"
#include "worker-protocol.hh"
#include "derivations.hh"
#include "nar-info.hh"
+2
View File
@@ -77,6 +77,7 @@ libstore_sources = files(
'ssh-store.cc',
'ssh.cc',
'store-api.cc',
'temporary-dir.cc',
'uds-remote-store.cc',
'worker-protocol.cc',
'build/child.cc',
@@ -160,6 +161,7 @@ libstore_headers = files(
'ssh-store.hh',
'store-api.hh',
'store-cast.hh',
'temporary-dir.hh',
'uds-remote-store.hh',
'worker-protocol-impl.hh',
'worker-protocol.hh',
+1
View File
@@ -4,6 +4,7 @@
#include "finally.hh"
#include "logging.hh"
#include "strings.hh"
#include "temporary-dir.hh"
namespace nix {
+42
View File
@@ -0,0 +1,42 @@
#include "temporary-dir.hh"
#include "file-system.hh"
#include "globals.hh"
namespace nix {
Path createTempDir(const Path & tmpRoot, const Path & prefix,
bool includePid, bool useGlobalCounter, mode_t mode)
{
return createTempSubdir(tmpRoot.empty() ? defaultTempDir() : tmpRoot, prefix, includePid, useGlobalCounter, mode);
}
std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix)
{
Path tmpl(defaultTempDir() + "/" + prefix + ".XXXXXX");
// FIXME: use O_TMPFILE.
AutoCloseFD fd(mkstemp(tmpl.data()));
if (!fd)
throw SysError("creating temporary file '%s'", tmpl);
closeOnExec(fd.get());
return {std::move(fd), tmpl};
}
Path defaultTempDir()
{
return settings.tempDir.get().or_else([] {
return getEnvNonEmpty("TMPDIR").and_then([](auto val) -> std::optional<Path> {
#if __APPLE__
/* On macOS, don't use the per-session TMPDIR (as set e.g. by
sshd). This breaks build users because they don't have access
to the TMPDIR, in particular in nix-store --serve. */
if (val.starts_with("/var/folders/")) {
return std::nullopt;
}
#endif
return val;
});
}).value_or("/tmp");
}
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
///@file
#include "file-system.hh"
namespace nix {
/**
* Create a temporary directory.
*/
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix",
bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755);
/**
* Create a temporary file, returning a file handle and its path.
*/
std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix = "nix");
/**
* Return settings.tempDir, `TMPDIR`, or the default temporary directory if unset or empty.
*/
Path defaultTempDir();
}
+5 -21
View File
@@ -562,21 +562,17 @@ void AutoDelete::reset(const Path & p, bool recursive) {
//////////////////////////////////////////////////////////////////////
std::string defaultTempDir() {
return getEnvNonEmpty("TMPDIR").value_or("/tmp");
}
static Path tempName(Path tmpRoot, const Path & prefix, bool includePid,
static Path tempName(PathView parent, const Path & prefix, bool includePid,
std::atomic<unsigned int> & counter)
{
tmpRoot = canonPath(tmpRoot.empty() ? defaultTempDir() : tmpRoot, true);
auto tmpRoot = canonPath(parent, true);
if (includePid)
return fmt("%1%/%2%-%3%-%4%", tmpRoot, prefix, getpid(), counter++);
else
return fmt("%1%/%2%-%3%", tmpRoot, prefix, counter++);
}
Path createTempDir(const Path & tmpRoot, const Path & prefix,
Path createTempSubdir(const Path & parent, const Path & prefix,
bool includePid, bool useGlobalCounter, mode_t mode)
{
static std::atomic<unsigned int> globalCounter = 0;
@@ -585,7 +581,7 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix,
while (1) {
checkInterrupt();
Path tmpDir = tempName(tmpRoot, prefix, includePid, counter);
Path tmpDir = tempName(parent, prefix, includePid, counter);
if (mkdir(tmpDir.c_str(), mode) == 0) {
#if __FreeBSD__
/* Explicitly set the group of the directory. This is to
@@ -606,18 +602,6 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix,
}
}
std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix)
{
Path tmpl(defaultTempDir() + "/" + prefix + ".XXXXXX");
// FIXME: use O_TMPFILE.
AutoCloseFD fd(mkstemp(tmpl.data()));
if (!fd)
throw SysError("creating temporary file '%s'", tmpl);
closeOnExec(fd.get());
return {std::move(fd), tmpl};
}
Path makeTempPath(const Path & root, const Path & suffix)
{
// start the counter at a random value to minimize issues with preexisting temp paths
@@ -721,7 +705,7 @@ void moveFile(const Path & oldName, const Path & newName)
auto newPath = fs::path(newName);
// For the move to be as atomic as possible, copy to a temporary
// directory
fs::path temp = createTempDir(newPath.parent_path(), "rename-tmp");
fs::path temp = createTempSubdir(newPath.parent_path(), "rename-tmp");
Finally removeTemp = [&]() { fs::remove(temp); };
auto tempCopyTarget = temp / "copy-target";
if (e.code().value() == EXDEV) {
+2 -12
View File
@@ -288,21 +288,11 @@ struct DIRDeleter
typedef std::unique_ptr<DIR, DIRDeleter> AutoCloseDir;
/**
* Create a temporary directory.
* Create a temporary directory in a given parent directory.
*/
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix",
Path createTempSubdir(const Path & parent, const Path & prefix = "nix",
bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755);
/**
* Create a temporary file, returning a file handle and its path.
*/
std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix = "nix");
/**
* Return `TMPDIR`, or the default temporary directory if unset or empty.
*/
Path defaultTempDir();
/**
* Return temporary path constructed by appending a suffix to a root path.
*
+1
View File
@@ -7,6 +7,7 @@
#include "outputs-spec.hh"
#include "derivations.hh"
#include "run.hh"
#include "temporary-dir.hh"
#include <iterator>
#include <memory>
+1
View File
@@ -8,6 +8,7 @@
#include "attr-path.hh"
#include "eval-inline.hh" // IWYU pragma: keep
#include "legacy.hh"
#include "temporary-dir.hh"
#include "terminal.hh"
#include "prefetch-command.hh"
+1
View File
@@ -13,6 +13,7 @@
#include "current-process.hh"
#if __linux__
#include "temporary-dir.hh"
#include <sys/mount.h>
#endif