Skip to content

Commit

Permalink
Fix IFD with CA derivations
Browse files Browse the repository at this point in the history
Rewrite the string taken by the IFD-like primops to contain the actual
output paths of the derivations rather than the placeholders

Fix #5805
  • Loading branch information
thufschmitt committed Dec 20, 2021
1 parent 6e6e998 commit c176b52
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 51 deletions.
5 changes: 4 additions & 1 deletion src/libexpr/eval.hh
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,10 @@ public:
/* Print statistics. */
void printStats();

void realiseContext(const PathSet & context);
/* Realise the given context, and return a mapping from the placeholders
* used to construct the associated value to their final store path
*/
[[nodiscard]] StringMap realiseContext(const PathSet & context);

private:

Expand Down
111 changes: 62 additions & 49 deletions src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ namespace nix {
InvalidPathError::InvalidPathError(const Path & path) :
EvalError("path '%s' is not valid", path), path(path) {}

void EvalState::realiseContext(const PathSet & context)
StringMap EvalState::realiseContext(const PathSet & context)
{
std::vector<DerivedPath::Built> drvs;
StringMap res;

for (auto & i : context) {
auto [ctxS, outputName] = decodeContext(i);
Expand All @@ -46,10 +47,12 @@ void EvalState::realiseContext(const PathSet & context)
throw InvalidPathError(store->printStorePath(ctx));
if (!outputName.empty() && ctx.isDerivation()) {
drvs.push_back({ctx, {outputName}});
} else {
res.insert_or_assign(ctxS, ctxS);
}
}

if (drvs.empty()) return;
if (drvs.empty()) return {};

if (!evalSettings.enableImportFromDerivation)
throw Error(
Expand All @@ -61,19 +64,40 @@ void EvalState::realiseContext(const PathSet & context)
for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d });
store->buildPaths(buildReqs);

/* Get all the output paths corresponding to the placeholders we had */
for (auto & [drvPath, outputs] : drvs) {
auto outputPaths = store->queryDerivationOutputMap(drvPath);
for (auto & outputName : outputs) {
if (outputPaths.count(outputName) == 0)
throw Error("derivation '%s' does not have an output named '%s'",
store->printStorePath(drvPath), outputName);
res.insert_or_assign(
downstreamPlaceholder(*store, drvPath, outputName),
store->printStorePath(outputPaths.at(outputName))
);
}
}

/* Add the output of this derivations to the allowed
paths. */
if (allowedPaths) {
for (auto & [drvPath, outputs] : drvs) {
auto outputPaths = store->queryDerivationOutputMap(drvPath);
for (auto & outputName : outputs) {
if (outputPaths.count(outputName) == 0)
throw Error("derivation '%s' does not have an output named '%s'",
store->printStorePath(drvPath), outputName);
allowPath(outputPaths.at(outputName));
}
for (auto & [_placeholder, outputPath] : res) {
allowPath(outputPath);
}
}

return res;
}

static Path realisePath(EvalState & state, const Pos & pos, Value & v)
{
PathSet context;
Path path = state.coerceToPath(pos, v, context);

StringMap rewrites = state.realiseContext(context);

return state.checkSourcePath(
state.toRealPath(rewriteStrings(path, rewrites), context));
}

/* Add and attribute to the given attribute map from the output name to
Expand Down Expand Up @@ -109,11 +133,9 @@ static void mkOutputString(EvalState & state, Value & v,
argument. */
static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vScope, Value & v)
{
PathSet context;
Path path = state.coerceToPath(pos, vPath, context);

Path path;
try {
state.realiseContext(context);
path = realisePath(state, pos, vPath);
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
Expand All @@ -124,8 +146,6 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
throw;
}

Path realPath = state.checkSourcePath(state.toRealPath(path, context));

// FIXME
auto isValidDerivationInStore = [&]() -> std::optional<StorePath> {
if (!state.store->isStorePath(path))
Expand Down Expand Up @@ -177,7 +197,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS

else {
if (!vScope)
state.evalFile(realPath, v);
state.evalFile(path, v);
else {
state.forceAttrs(*vScope);

Expand All @@ -195,8 +215,8 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
// No need to call staticEnv.sort(), because
// args[0]->attrs is already sorted.

printTalkative("evaluating file '%1%'", realPath);
Expr * e = state.parseExprFromFile(resolveExprPath(realPath), staticEnv);
printTalkative("evaluating file '%1%'", path);
Expr * e = state.parseExprFromFile(resolveExprPath(path), staticEnv);

e->eval(state, *env, v);
}
Expand Down Expand Up @@ -281,22 +301,19 @@ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
/* Load a ValueInitializer from a DSO and return whatever it initializes */
void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
Path path = state.coerceToPath(pos, *args[0], context);

Path path;
try {
state.realiseContext(context);
path = realisePath(state, pos, *args[0]);
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt(
"cannot import '%1%', since path '%2%' is not valid",
path, e.path),
.msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
.errPos = pos
});
} catch (Error & e) {
e.addTrace(pos, "while importing '%s'", path);
throw;
}

path = state.checkSourcePath(path);

string sym = state.forceStringNoCtx(*args[1], pos);

void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
Expand Down Expand Up @@ -338,7 +355,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
for (unsigned int i = 1; i < args[0]->listSize(); ++i)
commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false));
try {
state.realiseContext(context);
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
Expand Down Expand Up @@ -1349,10 +1366,9 @@ static RegisterPrimOp primop_storePath({

static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
Path path = state.coerceToPath(pos, *args[0], context);
Path path;
try {
state.realiseContext(context);
path = realisePath(state, pos, *args[0]);
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt(
Expand All @@ -1363,7 +1379,7 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
}

try {
mkBool(v, pathExists(state.checkSourcePath(path)));
mkBool(v, pathExists(path));
} catch (SysError & e) {
/* Don't give away info from errors while canonicalising
‘path’ in restricted mode. */
Expand Down Expand Up @@ -1426,17 +1442,16 @@ static RegisterPrimOp primop_dirOf({
/* Return the contents of a file as a string. */
static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
Path path = state.coerceToPath(pos, *args[0], context);
Path path;
try {
state.realiseContext(context);
path = realisePath(state, pos, *args[0]);
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
.errPos = pos
});
}
string s = readFile(state.checkSourcePath(state.toRealPath(path, context)));
string s = readFile(path);
if (s.find((char) 0) != string::npos)
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
mkString(v, s.c_str());
Expand Down Expand Up @@ -1475,11 +1490,10 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
pos
);

PathSet context;
string path = state.coerceToString(pos, *i->value, context, false, false);
Path path;

try {
state.realiseContext(context);
path = realisePath(state, pos, *i->value);
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
Expand Down Expand Up @@ -1512,15 +1526,14 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va
.errPos = pos
});

PathSet context;
Path path = state.coerceToPath(pos, *args[1], context);
Path path;
try {
state.realiseContext(context);
path = realisePath(state, pos, *args[1]);
} catch (InvalidPathError & e) {
throw EvalError("cannot read '%s' since path '%s' is not valid, at %s", path, e.path, pos);
}

mkString(v, hashFile(*ht, state.checkSourcePath(state.toRealPath(path, context))).to_string(Base16, false));
mkString(v, hashFile(*ht, path).to_string(Base16, false));
}

static RegisterPrimOp primop_hashFile({
Expand All @@ -1537,18 +1550,17 @@ static RegisterPrimOp primop_hashFile({
/* Read a directory (without . or ..) */
static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet ctx;
Path path = state.coerceToPath(pos, *args[0], ctx);
Path path;
try {
state.realiseContext(ctx);
path = realisePath(state, pos, *args[0]);
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
.errPos = pos
});
}

DirEntries entries = readDirectory(state.checkSourcePath(path));
DirEntries entries = readDirectory(path);
state.mkAttrs(v, entries.size());

for (auto & ent : entries) {
Expand Down Expand Up @@ -1875,7 +1887,8 @@ static void addPath(
try {
// FIXME: handle CA derivation outputs (where path needs to
// be rewritten to the actual output).
state.realiseContext(context);
auto rewrites = state.realiseContext(context);
path = state.toRealPath(rewriteStrings(path, rewrites), context);

StorePathSet refs;

Expand Down
6 changes: 6 additions & 0 deletions tests/ca/import-derivation.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
source common.sh

export NIX_TESTS_CA_BY_DEFAULT=1

cd .. && source import-derivation.sh

2 changes: 1 addition & 1 deletion tests/local.mk
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ nix_tests = \
local-store.sh remote-store.sh export.sh export-graph.sh \
db-migration.sh \
timeout.sh secure-drv-outputs.sh nix-channel.sh \
multiple-outputs.sh import-derivation.sh fetchurl.sh optimise-store.sh \
multiple-outputs.sh import-derivation.sh ca/import-derivation.sh fetchurl.sh optimise-store.sh \
binary-cache.sh \
substitute-with-invalid-ca.sh \
binary-cache-build-remote.sh \
Expand Down

0 comments on commit c176b52

Please sign in to comment.