Merge pull request #9985 from alois31/symlink-resolution

Restore `builtins.pathExists` behavior on broken symlinks

(cherry picked from commit d53c8901ef7f2033855dd99063522e3d56a19dab)

===

note that this variant differs markedly from the source commit because
we haven't endured quite as much lazy trees.

Change-Id: I0facf282f21fe0db4134be5c65a8368c1b3a06fc
This commit is contained in:
eldritch horrors 2024-03-07 04:06:03 +01:00
parent 1342c8f18e
commit b14f88e0d4
5 changed files with 50 additions and 18 deletions

View file

@ -1540,11 +1540,12 @@ static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args,
|| arg.str().ends_with("/.")); || arg.str().ends_with("/."));
try { try {
auto checked = state.checkSourcePath(path); auto checked = state
auto exists = checked.pathExists(); .checkSourcePath(path)
if (exists && mustBeDir) { .resolveSymlinks(mustBeDir ? SymlinkResolution::Full : SymlinkResolution::Ancestors);
exists = checked.lstat().type == InputAccessor::tDirectory;
} auto st = checked.maybeLstat();
auto exists = st && (!mustBeDir || st->type == InputAccessor::tDirectory);
v.mkBool(exists); v.mkBool(exists);
} catch (SysError & e) { } catch (SysError & e) {
/* Don't give away info from errors while canonicalising /* Don't give away info from errors while canonicalising

View file

@ -56,7 +56,7 @@ InputAccessor::DirEntries SourcePath::readDirectory() const
return res; return res;
} }
SourcePath SourcePath::resolveSymlinks() const SourcePath SourcePath::resolveSymlinks(SymlinkResolution mode) const
{ {
SourcePath res(CanonPath::root); SourcePath res(CanonPath::root);
@ -66,6 +66,8 @@ SourcePath SourcePath::resolveSymlinks() const
for (auto & c : path) for (auto & c : path)
todo.push_back(std::string(c)); todo.push_back(std::string(c));
bool resolve_last = mode == SymlinkResolution::Full;
while (!todo.empty()) { while (!todo.empty()) {
auto c = *todo.begin(); auto c = *todo.begin();
todo.pop_front(); todo.pop_front();
@ -75,6 +77,7 @@ SourcePath SourcePath::resolveSymlinks() const
res.path.pop(); res.path.pop();
else { else {
res.path.push(c); res.path.push(c);
if (resolve_last || !todo.empty()) {
if (auto st = res.maybeLstat(); st && st->type == InputAccessor::tSymlink) { if (auto st = res.maybeLstat(); st && st->type == InputAccessor::tSymlink) {
if (!linksAllowed--) if (!linksAllowed--)
throw Error("infinite symlink recursion in path '%s'", path); throw Error("infinite symlink recursion in path '%s'", path);
@ -86,6 +89,7 @@ SourcePath SourcePath::resolveSymlinks() const
} }
} }
} }
}
return res; return res;
} }

View file

@ -12,6 +12,26 @@
namespace nix { 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 * An abstraction for accessing source files during
* evaluation. Currently, it's just a wrapper around `CanonPath` that * evaluation. Currently, it's just a wrapper around `CanonPath` that
@ -121,11 +141,14 @@ struct SourcePath
} }
/** /**
* Resolve any symlinks in this `SourcePath` (including its * Resolve any symlinks in this `SourcePath` according to the
* parents). The result is a `SourcePath` in which no element is a * given resolution mode.
* symlink. *
* @param mode might only be a temporary solution for this.
* See the discussion in https://github.com/NixOS/nix/pull/9985.
*/ */
SourcePath resolveSymlinks() const; SourcePath resolveSymlinks(
SymlinkResolution mode = SymlinkResolution::Full) const;
}; };
std::ostream & operator << (std::ostream & str, const SourcePath & path); std::ostream & operator << (std::ostream & str, const SourcePath & path);

View file

@ -27,3 +27,6 @@ builtins.pathExists (./lib.nix)
&& !builtins.pathExists (builtins.toPath (builtins.toString ./bla.nix)) && !builtins.pathExists (builtins.toPath (builtins.toString ./bla.nix))
&& builtins.pathExists ./lib.nix && builtins.pathExists ./lib.nix
&& !builtins.pathExists ./bla.nix && !builtins.pathExists ./bla.nix
&& builtins.pathExists ./symlink-resolution/foo/overlays/overlay.nix
&& builtins.pathExists ./symlink-resolution/broken
&& builtins.pathExists (builtins.toString ./symlink-resolution/foo/overlays + "/.")

View file

@ -0,0 +1 @@
nonexistent