Compare commits
42 Commits
warn-non-o
...
2.29.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d78f74fe4 | ||
|
|
b3b137ec4a | ||
|
|
833406121c | ||
|
|
01619fbe2d | ||
|
|
075df0b446 | ||
|
|
2f6c758d3d | ||
|
|
4c7e5ce7a8 | ||
|
|
86fbaf3b14 | ||
|
|
5c3aed3e88 | ||
|
|
fcdffffa37 | ||
|
|
aeb627bebf | ||
|
|
a2567f6d7a | ||
|
|
c1e272b47d | ||
|
|
cfba4b3bf4 | ||
|
|
d761dad79c | ||
|
|
d068b82c2f | ||
|
|
8ca4d2ef08 | ||
|
|
acc3cd460d | ||
|
|
c4813b8cbc | ||
|
|
0cd1fce3c3 | ||
|
|
90eb2f759c | ||
|
|
d1e4be6fb4 | ||
|
|
29d98da636 | ||
|
|
91dc6e7fa0 | ||
|
|
b33fd1e4fb | ||
|
|
1c618a9d87 | ||
|
|
ab2abebfc3 | ||
|
|
0f132fc129 | ||
|
|
6fc6db3496 | ||
|
|
ca0bde3578 | ||
|
|
f51b537239 | ||
|
|
2b7e3e9b81 | ||
|
|
fee8454dd0 | ||
|
|
dc238ba102 | ||
|
|
fa7a5ab07a | ||
|
|
27932ae6da | ||
|
|
631d23788e | ||
|
|
4f03bfebd9 | ||
|
|
7f488dc7d3 | ||
|
|
1b2e88effd | ||
|
|
4dcf21a2f6 | ||
|
|
ff24751bdd |
@@ -20,8 +20,9 @@ prs: 1238
|
||||
Here's one or more paragraphs that describe the change.
|
||||
|
||||
- It's markdown
|
||||
- Add references to the manual using @docroot@
|
||||
- Add references to the manual using [links like this](@_at_docroot@/example.md)
|
||||
```
|
||||
<!-- for the raw markdown readers: that means using @docroot@ -->
|
||||
|
||||
Significant changes should add the following header, which moves them to the top.
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ Derivations can declare some infrequently used optional attributes.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> If set to `true`, other advanced attributes such as [`allowedReferences`](#adv-attr-allowedReferences), [`allowedReferences`](#adv-attr-allowedReferences), [`allowedRequisites`](#adv-attr-allowedRequisites),
|
||||
> If set to `true`, other advanced attributes such as [`allowedReferences`](#adv-attr-allowedReferences), [`allowedRequisites`](#adv-attr-allowedRequisites),
|
||||
[`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites), maxSize, and maxClosureSize.
|
||||
will have no effect.
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ The output spec for an output with a fixed content addresses additionally contai
|
||||
> **Design note**
|
||||
>
|
||||
> In principle, the output spec could also specify the references the store object should have, since the references and file system objects are equally parts of a content-addressed store object proper that contribute to its content-addressed.
|
||||
> However, at this time, the references are not not done because all fixed content-addressed outputs are required to have no references (including no self-reference).
|
||||
> However, at this time, the references are not done because all fixed content-addressed outputs are required to have no references (including no self-reference).
|
||||
>
|
||||
> Also in principle, rather than specifying the references and file system object data with separate hashes, a single hash that constraints both could be used.
|
||||
> This could be done with the final store path's digest, or better yet, the hash that will become the store path's digest before it is truncated.
|
||||
@@ -116,7 +116,7 @@ Because the derivation output is not fixed (just like with [input addressing]),
|
||||
> (The "environment", in this case, consists of attributes such as the Operating System Nix runs atop, along with the operating-system-specific privileges that Nix has been granted.
|
||||
> Because of how conventional operating systems like macos, Linux, etc. work, granting builders *fewer* privileges may ironically require that Nix be run with *more* privileges.)
|
||||
|
||||
That said, derivations producing floating content-addressed outputs may declare their builders as impure (like the builders of derivations producing producing fixed outputs).
|
||||
That said, derivations producing floating content-addressed outputs may declare their builders as impure (like the builders of derivations producing fixed outputs).
|
||||
This is provisionally supported as part of the [`impure-derivations`][xp-feature-impure-derivations] experimental feature.
|
||||
|
||||
### Compatibility negotiation
|
||||
@@ -144,7 +144,7 @@ A *deterministic* content-addressing derivation should produce outputs with the
|
||||
The choice of provisional store path can be thought of as an impurity, since it is an arbitrary choice.
|
||||
|
||||
If provisional outputs paths are deterministically chosen, we are in the first branch of part (1).
|
||||
The builder the data it produces based on it in arbitrary ways, but this gets us closer to to [input addressing].
|
||||
The builder the data it produces based on it in arbitrary ways, but this gets us closer to [input addressing].
|
||||
Deterministically choosing the provisional path may be considered "complete sandboxing" by removing an impurity, but this is unsatisfactory
|
||||
|
||||
<!--
|
||||
|
||||
@@ -83,7 +83,7 @@ The rules for this are fairly concise:
|
||||
|
||||
- A content-addressing derivation may be pure or impure
|
||||
|
||||
- If it is impure, it may be be fixed (typical), or it may be floating if the additional [`impure-derivations`][xp-feature-impure-derivations] experimental feature is enabled.
|
||||
- If it is impure, it may be fixed (typical), or it may be floating if the additional [`impure-derivations`][xp-feature-impure-derivations] experimental feature is enabled.
|
||||
|
||||
- If it is pure, it must be floating.
|
||||
|
||||
|
||||
@@ -57,6 +57,9 @@ def recursive_replace(data: dict[str, t.Any], book_root: Path, search_path: Path
|
||||
).replace(
|
||||
'@docroot@',
|
||||
("../" * len(path_to_chapter.parent.parts) or "./")[:-1]
|
||||
).replace(
|
||||
'@_at_',
|
||||
'@'
|
||||
),
|
||||
sub_items = [
|
||||
recursive_replace(sub_item, book_root, search_path)
|
||||
|
||||
6
flake.lock
generated
6
flake.lock
generated
@@ -63,11 +63,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1746141548,
|
||||
"narHash": "sha256-IgBWhX7A2oJmZFIrpRuMnw5RAufVnfvOgHWgIdds+hc=",
|
||||
"lastModified": 1747179050,
|
||||
"narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f02fddb8acef29a8b32f10a335d44828d7825b78",
|
||||
"rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
|
||||
officialRelease = false;
|
||||
officialRelease = true;
|
||||
|
||||
linux32BitSystems = [ "i686-linux" ];
|
||||
linux64BitSystems = [
|
||||
|
||||
@@ -63,6 +63,7 @@ scope: {
|
||||
"--with-coroutine"
|
||||
"--with-iostreams"
|
||||
];
|
||||
enableIcu = false;
|
||||
}).overrideAttrs
|
||||
(old: {
|
||||
# Need to remove `--with-*` to use `--with-libraries=...`
|
||||
|
||||
@@ -119,9 +119,6 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
|
||||
(pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript)
|
||||
pkgs.buildPackages.nixfmt-rfc-style
|
||||
]
|
||||
# TODO: Remove the darwin check once
|
||||
# https://github.com/NixOS/nixpkgs/pull/291814 is available
|
||||
++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear
|
||||
++ lib.optional (stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform) (
|
||||
lib.hiPrio pkgs.buildPackages.clang-tools
|
||||
)
|
||||
|
||||
@@ -570,7 +570,7 @@ LockedFlake lockFlake(
|
||||
|
||||
/* Get the input flake, resolve 'path:./...'
|
||||
flakerefs relative to the parent flake. */
|
||||
auto getInputFlake = [&](const FlakeRef & ref)
|
||||
auto getInputFlake = [&](const FlakeRef & ref, const fetchers::UseRegistries useRegistries)
|
||||
{
|
||||
if (auto resolvedPath = resolveRelativePath()) {
|
||||
return readFlake(state, ref, ref, ref, *resolvedPath, inputAttrPath);
|
||||
@@ -578,7 +578,7 @@ LockedFlake lockFlake(
|
||||
return getFlake(
|
||||
state,
|
||||
ref,
|
||||
useRegistriesInputs,
|
||||
useRegistries,
|
||||
inputAttrPath);
|
||||
}
|
||||
};
|
||||
@@ -660,7 +660,7 @@ LockedFlake lockFlake(
|
||||
}
|
||||
|
||||
if (mustRefetch) {
|
||||
auto inputFlake = getInputFlake(oldLock->lockedRef);
|
||||
auto inputFlake = getInputFlake(oldLock->lockedRef, useRegistriesInputs);
|
||||
nodePaths.emplace(childNode, inputFlake.path.parent());
|
||||
computeLocks(inputFlake.inputs, childNode, inputAttrPath, oldLock, followsPrefix,
|
||||
inputFlake.path, false);
|
||||
@@ -685,10 +685,11 @@ LockedFlake lockFlake(
|
||||
nuked the next time we update the lock
|
||||
file. That is, overrides are sticky unless you
|
||||
use --no-write-lock-file. */
|
||||
auto ref = (input2.ref && explicitCliOverrides.contains(inputAttrPath)) ? *input2.ref : *input.ref;
|
||||
auto inputIsOverride = explicitCliOverrides.contains(inputAttrPath);
|
||||
auto ref = (input2.ref && inputIsOverride) ? *input2.ref : *input.ref;
|
||||
|
||||
if (input.isFlake) {
|
||||
auto inputFlake = getInputFlake(*input.ref);
|
||||
auto inputFlake = getInputFlake(*input.ref, inputIsOverride ? fetchers::UseRegistries::All : useRegistriesInputs);
|
||||
|
||||
auto childNode = make_ref<LockedNode>(
|
||||
inputFlake.lockedRef,
|
||||
|
||||
@@ -211,8 +211,13 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt
|
||||
auto e = optionalValueAt(parsed->structuredAttrs, "exportReferencesGraph");
|
||||
if (!e || !e->is_object())
|
||||
return ret;
|
||||
for (auto & [key, storePathsJson] : getObject(*e)) {
|
||||
ret.insert_or_assign(key, storePathsJson);
|
||||
for (auto & [key, value] : getObject(*e)) {
|
||||
if (value.is_array())
|
||||
ret.insert_or_assign(key, value);
|
||||
else if (value.is_string())
|
||||
ret.insert_or_assign(key, StringSet{value});
|
||||
else
|
||||
throw Error("'exportReferencesGraph' value is not an array or a string");
|
||||
}
|
||||
} else {
|
||||
auto s = getOr(env, "exportReferencesGraph", "");
|
||||
|
||||
@@ -13,10 +13,11 @@
|
||||
# include "nix/util/processes.hh"
|
||||
#endif
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
#include <random>
|
||||
|
||||
#include <climits>
|
||||
@@ -331,8 +332,8 @@ static void readProcLink(const std::filesystem::path & file, UncheckedRoots & ro
|
||||
|
||||
static std::string quoteRegexChars(const std::string & raw)
|
||||
{
|
||||
static auto specialRegex = std::regex(R"([.^$\\*+?()\[\]{}|])");
|
||||
return std::regex_replace(raw, specialRegex, R"(\$&)");
|
||||
static auto specialRegex = boost::regex(R"([.^$\\*+?()\[\]{}|])");
|
||||
return boost::regex_replace(raw, specialRegex, R"(\$&)");
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
@@ -354,12 +355,12 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
||||
auto procDir = AutoCloseDir{opendir("/proc")};
|
||||
if (procDir) {
|
||||
struct dirent * ent;
|
||||
auto digitsRegex = std::regex(R"(^\d+$)");
|
||||
auto mapRegex = std::regex(R"(^\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(/\S+)\s*$)");
|
||||
auto storePathRegex = std::regex(quoteRegexChars(storeDir) + R"(/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*)");
|
||||
static const auto digitsRegex = boost::regex(R"(^\d+$)");
|
||||
static const auto mapRegex = boost::regex(R"(^\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(/\S+)\s*$)");
|
||||
auto storePathRegex = boost::regex(quoteRegexChars(storeDir) + R"(/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*)");
|
||||
while (errno = 0, ent = readdir(procDir.get())) {
|
||||
checkInterrupt();
|
||||
if (std::regex_match(ent->d_name, digitsRegex)) {
|
||||
if (boost::regex_match(ent->d_name, digitsRegex)) {
|
||||
try {
|
||||
readProcLink(fmt("/proc/%s/exe" ,ent->d_name), unchecked);
|
||||
readProcLink(fmt("/proc/%s/cwd", ent->d_name), unchecked);
|
||||
@@ -386,15 +387,15 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
||||
std::filesystem::path mapFile = fmt("/proc/%s/maps", ent->d_name);
|
||||
auto mapLines = tokenizeString<std::vector<std::string>>(readFile(mapFile.string()), "\n");
|
||||
for (const auto & line : mapLines) {
|
||||
auto match = std::smatch{};
|
||||
if (std::regex_match(line, match, mapRegex))
|
||||
auto match = boost::smatch{};
|
||||
if (boost::regex_match(line, match, mapRegex))
|
||||
unchecked[match[1]].emplace(mapFile.string());
|
||||
}
|
||||
|
||||
auto envFile = fmt("/proc/%s/environ", ent->d_name);
|
||||
auto envString = readFile(envFile);
|
||||
auto env_end = std::sregex_iterator{};
|
||||
for (auto i = std::sregex_iterator{envString.begin(), envString.end(), storePathRegex}; i != env_end; ++i)
|
||||
auto env_end = boost::sregex_iterator{};
|
||||
for (auto i = boost::sregex_iterator{envString.begin(), envString.end(), storePathRegex}; i != env_end; ++i)
|
||||
unchecked[i->str()].emplace(envFile);
|
||||
} catch (SystemError & e) {
|
||||
if (errno == ENOENT || errno == EACCES || errno == ESRCH)
|
||||
@@ -413,12 +414,12 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
||||
// Because of this we disable lsof when running the tests.
|
||||
if (getEnv("_NIX_TEST_NO_LSOF") != "1") {
|
||||
try {
|
||||
std::regex lsofRegex(R"(^n(/.*)$)");
|
||||
boost::regex lsofRegex(R"(^n(/.*)$)");
|
||||
auto lsofLines =
|
||||
tokenizeString<std::vector<std::string>>(runProgram(LSOF, true, { "-n", "-w", "-F", "n" }), "\n");
|
||||
for (const auto & line : lsofLines) {
|
||||
std::smatch match;
|
||||
if (std::regex_match(line, match, lsofRegex))
|
||||
boost::smatch match;
|
||||
if (boost::regex_match(line, match, lsofRegex))
|
||||
unchecked[match[1].str()].emplace("{lsof}");
|
||||
}
|
||||
} catch (ExecError & e) {
|
||||
|
||||
@@ -176,13 +176,13 @@ protected:
|
||||
void getFile(const std::string & path,
|
||||
Callback<std::optional<std::string>> callback) noexcept override
|
||||
{
|
||||
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
||||
|
||||
try {
|
||||
checkEnabled();
|
||||
|
||||
auto request(makeRequest(path));
|
||||
|
||||
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
||||
|
||||
getFileTransfer()->enqueueFileTransfer(request,
|
||||
{[callbackPtr, this](std::future<FileTransferResult> result) {
|
||||
try {
|
||||
@@ -198,7 +198,7 @@ protected:
|
||||
}});
|
||||
|
||||
} catch (...) {
|
||||
callback.rethrow();
|
||||
callbackPtr->rethrow();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ struct MixStoreDirMethods
|
||||
* Need to make this a separate class so I can get the right
|
||||
* initialization order in the constructor for `StoreDirConfig`.
|
||||
*/
|
||||
struct StoreDirConfigItself : Config
|
||||
struct StoreDirConfigBase : Config
|
||||
{
|
||||
using Config::Config;
|
||||
|
||||
@@ -118,12 +118,12 @@ struct StoreDirConfigItself : Config
|
||||
};
|
||||
|
||||
/**
|
||||
* The order of `StoreDirConfigItself` and then `MixStoreDirMethods` is
|
||||
* very important. This ensures that `StoreDirConfigItself::storeDir_`
|
||||
* The order of `StoreDirConfigBase` and then `MixStoreDirMethods` is
|
||||
* very important. This ensures that `StoreDirConfigBase::storeDir_`
|
||||
* is initialized before we have our one chance (because references are
|
||||
* immutable) to initialize `MixStoreDirMethods::storeDir`.
|
||||
*/
|
||||
struct StoreDirConfig : StoreDirConfigItself, MixStoreDirMethods
|
||||
struct StoreDirConfig : StoreDirConfigBase, MixStoreDirMethods
|
||||
{
|
||||
using Params = std::map<std::string, std::string>;
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ LocalStore::LocalStore(ref<const Config> config)
|
||||
else if (curSchema == 0) { /* new store */
|
||||
curSchema = nixSchemaVersion;
|
||||
openDB(*state, true);
|
||||
writeFile(schemaPath, fmt("%1%", curSchema), 0666, true);
|
||||
writeFile(schemaPath, fmt("%1%", curSchema), 0666, FsSync::Yes);
|
||||
}
|
||||
|
||||
else if (curSchema < nixSchemaVersion) {
|
||||
@@ -298,7 +298,7 @@ LocalStore::LocalStore(ref<const Config> config)
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
writeFile(schemaPath, fmt("%1%", nixSchemaVersion), 0666, true);
|
||||
writeFile(schemaPath, fmt("%1%", nixSchemaVersion), 0666, FsSync::Yes);
|
||||
|
||||
lockFile(globalLock.get(), ltRead, true);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ subdir('nix-meson-build-support/libatomic')
|
||||
|
||||
boost = dependency(
|
||||
'boost',
|
||||
modules : ['container'],
|
||||
modules : ['container', 'regex'],
|
||||
include_type: 'system',
|
||||
)
|
||||
# boost is a public dependency, but not a pkg-config dependency unfortunately, so we
|
||||
|
||||
@@ -225,6 +225,8 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
|
||||
auto parsedDrv = StructuredAttrs::tryParse(drv->env);
|
||||
DerivationOptions drvOptions;
|
||||
try {
|
||||
// FIXME: this is a lot of work just to get the value
|
||||
// of `allowSubstitutes`.
|
||||
drvOptions = DerivationOptions::fromStructuredAttrs(
|
||||
drv->env,
|
||||
parsedDrv ? &*parsedDrv : nullptr);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
namespace nix {
|
||||
|
||||
StoreDirConfig::StoreDirConfig(const Params & params)
|
||||
: StoreDirConfigItself(params)
|
||||
: StoreDirConfigBase(params)
|
||||
, MixStoreDirMethods{storeDir_}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -129,6 +129,11 @@ private:
|
||||
*/
|
||||
Path topTmpDir;
|
||||
|
||||
/**
|
||||
* The file descriptor of the temporary directory.
|
||||
*/
|
||||
AutoCloseFD tmpDirFd;
|
||||
|
||||
/**
|
||||
* The path of the temporary directory in the sandbox.
|
||||
*/
|
||||
@@ -325,9 +330,24 @@ private:
|
||||
|
||||
/**
|
||||
* Make a file owned by the builder.
|
||||
*
|
||||
* SAFETY: this function is prone to TOCTOU as it receives a path and not a descriptor.
|
||||
* It's only safe to call in a child of a directory only visible to the owner.
|
||||
*/
|
||||
void chownToBuilder(const Path & path);
|
||||
|
||||
/**
|
||||
* Make a file owned by the builder addressed by its file descriptor.
|
||||
*/
|
||||
void chownToBuilder(int fd, const Path & path);
|
||||
|
||||
/**
|
||||
* Create a file in `tmpDir` owned by the builder.
|
||||
*/
|
||||
void writeBuilderFile(
|
||||
const std::string & name,
|
||||
std::string_view contents);
|
||||
|
||||
/**
|
||||
* Run the builder's process.
|
||||
*/
|
||||
@@ -895,7 +915,14 @@ void DerivationBuilderImpl::startBuilder()
|
||||
} else {
|
||||
tmpDir = topTmpDir;
|
||||
}
|
||||
chownToBuilder(tmpDir);
|
||||
|
||||
/* The TOCTOU between the previous mkdir call and this open call is unavoidable due to
|
||||
POSIX semantics.*/
|
||||
tmpDirFd = AutoCloseFD{open(tmpDir.c_str(), O_RDONLY | O_NOFOLLOW | O_DIRECTORY)};
|
||||
if (!tmpDirFd)
|
||||
throw SysError("failed to open the build temporary directory descriptor '%1%'", tmpDir);
|
||||
|
||||
chownToBuilder(tmpDirFd.get(), tmpDir);
|
||||
|
||||
for (auto & [outputName, status] : initialOutputs) {
|
||||
/* Set scratch path we'll actually use during the build.
|
||||
@@ -1469,9 +1496,7 @@ void DerivationBuilderImpl::initTmpDir()
|
||||
} else {
|
||||
auto hash = hashString(HashAlgorithm::SHA256, i.first);
|
||||
std::string fn = ".attr-" + hash.to_string(HashFormat::Nix32, false);
|
||||
Path p = tmpDir + "/" + fn;
|
||||
writeFile(p, rewriteStrings(i.second, inputRewrites));
|
||||
chownToBuilder(p);
|
||||
writeBuilderFile(fn, rewriteStrings(i.second, inputRewrites));
|
||||
env[i.first + "Path"] = tmpDirInSandbox + "/" + fn;
|
||||
}
|
||||
}
|
||||
@@ -1580,11 +1605,9 @@ void DerivationBuilderImpl::writeStructuredAttrs()
|
||||
|
||||
auto jsonSh = StructuredAttrs::writeShell(json);
|
||||
|
||||
writeFile(tmpDir + "/.attrs.sh", rewriteStrings(jsonSh, inputRewrites));
|
||||
chownToBuilder(tmpDir + "/.attrs.sh");
|
||||
writeBuilderFile(".attrs.sh", rewriteStrings(jsonSh, inputRewrites));
|
||||
env["NIX_ATTRS_SH_FILE"] = tmpDirInSandbox + "/.attrs.sh";
|
||||
writeFile(tmpDir + "/.attrs.json", rewriteStrings(json.dump(), inputRewrites));
|
||||
chownToBuilder(tmpDir + "/.attrs.json");
|
||||
writeBuilderFile(".attrs.json", rewriteStrings(json.dump(), inputRewrites));
|
||||
env["NIX_ATTRS_JSON_FILE"] = tmpDirInSandbox + "/.attrs.json";
|
||||
}
|
||||
}
|
||||
@@ -1838,6 +1861,24 @@ void setupSeccomp()
|
||||
#endif
|
||||
}
|
||||
|
||||
void DerivationBuilderImpl::chownToBuilder(int fd, const Path & path)
|
||||
{
|
||||
if (!buildUser) return;
|
||||
if (fchown(fd, buildUser->getUID(), buildUser->getGID()) == -1)
|
||||
throw SysError("cannot change ownership of file '%1%'", path);
|
||||
}
|
||||
|
||||
void DerivationBuilderImpl::writeBuilderFile(
|
||||
const std::string & name,
|
||||
std::string_view contents)
|
||||
{
|
||||
auto path = std::filesystem::path(tmpDir) / name;
|
||||
AutoCloseFD fd{openat(tmpDirFd.get(), name.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC | O_EXCL | O_NOFOLLOW, 0666)};
|
||||
if (!fd)
|
||||
throw SysError("creating file %s", path);
|
||||
writeFile(fd, path, contents);
|
||||
chownToBuilder(fd.get(), path);
|
||||
}
|
||||
|
||||
void DerivationBuilderImpl::runChild()
|
||||
{
|
||||
@@ -3043,6 +3084,15 @@ void DerivationBuilderImpl::checkOutputs(const std::map<std::string, ValidPathIn
|
||||
void DerivationBuilderImpl::deleteTmpDir(bool force)
|
||||
{
|
||||
if (topTmpDir != "") {
|
||||
/* As an extra precaution, even in the event of `deletePath` failing to
|
||||
* clean up, the `tmpDir` will be chowned as if we were to move
|
||||
* it inside the Nix store.
|
||||
*
|
||||
* This hardens against an attack which smuggles a file descriptor
|
||||
* to make use of the temporary directory.
|
||||
*/
|
||||
chmod(topTmpDir.c_str(), 0000);
|
||||
|
||||
/* Don't keep temporary directories for builtins because they
|
||||
might have privileged stuff (like a copy of netrc). */
|
||||
if (settings.keepFailed && !force && !drv.isBuiltin()) {
|
||||
|
||||
@@ -131,7 +131,7 @@ TEST(getString, wrongAssertions) {
|
||||
TEST(getIntegralNumber, rightAssertions) {
|
||||
auto simple = R"({ "int": 0, "signed": -1 })"_json;
|
||||
|
||||
ASSERT_EQ(getUnsigned(valueAt(getObject(simple), "int")), 0);
|
||||
ASSERT_EQ(getUnsigned(valueAt(getObject(simple), "int")), 0u);
|
||||
ASSERT_EQ(getInteger<int8_t>(valueAt(getObject(simple), "int")), 0);
|
||||
ASSERT_EQ(getInteger<int8_t>(valueAt(getObject(simple), "signed")), -1);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ void restorePath(
|
||||
{
|
||||
switch (method) {
|
||||
case FileSerialisationMethod::Flat:
|
||||
writeFile(path, source, 0666, startFsync);
|
||||
writeFile(path, source, 0666, startFsync ? FsSync::Yes : FsSync::No);
|
||||
break;
|
||||
case FileSerialisationMethod::NixArchive:
|
||||
restorePath(path, source, startFsync);
|
||||
|
||||
@@ -303,7 +303,7 @@ void readFile(const Path & path, Sink & sink, bool memory_map)
|
||||
}
|
||||
|
||||
|
||||
void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync)
|
||||
void writeFile(const Path & path, std::string_view s, mode_t mode, FsSync sync)
|
||||
{
|
||||
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT
|
||||
// TODO
|
||||
@@ -313,22 +313,29 @@ void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync)
|
||||
, mode));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
try {
|
||||
writeFull(fd.get(), s);
|
||||
} catch (Error & e) {
|
||||
e.addTrace({}, "writing file '%1%'", path);
|
||||
throw;
|
||||
}
|
||||
if (sync)
|
||||
fd.fsync();
|
||||
// Explicitly close to make sure exceptions are propagated.
|
||||
|
||||
writeFile(fd, path, s, mode, sync);
|
||||
|
||||
/* Close explicitly to propagate the exceptions. */
|
||||
fd.close();
|
||||
if (sync)
|
||||
syncParent(path);
|
||||
}
|
||||
|
||||
void writeFile(AutoCloseFD & fd, const Path & origPath, std::string_view s, mode_t mode, FsSync sync)
|
||||
{
|
||||
assert(fd);
|
||||
try {
|
||||
writeFull(fd.get(), s);
|
||||
|
||||
void writeFile(const Path & path, Source & source, mode_t mode, bool sync)
|
||||
if (sync == FsSync::Yes)
|
||||
fd.fsync();
|
||||
|
||||
} catch (Error & e) {
|
||||
e.addTrace({}, "writing file '%1%'", origPath);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void writeFile(const Path & path, Source & source, mode_t mode, FsSync sync)
|
||||
{
|
||||
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT
|
||||
// TODO
|
||||
@@ -352,11 +359,11 @@ void writeFile(const Path & path, Source & source, mode_t mode, bool sync)
|
||||
e.addTrace({}, "writing file '%1%'", path);
|
||||
throw;
|
||||
}
|
||||
if (sync)
|
||||
if (sync == FsSync::Yes)
|
||||
fd.fsync();
|
||||
// Explicitly close to make sure exceptions are propagated.
|
||||
fd.close();
|
||||
if (sync)
|
||||
if (sync == FsSync::Yes)
|
||||
syncParent(path);
|
||||
}
|
||||
|
||||
@@ -419,7 +426,8 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
|
||||
#ifndef _WIN32
|
||||
checkInterrupt();
|
||||
|
||||
std::string name(baseNameOf(path.native()));
|
||||
std::string name(path.filename());
|
||||
assert(name != "." && name != ".." && !name.empty());
|
||||
|
||||
struct stat st;
|
||||
if (fstatat(parentfd, name.c_str(), &st,
|
||||
@@ -460,7 +468,7 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
|
||||
throw SysError("chmod %1%", path);
|
||||
}
|
||||
|
||||
int fd = openat(parentfd, path.c_str(), O_RDONLY);
|
||||
int fd = openat(parentfd, name.c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
|
||||
if (fd == -1)
|
||||
throw SysError("opening directory %1%", path);
|
||||
AutoCloseDir dir(fdopendir(fd));
|
||||
@@ -472,7 +480,7 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
|
||||
checkInterrupt();
|
||||
std::string childName = dirent->d_name;
|
||||
if (childName == "." || childName == "..") continue;
|
||||
_deletePath(dirfd(dir.get()), path + "/" + childName, bytesFreed);
|
||||
_deletePath(dirfd(dir.get()), path / childName, bytesFreed);
|
||||
}
|
||||
if (errno) throw SysError("reading directory %1%", path);
|
||||
}
|
||||
@@ -490,14 +498,13 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
|
||||
|
||||
static void _deletePath(const std::filesystem::path & path, uint64_t & bytesFreed)
|
||||
{
|
||||
Path dir = dirOf(path.string());
|
||||
if (dir == "")
|
||||
dir = "/";
|
||||
assert(path.is_absolute());
|
||||
assert(path.parent_path() != path);
|
||||
|
||||
AutoCloseFD dirfd = toDescriptor(open(dir.c_str(), O_RDONLY));
|
||||
AutoCloseFD dirfd = toDescriptor(open(path.parent_path().string().c_str(), O_RDONLY));
|
||||
if (!dirfd) {
|
||||
if (errno == ENOENT) return;
|
||||
throw SysError("opening directory '%1%'", path);
|
||||
throw SysError("opening directory %s", path.parent_path());
|
||||
}
|
||||
|
||||
_deletePath(dirfd.get(), path, bytesFreed);
|
||||
|
||||
@@ -175,21 +175,27 @@ std::string readFile(const Path & path);
|
||||
std::string readFile(const std::filesystem::path & path);
|
||||
void readFile(const Path & path, Sink & sink, bool memory_map = true);
|
||||
|
||||
enum struct FsSync { Yes, No };
|
||||
|
||||
/**
|
||||
* Write a string to a file.
|
||||
*/
|
||||
void writeFile(const Path & path, std::string_view s, mode_t mode = 0666, bool sync = false);
|
||||
static inline void writeFile(const std::filesystem::path & path, std::string_view s, mode_t mode = 0666, bool sync = false)
|
||||
void writeFile(const Path & path, std::string_view s, mode_t mode = 0666, FsSync sync = FsSync::No);
|
||||
|
||||
static inline void writeFile(const std::filesystem::path & path, std::string_view s, mode_t mode = 0666, FsSync sync = FsSync::No)
|
||||
{
|
||||
return writeFile(path.string(), s, mode, sync);
|
||||
}
|
||||
|
||||
void writeFile(const Path & path, Source & source, mode_t mode = 0666, bool sync = false);
|
||||
static inline void writeFile(const std::filesystem::path & path, Source & source, mode_t mode = 0666, bool sync = false)
|
||||
void writeFile(const Path & path, Source & source, mode_t mode = 0666, FsSync sync = FsSync::No);
|
||||
|
||||
static inline void writeFile(const std::filesystem::path & path, Source & source, mode_t mode = 0666, FsSync sync = FsSync::No)
|
||||
{
|
||||
return writeFile(path.string(), source, mode, sync);
|
||||
}
|
||||
|
||||
void writeFile(AutoCloseFD & fd, const Path & origPath, std::string_view s, mode_t mode = 0666, FsSync sync = FsSync::No);
|
||||
|
||||
/**
|
||||
* Flush a path's parent directory to disk.
|
||||
*/
|
||||
|
||||
@@ -187,6 +187,10 @@ void MemorySink::createSymlink(const CanonPath & path, const std::string & targe
|
||||
ref<SourceAccessor> makeEmptySourceAccessor()
|
||||
{
|
||||
static auto empty = make_ref<MemorySourceAccessor>().cast<SourceAccessor>();
|
||||
/* Don't forget to clear the display prefix, as the default constructed
|
||||
SourceAccessor has the «unknown» prefix. Since this accessor is supposed
|
||||
to mimic an empty root directory the prefix needs to be empty. */
|
||||
empty->setPathDisplay("");
|
||||
return empty;
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ expect 1 nix build -o "$TEST_ROOT/result" "$flake2Dir#bar" --no-update-lock-file
|
||||
nix build -o "$TEST_ROOT/result" "$flake2Dir#bar" --commit-lock-file
|
||||
[[ -e "$flake2Dir/flake.lock" ]]
|
||||
[[ -z $(git -C "$flake2Dir" diff main || echo failed) ]]
|
||||
[[ $(jq --indent 0 . < "$flake2Dir/flake.lock") =~ ^'{"nodes":{"flake1":{"locked":{"lastModified":'.*',"narHash":"sha256-'.*'","ref":"refs/heads/master","rev":"'.*'","revCount":2,"type":"git","url":"file:///'.*'"},"original":{"id":"flake1","type":"indirect"}},"root":{"inputs":{"flake1":"flake1"}}},"root":"root","version":7}'$ ]]
|
||||
[[ $(jq --indent 0 --compact-output . < "$flake2Dir/flake.lock") =~ ^'{"nodes":{"flake1":{"locked":{"lastModified":'.*',"narHash":"sha256-'.*'","ref":"refs/heads/master","rev":"'.*'","revCount":2,"type":"git","url":"file:///'.*'"},"original":{"id":"flake1","type":"indirect"}},"root":{"inputs":{"flake1":"flake1"}}},"root":"root","version":7}'$ ]]
|
||||
|
||||
# Rerunning the build should not change the lockfile.
|
||||
nix build -o "$TEST_ROOT/result" "$flake2Dir#bar"
|
||||
|
||||
@@ -69,7 +69,7 @@ git -C "$rootFlake" add flake.nix sub2/flake.nix
|
||||
git -C "$rootFlake" add sub2/flake.lock
|
||||
[[ $(nix eval "$subflake2#y") = 15 ]]
|
||||
|
||||
[[ $(jq --indent 0 . < "$subflake2/flake.lock") =~ ^'{"nodes":{"root":{"inputs":{"root":"root_2","sub1":"sub1"}},"root_2":{"inputs":{"sub0":"sub0"},"locked":{"path":"..","type":"path"},"original":{"path":"..","type":"path"},"parent":[]},"root_3":{"inputs":{"sub0":"sub0_2"},"locked":{"path":"../","type":"path"},"original":{"path":"../","type":"path"},"parent":["sub1"]},"sub0":{"locked":{"path":"sub0","type":"path"},"original":{"path":"sub0","type":"path"},"parent":["root"]},"sub0_2":{"locked":{"path":"sub0","type":"path"},"original":{"path":"sub0","type":"path"},"parent":["sub1","root"]},"sub1":{"inputs":{"root":"root_3"},"locked":{"path":"../sub1","type":"path"},"original":{"path":"../sub1","type":"path"},"parent":[]}},"root":"root","version":7}'$ ]]
|
||||
[[ $(jq --indent 0 --compact-output . < "$subflake2/flake.lock") =~ ^'{"nodes":{"root":{"inputs":{"root":"root_2","sub1":"sub1"}},"root_2":{"inputs":{"sub0":"sub0"},"locked":{"path":"..","type":"path"},"original":{"path":"..","type":"path"},"parent":[]},"root_3":{"inputs":{"sub0":"sub0_2"},"locked":{"path":"../","type":"path"},"original":{"path":"../","type":"path"},"parent":["sub1"]},"sub0":{"locked":{"path":"sub0","type":"path"},"original":{"path":"sub0","type":"path"},"parent":["root"]},"sub0_2":{"locked":{"path":"sub0","type":"path"},"original":{"path":"sub0","type":"path"},"parent":["sub1","root"]},"sub1":{"inputs":{"root":"root_3"},"locked":{"path":"../sub1","type":"path"},"original":{"path":"../sub1","type":"path"},"parent":[]}},"root":"root","version":7}'$ ]]
|
||||
|
||||
# Make sure there are no content locks for relative path flakes.
|
||||
(! grep "$TEST_ROOT" "$subflake2/flake.lock")
|
||||
|
||||
@@ -34,3 +34,15 @@ rm -rf $TEST_ROOT/eval-out
|
||||
(! nix eval --store dummy:// --write-to $TEST_ROOT/eval-out --expr '{ "." = "bla"; }')
|
||||
|
||||
(! nix eval --expr '~/foo')
|
||||
|
||||
expectStderr 0 nix eval --expr "/some/absolute/path" \
|
||||
| grepQuiet "/some/absolute/path"
|
||||
|
||||
expectStderr 0 nix eval --expr "/some/absolute/path" --impure \
|
||||
| grepQuiet "/some/absolute/path"
|
||||
|
||||
expectStderr 0 nix eval --expr "some/relative/path" \
|
||||
| grepQuiet "$PWD/some/relative/path"
|
||||
|
||||
expectStderr 0 nix eval --expr "some/relative/path" --impure \
|
||||
| grepQuiet "$PWD/some/relative/path"
|
||||
|
||||
@@ -163,7 +163,8 @@ foo + baz
|
||||
# - Re-eval it
|
||||
# - Check that the result has changed
|
||||
mkfifo repl_fifo
|
||||
nix repl ./flake --experimental-features 'flakes' < repl_fifo > repl_output 2>&1 &
|
||||
touch repl_output
|
||||
nix repl ./flake --experimental-features 'flakes' < repl_fifo >> repl_output 2>&1 &
|
||||
repl_pid=$!
|
||||
exec 3>repl_fifo # Open fifo for writing
|
||||
echo "changingThing" >&3
|
||||
|
||||
@@ -21,7 +21,7 @@ mkDerivation {
|
||||
"b"
|
||||
"c"
|
||||
];
|
||||
exportReferencesGraph.refs = [ dep ];
|
||||
exportReferencesGraph.refs = dep;
|
||||
buildCommand = ''
|
||||
touch ''${outputs[out]}; touch ''${outputs[dev]}
|
||||
'';
|
||||
|
||||
@@ -94,13 +94,6 @@ let
|
||||
);
|
||||
};
|
||||
|
||||
otherNixes.nix_2_18.setNixPackage =
|
||||
{ lib, pkgs, ... }:
|
||||
{
|
||||
imports = [ checkOverrideNixVersion ];
|
||||
nix.package = lib.mkForce pkgs.nixVersions.nix_2_18;
|
||||
};
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
@@ -61,12 +61,10 @@ in
|
||||
"${pkgs.openssh}/bin/ssh-keygen", "-t", "ed25519", "-f", "key", "-N", ""
|
||||
], capture_output=True, check=True)
|
||||
|
||||
client.succeed("mkdir -m 700 /root/.ssh")
|
||||
client.copy_from_host("key", "/root/.ssh/id_ed25519")
|
||||
client.succeed("chmod 600 /root/.ssh/id_ed25519")
|
||||
|
||||
# Install the SSH key on the server.
|
||||
server.succeed("mkdir -m 700 /root/.ssh")
|
||||
server.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
|
||||
server.wait_for_unit("sshd")
|
||||
server.wait_for_unit("multi-user.target")
|
||||
|
||||
Reference in New Issue
Block a user