diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 78da6a14..4431b68e 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -273,7 +273,7 @@ EvalState::EvalState(const Strings & _searchPath) /* Initialise the Nix expression search path. */ Strings paths = parseNixPath(getEnv("NIX_PATH", "")); - for (auto & i : _searchPath) addToSearchPath(i, true); + for (auto & i : _searchPath) addToSearchPath(i); for (auto & i : paths) addToSearchPath(i); addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs"); @@ -296,11 +296,15 @@ Path EvalState::checkSourcePath(const Path & path_) if (!restricted) return path_; /* Resolve symlinks. */ + debug(format("checking access to ‘%s’") % path_); Path path = canonPath(path_, true); - for (auto & i : searchPath) - if (path == i.second || isInDir(path, i.second)) + for (auto & i : searchPath) { + auto r = resolveSearchPathElem(i); + if (!r.first) continue; + if (path == r.second || isInDir(path, r.second)) return path; + } /* To support import-from-derivation, allow access to anything in the store. FIXME: only allow access to paths that have been diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 2d7b7bcd..e5c88cf3 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -55,7 +55,8 @@ typedef std::map SrcToStore; std::ostream & operator << (std::ostream & str, const Value & v); -typedef list > SearchPath; +typedef std::pair SearchPathElem; +typedef std::list SearchPath; /* Initialise the Boehm GC, if applicable. */ @@ -95,12 +96,14 @@ private: SearchPath searchPath; + std::map> searchPathResolved; + public: EvalState(const Strings & _searchPath); ~EvalState(); - void addToSearchPath(const string & s, bool warn = false); + void addToSearchPath(const string & s); Path checkSourcePath(const Path & path); @@ -122,6 +125,9 @@ public: Path findFile(const string & path); Path findFile(SearchPath & searchPath, const string & path, const Pos & pos = noPos); + /* If the specified search path element is a URI, download it. */ + std::pair resolveSearchPathElem(const SearchPathElem & elem); + /* Evaluate an expression to normal form, storing the result in value `v'. */ void eval(Expr * e, Value & v); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index d34882f3..d8fb60bd 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -590,7 +590,7 @@ Expr * EvalState::parseExprFromString(const string & s, const Path & basePath) } -void EvalState::addToSearchPath(const string & s, bool warn) +void EvalState::addToSearchPath(const string & s) { size_t pos = s.find('='); string prefix; @@ -602,16 +602,7 @@ void EvalState::addToSearchPath(const string & s, bool warn) path = string(s, pos + 1); } - if (isUri(path)) - path = downloadFileCached(path, true); - - path = absPath(path); - if (pathExists(path)) { - debug(format("adding path ‘%1%’ to the search path") % path); - /* Resolve symlinks in the path to support restricted mode. */ - searchPath.push_back(std::pair(prefix, canonPath(path, true))); - } else if (warn) - printMsg(lvlError, format("warning: Nix search path entry ‘%1%’ does not exist, ignoring") % path); + searchPath.emplace_back(prefix, path); } @@ -624,17 +615,19 @@ Path EvalState::findFile(const string & path) Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos & pos) { for (auto & i : searchPath) { - assert(!isUri(i.second)); - Path res; + std::string suffix; if (i.first.empty()) - res = i.second + "/" + path; + suffix = "/" + path; else { - if (path.compare(0, i.first.size(), i.first) != 0 || - (path.size() > i.first.size() && path[i.first.size()] != '/')) + auto s = i.first.size(); + if (path.compare(0, s, i.first) != 0 || + (path.size() > s && path[s] != '/')) continue; - res = i.second + - (path.size() == i.first.size() ? "" : "/" + string(path, i.first.size())); + suffix = path.size() == s ? "" : "/" + string(path, s); } + auto r = resolveSearchPathElem(i); + if (!r.first) continue; + Path res = r.second + suffix; if (pathExists(res)) return canonPath(res); } format f = format( @@ -645,4 +638,35 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos } +std::pair EvalState::resolveSearchPathElem(const SearchPathElem & elem) +{ + auto i = searchPathResolved.find(elem.second); + if (i != searchPathResolved.end()) return i->second; + + std::pair res; + + if (isUri(elem.second)) { + try { + res = { true, downloadFileCached(elem.second, true) }; + } catch (DownloadError & e) { + printMsg(lvlError, format("warning: Nix search path entry ‘%1%’ cannot be downloaded, ignoring") % elem.second); + res = { false, "" }; + } + } else { + auto path = absPath(elem.second); + if (pathExists(path)) + res = { true, path }; + else { + printMsg(lvlError, format("warning: Nix search path entry ‘%1%’ does not exist, ignoring") % elem.second); + res = { false, "" }; + } + } + + debug(format("resolved search path element ‘%s’ to ‘%s’") % elem.second % res.second); + + searchPathResolved[elem.second] = res; + return res; +} + + } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 25bf42e1..286b51e5 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -765,7 +765,6 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va SearchPath searchPath; - PathSet context; for (unsigned int n = 0; n < args[0]->listSize(); ++n) { Value & v2(*args[0]->listElems()[n]); state.forceAttrs(v2, pos); @@ -778,21 +777,23 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va i = v2.attrs->find(state.symbols.create("path")); if (i == v2.attrs->end()) throw EvalError(format("attribute ‘path’ missing, at %1%") % pos); - string path = state.coerceToPath(pos, *i->value, context); - searchPath.push_back(std::pair(prefix, state.checkSourcePath(path))); + PathSet context; + string path = state.coerceToString(pos, *i->value, context, false, false); + + try { + realiseContext(context); + } catch (InvalidPathError & e) { + throw EvalError(format("cannot find ‘%1%’, since path ‘%2%’ is not valid, at %3%") + % path % e.path % pos); + } + + searchPath.emplace_back(prefix, path); } string path = state.forceStringNoCtx(*args[1], pos); - try { - realiseContext(context); - } catch (InvalidPathError & e) { - throw EvalError(format("cannot find ‘%1%’, since path ‘%2%’ is not valid, at %3%") - % path % e.path % pos); - } - - mkPath(v, state.findFile(searchPath, path, pos).c_str()); + mkPath(v, state.checkSourcePath(state.findFile(searchPath, path, pos)).c_str()); } /* Read a directory (without . or ..) */ diff --git a/tests/tarball.sh b/tests/tarball.sh index cb5258a9..e22a72e3 100644 --- a/tests/tarball.sh +++ b/tests/tarball.sh @@ -20,4 +20,10 @@ nix-build file://$tarball nix-build '' -I foo=file://$tarball -nix-build -E "import (fetchTarball file://$tarball)" +nix-build -o $TMPDIR/result -E "import (fetchTarball file://$tarball)" + +nix-instantiate --eval -E '1 + 2' -I fnord=file://no-such-tarball.tar.xz +nix-instantiate --eval -E 'with ; 1 + 2' -I fnord=file://no-such-tarball.tar.xz +(! nix-instantiate --eval -E ' 1' -I fnord=file://no-such-tarball.tar.xz) + +nix-instantiate --eval -E '' -I fnord=file://no-such-tarball.tar.xz -I fnord=.