diff options
author | eldritch horrors <pennae@lix.systems> | 2024-03-07 04:06:03 +0100 |
---|---|---|
committer | eldritch horrors <pennae@lix.systems> | 2024-03-07 00:43:51 -0700 |
commit | b14f88e0d46c61280a69da9559cf54cbce058eb5 (patch) | |
tree | 7b1c0be985b3f1aa7ae86ee1727d7a08c36c11a5 /src | |
parent | 1342c8f18e48afd1577cfdc319c254ce7c42637e (diff) |
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
Diffstat (limited to 'src')
-rw-r--r-- | src/libexpr/primops.cc | 11 | ||||
-rw-r--r-- | src/libutil/source-path.cc | 22 | ||||
-rw-r--r-- | src/libutil/source-path.hh | 31 |
3 files changed, 46 insertions, 18 deletions
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 1a961582f..c4fdc6098 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1540,11 +1540,12 @@ static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args, || arg.str().ends_with("/.")); try { - auto checked = state.checkSourcePath(path); - auto exists = checked.pathExists(); - if (exists && mustBeDir) { - exists = checked.lstat().type == InputAccessor::tDirectory; - } + auto checked = state + .checkSourcePath(path) + .resolveSymlinks(mustBeDir ? SymlinkResolution::Full : SymlinkResolution::Ancestors); + + auto st = checked.maybeLstat(); + auto exists = st && (!mustBeDir || st->type == InputAccessor::tDirectory); v.mkBool(exists); } catch (SysError & e) { /* Don't give away info from errors while canonicalising diff --git a/src/libutil/source-path.cc b/src/libutil/source-path.cc index b6563ee90..3ccbca06b 100644 --- a/src/libutil/source-path.cc +++ b/src/libutil/source-path.cc @@ -56,7 +56,7 @@ InputAccessor::DirEntries SourcePath::readDirectory() const return res; } -SourcePath SourcePath::resolveSymlinks() const +SourcePath SourcePath::resolveSymlinks(SymlinkResolution mode) const { SourcePath res(CanonPath::root); @@ -66,6 +66,8 @@ SourcePath SourcePath::resolveSymlinks() const 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(); @@ -75,14 +77,16 @@ SourcePath SourcePath::resolveSymlinks() const res.path.pop(); else { res.path.push(c); - 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 (hasPrefix(target, "/")) - res.path = CanonPath::root; - todo.splice(todo.begin(), tokenizeString<std::list<std::string>>(target, "/")); + 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 (hasPrefix(target, "/")) + res.path = CanonPath::root; + todo.splice(todo.begin(), tokenizeString<std::list<std::string>>(target, "/")); + } } } } diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index 54698de77..03cc998e3 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -13,6 +13,26 @@ 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 * accesses files in the regular filesystem, but in the future it will @@ -121,11 +141,14 @@ struct SourcePath } /** - * Resolve any symlinks in this `SourcePath` (including its - * parents). The result is a `SourcePath` in which no element is a - * symlink. + * 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() const; + SourcePath resolveSymlinks( + SymlinkResolution mode = SymlinkResolution::Full) const; }; std::ostream & operator << (std::ostream & str, const SourcePath & path); |