Checkpoint

This commit is contained in:
Eelco Dolstra 2021-12-06 11:14:52 +01:00
parent 2d572a250f
commit c56e17b718
17 changed files with 428 additions and 79 deletions

View file

@ -184,9 +184,11 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
if (file) { if (file) {
evalSettings.pureEval = false; evalSettings.pureEval = false;
auto state = getEvalState(); auto state = getEvalState();
Expr *e = state->parseExprFromFile( Expr *e =
resolveExprPath(state->checkSourcePath(lookupFileArg(*state, *file))) state->parseExprFromFile(
); resolveExprPath(
state->rootPath(
lookupFileArg(*state, *file))));
Value root; Value root;
state->eval(e, root); state->eval(e, root);
@ -700,8 +702,9 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
if (file == "-") { if (file == "-") {
auto e = state->parseStdin(); auto e = state->parseStdin();
state->eval(e, *vFile); state->eval(e, *vFile);
} else if (file) }
state->evalFile(lookupFileArg(*state, *file), *vFile); else if (file)
state->evalFile(state->rootPath(lookupFileArg(*state, *file)), *vFile);
else { else {
auto e = state->parseExprFromString(*expr, absPath(".")); auto e = state->parseExprFromString(*expr, absPath("."));
state->eval(e, *vFile); state->eval(e, *vFile);

View file

@ -450,6 +450,7 @@ EvalState::EvalState(
, sPrefix(symbols.create("prefix")) , sPrefix(symbols.create("prefix"))
, repair(NoRepair) , repair(NoRepair)
, emptyBindings(0) , emptyBindings(0)
, rootFS(makeFSInputAccessor(""))
, store(store) , store(store)
, buildStore(buildStore ? buildStore : store) , buildStore(buildStore ? buildStore : store)
, regexCache(makeRegexCache()) , regexCache(makeRegexCache())
@ -527,6 +528,7 @@ void EvalState::allowAndSetStorePathString(const StorePath &storePath, Value & v
v.mkString(path, PathSet({path})); v.mkString(path, PathSet({path}));
} }
#if 0
Path EvalState::checkSourcePath(const Path & path_) Path EvalState::checkSourcePath(const Path & path_)
{ {
if (!allowedPaths) return path_; if (!allowedPaths) return path_;
@ -572,6 +574,7 @@ Path EvalState::checkSourcePath(const Path & path_)
throw RestrictedPathError("access to canonical path '%1%' is forbidden in restricted mode", path); throw RestrictedPathError("access to canonical path '%1%' is forbidden in restricted mode", path);
} }
#endif
void EvalState::checkURI(const std::string & uri) void EvalState::checkURI(const std::string & uri)
@ -593,12 +596,14 @@ void EvalState::checkURI(const std::string & uri)
/* If the URI is a path, then check it against allowedPaths as /* If the URI is a path, then check it against allowedPaths as
well. */ well. */
if (hasPrefix(uri, "/")) { if (hasPrefix(uri, "/")) {
checkSourcePath(uri); // FIXME: use rootPath
//checkSourcePath(uri);
return; return;
} }
if (hasPrefix(uri, "file://")) { if (hasPrefix(uri, "file://")) {
checkSourcePath(std::string(uri, 7)); // FIXME: use rootPath
//checkSourcePath(std::string(uri, 7));
return; return;
} }
@ -970,17 +975,23 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
} }
void EvalState::evalFile(const Path & path_, Value & v, bool mustBeTrivial) void EvalState::evalFile(const SourcePath & path_, Value & v, bool mustBeTrivial)
{ {
#if 0
auto path = checkSourcePath(path_); auto path = checkSourcePath(path_);
#endif
auto path = packPath(path_);
// FIXME: use SourcePath as cache key
FileEvalCache::iterator i; FileEvalCache::iterator i;
if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) { if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
v = i->second; v = i->second;
return; return;
} }
Path resolvedPath = resolveExprPath(path); auto resolvedPath_ = resolveExprPath(path_);
auto resolvedPath = packPath(resolvedPath_);
if ((i = fileEvalCache.find(resolvedPath)) != fileEvalCache.end()) { if ((i = fileEvalCache.find(resolvedPath)) != fileEvalCache.end()) {
v = i->second; v = i->second;
return; return;
@ -994,7 +1005,10 @@ void EvalState::evalFile(const Path & path_, Value & v, bool mustBeTrivial)
e = j->second; e = j->second;
if (!e) if (!e)
e = parseExprFromFile(resolvedPath_);
#if 0
e = parseExprFromFile(checkSourcePath(resolvedPath)); e = parseExprFromFile(checkSourcePath(resolvedPath));
#endif
cacheFile(path, resolvedPath, e, v, mustBeTrivial); cacheFile(path, resolvedPath, e, v, mustBeTrivial);
} }
@ -2045,9 +2059,19 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path)
if (i != srcToStore.end()) if (i != srcToStore.end())
dstPath = store->printStorePath(i->second); dstPath = store->printStorePath(i->second);
else { else {
// FIXME: use SourcePath
printError("COPY %s", path);
auto path2 = unpackPath(path);
#if 0
auto p = settings.readOnlyMode auto p = settings.readOnlyMode
? store->computeStorePathForPath(std::string(baseNameOf(path)), checkSourcePath(path)).first ? store->computeStorePathForPath(std::string(baseNameOf(path)), canonPath(path)).first
: store->addToStore(std::string(baseNameOf(path)), checkSourcePath(path), FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, repair); : store->addToStore(std::string(baseNameOf(path)), canonPath(path), FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, repair);
#endif
auto source = sinkToSource([&](Sink & sink) {
path2.accessor->dumpPath(path2.path, sink);
});
// FIXME: readOnlyMode
auto p = store->addToStoreFromDump(*source, std::string(baseNameOf(path)), FileIngestionMethod::Recursive, htSHA256, repair);
dstPath = store->printStorePath(p); dstPath = store->printStorePath(p);
allowPath(p); allowPath(p);
srcToStore.insert_or_assign(path, std::move(p)); srcToStore.insert_or_assign(path, std::move(p));

View file

@ -7,6 +7,7 @@
#include "symbol-table.hh" #include "symbol-table.hh"
#include "config.hh" #include "config.hh"
#include "experimental-features.hh" #include "experimental-features.hh"
#include "input-accessor.hh"
#include <map> #include <map>
#include <optional> #include <optional>
@ -20,6 +21,7 @@ namespace nix {
class Store; class Store;
class EvalState; class EvalState;
class StorePath; class StorePath;
struct SourcePath;
enum RepairFlag : bool; enum RepairFlag : bool;
@ -95,6 +97,10 @@ public:
Bindings emptyBindings; Bindings emptyBindings;
ref<InputAccessor> rootFS;
std::unordered_map<size_t, ref<InputAccessor>> inputAccessors;
/* Store used to materialise .drv files. */ /* Store used to materialise .drv files. */
const ref<Store> store; const ref<Store> store;
@ -153,6 +159,12 @@ public:
SearchPath getSearchPath() { return searchPath; } SearchPath getSearchPath() { return searchPath; }
Path packPath(const SourcePath & path);
SourcePath unpackPath(const Path & path);
SourcePath rootPath(const Path & path);
/* Allow access to a path. */ /* Allow access to a path. */
void allowPath(const Path & path); void allowPath(const Path & path);
@ -163,10 +175,6 @@ public:
/* Allow access to a store path and return it as a string. */ /* Allow access to a store path and return it as a string. */
void allowAndSetStorePathString(const StorePath & storePath, Value & v); void allowAndSetStorePathString(const StorePath & storePath, Value & v);
/* Check whether access to a path is allowed and throw an error if
not. Otherwise return the canonicalised path. */
Path checkSourcePath(const Path & path);
void checkURI(const std::string & uri); void checkURI(const std::string & uri);
/* When using a diverted store and 'path' is in the Nix store, map /* When using a diverted store and 'path' is in the Nix store, map
@ -179,8 +187,8 @@ public:
Path toRealPath(const Path & path, const PathSet & context); Path toRealPath(const Path & path, const PathSet & context);
/* Parse a Nix expression from the specified file. */ /* Parse a Nix expression from the specified file. */
Expr * parseExprFromFile(const Path & path); Expr * parseExprFromFile(const SourcePath & path);
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv); Expr * parseExprFromFile(const SourcePath & path, StaticEnv & staticEnv);
/* Parse a Nix expression from the specified string. */ /* Parse a Nix expression from the specified string. */
Expr * parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv); Expr * parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv);
@ -191,7 +199,7 @@ public:
/* Evaluate an expression read from the given file to normal /* Evaluate an expression read from the given file to normal
form. Optionally enforce that the top-level expression is form. Optionally enforce that the top-level expression is
trivial (i.e. doesn't require arbitrary computation). */ trivial (i.e. doesn't require arbitrary computation). */
void evalFile(const Path & path, Value & v, bool mustBeTrivial = false); void evalFile(const SourcePath & path, Value & v, bool mustBeTrivial = false);
/* Like `cacheFile`, but with an already parsed expression. */ /* Like `cacheFile`, but with an already parsed expression. */
void cacheFile( void cacheFile(
@ -269,6 +277,7 @@ public:
/* Path coercion. Converts strings, paths and derivations to a /* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */ path. Nothing is copied to the store. */
// FIXME: return SourcePath
Path coerceToPath(const Pos & pos, Value & v, PathSet & context); Path coerceToPath(const Pos & pos, Value & v, PathSet & context);
/* Like coerceToPath, but the result must be a store path. */ /* Like coerceToPath, but the result must be a store path. */
@ -427,7 +436,7 @@ std::string showType(const Value & v);
NixStringContextElem decodeContext(const Store & store, std::string_view s); NixStringContextElem decodeContext(const Store & store, std::string_view s);
/* If `path' refers to a directory, then append "/default.nix". */ /* If `path' refers to a directory, then append "/default.nix". */
Path resolveExprPath(Path path); SourcePath resolveExprPath(const SourcePath & path);
struct InvalidPathError : EvalError struct InvalidPathError : EvalError
{ {

View file

@ -216,7 +216,8 @@ static Flake getFlake(
throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", lockedRef, lockedRef.subdir); throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", lockedRef, lockedRef.subdir);
Value vInfo; Value vInfo;
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack // FIXME: use accessor
state.evalFile(state.rootPath(flakeFile), vInfo, true); // FIXME: symlink attack
expectType(state, nAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0)); expectType(state, nAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0));

View file

@ -674,10 +674,9 @@ Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
} }
Path resolveExprPath(Path path) SourcePath resolveExprPath(const SourcePath & path)
{ {
assert(path[0] == '/'); #if 0
unsigned int followCount = 0, maxFollow = 1024; unsigned int followCount = 0, maxFollow = 1024;
/* If `path' is a symlink, follow it. This is so that relative /* If `path' is a symlink, follow it. This is so that relative
@ -697,21 +696,30 @@ Path resolveExprPath(Path path)
path = canonPath(path + "/default.nix"); path = canonPath(path + "/default.nix");
return path; return path;
#endif
// FIXME
auto path2 = path.path + "/default.nix";
if (path.accessor->pathExists(path2))
return {path.accessor, path2};
return path;
} }
Expr * EvalState::parseExprFromFile(const Path & path) Expr * EvalState::parseExprFromFile(const SourcePath & path)
{ {
return parseExprFromFile(path, staticBaseEnv); return parseExprFromFile(path, staticBaseEnv);
} }
Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv) Expr * EvalState::parseExprFromFile(const SourcePath & path, StaticEnv & staticEnv)
{ {
auto buffer = readFile(path); auto packed = packPath(path);
// readFile should have left some extra space for terminators auto buffer = path.accessor->readFile(path.path);
// readFile hopefully have left some extra space for terminators
buffer.append("\0\0", 2); buffer.append("\0\0", 2);
return parse(buffer.data(), buffer.size(), foFile, path, dirOf(path), staticEnv); return parse(buffer.data(), buffer.size(), foFile, packed, dirOf(packed), staticEnv);
} }

39
src/libexpr/paths.cc Normal file
View file

@ -0,0 +1,39 @@
#include "eval.hh"
#include "util.hh"
namespace nix {
static constexpr std::string_view marker = "/__virtual/";
Path EvalState::packPath(const SourcePath & path)
{
printError("PACK %s", path.path);
assert(hasPrefix(path.path, "/"));
inputAccessors.emplace(path.accessor->number, path.accessor);
return std::string(marker) + std::to_string(path.accessor->number) + path.path;
}
SourcePath EvalState::unpackPath(const Path & path)
{
if (hasPrefix(path, marker)) {
auto s = path.substr(marker.size());
auto slash = s.find('/');
assert(slash != s.npos);
auto n = std::stoi(s.substr(0, slash));
printError("GOT %d", n);
auto i = inputAccessors.find(n);
assert(i != inputAccessors.end());
return {i->second, s.substr(slash)};
} else {
printError("FIXME: %s", path);
return rootPath(path);
}
}
SourcePath EvalState::rootPath(const Path & path)
{
printError("ROOT %s", path);
return {rootFS, path};
}
}

View file

@ -96,20 +96,23 @@ struct RealisePathFlags {
bool checkForPureEval = true; bool checkForPureEval = true;
}; };
static Path realisePath(EvalState & state, const Pos & pos, Value & v, const RealisePathFlags flags = {}) static SourcePath realisePath(EvalState & state, const Pos & pos, Value & v, const RealisePathFlags flags = {})
{ {
PathSet context; PathSet context;
auto path = [&]() auto path = [&]()
{ {
try { try {
return state.coerceToPath(pos, v, context); return state.unpackPath(state.coerceToPath(pos, v, context));
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, "while realising the context of a path"); e.addTrace(pos, "while realising the context of a path");
throw; throw;
} }
}(); }();
return path;
#if 0
try { try {
StringMap rewrites = state.realiseContext(context); StringMap rewrites = state.realiseContext(context);
@ -122,6 +125,7 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea
e.addTrace(pos, "while realising the context of path '%s'", path); e.addTrace(pos, "while realising the context of path '%s'", path);
throw; throw;
} }
#endif
} }
/* Add and attribute to the given attribute map from the output name to /* Add and attribute to the given attribute map from the output name to
@ -161,6 +165,18 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
{ {
auto path = realisePath(state, pos, vPath); auto path = realisePath(state, pos, vPath);
#if 0
// FIXME: use InputAccessor
if (path == corepkgsPrefix + "fetchurl.nix") {
state.eval(state.parseExprFromString(
#include "fetchurl.nix.gen.hh"
, "/"), v);
}
#endif
state.evalFile(path, v);
#if 0
// FIXME // FIXME
auto isValidDerivationInStore = [&]() -> std::optional<StorePath> { auto isValidDerivationInStore = [&]() -> std::optional<StorePath> {
if (!state.store->isStorePath(path)) if (!state.store->isStorePath(path))
@ -200,6 +216,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
state.forceAttrs(v, pos); state.forceAttrs(v, pos);
} }
// FIXME: use InputAccessor
else if (path == corepkgsPrefix + "fetchurl.nix") { else if (path == corepkgsPrefix + "fetchurl.nix") {
state.eval(state.parseExprFromString( state.eval(state.parseExprFromString(
#include "fetchurl.nix.gen.hh" #include "fetchurl.nix.gen.hh"
@ -232,6 +249,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
e->eval(state, *env, v); e->eval(state, *env, v);
} }
} }
#endif
} }
static RegisterPrimOp primop_scopedImport(RegisterPrimOp::Info { static RegisterPrimOp primop_scopedImport(RegisterPrimOp::Info {
@ -312,6 +330,9 @@ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
/* Load a ValueInitializer from a DSO and return whatever it initializes */ /* Load a ValueInitializer from a DSO and return whatever it initializes */
void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v) void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
throw UnimplementedError("importNative");
#if 0
auto path = realisePath(state, pos, *args[0]); auto path = realisePath(state, pos, *args[0]);
std::string sym(state.forceStringNoCtx(*args[1], pos)); std::string sym(state.forceStringNoCtx(*args[1], pos));
@ -334,6 +355,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
(func)(state, v); (func)(state, v);
/* We don't dlclose because v may be a primop referencing a function in the shared object file */ /* We don't dlclose because v may be a primop referencing a function in the shared object file */
#endif
} }
@ -1343,7 +1365,8 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V
}); });
PathSet context; PathSet context;
Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context)); // FIXME: check rootPath
Path path = state.coerceToPath(pos, *args[0], context);
/* Resolve symlinks in path, unless path itself is a symlink /* Resolve symlinks in path, unless path itself is a symlink
directly in the store. The latter condition is necessary so directly in the store. The latter condition is necessary so
e.g. nix-push does the right thing. */ e.g. nix-push does the right thing. */
@ -1381,14 +1404,14 @@ static RegisterPrimOp primop_storePath({
static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
/* We dont check the path right now, because we dont want to /* We dont check the path right now, because we dont want to
throw if the path isnt allowed, but just return false (and we throw if the path isnt allowed, but just return false (and we
cant just catch the exception here because we still want to cant just catch the exception here because we still want to
throw if something in the evaluation of `*args[0]` tries to throw if something in the evaluation of `*args[0]` tries to
access an unauthorized path). */ access an unauthorized path). */
auto path = realisePath(state, pos, *args[0], { .checkForPureEval = false }); auto path = realisePath(state, pos, *args[0], { .checkForPureEval = false });
try { try {
v.mkBool(pathExists(state.checkSourcePath(path))); v.mkBool(path.accessor->pathExists(path.path));
} catch (SysError & e) { } catch (SysError & e) {
/* Don't give away info from errors while canonicalising /* Don't give away info from errors while canonicalising
path in restricted mode. */ path in restricted mode. */
@ -1453,16 +1476,15 @@ static RegisterPrimOp primop_dirOf({
static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto path = realisePath(state, pos, *args[0]); auto path = realisePath(state, pos, *args[0]);
auto s = readFile(path); auto s = path.accessor->readFile(path.path);
if (s.find((char) 0) != std::string::npos) if (s.find((char) 0) != std::string::npos)
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path); throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
StorePathSet refs; auto refs =
if (state.store->isInStore(path)) { #if 0
try { state.store->isInStore(path) ?
refs = state.store->queryPathInfo(state.store->toStorePath(path).first)->references; state.store->queryPathInfo(state.store->toStorePath(path).first)->references :
} catch (Error &) { // FIXME: should be InvalidPathError #endif
} StorePathSet{};
}
auto context = state.store->printStorePathSet(refs); auto context = state.store->printStorePathSet(refs);
v.mkString(s, context); v.mkString(s, context);
} }
@ -1480,6 +1502,7 @@ static RegisterPrimOp primop_readFile({
which are desugared to 'findFile __nixPath "x"'. */ which are desugared to 'findFile __nixPath "x"'. */
static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
#if 0
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
SearchPath searchPath; SearchPath searchPath;
@ -1500,26 +1523,18 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
pos pos
); );
PathSet context; auto path = realisePath(state, pos, *i->value, { .requireAbsolutePath = false });
auto path = state.coerceToString(pos, *i->value, context, false, false).toOwned();
try {
auto rewrites = state.realiseContext(context);
path = rewriteStrings(path, rewrites);
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
.errPos = pos
});
}
searchPath.emplace_back(prefix, path); searchPath.emplace_back(prefix, path);
} }
auto path = state.forceStringNoCtx(*args[1], pos); auto path = state.forceStringNoCtx(*args[1], pos);
v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos))); // FIXME: checkSourcePath?
v.mkPath(state.findFile(searchPath, path, pos));
#endif
throw UnimplementedError("findFile");
} }
static RegisterPrimOp primop_findFile(RegisterPrimOp::Info { static RegisterPrimOp primop_findFile(RegisterPrimOp::Info {
@ -1541,7 +1556,8 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va
auto path = realisePath(state, pos, *args[1]); auto path = realisePath(state, pos, *args[1]);
v.mkString(hashFile(*ht, path).to_string(Base16, false)); // FIXME: state.toRealPath(path, context)
v.mkString(hashString(*ht, path.accessor->readFile(path.path)).to_string(Base16, false));
} }
static RegisterPrimOp primop_hashFile({ static RegisterPrimOp primop_hashFile({
@ -1560,17 +1576,19 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
{ {
auto path = realisePath(state, pos, *args[0]); auto path = realisePath(state, pos, *args[0]);
DirEntries entries = readDirectory(path); auto entries = path.accessor->readDirectory(path.path);
auto attrs = state.buildBindings(entries.size()); auto attrs = state.buildBindings(entries.size());
for (auto & ent : entries) { for (auto & [name, type] : entries) {
if (ent.type == DT_UNKNOWN) #if 0
ent.type = getFileType(path + "/" + ent.name); // FIXME?
attrs.alloc(ent.name).mkString( if (type == InputAccessor::Type::Misc)
ent.type == DT_REG ? "regular" : ent.type = getFileType(path + "/" + name);
ent.type == DT_DIR ? "directory" : #endif
ent.type == DT_LNK ? "symlink" : attrs.alloc(name).mkString(
type == InputAccessor::Type::tRegular ? "regular" :
type == InputAccessor::Type::tDirectory ? "directory" :
type == InputAccessor::Type::tSymlink ? "symlink" :
"unknown"); "unknown");
} }
@ -1902,9 +1920,12 @@ static void addPath(
} }
} }
// FIXME
#if 0
path = evalSettings.pureEval && expectedHash path = evalSettings.pureEval && expectedHash
? path ? path
: state.checkSourcePath(path); : state.checkSourcePath(path);
#endif
PathFilter filter = filterFun ? ([&](const Path & path) { PathFilter filter = filterFun ? ([&](const Path & path) {
auto st = lstat(path); auto st = lstat(path);

View file

@ -5,6 +5,7 @@
#include "path.hh" #include "path.hh"
#include "attrs.hh" #include "attrs.hh"
#include "url.hh" #include "url.hh"
#include "input-accessor.hh"
#include <memory> #include <memory>
@ -27,7 +28,6 @@ struct InputScheme;
* "fromURL()" or "fromAttrs()" static functions which are provided * "fromURL()" or "fromAttrs()" static functions which are provided
* the url or attrset specified in the flake file. * the url or attrset specified in the flake file.
*/ */
struct Input struct Input
{ {
friend struct InputScheme; friend struct InputScheme;
@ -98,7 +98,6 @@ public:
std::optional<time_t> getLastModified() const; std::optional<time_t> getLastModified() const;
}; };
/* The InputScheme represents a type of fetcher. Each fetcher /* The InputScheme represents a type of fetcher. Each fetcher
* registers with nix at startup time. When processing an input for a * registers with nix at startup time. When processing an input for a
* flake, each scheme is given an opportunity to "recognize" that * flake, each scheme is given an opportunity to "recognize" that
@ -107,7 +106,6 @@ public:
* recognized. The Input object contains the information the fetcher * recognized. The Input object contains the information the fetcher
* needs to actually perform the "fetch()" when called. * needs to actually perform the "fetch()" when called.
*/ */
struct InputScheme struct InputScheme
{ {
virtual ~InputScheme() virtual ~InputScheme()
@ -133,6 +131,11 @@ struct InputScheme
virtual void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg); virtual void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg);
virtual std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) = 0; virtual std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) = 0;
virtual ref<InputAccessor> getAccessor()
{
throw UnimplementedError("getAccessor");
}
}; };
void registerInputScheme(std::shared_ptr<InputScheme> && fetcher); void registerInputScheme(std::shared_ptr<InputScheme> && fetcher);

View file

@ -0,0 +1,178 @@
#include "input-accessor.hh"
#include "util.hh"
#include <atomic>
namespace nix {
static std::atomic<size_t> nextNumber{0};
InputAccessor::InputAccessor()
: number(++nextNumber)
{
printError("CREATE %d", number);
}
// FIXME: merge with archive.cc.
const std::string narVersionMagic1 = "nix-archive-1";
static string caseHackSuffix = "~nix~case~hack~";
void InputAccessor::dumpPath(
const Path & path,
Sink & sink,
PathFilter & filter)
{
auto dumpContents = [&](std::string_view path)
{
// FIXME: pipe
auto s = readFile(path);
sink << "contents" << s.size();
sink(s);
writePadding(s.size(), sink);
};
std::function<void(const std::string & path)> dump;
dump = [&](const std::string & path) {
checkInterrupt();
auto st = lstat(path);
sink << "(";
if (st.type == tRegular) {
sink << "type" << "regular";
if (st.isExecutable)
sink << "executable" << "";
dumpContents(path);
}
else if (st.type == tDirectory) {
sink << "type" << "directory";
/* If we're on a case-insensitive system like macOS, undo
the case hack applied by restorePath(). */
std::map<string, string> unhacked;
for (auto & i : readDirectory(path))
if (/* archiveSettings.useCaseHack */ false) { // FIXME
string name(i.first);
size_t pos = i.first.find(caseHackSuffix);
if (pos != string::npos) {
debug(format("removing case hack suffix from '%s'") % (path + "/" + i.first));
name.erase(pos);
}
if (!unhacked.emplace(name, i.first).second)
throw Error("file name collision in between '%s' and '%s'",
(path + "/" + unhacked[name]),
(path + "/" + i.first));
} else
unhacked.emplace(i.first, i.first);
for (auto & i : unhacked)
if (filter(path + "/" + i.first)) {
sink << "entry" << "(" << "name" << i.first << "node";
dump(path + "/" + i.second);
sink << ")";
}
}
else if (st.type == tSymlink)
sink << "type" << "symlink" << "target" << readLink(path);
else throw Error("file '%s' has an unsupported type", path);
sink << ")";
};
sink << narVersionMagic1;
dump(path);
}
struct FSInputAccessor : InputAccessor
{
Path root;
FSInputAccessor(const Path & root)
: root(root)
{ }
std::string readFile(std::string_view path) override
{
auto absPath = makeAbsPath(path);
printError("READ %s", absPath);
checkAllowed(absPath);
return nix::readFile(absPath);
}
bool pathExists(std::string_view path) override
{
auto absPath = makeAbsPath(path);
printError("EXISTS %s", absPath);
return isAllowed(absPath) && nix::pathExists(absPath);
}
Stat lstat(std::string_view path) override
{
auto absPath = makeAbsPath(path);
printError("LSTAT %s", absPath);
checkAllowed(absPath);
auto st = nix::lstat(absPath);
return Stat {
.type =
S_ISREG(st.st_mode) ? tRegular :
S_ISDIR(st.st_mode) ? tDirectory :
S_ISLNK(st.st_mode) ? tSymlink :
tMisc,
.isExecutable = S_ISREG(st.st_mode) && st.st_mode & S_IXUSR
};
}
DirEntries readDirectory(std::string_view path) override
{
auto absPath = makeAbsPath(path);
printError("READDIR %s", absPath);
checkAllowed(absPath);
abort();
}
std::string readLink(std::string_view path) override
{
auto absPath = makeAbsPath(path);
printError("READLINK %s", absPath);
checkAllowed(absPath);
return nix::readLink(absPath);
}
Path makeAbsPath(std::string_view path)
{
assert(hasPrefix(path, "/"));
return canonPath(root + std::string(path));
}
void checkAllowed(std::string_view absPath)
{
if (!isAllowed(absPath))
throw Error("access to path '%s' is not allowed", absPath);
}
bool isAllowed(std::string_view absPath)
{
if (!isDirOrInDir(absPath, root))
return false;
return true;
}
};
ref<InputAccessor> makeFSInputAccessor(const Path & root)
{
return make_ref<FSInputAccessor>(root);
}
std::ostream & operator << (std::ostream & str, const SourcePath & path)
{
str << path.path; // FIXME
return str;
}
}

View file

@ -0,0 +1,57 @@
#pragma once
#include "ref.hh"
#include "types.hh"
#include "archive.hh"
namespace nix {
struct InputAccessor
{
const size_t number;
InputAccessor();
virtual ~InputAccessor()
{ }
virtual std::string readFile(std::string_view path) = 0;
virtual bool pathExists(std::string_view path) = 0;
enum Type { tRegular, tSymlink, tDirectory, tMisc };
struct Stat
{
Type type = tMisc;
//uint64_t fileSize = 0; // regular files only
bool isExecutable = false; // regular files only
};
virtual Stat lstat(std::string_view path) = 0;
typedef std::optional<Type> DirEntry;
typedef std::map<std::string, DirEntry> DirEntries;
virtual DirEntries readDirectory(std::string_view path) = 0;
virtual std::string readLink(std::string_view path) = 0;
virtual void dumpPath(
const Path & path,
Sink & sink,
PathFilter & filter = defaultPathFilter);
};
ref<InputAccessor> makeFSInputAccessor(const Path & root);
struct SourcePath
{
ref<InputAccessor> accessor;
Path path;
};
std::ostream & operator << (std::ostream & str, const SourcePath & path);
}

View file

@ -304,7 +304,11 @@ static void main_nix_build(int argc, char * * argv)
else else
/* If we're in a #! script, interpret filenames /* If we're in a #! script, interpret filenames
relative to the script. */ relative to the script. */
exprs.push_back(state->parseExprFromFile(resolveExprPath(state->checkSourcePath(lookupFileArg(*state, exprs.push_back(
state->parseExprFromFile(
resolveExprPath(
state->rootPath(
lookupFileArg(*state,
inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i))))); inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i)))));
} }
} }

View file

@ -159,7 +159,7 @@ static void loadSourceExpr(EvalState & state, const Path & path, Value & v)
throw SysError("getting information about '%1%'", path); throw SysError("getting information about '%1%'", path);
if (isNixExpr(path, st)) if (isNixExpr(path, st))
state.evalFile(path, v); state.evalFile(state.rootPath(path), v);
/* The path is a directory. Put the Nix expressions in the /* The path is a directory. Put the Nix expressions in the
directory in a set, with the file name of each expression as directory in a set, with the file name of each expression as

View file

@ -22,7 +22,7 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
Path manifestFile = userEnv + "/manifest.nix"; Path manifestFile = userEnv + "/manifest.nix";
if (pathExists(manifestFile)) { if (pathExists(manifestFile)) {
Value v; Value v;
state.evalFile(manifestFile, v); state.evalFile(state.rootPath(manifestFile), v);
Bindings & bindings(*state.allocBindings(0)); Bindings & bindings(*state.allocBindings(0));
getDerivations(state, v, "", bindings, elems, false); getDerivations(state, v, "", bindings, elems, false);
} }

View file

@ -182,7 +182,7 @@ static int main_nix_instantiate(int argc, char * * argv)
for (auto & i : files) { for (auto & i : files) {
Expr * e = fromArgs Expr * e = fromArgs
? state->parseExprFromString(i, absPath(".")) ? state->parseExprFromString(i, absPath("."))
: state->parseExprFromFile(resolveExprPath(state->checkSourcePath(lookupFileArg(*state, i)))); : state->parseExprFromFile(resolveExprPath(state->rootPath(lookupFileArg(*state, i))));
processExpr(*state, attrPaths, parseOnly, strict, autoArgs, processExpr(*state, attrPaths, parseOnly, strict, autoArgs,
evalOnly, outputKind, xmlOutputSourceLocation, e); evalOnly, outputKind, xmlOutputSourceLocation, e);
} }

View file

@ -192,9 +192,11 @@ static int main_nix_prefetch_url(int argc, char * * argv)
throw UsageError("you must specify a URL"); throw UsageError("you must specify a URL");
url = args[0]; url = args[0];
} else { } else {
Path path = resolveExprPath(lookupFileArg(*state, args.empty() ? "." : args[0]));
Value vRoot; Value vRoot;
state->evalFile(path, vRoot); state->evalFile(
resolveExprPath(
state->rootPath(lookupFileArg(*state, args.empty() ? "." : args[0]))),
vRoot);
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first); Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
state->forceAttrs(v, noPos); state->forceAttrs(v, noPos);

View file

@ -624,7 +624,7 @@ void NixRepl::loadFile(const Path & path)
loadedFiles.remove(path); loadedFiles.remove(path);
loadedFiles.push_back(path); loadedFiles.push_back(path);
Value v, v2; Value v, v2;
state->evalFile(lookupFileArg(*state, path), v); state->evalFile(state->rootPath(lookupFileArg(*state, path)), v);
state->autoCallFunction(*autoArgs, v, v2); state->autoCallFunction(*autoArgs, v, v2);
addAttrsToScope(v2); addAttrsToScope(v2);
} }

View file

@ -8,4 +8,4 @@ libplugintest_ALLOW_UNDEFINED := 1
libplugintest_EXCLUDE_FROM_LIBRARY_LIST := 1 libplugintest_EXCLUDE_FROM_LIBRARY_LIST := 1
libplugintest_CXXFLAGS := -I src/libutil -I src/libexpr libplugintest_CXXFLAGS := -I src/libutil -I src/libexpr -I src/libfetchers