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. */
@ -1388,7 +1411,7 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
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