libutil: remove SourcePath::resolveSymlinks
in pure mode it is entirely useless. in impure mode it's mostly useless since the way in which it is used is either equivalent to not being run at all, or is equivalent to turning the following lstat into a stat. we add a stat method instead for all those who need final symlinks stat'd. Change-Id: I801886d18eb34b26e62b4c05d53318c6421a69bf
This commit is contained in:
committed by
Jade Lovelace
parent
f93af1db1f
commit
5af069b248
@@ -123,7 +123,7 @@ static void getAllExprs(Evaluator & state,
|
||||
|
||||
InputAccessor::Stat st;
|
||||
try {
|
||||
st = path2.resolveSymlinks().lstat();
|
||||
st = path2.stat();
|
||||
} catch (Error &) {
|
||||
continue; // ignore dangling symlinks in ~/.nix-defexpr
|
||||
}
|
||||
@@ -162,7 +162,7 @@ static void getAllExprs(Evaluator & state,
|
||||
|
||||
static void loadSourceExpr(EvalState & state, const SourcePath & path, Value & v)
|
||||
{
|
||||
auto st = path.resolveSymlinks().lstat();
|
||||
auto st = path.stat();
|
||||
|
||||
if (isNixExpr(path, st))
|
||||
state.evalFile(path, v);
|
||||
|
||||
+1
-1
@@ -676,7 +676,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
|
||||
// Reload right after exiting the editor if path is not in store
|
||||
// Store is immutable, so there could be no changes, so there's no need to reload
|
||||
if (!evaluator.store->isInStore(path.resolveSymlinks().canonical().abs())) {
|
||||
if (!evaluator.store->isInStore(canonPath(path.canonical().abs(), true))) {
|
||||
state.resetFileCache();
|
||||
reloadFiles();
|
||||
}
|
||||
|
||||
@@ -1206,12 +1206,13 @@ static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args,
|
||||
|| arg.str().ends_with("/."));
|
||||
|
||||
try {
|
||||
auto checked = state
|
||||
.ctx.paths
|
||||
.checkSourcePath(path)
|
||||
.resolveSymlinks(mustBeDir ? SymlinkResolution::Full : SymlinkResolution::Ancestors);
|
||||
auto checked = state.ctx.paths.checkSourcePath(path);
|
||||
|
||||
auto st = checked.maybeLstat();
|
||||
// previously we fully resolved symlinks in the mustBeDir case or in pure eval
|
||||
// mode (by accident, since checkSourcePath does this in that case), and up to
|
||||
// the last component otherwise. this is equivalent to calling stat and lstat,
|
||||
// respectively. (in neither case do intermediate symlinks affect the result.)
|
||||
auto st = mustBeDir ? checked.maybeStat() : checked.maybeLstat();
|
||||
auto exists = st && (!mustBeDir || st->type == InputAccessor::tDirectory);
|
||||
v.mkBool(exists);
|
||||
} catch (SysError & e) {
|
||||
|
||||
+26
-44
@@ -1,4 +1,5 @@
|
||||
#include "lix/libutil/source-path.hh"
|
||||
#include "file-system.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
|
||||
namespace nix {
|
||||
@@ -21,9 +22,8 @@ SourcePath SourcePath::parent() const
|
||||
return std::move(*p);
|
||||
}
|
||||
|
||||
InputAccessor::Stat SourcePath::lstat() const
|
||||
static InputAccessor::Stat convertStat(const struct stat & st)
|
||||
{
|
||||
auto st = nix::lstat(path.abs());
|
||||
return InputAccessor::Stat {
|
||||
.type =
|
||||
S_ISREG(st.st_mode) ? InputAccessor::tRegular :
|
||||
@@ -34,12 +34,32 @@ InputAccessor::Stat SourcePath::lstat() const
|
||||
};
|
||||
}
|
||||
|
||||
InputAccessor::Stat SourcePath::lstat() const
|
||||
{
|
||||
return convertStat(nix::lstat(path.abs()));
|
||||
}
|
||||
|
||||
std::optional<InputAccessor::Stat> SourcePath::maybeLstat() const
|
||||
{
|
||||
// FIXME: merge these into one operation.
|
||||
if (!pathExists())
|
||||
return {};
|
||||
return lstat();
|
||||
if (auto st = nix::maybeLstat(path.abs())) {
|
||||
return convertStat(*st);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
InputAccessor::Stat SourcePath::stat() const
|
||||
{
|
||||
return convertStat(nix::stat(path.abs()));
|
||||
}
|
||||
|
||||
std::optional<InputAccessor::Stat> SourcePath::maybeStat() const
|
||||
{
|
||||
if (auto st = nix::maybeStat(path.abs())) {
|
||||
return convertStat(*st);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
InputAccessor::DirEntries SourcePath::readDirectory() const
|
||||
@@ -58,42 +78,4 @@ InputAccessor::DirEntries SourcePath::readDirectory() const
|
||||
return res;
|
||||
}
|
||||
|
||||
SourcePath SourcePath::resolveSymlinks(SymlinkResolution mode) const
|
||||
{
|
||||
SourcePath res(CanonPath::root);
|
||||
|
||||
int linksAllowed = 1024;
|
||||
|
||||
std::list<std::string> todo;
|
||||
for (auto & c : path)
|
||||
todo.push_back(std::string(c));
|
||||
|
||||
bool resolve_last = mode == SymlinkResolution::Full;
|
||||
|
||||
while (!todo.empty()) {
|
||||
auto c = *todo.begin();
|
||||
todo.pop_front();
|
||||
if (c == "" || c == ".")
|
||||
;
|
||||
else if (c == "..")
|
||||
res.path.pop();
|
||||
else {
|
||||
res.path.push(c);
|
||||
if (resolve_last || !todo.empty()) {
|
||||
if (auto st = res.maybeLstat(); st && st->type == InputAccessor::tSymlink) {
|
||||
if (!linksAllowed--)
|
||||
throw Error("infinite symlink recursion in path '%s'", path);
|
||||
auto target = res.readLink();
|
||||
res.path.pop();
|
||||
if (target.starts_with("/"))
|
||||
res.path = CanonPath::root;
|
||||
todo.splice(todo.begin(), tokenizeString<std::list<std::string>>(target, "/"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-30
@@ -14,26 +14,6 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Note there is a decent chance this type soon goes away because the problem is solved another way.
|
||||
* See the discussion in https://github.com/NixOS/nix/pull/9985.
|
||||
*/
|
||||
enum class SymlinkResolution {
|
||||
/**
|
||||
* Resolve symlinks in the ancestors only.
|
||||
*
|
||||
* Only the last component of the result is possibly a symlink.
|
||||
*/
|
||||
Ancestors,
|
||||
|
||||
/**
|
||||
* Resolve symlinks fully, realpath(3)-style.
|
||||
*
|
||||
* No component of the result will be a symlink.
|
||||
*/
|
||||
Full,
|
||||
};
|
||||
|
||||
/**
|
||||
* An abstraction for accessing source files during
|
||||
* evaluation. Currently, it's just a wrapper around `CanonPath` that
|
||||
@@ -84,6 +64,18 @@ public:
|
||||
*/
|
||||
std::optional<InputAccessor::Stat> maybeLstat() const;
|
||||
|
||||
/**
|
||||
* Return stats about this `SourcePath`, or throw an exception if
|
||||
* it doesn't exist. Symlinks are resolved by this function.
|
||||
*/
|
||||
InputAccessor::Stat stat() const;
|
||||
|
||||
/**
|
||||
* Return stats about this `SourcePath`, or std::nullopt if it
|
||||
* doesn't exist. Symlinks are resolved by this function.
|
||||
*/
|
||||
std::optional<InputAccessor::Stat> maybeStat() const;
|
||||
|
||||
/**
|
||||
* If this `SourcePath` denotes a directory (not a symlink),
|
||||
* return its directory entries; otherwise throw an error.
|
||||
@@ -138,16 +130,6 @@ public:
|
||||
{
|
||||
return path < x.path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve any symlinks in this `SourcePath` according to the
|
||||
* given resolution mode.
|
||||
*
|
||||
* @param mode might only be a temporary solution for this.
|
||||
* See the discussion in https://github.com/NixOS/nix/pull/9985.
|
||||
*/
|
||||
SourcePath resolveSymlinks(
|
||||
SymlinkResolution mode = SymlinkResolution::Full) const;
|
||||
};
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const SourcePath & path);
|
||||
|
||||
Reference in New Issue
Block a user