Skip to content

Commit ea38605

Browse files
committed
Introduce FSInputAccessor and use it
Backported from the lazy-trees branch. Note that this doesn't yet use the access control features of FSInputAccessor.
1 parent e92cac7 commit ea38605

File tree

18 files changed

+491
-115
lines changed

18 files changed

+491
-115
lines changed

src/libexpr/attr-path.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
132132
if (colon == std::string::npos) fail();
133133
std::string filename(fn, 0, colon);
134134
auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
135-
return {CanonPath(fn.substr(0, colon)), lineno};
135+
return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno};
136136
} catch (std::invalid_argument & e) {
137137
fail();
138138
abort();

src/libexpr/eval.cc

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "function-trace.hh"
1313
#include "profiles.hh"
1414
#include "print.hh"
15+
#include "fs-input-accessor.hh"
1516

1617
#include <algorithm>
1718
#include <chrono>
@@ -503,6 +504,18 @@ EvalState::EvalState(
503504
, sOutputSpecified(symbols.create("outputSpecified"))
504505
, repair(NoRepair)
505506
, emptyBindings(0)
507+
, rootFS(
508+
makeFSInputAccessor(
509+
CanonPath::root,
510+
evalSettings.restrictEval || evalSettings.pureEval
511+
? std::optional<std::set<CanonPath>>(std::set<CanonPath>())
512+
: std::nullopt,
513+
[](const CanonPath & path) -> RestrictedPathError {
514+
auto modeInformation = evalSettings.pureEval
515+
? "in pure evaluation mode (use '--impure' to override)"
516+
: "in restricted mode";
517+
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation);
518+
}))
506519
, derivationInternal(rootPath(CanonPath("/builtin/derivation.nix")))
507520
, store(store)
508521
, buildStore(buildStore ? buildStore : store)
@@ -518,6 +531,8 @@ EvalState::EvalState(
518531
, baseEnv(allocEnv(128))
519532
, staticBaseEnv{std::make_shared<StaticEnv>(false, nullptr)}
520533
{
534+
rootFS->allowPath(CanonPath::root); // FIXME
535+
521536
countCalls = getEnv("NIX_COUNT_CALLS").value_or("0") != "0";
522537

523538
assert(gcInitialised);
@@ -599,7 +614,7 @@ SourcePath EvalState::checkSourcePath(const SourcePath & path_)
599614
*/
600615
Path abspath = canonPath(path_.path.abs());
601616

602-
if (hasPrefix(abspath, corepkgsPrefix)) return CanonPath(abspath);
617+
if (hasPrefix(abspath, corepkgsPrefix)) return rootPath(CanonPath(abspath));
603618

604619
for (auto & i : *allowedPaths) {
605620
if (isDirOrInDir(abspath, i)) {
@@ -617,7 +632,7 @@ SourcePath EvalState::checkSourcePath(const SourcePath & path_)
617632

618633
/* Resolve symlinks. */
619634
debug("checking access to '%s'", abspath);
620-
SourcePath path = CanonPath(canonPath(abspath, true));
635+
SourcePath path = rootPath(CanonPath(canonPath(abspath, true)));
621636

622637
for (auto & i : *allowedPaths) {
623638
if (isDirOrInDir(path.path.abs(), i)) {
@@ -649,12 +664,12 @@ void EvalState::checkURI(const std::string & uri)
649664
/* If the URI is a path, then check it against allowedPaths as
650665
well. */
651666
if (hasPrefix(uri, "/")) {
652-
checkSourcePath(CanonPath(uri));
667+
checkSourcePath(rootPath(CanonPath(uri)));
653668
return;
654669
}
655670

656671
if (hasPrefix(uri, "file://")) {
657-
checkSourcePath(CanonPath(std::string(uri, 7)));
672+
checkSourcePath(rootPath(CanonPath(std::string(uri, 7))));
658673
return;
659674
}
660675

@@ -950,7 +965,7 @@ void Value::mkStringMove(const char * s, const NixStringContext & context)
950965

951966
void Value::mkPath(const SourcePath & path)
952967
{
953-
mkPath(makeImmutableString(path.path.abs()));
968+
mkPath(&*path.accessor, makeImmutableString(path.path.abs()));
954969
}
955970

956971

@@ -2037,7 +2052,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
20372052
else if (firstType == nPath) {
20382053
if (!context.empty())
20392054
state.error("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow<EvalError>();
2040-
v.mkPath(CanonPath(canonPath(str())));
2055+
v.mkPath(state.rootPath(CanonPath(canonPath(str()))));
20412056
} else
20422057
v.mkStringMove(c_str(), context);
20432058
}
@@ -2236,7 +2251,7 @@ BackedStringView EvalState::coerceToString(
22362251
!canonicalizePath && !copyToStore
22372252
? // FIXME: hack to preserve path literals that end in a
22382253
// slash, as in /foo/${x}.
2239-
v._path
2254+
v._path.path
22402255
: copyToStore
22412256
? store->printStorePath(copyPathToStore(context, v.path()))
22422257
: std::string(v.path().path.abs());
@@ -2329,7 +2344,7 @@ SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext
23292344
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
23302345
if (path == "" || path[0] != '/')
23312346
error("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow<EvalError>();
2332-
return CanonPath(path);
2347+
return rootPath(CanonPath(path));
23332348
}
23342349

23352350

@@ -2429,7 +2444,9 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
24292444
return v1.string_view().compare(v2.string_view()) == 0;
24302445

24312446
case nPath:
2432-
return strcmp(v1._path, v2._path) == 0;
2447+
return
2448+
v1._path.accessor == v2._path.accessor
2449+
&& strcmp(v1._path.path, v2._path.path) == 0;
24332450

24342451
case nNull:
24352452
return true;

src/libexpr/eval.hh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class EvalState;
2424
class StorePath;
2525
struct SingleDerivedPath;
2626
enum RepairFlag : bool;
27+
struct FSInputAccessor;
2728

2829

2930
/**
@@ -211,6 +212,8 @@ public:
211212

212213
Bindings emptyBindings;
213214

215+
const ref<FSInputAccessor> rootFS;
216+
214217
const SourcePath derivationInternal;
215218

216219
/**

src/libexpr/flake/flake.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,9 @@ static Flake getFlake(
223223
throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", lockedRef, lockedRef.subdir);
224224

225225
Value vInfo;
226-
state.evalFile(CanonPath(flakeFile), vInfo, true); // FIXME: symlink attack
226+
state.evalFile(state.rootPath(CanonPath(flakeFile)), vInfo, true); // FIXME: symlink attack
227227

228-
expectType(state, nAttrs, vInfo, state.positions.add({CanonPath(flakeFile)}, 1, 1));
228+
expectType(state, nAttrs, vInfo, state.positions.add({state.rootPath(CanonPath(flakeFile))}, 1, 1));
229229

230230
if (auto description = vInfo.attrs->get(state.sDescription)) {
231231
expectType(state, nString, *description->value, description->pos);
@@ -741,7 +741,7 @@ void callFlake(EvalState & state,
741741
state.vCallFlake = allocRootValue(state.allocValue());
742742
state.eval(state.parseExprFromString(
743743
#include "call-flake.nix.gen.hh"
744-
, CanonPath::root), **state.vCallFlake);
744+
, state.rootPath(CanonPath::root)), **state.vCallFlake);
745745
}
746746

747747
state.callFunction(**state.vCallFlake, *vLocks, *vTmp1, noPos);

src/libexpr/nixexpr.hh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ MakeError(Abort, EvalError);
2020
MakeError(TypeError, EvalError);
2121
MakeError(UndefinedVarError, Error);
2222
MakeError(MissingArgumentError, EvalError);
23-
MakeError(RestrictedPathError, Error);
2423

2524
/**
2625
* Position objects.
@@ -200,9 +199,13 @@ struct ExprString : Expr
200199

201200
struct ExprPath : Expr
202201
{
202+
ref<InputAccessor> accessor;
203203
std::string s;
204204
Value v;
205-
ExprPath(std::string s) : s(std::move(s)) { v.mkPath(this->s.c_str()); };
205+
ExprPath(ref<InputAccessor> accessor, std::string s) : accessor(accessor), s(std::move(s))
206+
{
207+
v.mkPath(&*accessor, this->s.c_str());
208+
}
206209
Value * maybeThunk(EvalState & state, Env & env) override;
207210
COMMON_METHODS
208211
};

src/libexpr/parser.y

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct StringToken {
6464

6565
#include "parser-tab.hh"
6666
#include "lexer-tab.hh"
67+
#include "fs-input-accessor.hh"
6768

6869
YY_DECL;
6970

@@ -520,7 +521,7 @@ path_start
520521
/* add back in the trailing '/' to the first segment */
521522
if ($1.p[$1.l-1] == '/' && $1.l > 1)
522523
path += "/";
523-
$$ = new ExprPath(std::move(path));
524+
$$ = new ExprPath(ref<InputAccessor>(data->state.rootFS), std::move(path));
524525
}
525526
| HPATH {
526527
if (evalSettings.pureEval) {
@@ -530,7 +531,7 @@ path_start
530531
);
531532
}
532533
Path path(getHome() + std::string($1.p + 1, $1.l - 1));
533-
$$ = new ExprPath(std::move(path));
534+
$$ = new ExprPath(ref<InputAccessor>(data->state.rootFS), std::move(path));
534535
}
535536
;
536537

@@ -756,11 +757,11 @@ SourcePath EvalState::findFile(const SearchPath & searchPath, const std::string_
756757
auto r = *rOpt;
757758

758759
Path res = suffix == "" ? r : concatStrings(r, "/", suffix);
759-
if (pathExists(res)) return CanonPath(canonPath(res));
760+
if (pathExists(res)) return rootPath(CanonPath(canonPath(res)));
760761
}
761762

762763
if (hasPrefix(path, "nix/"))
763-
return CanonPath(concatStrings(corepkgsPrefix, path.substr(4)));
764+
return rootPath(CanonPath(concatStrings(corepkgsPrefix, path.substr(4))));
764765

765766
debugThrow(ThrownError({
766767
.msg = hintfmt(evalSettings.pureEval

src/libexpr/paths.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#include "eval.hh"
2+
#include "fs-input-accessor.hh"
23

34
namespace nix {
45

56
SourcePath EvalState::rootPath(CanonPath path)
67
{
7-
return path;
8+
return {rootFS, std::move(path)};
89
}
910

1011
}

src/libexpr/primops.cc

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
202202
state.vImportedDrvToDerivation = allocRootValue(state.allocValue());
203203
state.eval(state.parseExprFromString(
204204
#include "imported-drv-to-derivation.nix.gen.hh"
205-
, CanonPath::root), **state.vImportedDrvToDerivation);
205+
, state.rootPath(CanonPath::root)), **state.vImportedDrvToDerivation);
206206
}
207207

208208
state.forceFunction(**state.vImportedDrvToDerivation, pos, "while evaluating imported-drv-to-derivation.nix.gen.hh");
@@ -213,7 +213,7 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
213213
else if (path2 == corepkgsPrefix + "fetchurl.nix") {
214214
state.eval(state.parseExprFromString(
215215
#include "fetchurl.nix.gen.hh"
216-
, CanonPath::root), v);
216+
, state.rootPath(CanonPath::root)), v);
217217
}
218218

219219
else {
@@ -599,7 +599,8 @@ struct CompareValues
599599
case nString:
600600
return v1->string_view().compare(v2->string_view()) < 0;
601601
case nPath:
602-
return strcmp(v1->_path, v2->_path) < 0;
602+
// FIXME: handle accessor?
603+
return strcmp(v1->_path.path, v2->_path.path) < 0;
603604
case nList:
604605
// Lexicographic comparison
605606
for (size_t i = 0;; i++) {
@@ -2203,7 +2204,7 @@ static void addPath(
22032204

22042205
path = evalSettings.pureEval && expectedHash
22052206
? path
2206-
: state.checkSourcePath(CanonPath(path)).path.abs();
2207+
: state.checkSourcePath(state.rootPath(CanonPath(path))).path.abs();
22072208

22082209
PathFilter filter = filterFun ? ([&](const Path & path) {
22092210
auto st = lstat(path);
@@ -2236,9 +2237,10 @@ static void addPath(
22362237
});
22372238

22382239
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
2239-
StorePath dstPath = settings.readOnlyMode
2240-
? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first
2241-
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs);
2240+
// FIXME
2241+
if (method != FileIngestionMethod::Recursive)
2242+
throw Error("'recursive = false' is not implemented");
2243+
auto dstPath = state.rootPath(CanonPath(path)).fetchToStore(state.store, name, &filter, state.repair);
22422244
if (expectedHash && expectedStorePath != dstPath)
22432245
state.debugThrowLastTrace(Error("store path mismatch in (possibly filtered) path added from '%s'", path));
22442246
state.allowAndSetStorePathString(dstPath, v);
@@ -4447,7 +4449,7 @@ void EvalState::createBaseEnv()
44474449
// the parser needs two NUL bytes as terminators; one of them
44484450
// is implied by being a C string.
44494451
"\0";
4450-
eval(parse(code, sizeof(code), derivationInternal, {CanonPath::root}, staticBaseEnv), *vDerivation);
4452+
eval(parse(code, sizeof(code), derivationInternal, rootPath(CanonPath::root), staticBaseEnv), *vDerivation);
44514453
}
44524454

44534455

src/libexpr/tests/json.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ namespace nix {
6262
// not supported by store 'dummy'" thrown in the test body.
6363
TEST_F(JSONValueTest, DISABLED_Path) {
6464
Value v;
65-
v.mkPath("test");
65+
v.mkPath(state.rootPath(CanonPath("/test")));
6666
ASSERT_EQ(getJSONValue(v), "\"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x\"");
6767
}
6868
} /* namespace nix */

src/libexpr/tests/libexpr.hh

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,17 @@ namespace nix {
103103
}
104104

105105
MATCHER_P(IsPathEq, p, fmt("Is a path equal to \"%1%\"", p)) {
106-
if (arg.type() != nPath) {
107-
*result_listener << "Expected a path got " << arg.type();
108-
return false;
109-
} else if (std::string_view(arg._path) != p) {
110-
*result_listener << "Expected a path that equals \"" << p << "\" but got: " << arg.c_str();
106+
if (arg.type() != nPath) {
107+
*result_listener << "Expected a path got " << arg.type();
108+
return false;
109+
} else {
110+
auto path = arg.path();
111+
if (path.path != CanonPath(p)) {
112+
*result_listener << "Expected a path that equals \"" << p << "\" but got: " << path.path;
111113
return false;
112114
}
113-
return true;
115+
}
116+
return true;
114117
}
115118

116119

0 commit comments

Comments
 (0)