mirror of
https://github.com/NixOS/nix.git
synced 2025-11-29 21:50:58 +01:00
Merge remote-tracking branch 'origin/master' into lazy-trees
This commit is contained in:
commit
12f141391c
63 changed files with 819 additions and 312 deletions
|
|
@ -127,6 +127,16 @@ ref<EvalState> EvalCommand::getEvalState()
|
|||
return ref<EvalState>(evalState);
|
||||
}
|
||||
|
||||
MixOperateOnOptions::MixOperateOnOptions()
|
||||
{
|
||||
addFlag({
|
||||
.longName = "derivation",
|
||||
.description = "Operate on the [store derivation](../../glossary.md#gloss-store-derivation) rather than its outputs.",
|
||||
.category = installablesCategory,
|
||||
.handler = {&operateOn, OperateOn::Derivation},
|
||||
});
|
||||
}
|
||||
|
||||
BuiltPathsCommand::BuiltPathsCommand(bool recursive)
|
||||
: recursive(recursive)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -96,9 +96,6 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
|
|||
std::optional<std::string> expr;
|
||||
bool readOnlyMode = false;
|
||||
|
||||
// FIXME: move this; not all commands (e.g. 'nix run') use it.
|
||||
OperateOn operateOn = OperateOn::Output;
|
||||
|
||||
SourceExprCommand(bool supportReadOnlyMode = false);
|
||||
|
||||
std::vector<std::shared_ptr<Installable>> parseInstallables(
|
||||
|
|
@ -153,8 +150,15 @@ private:
|
|||
std::string _installable{"."};
|
||||
};
|
||||
|
||||
struct MixOperateOnOptions : virtual Args
|
||||
{
|
||||
OperateOn operateOn = OperateOn::Output;
|
||||
|
||||
MixOperateOnOptions();
|
||||
};
|
||||
|
||||
/* A command that operates on zero or more store paths. */
|
||||
struct BuiltPathsCommand : public InstallablesCommand
|
||||
struct BuiltPathsCommand : InstallablesCommand, virtual MixOperateOnOptions
|
||||
{
|
||||
private:
|
||||
|
||||
|
|
|
|||
70
src/libcmd/installable-derived-path.cc
Normal file
70
src/libcmd/installable-derived-path.cc
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#include "installable-derived-path.hh"
|
||||
#include "derivations.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::string InstallableDerivedPath::what() const
|
||||
{
|
||||
return derivedPath.to_string(*store);
|
||||
}
|
||||
|
||||
DerivedPathsWithInfo InstallableDerivedPath::toDerivedPaths()
|
||||
{
|
||||
return {{.path = derivedPath, .info = {} }};
|
||||
}
|
||||
|
||||
std::optional<StorePath> InstallableDerivedPath::getStorePath()
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
[&](const DerivedPath::Built & bfd) {
|
||||
return bfd.drvPath;
|
||||
},
|
||||
[&](const DerivedPath::Opaque & bo) {
|
||||
return bo.path;
|
||||
},
|
||||
}, derivedPath.raw());
|
||||
}
|
||||
|
||||
InstallableDerivedPath InstallableDerivedPath::parse(
|
||||
ref<Store> store,
|
||||
std::string_view prefix,
|
||||
ExtendedOutputsSpec extendedOutputsSpec)
|
||||
{
|
||||
auto derivedPath = std::visit(overloaded {
|
||||
// If the user did not use ^, we treat the output more liberally.
|
||||
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
|
||||
// First, we accept a symlink chain or an actual store path.
|
||||
auto storePath = store->followLinksToStorePath(prefix);
|
||||
// Second, we see if the store path ends in `.drv` to decide what sort
|
||||
// of derived path they want.
|
||||
//
|
||||
// This handling predates the `^` syntax. The `^*` in
|
||||
// `/nix/store/hash-foo.drv^*` unambiguously means "do the
|
||||
// `DerivedPath::Built` case", so plain `/nix/store/hash-foo.drv` could
|
||||
// also unambiguously mean "do the DerivedPath::Opaque` case".
|
||||
//
|
||||
// Issue #7261 tracks reconsidering this `.drv` dispatching.
|
||||
return storePath.isDerivation()
|
||||
? (DerivedPath) DerivedPath::Built {
|
||||
.drvPath = std::move(storePath),
|
||||
.outputs = OutputsSpec::All {},
|
||||
}
|
||||
: (DerivedPath) DerivedPath::Opaque {
|
||||
.path = std::move(storePath),
|
||||
};
|
||||
},
|
||||
// If the user did use ^, we just do exactly what is written.
|
||||
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath {
|
||||
return DerivedPath::Built {
|
||||
.drvPath = store->parseStorePath(prefix),
|
||||
.outputs = outputSpec,
|
||||
};
|
||||
},
|
||||
}, extendedOutputsSpec.raw());
|
||||
return InstallableDerivedPath {
|
||||
store,
|
||||
std::move(derivedPath),
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
28
src/libcmd/installable-derived-path.hh
Normal file
28
src/libcmd/installable-derived-path.hh
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include "installables.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct InstallableDerivedPath : Installable
|
||||
{
|
||||
ref<Store> store;
|
||||
DerivedPath derivedPath;
|
||||
|
||||
InstallableDerivedPath(ref<Store> store, DerivedPath && derivedPath)
|
||||
: store(store), derivedPath(std::move(derivedPath))
|
||||
{ }
|
||||
|
||||
std::string what() const override;
|
||||
|
||||
DerivedPathsWithInfo toDerivedPaths() override;
|
||||
|
||||
std::optional<StorePath> getStorePath() override;
|
||||
|
||||
static InstallableDerivedPath parse(
|
||||
ref<Store> store,
|
||||
std::string_view prefix,
|
||||
ExtendedOutputsSpec extendedOutputsSpec);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#include "globals.hh"
|
||||
#include "installables.hh"
|
||||
#include "installable-derived-path.hh"
|
||||
#include "outputs-spec.hh"
|
||||
#include "util.hh"
|
||||
#include "command.hh"
|
||||
|
|
@ -167,13 +168,6 @@ SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
|
|||
.handler = {&expr}
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "derivation",
|
||||
.description = "Operate on the [store derivation](../../glossary.md#gloss-store-derivation) rather than its outputs.",
|
||||
.category = installablesCategory,
|
||||
.handler = {&operateOn, OperateOn::Derivation},
|
||||
});
|
||||
|
||||
if (supportReadOnlyMode) {
|
||||
addFlag({
|
||||
.longName = "read-only",
|
||||
|
|
@ -397,38 +391,6 @@ static StorePath getDeriver(
|
|||
return *derivers.begin();
|
||||
}
|
||||
|
||||
struct InstallableStorePath : Installable
|
||||
{
|
||||
ref<Store> store;
|
||||
DerivedPath req;
|
||||
|
||||
InstallableStorePath(ref<Store> store, DerivedPath && req)
|
||||
: store(store), req(std::move(req))
|
||||
{ }
|
||||
|
||||
std::string what() const override
|
||||
{
|
||||
return req.to_string(*store);
|
||||
}
|
||||
|
||||
DerivedPathsWithInfo toDerivedPaths() override
|
||||
{
|
||||
return {{req}};
|
||||
}
|
||||
|
||||
std::optional<StorePath> getStorePath() override
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
[&](const DerivedPath::Built & bfd) {
|
||||
return bfd.drvPath;
|
||||
},
|
||||
[&](const DerivedPath::Opaque & bo) {
|
||||
return bo.path;
|
||||
},
|
||||
}, req.raw());
|
||||
}
|
||||
};
|
||||
|
||||
struct InstallableAttrPath : InstallableValue
|
||||
{
|
||||
SourceExprCommand & cmd;
|
||||
|
|
@ -792,41 +754,10 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
|||
auto prefix = std::move(prefix_);
|
||||
auto extendedOutputsSpec = std::move(extendedOutputsSpec_);
|
||||
|
||||
auto found = prefix.find('/');
|
||||
if (found != std::string::npos) {
|
||||
if (prefix.find('/') != std::string::npos) {
|
||||
try {
|
||||
auto derivedPath = std::visit(overloaded {
|
||||
// If the user did not use ^, we treat the output more liberally.
|
||||
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
|
||||
// First, we accept a symlink chain or an actual store path.
|
||||
auto storePath = store->followLinksToStorePath(prefix);
|
||||
// Second, we see if the store path ends in `.drv` to decide what sort
|
||||
// of derived path they want.
|
||||
//
|
||||
// This handling predates the `^` syntax. The `^*` in
|
||||
// `/nix/store/hash-foo.drv^*` unambiguously means "do the
|
||||
// `DerivedPath::Built` case", so plain `/nix/store/hash-foo.drv` could
|
||||
// also unambiguously mean "do the DerivedPath::Opaque` case".
|
||||
//
|
||||
// Issue #7261 tracks reconsidering this `.drv` dispatching.
|
||||
return storePath.isDerivation()
|
||||
? (DerivedPath) DerivedPath::Built {
|
||||
.drvPath = std::move(storePath),
|
||||
.outputs = OutputsSpec::All {},
|
||||
}
|
||||
: (DerivedPath) DerivedPath::Opaque {
|
||||
.path = std::move(storePath),
|
||||
};
|
||||
},
|
||||
// If the user did use ^, we just do exactly what is written.
|
||||
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath {
|
||||
return DerivedPath::Built {
|
||||
.drvPath = store->parseStorePath(prefix),
|
||||
.outputs = outputSpec,
|
||||
};
|
||||
},
|
||||
}, extendedOutputsSpec.raw());
|
||||
result.push_back(std::make_shared<InstallableStorePath>(store, std::move(derivedPath)));
|
||||
result.push_back(std::make_shared<InstallableDerivedPath>(
|
||||
InstallableDerivedPath::parse(store, prefix, extendedOutputsSpec)));
|
||||
continue;
|
||||
} catch (BadStorePath &) {
|
||||
} catch (...) {
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ Name: Nix
|
|||
Description: Nix Package Manager
|
||||
Version: @PACKAGE_VERSION@
|
||||
Libs: -L${libdir} -lnixcmd
|
||||
Cflags: -I${includedir}/nix -std=c++20
|
||||
Cflags: -I${includedir}/nix -std=c++2a
|
||||
|
|
|
|||
|
|
@ -2491,7 +2491,7 @@ Strings EvalSettings::getDefaultNixPath()
|
|||
res.push_back(s ? *s + "=" + p : p);
|
||||
};
|
||||
|
||||
add(getHome() + "/.nix-defexpr/channels");
|
||||
add(settings.useXDGBaseDirectories ? getStateDir() + "/nix/defexpr/channels" : getHome() + "/.nix-defexpr/channels");
|
||||
add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs");
|
||||
add(settings.nixStateDir + "/profiles/per-user/root/channels");
|
||||
|
||||
|
|
|
|||
|
|
@ -7,4 +7,4 @@ Description: Nix Package Manager
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires: nix-store bdw-gc
|
||||
Libs: -L${libdir} -lnixexpr
|
||||
Cflags: -I${includedir}/nix -std=c++20
|
||||
Cflags: -I${includedir}/nix -std=c++2a
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ struct ExprString : Expr
|
|||
{
|
||||
std::string s;
|
||||
Value v;
|
||||
ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
|
||||
ExprString(std::string &&s) : s(std::move(s)) { v.mkString(this->s.data()); };
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
|
@ -236,7 +236,7 @@ struct ExprSelect : Expr
|
|||
PosIdx pos;
|
||||
Expr * e, * def;
|
||||
AttrPath attrPath;
|
||||
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
|
||||
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath && attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(attrPath)) { };
|
||||
ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
COMMON_METHODS
|
||||
|
|
@ -246,7 +246,7 @@ struct ExprOpHasAttr : Expr
|
|||
{
|
||||
Expr * e;
|
||||
AttrPath attrPath;
|
||||
ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { };
|
||||
ExprOpHasAttr(Expr * e, const AttrPath && attrPath) : e(e), attrPath(std::move(attrPath)) { };
|
||||
PosIdx getPos() const override { return e->getPos(); }
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ static void dupAttr(const EvalState & state, Symbol attr, const PosIdx pos, cons
|
|||
}
|
||||
|
||||
|
||||
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||
static void addAttr(ExprAttrs * attrs, AttrPath && attrPath,
|
||||
Expr * e, const PosIdx pos, const nix::EvalState & state)
|
||||
{
|
||||
AttrPath::iterator i;
|
||||
|
|
@ -188,7 +188,7 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
|
|||
|
||||
|
||||
static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
|
||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> & es)
|
||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
||||
{
|
||||
if (es.empty()) return new ExprString("");
|
||||
|
||||
|
|
@ -268,7 +268,7 @@ static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
|
|||
s2 = std::string(s2, 0, p + 1);
|
||||
}
|
||||
|
||||
es2->emplace_back(i->first, new ExprString(s2));
|
||||
es2->emplace_back(i->first, new ExprString(std::move(s2)));
|
||||
};
|
||||
for (; i != es.end(); ++i, --n) {
|
||||
std::visit(overloaded { trimExpr, trimString }, i->second);
|
||||
|
|
@ -413,7 +413,7 @@ expr_op
|
|||
| expr_op OR expr_op { $$ = new ExprOpOr(makeCurPos(@2, data), $1, $3); }
|
||||
| expr_op IMPL expr_op { $$ = new ExprOpImpl(makeCurPos(@2, data), $1, $3); }
|
||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(makeCurPos(@2, data), $1, $3); }
|
||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, std::move(*$3)); delete $3; }
|
||||
| expr_op '+' expr_op
|
||||
{ $$ = new ExprConcatStrings(makeCurPos(@2, data), false, new std::vector<std::pair<PosIdx, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
||||
| expr_op '-' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
||||
|
|
@ -436,14 +436,14 @@ expr_app
|
|||
|
||||
expr_select
|
||||
: expr_simple '.' attrpath
|
||||
{ $$ = new ExprSelect(CUR_POS, $1, *$3, 0); }
|
||||
{ $$ = new ExprSelect(CUR_POS, $1, std::move(*$3), nullptr); delete $3; }
|
||||
| expr_simple '.' attrpath OR_KW expr_select
|
||||
{ $$ = new ExprSelect(CUR_POS, $1, *$3, $5); }
|
||||
{ $$ = new ExprSelect(CUR_POS, $1, std::move(*$3), $5); delete $3; }
|
||||
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
||||
function named ‘or’, allow stuff like ‘map or [...]’. */
|
||||
expr_simple OR_KW
|
||||
{ $$ = new ExprCall(CUR_POS, $1, {new ExprVar(CUR_POS, data->symbols.create("or"))}); }
|
||||
| expr_simple { $$ = $1; }
|
||||
| expr_simple
|
||||
;
|
||||
|
||||
expr_simple
|
||||
|
|
@ -458,7 +458,8 @@ expr_simple
|
|||
| FLOAT { $$ = new ExprFloat($1); }
|
||||
| '"' string_parts '"' { $$ = $2; }
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
$$ = stripIndentation(CUR_POS, data->symbols, *$2);
|
||||
$$ = stripIndentation(CUR_POS, data->symbols, std::move(*$2));
|
||||
delete $2;
|
||||
}
|
||||
| path_start PATH_END { $$ = $1.e; }
|
||||
| path_start string_parts_interpolated PATH_END {
|
||||
|
|
@ -472,7 +473,7 @@ expr_simple
|
|||
$$ = new ExprCall(CUR_POS,
|
||||
new ExprVar(data->symbols.create("__findFile")),
|
||||
{new ExprVar(data->symbols.create("__nixPath")),
|
||||
new ExprString(path)});
|
||||
new ExprString(std::move(path))});
|
||||
}
|
||||
| URI {
|
||||
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
|
||||
|
|
@ -546,7 +547,7 @@ ind_string_parts
|
|||
;
|
||||
|
||||
binds
|
||||
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data), data->state); }
|
||||
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, std::move(*$2), $4, makeCurPos(@2, data), data->state); delete $2; }
|
||||
| binds INHERIT attrs ';'
|
||||
{ $$ = $1;
|
||||
for (auto & i : *$3) {
|
||||
|
|
@ -555,6 +556,7 @@ binds
|
|||
auto pos = makeCurPos(@3, data);
|
||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
||||
}
|
||||
delete $3;
|
||||
}
|
||||
| binds INHERIT '(' expr ')' attrs ';'
|
||||
{ $$ = $1;
|
||||
|
|
@ -564,6 +566,7 @@ binds
|
|||
dupAttr(data->state, i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
|
||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
|
||||
}
|
||||
delete $6;
|
||||
}
|
||||
| { $$ = new ExprAttrs(makeCurPos(@0, data)); }
|
||||
;
|
||||
|
|
@ -609,7 +612,7 @@ attrpath
|
|||
;
|
||||
|
||||
attr
|
||||
: ID { $$ = $1; }
|
||||
: ID
|
||||
| OR_KW { $$ = {"or", 2}; }
|
||||
;
|
||||
|
||||
|
|
@ -625,9 +628,9 @@ expr_list
|
|||
|
||||
formals
|
||||
: formal ',' formals
|
||||
{ $$ = $3; $$->formals.push_back(*$1); }
|
||||
{ $$ = $3; $$->formals.emplace_back(*$1); delete $1; }
|
||||
| formal
|
||||
{ $$ = new ParserFormals; $$->formals.push_back(*$1); $$->ellipsis = false; }
|
||||
{ $$ = new ParserFormals; $$->formals.emplace_back(*$1); $$->ellipsis = false; delete $1; }
|
||||
|
|
||||
{ $$ = new ParserFormals; $$->ellipsis = false; }
|
||||
| ELLIPSIS
|
||||
|
|
|
|||
|
|
@ -713,15 +713,42 @@ struct GitInputScheme : InputScheme
|
|||
AutoDelete delTmpGitDir(tmpGitDir, true);
|
||||
|
||||
runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", tmpDir, "--separate-git-dir", tmpGitDir });
|
||||
// TODO: repoDir might lack the ref (it only checks if rev
|
||||
// exists, see FIXME above) so use a big hammer and fetch
|
||||
// everything to ensure we get the rev.
|
||||
runProgram("git", true, { "-C", tmpDir, "fetch", "--quiet", "--force",
|
||||
"--update-head-ok", "--", repoDir, "refs/*:refs/*" });
|
||||
|
||||
{
|
||||
// TODO: repoDir might lack the ref (it only checks if rev
|
||||
// exists, see FIXME above) so use a big hammer and fetch
|
||||
// everything to ensure we get the rev.
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("making temporary clone of '%s'", repoDir));
|
||||
runProgram("git", true, { "-C", tmpDir, "fetch", "--quiet", "--force",
|
||||
"--update-head-ok", "--", repoDir, "refs/*:refs/*" });
|
||||
}
|
||||
|
||||
runProgram("git", true, { "-C", tmpDir, "checkout", "--quiet", rev.gitRev() });
|
||||
runProgram("git", true, { "-C", tmpDir, "remote", "add", "origin", repoInfo.url });
|
||||
runProgram("git", true, { "-C", tmpDir, "submodule", "--quiet", "update", "--init", "--recursive" });
|
||||
|
||||
/* Ensure that we use the correct origin for fetching
|
||||
submodules. This matters for submodules with relative
|
||||
URLs. */
|
||||
if (repoInfo.isLocal) {
|
||||
writeFile(tmpGitDir + "/config", readFile(repoDir + "/" + repoInfo.gitDir + "/config"));
|
||||
|
||||
/* Restore the config.bare setting we may have just
|
||||
copied erroneously from the user's repo. */
|
||||
runProgram("git", true, { "-C", tmpDir, "config", "core.bare", "false" });
|
||||
} else
|
||||
runProgram("git", true, { "-C", tmpDir, "config", "remote.origin.url", repoInfo.url });
|
||||
|
||||
/* As an optimisation, copy the modules directory of the
|
||||
source repo if it exists. */
|
||||
auto modulesPath = repoDir + "/" + repoInfo.gitDir + "/modules";
|
||||
if (pathExists(modulesPath)) {
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying submodules of '%s'", repoInfo.url));
|
||||
runProgram("cp", true, { "-R", "--", modulesPath, tmpGitDir + "/modules" });
|
||||
}
|
||||
|
||||
{
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching submodules of '%s'", repoInfo.url));
|
||||
runProgram("git", true, { "-C", tmpDir, "submodule", "--quiet", "update", "--init", "--recursive" });
|
||||
}
|
||||
|
||||
filter = isNotDotGitDirectory;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ Name: Nix
|
|||
Description: Nix Package Manager
|
||||
Version: @PACKAGE_VERSION@
|
||||
Libs: -L${libdir} -lnixmain
|
||||
Cflags: -I${includedir}/nix -std=c++20
|
||||
Cflags: -I${includedir}/nix -std=c++2a
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ void LocalDerivationGoal::tryLocalBuild()
|
|||
|
||||
#if __linux__
|
||||
if (useChroot) {
|
||||
if (!mountNamespacesSupported() || !pidNamespacesSupported()) {
|
||||
if (!mountAndPidNamespacesSupported()) {
|
||||
if (!settings.sandboxFallback)
|
||||
throw Error("this system does not support the kernel namespaces that are required for sandboxing; use '--no-sandbox' to disable sandboxing");
|
||||
debug("auto-disabling sandboxing because the prerequisite namespaces are not available");
|
||||
|
|
@ -385,12 +385,6 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck()
|
|||
}
|
||||
|
||||
|
||||
int childEntry(void * arg)
|
||||
{
|
||||
((LocalDerivationGoal *) arg)->runChild();
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if __linux__
|
||||
static void linkOrCopy(const Path & from, const Path & to)
|
||||
{
|
||||
|
|
@ -676,7 +670,8 @@ void LocalDerivationGoal::startBuilder()
|
|||
nobody account. The latter is kind of a hack to support
|
||||
Samba-in-QEMU. */
|
||||
createDirs(chrootRootDir + "/etc");
|
||||
chownToBuilder(chrootRootDir + "/etc");
|
||||
if (parsedDrv->useUidRange())
|
||||
chownToBuilder(chrootRootDir + "/etc");
|
||||
|
||||
if (parsedDrv->useUidRange() && (!buildUser || buildUser->getUIDCount() < 65536))
|
||||
throw Error("feature 'uid-range' requires the setting '%s' to be enabled", settings.autoAllocateUids.name);
|
||||
|
|
@ -916,21 +911,15 @@ void LocalDerivationGoal::startBuilder()
|
|||
if (getuid() == 0 && setgroups(0, 0) == -1)
|
||||
throw SysError("setgroups failed");
|
||||
|
||||
size_t stackSize = 1 * 1024 * 1024;
|
||||
char * stack = (char *) mmap(0, stackSize,
|
||||
PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
|
||||
if (stack == MAP_FAILED) throw SysError("allocating stack");
|
||||
|
||||
int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD;
|
||||
ProcessOptions options;
|
||||
options.cloneFlags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD;
|
||||
if (privateNetwork)
|
||||
flags |= CLONE_NEWNET;
|
||||
options.cloneFlags |= CLONE_NEWNET;
|
||||
if (usingUserNamespace)
|
||||
flags |= CLONE_NEWUSER;
|
||||
options.cloneFlags |= CLONE_NEWUSER;
|
||||
|
||||
pid_t child = clone(childEntry, stack + stackSize, flags, this);
|
||||
pid_t child = startProcess([&]() { runChild(); }, options);
|
||||
|
||||
if (child == -1)
|
||||
throw SysError("creating sandboxed builder process using clone()");
|
||||
writeFull(builderOut.writeSide.get(),
|
||||
fmt("%d %d\n", usingUserNamespace, child));
|
||||
_exit(0);
|
||||
|
|
@ -982,6 +971,10 @@ void LocalDerivationGoal::startBuilder()
|
|||
"nobody:x:65534:65534:Nobody:/:/noshell\n",
|
||||
sandboxUid(), sandboxGid(), settings.sandboxBuildDir));
|
||||
|
||||
/* Make /etc unwritable */
|
||||
if (!parsedDrv->useUidRange())
|
||||
chmod_(chrootRootDir + "/etc", 0555);
|
||||
|
||||
/* Save the mount- and user namespace of the child. We have to do this
|
||||
*before* the child does a chroot. */
|
||||
sandboxMountNamespace = open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY);
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ struct curlFileTransfer : public FileTransfer
|
|||
this->result.data.append(data);
|
||||
})
|
||||
{
|
||||
requestHeaders = curl_slist_append(requestHeaders, "Accept-Encoding: zstd, br, gzip, deflate, bzip2, xz");
|
||||
if (!request.expectedETag.empty())
|
||||
requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + request.expectedETag).c_str());
|
||||
if (!request.mimeType.empty())
|
||||
|
|
|
|||
|
|
@ -945,6 +945,27 @@ public:
|
|||
resolves to a different location from that of the build machine. You
|
||||
can enable this setting if you are sure you're not going to do that.
|
||||
)"};
|
||||
|
||||
Setting<bool> useXDGBaseDirectories{
|
||||
this, false, "use-xdg-base-directories",
|
||||
R"(
|
||||
If set to `true`, Nix will conform to the [XDG Base Directory Specification] for files in `$HOME`.
|
||||
The environment variables used to implement this are documented in the [Environment Variables section](@docroot@/installation/env-variables.md).
|
||||
|
||||
[XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
|
||||
> **Warning**
|
||||
> This changes the location of some well-known symlinks that Nix creates, which might break tools that rely on the old, non-XDG-conformant locations.
|
||||
|
||||
In particular, the following locations change:
|
||||
|
||||
| Old | New |
|
||||
|-------------------|--------------------------------|
|
||||
| `~/.nix-profile` | `$XDG_STATE_HOME/nix/profile` |
|
||||
| `~/.nix-defexpr` | `$XDG_STATE_HOME/nix/defexpr` |
|
||||
| `~/.nix-channels` | `$XDG_STATE_HOME/nix/channels` |
|
||||
)"
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ public:
|
|||
void init() override
|
||||
{
|
||||
// FIXME: do this lazily?
|
||||
if (auto cacheInfo = diskCache->cacheExists(cacheUri)) {
|
||||
if (auto cacheInfo = diskCache->upToDateCacheExists(cacheUri)) {
|
||||
wantMassQuery.setDefault(cacheInfo->wantMassQuery);
|
||||
priority.setDefault(cacheInfo->priority);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -84,11 +84,10 @@ public:
|
|||
|
||||
Sync<State> _state;
|
||||
|
||||
NarInfoDiskCacheImpl()
|
||||
NarInfoDiskCacheImpl(Path dbPath = getCacheDir() + "/nix/binary-cache-v6.sqlite")
|
||||
{
|
||||
auto state(_state.lock());
|
||||
|
||||
Path dbPath = getCacheDir() + "/nix/binary-cache-v6.sqlite";
|
||||
createDirs(dirOf(dbPath));
|
||||
|
||||
state->db = SQLite(dbPath);
|
||||
|
|
@ -98,7 +97,7 @@ public:
|
|||
state->db.exec(schema);
|
||||
|
||||
state->insertCache.create(state->db,
|
||||
"insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)");
|
||||
"insert into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?1, ?2, ?3, ?4, ?5) on conflict (url) do update set timestamp = ?2, storeDir = ?3, wantMassQuery = ?4, priority = ?5 returning id;");
|
||||
|
||||
state->queryCache.create(state->db,
|
||||
"select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ? and timestamp > ?");
|
||||
|
|
@ -166,6 +165,8 @@ public:
|
|||
return i->second;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::optional<Cache> queryCacheRaw(State & state, const std::string & uri)
|
||||
{
|
||||
auto i = state.caches.find(uri);
|
||||
|
|
@ -173,15 +174,21 @@ public:
|
|||
auto queryCache(state.queryCache.use()(uri)(time(0) - cacheInfoTtl));
|
||||
if (!queryCache.next())
|
||||
return std::nullopt;
|
||||
state.caches.emplace(uri,
|
||||
Cache{(int) queryCache.getInt(0), queryCache.getStr(1), queryCache.getInt(2) != 0, (int) queryCache.getInt(3)});
|
||||
auto cache = Cache {
|
||||
.id = (int) queryCache.getInt(0),
|
||||
.storeDir = queryCache.getStr(1),
|
||||
.wantMassQuery = queryCache.getInt(2) != 0,
|
||||
.priority = (int) queryCache.getInt(3),
|
||||
};
|
||||
state.caches.emplace(uri, cache);
|
||||
}
|
||||
return getCache(state, uri);
|
||||
}
|
||||
|
||||
void createCache(const std::string & uri, const Path & storeDir, bool wantMassQuery, int priority) override
|
||||
public:
|
||||
int createCache(const std::string & uri, const Path & storeDir, bool wantMassQuery, int priority) override
|
||||
{
|
||||
retrySQLite<void>([&]() {
|
||||
return retrySQLite<int>([&]() {
|
||||
auto state(_state.lock());
|
||||
SQLiteTxn txn(state->db);
|
||||
|
||||
|
|
@ -190,17 +197,29 @@ public:
|
|||
auto cache(queryCacheRaw(*state, uri));
|
||||
|
||||
if (cache)
|
||||
return;
|
||||
return cache->id;
|
||||
|
||||
state->insertCache.use()(uri)(time(0))(storeDir)(wantMassQuery)(priority).exec();
|
||||
assert(sqlite3_changes(state->db) == 1);
|
||||
state->caches[uri] = Cache{(int) sqlite3_last_insert_rowid(state->db), storeDir, wantMassQuery, priority};
|
||||
Cache ret {
|
||||
.id = -1, // set below
|
||||
.storeDir = storeDir,
|
||||
.wantMassQuery = wantMassQuery,
|
||||
.priority = priority,
|
||||
};
|
||||
|
||||
{
|
||||
auto r(state->insertCache.use()(uri)(time(0))(storeDir)(wantMassQuery)(priority));
|
||||
assert(r.next());
|
||||
ret.id = (int) r.getInt(0);
|
||||
}
|
||||
|
||||
state->caches[uri] = ret;
|
||||
|
||||
txn.commit();
|
||||
return ret.id;
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<CacheInfo> cacheExists(const std::string & uri) override
|
||||
std::optional<CacheInfo> upToDateCacheExists(const std::string & uri) override
|
||||
{
|
||||
return retrySQLite<std::optional<CacheInfo>>([&]() -> std::optional<CacheInfo> {
|
||||
auto state(_state.lock());
|
||||
|
|
@ -208,6 +227,7 @@ public:
|
|||
if (!cache)
|
||||
return std::nullopt;
|
||||
return CacheInfo {
|
||||
.id = cache->id,
|
||||
.wantMassQuery = cache->wantMassQuery,
|
||||
.priority = cache->priority
|
||||
};
|
||||
|
|
@ -371,4 +391,9 @@ ref<NarInfoDiskCache> getNarInfoDiskCache()
|
|||
return cache;
|
||||
}
|
||||
|
||||
ref<NarInfoDiskCache> getTestNarInfoDiskCache(Path dbPath)
|
||||
{
|
||||
return make_ref<NarInfoDiskCacheImpl>(dbPath);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,16 +13,17 @@ public:
|
|||
|
||||
virtual ~NarInfoDiskCache() { }
|
||||
|
||||
virtual void createCache(const std::string & uri, const Path & storeDir,
|
||||
virtual int createCache(const std::string & uri, const Path & storeDir,
|
||||
bool wantMassQuery, int priority) = 0;
|
||||
|
||||
struct CacheInfo
|
||||
{
|
||||
int id;
|
||||
bool wantMassQuery;
|
||||
int priority;
|
||||
};
|
||||
|
||||
virtual std::optional<CacheInfo> cacheExists(const std::string & uri) = 0;
|
||||
virtual std::optional<CacheInfo> upToDateCacheExists(const std::string & uri) = 0;
|
||||
|
||||
virtual std::pair<Outcome, std::shared_ptr<NarInfo>> lookupNarInfo(
|
||||
const std::string & uri, const std::string & hashPart) = 0;
|
||||
|
|
@ -45,4 +46,6 @@ public:
|
|||
multiple threads. */
|
||||
ref<NarInfoDiskCache> getNarInfoDiskCache();
|
||||
|
||||
ref<NarInfoDiskCache> getTestNarInfoDiskCache(Path dbPath);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ Name: Nix
|
|||
Description: Nix Package Manager
|
||||
Version: @PACKAGE_VERSION@
|
||||
Libs: -L${libdir} -lnixstore -lnixutil
|
||||
Cflags: -I${includedir}/nix -std=c++20
|
||||
Cflags: -I${includedir}/nix -std=c++2a
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ std::string optimisticLockProfile(const Path & profile)
|
|||
|
||||
Path profilesDir()
|
||||
{
|
||||
auto profileRoot = getDataDir() + "/nix/profiles";
|
||||
auto profileRoot = createNixStateDir() + "/profiles";
|
||||
createDirs(profileRoot);
|
||||
return profileRoot;
|
||||
}
|
||||
|
|
@ -290,7 +290,7 @@ Path profilesDir()
|
|||
|
||||
Path getDefaultProfile()
|
||||
{
|
||||
Path profileLink = getHome() + "/.nix-profile";
|
||||
Path profileLink = settings.useXDGBaseDirectories ? createNixStateDir() + "/profile" : getHome() + "/.nix-profile";
|
||||
try {
|
||||
auto profile =
|
||||
getuid() == 0
|
||||
|
|
|
|||
|
|
@ -72,8 +72,9 @@ std::string optimisticLockProfile(const Path & profile);
|
|||
profiles. */
|
||||
Path profilesDir();
|
||||
|
||||
/* Resolve ~/.nix-profile. If ~/.nix-profile doesn't exist yet, create
|
||||
it. */
|
||||
/* Resolve the default profile (~/.nix-profile by default, $XDG_STATE_HOME/
|
||||
nix/profile if XDG Base Directory Support is enabled), and create if doesn't
|
||||
exist */
|
||||
Path getDefaultProfile();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
|
|||
|
||||
void init() override
|
||||
{
|
||||
if (auto cacheInfo = diskCache->cacheExists(getUri())) {
|
||||
if (auto cacheInfo = diskCache->upToDateCacheExists(getUri())) {
|
||||
wantMassQuery.setDefault(cacheInfo->wantMassQuery);
|
||||
priority.setDefault(cacheInfo->priority);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,15 @@ SQLiteError::SQLiteError(const char *path, const char *errMsg, int errNo, int ex
|
|||
throw SQLiteError(path, errMsg, err, exterr, offset, std::move(hf));
|
||||
}
|
||||
|
||||
static void traceSQL(void * x, const char * sql)
|
||||
{
|
||||
// wacky delimiters:
|
||||
// so that we're quite unambiguous without escaping anything
|
||||
// notice instead of trace:
|
||||
// so that this can be enabled without getting the firehose in our face.
|
||||
notice("SQL<[%1%]>", sql);
|
||||
};
|
||||
|
||||
SQLite::SQLite(const Path & path, bool create)
|
||||
{
|
||||
// useSQLiteWAL also indicates what virtual file system we need. Using
|
||||
|
|
@ -58,6 +67,11 @@ SQLite::SQLite(const Path & path, bool create)
|
|||
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
||||
SQLiteError::throw_(db, "setting timeout");
|
||||
|
||||
if (getEnv("NIX_DEBUG_SQLITE_TRACES") == "1") {
|
||||
// To debug sqlite statements; trace all of them
|
||||
sqlite3_trace(db, &traceSQL, nullptr);
|
||||
}
|
||||
|
||||
exec("pragma foreign_keys = 1");
|
||||
}
|
||||
|
||||
|
|
|
|||
123
src/libstore/tests/nar-info-disk-cache.cc
Normal file
123
src/libstore/tests/nar-info-disk-cache.cc
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#include "nar-info-disk-cache.hh"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <rapidcheck/gtest.h>
|
||||
#include "sqlite.hh"
|
||||
#include <sqlite3.h>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
TEST(NarInfoDiskCacheImpl, create_and_read) {
|
||||
// This is a large single test to avoid some setup overhead.
|
||||
|
||||
int prio = 12345;
|
||||
bool wantMassQuery = true;
|
||||
|
||||
Path tmpDir = createTempDir();
|
||||
AutoDelete delTmpDir(tmpDir);
|
||||
Path dbPath(tmpDir + "/test-narinfo-disk-cache.sqlite");
|
||||
|
||||
int savedId;
|
||||
int barId;
|
||||
SQLite db;
|
||||
SQLiteStmt getIds;
|
||||
|
||||
{
|
||||
auto cache = getTestNarInfoDiskCache(dbPath);
|
||||
|
||||
// Set up "background noise" and check that different caches receive different ids
|
||||
{
|
||||
auto bc1 = cache->createCache("https://bar", "/nix/storedir", wantMassQuery, prio);
|
||||
auto bc2 = cache->createCache("https://xyz", "/nix/storedir", false, 12);
|
||||
ASSERT_NE(bc1, bc2);
|
||||
barId = bc1;
|
||||
}
|
||||
|
||||
// Check that the fields are saved and returned correctly. This does not test
|
||||
// the select statement yet, because of in-memory caching.
|
||||
savedId = cache->createCache("http://foo", "/nix/storedir", wantMassQuery, prio);;
|
||||
{
|
||||
auto r = cache->upToDateCacheExists("http://foo");
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(r->priority, prio);
|
||||
ASSERT_EQ(r->wantMassQuery, wantMassQuery);
|
||||
ASSERT_EQ(savedId, r->id);
|
||||
}
|
||||
|
||||
// We're going to pay special attention to the id field because we had a bug
|
||||
// that changed it.
|
||||
db = SQLite(dbPath);
|
||||
getIds.create(db, "select id from BinaryCaches where url = 'http://foo'");
|
||||
|
||||
{
|
||||
auto q(getIds.use());
|
||||
ASSERT_TRUE(q.next());
|
||||
ASSERT_EQ(savedId, q.getInt(0));
|
||||
ASSERT_FALSE(q.next());
|
||||
}
|
||||
|
||||
// Pretend that the caches are older, but keep one up to date, as "background noise"
|
||||
db.exec("update BinaryCaches set timestamp = timestamp - 1 - 7 * 24 * 3600 where url <> 'https://xyz';");
|
||||
|
||||
// This shows that the in-memory cache works
|
||||
{
|
||||
auto r = cache->upToDateCacheExists("http://foo");
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(r->priority, prio);
|
||||
ASSERT_EQ(r->wantMassQuery, wantMassQuery);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// We can't clear the in-memory cache, so we use a new cache object. This is
|
||||
// more realistic anyway.
|
||||
auto cache2 = getTestNarInfoDiskCache(dbPath);
|
||||
|
||||
{
|
||||
auto r = cache2->upToDateCacheExists("http://foo");
|
||||
ASSERT_FALSE(r);
|
||||
}
|
||||
|
||||
// "Update", same data, check that the id number is reused
|
||||
cache2->createCache("http://foo", "/nix/storedir", wantMassQuery, prio);
|
||||
|
||||
{
|
||||
auto r = cache2->upToDateCacheExists("http://foo");
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(r->priority, prio);
|
||||
ASSERT_EQ(r->wantMassQuery, wantMassQuery);
|
||||
ASSERT_EQ(r->id, savedId);
|
||||
}
|
||||
|
||||
{
|
||||
auto q(getIds.use());
|
||||
ASSERT_TRUE(q.next());
|
||||
auto currentId = q.getInt(0);
|
||||
ASSERT_FALSE(q.next());
|
||||
ASSERT_EQ(currentId, savedId);
|
||||
}
|
||||
|
||||
// Check that the fields can be modified, and the id remains the same
|
||||
{
|
||||
auto r0 = cache2->upToDateCacheExists("https://bar");
|
||||
ASSERT_FALSE(r0);
|
||||
|
||||
cache2->createCache("https://bar", "/nix/storedir", !wantMassQuery, prio + 10);
|
||||
auto r = cache2->upToDateCacheExists("https://bar");
|
||||
ASSERT_EQ(r->wantMassQuery, !wantMassQuery);
|
||||
ASSERT_EQ(r->priority, prio + 10);
|
||||
ASSERT_EQ(r->id, barId);
|
||||
}
|
||||
|
||||
// // Force update (no use case yet; we only retrieve cache metadata when stale based on timestamp)
|
||||
// {
|
||||
// cache2->createCache("https://bar", "/nix/storedir", wantMassQuery, prio + 20);
|
||||
// auto r = cache2->upToDateCacheExists("https://bar");
|
||||
// ASSERT_EQ(r->wantMassQuery, wantMassQuery);
|
||||
// ASSERT_EQ(r->priority, prio + 20);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -29,7 +29,15 @@ void Args::removeFlag(const std::string & longName)
|
|||
|
||||
void Completions::add(std::string completion, std::string description)
|
||||
{
|
||||
assert(description.find('\n') == std::string::npos);
|
||||
description = trim(description);
|
||||
// ellipsize overflowing content on the back of the description
|
||||
auto end_index = description.find_first_of(".\n");
|
||||
if (end_index != std::string::npos) {
|
||||
auto needs_ellipsis = end_index != description.size() - 1;
|
||||
description.resize(end_index);
|
||||
if (needs_ellipsis)
|
||||
description.append(" [...]");
|
||||
}
|
||||
insert(Completion {
|
||||
.completion = completion,
|
||||
.description = description
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include "util.hh"
|
||||
#include "finally.hh"
|
||||
|
||||
#include <mntent.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
@ -33,63 +33,60 @@ bool userNamespacesSupported()
|
|||
return false;
|
||||
}
|
||||
|
||||
Pid pid = startProcess([&]()
|
||||
{
|
||||
auto res = unshare(CLONE_NEWUSER);
|
||||
_exit(res ? 1 : 0);
|
||||
});
|
||||
try {
|
||||
Pid pid = startProcess([&]()
|
||||
{
|
||||
_exit(0);
|
||||
}, {
|
||||
.cloneFlags = CLONE_NEWUSER
|
||||
});
|
||||
|
||||
bool supported = pid.wait() == 0;
|
||||
auto r = pid.wait();
|
||||
assert(!r);
|
||||
} catch (SysError & e) {
|
||||
debug("user namespaces do not work on this system: %s", e.msg());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!supported)
|
||||
debug("user namespaces do not work on this system");
|
||||
|
||||
return supported;
|
||||
return true;
|
||||
}();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool mountNamespacesSupported()
|
||||
bool mountAndPidNamespacesSupported()
|
||||
{
|
||||
static auto res = [&]() -> bool
|
||||
{
|
||||
bool useUserNamespace = userNamespacesSupported();
|
||||
try {
|
||||
|
||||
Pid pid = startProcess([&]()
|
||||
{
|
||||
auto res = unshare(CLONE_NEWNS | (useUserNamespace ? CLONE_NEWUSER : 0));
|
||||
_exit(res ? 1 : 0);
|
||||
});
|
||||
Pid pid = startProcess([&]()
|
||||
{
|
||||
/* Make sure we don't remount the parent's /proc. */
|
||||
if (mount(0, "/", 0, MS_PRIVATE | MS_REC, 0) == -1)
|
||||
_exit(1);
|
||||
|
||||
bool supported = pid.wait() == 0;
|
||||
/* Test whether we can remount /proc. The kernel disallows
|
||||
this if /proc is not fully visible, i.e. if there are
|
||||
filesystems mounted on top of files inside /proc. See
|
||||
https://lore.kernel.org/lkml/87tvsrjai0.fsf@xmission.com/T/. */
|
||||
if (mount("none", "/proc", "proc", 0, 0) == -1)
|
||||
_exit(2);
|
||||
|
||||
if (!supported)
|
||||
debug("mount namespaces do not work on this system");
|
||||
_exit(0);
|
||||
}, {
|
||||
.cloneFlags = CLONE_NEWNS | CLONE_NEWPID | (userNamespacesSupported() ? CLONE_NEWUSER : 0)
|
||||
});
|
||||
|
||||
return supported;
|
||||
}();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool pidNamespacesSupported()
|
||||
{
|
||||
static auto res = [&]() -> bool
|
||||
{
|
||||
/* Check whether /proc is fully visible, i.e. there are no
|
||||
filesystems mounted on top of files inside /proc. If this
|
||||
is not the case, then we cannot mount a new /proc inside
|
||||
the sandbox that matches the sandbox's PID namespace.
|
||||
See https://lore.kernel.org/lkml/87tvsrjai0.fsf@xmission.com/T/. */
|
||||
auto fp = fopen("/proc/mounts", "r");
|
||||
if (!fp) return false;
|
||||
Finally delFP = [&]() { fclose(fp); };
|
||||
|
||||
while (auto ent = getmntent(fp))
|
||||
if (hasPrefix(std::string_view(ent->mnt_dir), "/proc/")) {
|
||||
debug("PID namespaces do not work because /proc is not fully visible; disabling sandboxing");
|
||||
if (pid.wait()) {
|
||||
debug("PID namespaces do not work on this system: cannot remount /proc");
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (SysError & e) {
|
||||
debug("mount namespaces do not work on this system: %s", e.msg());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}();
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ namespace nix {
|
|||
|
||||
bool userNamespacesSupported();
|
||||
|
||||
bool mountNamespacesSupported();
|
||||
|
||||
bool pidNamespacesSupported();
|
||||
bool mountAndPidNamespacesSupported();
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <cmath>
|
||||
#endif
|
||||
|
|
@ -608,6 +609,19 @@ Path getDataDir()
|
|||
return dataDir ? *dataDir : getHome() + "/.local/share";
|
||||
}
|
||||
|
||||
Path getStateDir()
|
||||
{
|
||||
auto stateDir = getEnv("XDG_STATE_HOME");
|
||||
return stateDir ? *stateDir : getHome() + "/.local/state";
|
||||
}
|
||||
|
||||
Path createNixStateDir()
|
||||
{
|
||||
Path dir = getStateDir() + "/nix";
|
||||
createDirs(dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
|
||||
std::optional<Path> getSelfExe()
|
||||
{
|
||||
|
|
@ -1051,9 +1065,17 @@ static pid_t doFork(bool allowVfork, std::function<void()> fun)
|
|||
}
|
||||
|
||||
|
||||
static int childEntry(void * arg)
|
||||
{
|
||||
auto main = (std::function<void()> *) arg;
|
||||
(*main)();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
|
||||
{
|
||||
auto wrapper = [&]() {
|
||||
std::function<void()> wrapper = [&]() {
|
||||
if (!options.allowVfork)
|
||||
logger = makeSimpleLogger();
|
||||
try {
|
||||
|
|
@ -1073,7 +1095,27 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
|
|||
_exit(1);
|
||||
};
|
||||
|
||||
pid_t pid = doFork(options.allowVfork, wrapper);
|
||||
pid_t pid = -1;
|
||||
|
||||
if (options.cloneFlags) {
|
||||
#ifdef __linux__
|
||||
// Not supported, since then we don't know when to free the stack.
|
||||
assert(!(options.cloneFlags & CLONE_VM));
|
||||
|
||||
size_t stackSize = 1 * 1024 * 1024;
|
||||
auto stack = (char *) mmap(0, stackSize,
|
||||
PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
|
||||
if (stack == MAP_FAILED) throw SysError("allocating stack");
|
||||
|
||||
Finally freeStack([&]() { munmap(stack, stackSize); });
|
||||
|
||||
pid = clone(childEntry, stack + stackSize, options.cloneFlags | SIGCHLD, &wrapper);
|
||||
#else
|
||||
throw Error("clone flags are only supported on Linux");
|
||||
#endif
|
||||
} else
|
||||
pid = doFork(options.allowVfork, wrapper);
|
||||
|
||||
if (pid == -1) throw SysError("unable to fork");
|
||||
|
||||
return pid;
|
||||
|
|
|
|||
|
|
@ -158,6 +158,12 @@ Path getDataDir();
|
|||
/* Return the path of the current executable. */
|
||||
std::optional<Path> getSelfExe();
|
||||
|
||||
/* Return $XDG_STATE_HOME or $HOME/.local/state. */
|
||||
Path getStateDir();
|
||||
|
||||
/* Create the Nix state directory and return the path to it. */
|
||||
Path createNixStateDir();
|
||||
|
||||
/* Create a directory and all its parents, if necessary. Returns the
|
||||
list of created directories, in order of creation. */
|
||||
Paths createDirs(const Path & path);
|
||||
|
|
@ -301,6 +307,7 @@ struct ProcessOptions
|
|||
bool dieWithParent = true;
|
||||
bool runExitHandlers = false;
|
||||
bool allowVfork = false;
|
||||
int cloneFlags = 0; // use clone() with the specified flags (Linux only)
|
||||
};
|
||||
|
||||
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
||||
|
|
|
|||
|
|
@ -164,8 +164,8 @@ static int main_nix_channel(int argc, char ** argv)
|
|||
{
|
||||
// Figure out the name of the `.nix-channels' file to use
|
||||
auto home = getHome();
|
||||
channelsList = home + "/.nix-channels";
|
||||
nixDefExpr = home + "/.nix-defexpr";
|
||||
channelsList = settings.useXDGBaseDirectories ? createNixStateDir() + "/channels" : home + "/.nix-channels";
|
||||
nixDefExpr = settings.useXDGBaseDirectories ? createNixStateDir() + "/defexpr" : home + "/.nix-defexpr";
|
||||
|
||||
// Figure out the name of the channels profile.
|
||||
profile = profilesDir() + "/channels";
|
||||
|
|
|
|||
|
|
@ -1291,7 +1291,7 @@ static void opSwitchProfile(Globals & globals, Strings opFlags, Strings opArgs)
|
|||
throw UsageError("exactly one argument expected");
|
||||
|
||||
Path profile = absPath(opArgs.front());
|
||||
Path profileLink = getHome() + "/.nix-profile";
|
||||
Path profileLink = settings.useXDGBaseDirectories ? createNixStateDir() + "/profile" : getHome() + "/.nix-profile";
|
||||
|
||||
switchLink(profileLink, profile);
|
||||
}
|
||||
|
|
@ -1391,14 +1391,14 @@ static int main_nix_env(int argc, char * * argv)
|
|||
Operation op = 0;
|
||||
RepairFlag repair = NoRepair;
|
||||
std::string file;
|
||||
Path nixExprPath;
|
||||
|
||||
Globals globals;
|
||||
|
||||
globals.instSource.type = srcUnknown;
|
||||
nixExprPath = getHome() + "/.nix-defexpr";
|
||||
globals.instSource.systemFilter = "*";
|
||||
|
||||
Path nixExprPath = settings.useXDGBaseDirectories ? createNixStateDir() + "/defexpr" : getHome() + "/.nix-defexpr";
|
||||
|
||||
if (!pathExists(nixExprPath)) {
|
||||
try {
|
||||
createDirs(nixExprPath);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "installables.hh"
|
||||
#include "installable-derived-path.hh"
|
||||
#include "store-api.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "eval-cache.hh"
|
||||
|
|
@ -8,30 +9,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
struct InstallableDerivedPath : Installable
|
||||
{
|
||||
ref<Store> store;
|
||||
const DerivedPath derivedPath;
|
||||
|
||||
InstallableDerivedPath(ref<Store> store, const DerivedPath & derivedPath)
|
||||
: store(store)
|
||||
, derivedPath(derivedPath)
|
||||
{
|
||||
}
|
||||
|
||||
std::string what() const override { return derivedPath.to_string(*store); }
|
||||
|
||||
DerivedPathsWithInfo toDerivedPaths() override
|
||||
{
|
||||
return {{derivedPath}};
|
||||
}
|
||||
|
||||
std::optional<StorePath> getStorePath() override
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the rewrites that are needed to resolve a string whose context is
|
||||
* included in `dependencies`.
|
||||
|
|
@ -146,7 +123,7 @@ App UnresolvedApp::resolve(ref<Store> evalStore, ref<Store> store)
|
|||
|
||||
for (auto & ctxElt : unresolved.context)
|
||||
installableContext.push_back(
|
||||
std::make_shared<InstallableDerivedPath>(store, ctxElt));
|
||||
std::make_shared<InstallableDerivedPath>(store, DerivedPath { ctxElt }));
|
||||
|
||||
auto builtContext = Installable::build(evalStore, store, Realise::Outputs, installableContext);
|
||||
res.program = resolveString(*store, unresolved.program, builtContext);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
using namespace nix;
|
||||
using namespace nix::daemon;
|
||||
|
||||
struct UserSettings : Config {
|
||||
struct AuthorizationSettings : Config {
|
||||
|
||||
Setting<Strings> trustedUsers{
|
||||
this, {"root"}, "trusted-users",
|
||||
|
|
@ -67,9 +67,9 @@ struct UserSettings : Config {
|
|||
)"};
|
||||
};
|
||||
|
||||
UserSettings userSettings;
|
||||
AuthorizationSettings authorizationSettings;
|
||||
|
||||
static GlobalConfig::Register rSettings(&userSettings);
|
||||
static GlobalConfig::Register rSettings(&authorizationSettings);
|
||||
|
||||
#ifndef __linux__
|
||||
#define SPLICE_F_MOVE 0
|
||||
|
|
@ -240,8 +240,8 @@ static void daemonLoop()
|
|||
struct group * gr = peer.gidKnown ? getgrgid(peer.gid) : 0;
|
||||
std::string group = gr ? gr->gr_name : std::to_string(peer.gid);
|
||||
|
||||
Strings trustedUsers = userSettings.trustedUsers;
|
||||
Strings allowedUsers = userSettings.allowedUsers;
|
||||
Strings trustedUsers = authorizationSettings.trustedUsers;
|
||||
Strings allowedUsers = authorizationSettings.allowedUsers;
|
||||
|
||||
if (matchUser(user, group, trustedUsers))
|
||||
trusted = Trusted;
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ void printClosureDiff(
|
|||
|
||||
using namespace nix;
|
||||
|
||||
struct CmdDiffClosures : SourceExprCommand
|
||||
struct CmdDiffClosures : SourceExprCommand, MixOperateOnOptions
|
||||
{
|
||||
std::string _before, _after;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ static std::string filterPrintable(const std::string & s)
|
|||
return res;
|
||||
}
|
||||
|
||||
struct CmdWhyDepends : SourceExprCommand
|
||||
struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||
{
|
||||
std::string _package, _dependency;
|
||||
bool all = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue