Fix SourcePath::resolveSymlinks()

This fixes handling of symlinks that start with '..', and symlink
targets that contain symlinks themselves.
This commit is contained in:
Eelco Dolstra 2023-06-05 15:43:12 +02:00
parent 9c6ede85fc
commit f5c6b29940
2 changed files with 24 additions and 12 deletions

View file

@ -75,22 +75,28 @@ SourcePath SourcePath::resolveSymlinks() const
int linksAllowed = 1024; int linksAllowed = 1024;
for (auto & component : path) { std::list<std::string> todo;
res.path.push(component); for (auto & c : path)
while (true) { todo.push_back(std::string(c));
if (auto st = res.maybeLstat()) {
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 (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);
if (st->type != InputAccessor::tSymlink) break;
auto target = res.readLink(); auto target = res.readLink();
res.path.pop();
if (hasPrefix(target, "/")) if (hasPrefix(target, "/"))
res = CanonPath(target); res.path = CanonPath::root;
else { todo.splice(todo.begin(), tokenizeString<std::list<std::string>>(target, "/"));
res.path.pop(); }
res.path.extend(CanonPath(target));
}
} else
break;
} }
} }

View file

@ -35,3 +35,9 @@ nix-instantiate --eval -E 'assert 1 + 2 == 3; true'
# Check that symlink cycles don't cause a hang. # Check that symlink cycles don't cause a hang.
ln -sfn cycle.nix $TEST_ROOT/cycle.nix ln -sfn cycle.nix $TEST_ROOT/cycle.nix
(! nix eval --file $TEST_ROOT/cycle.nix) (! nix eval --file $TEST_ROOT/cycle.nix)
# Check that relative symlinks are resolved correctly.
mkdir -p $TEST_ROOT/xyzzy $TEST_ROOT/foo
ln -sfn ../xyzzy $TEST_ROOT/foo/bar
printf 123 > $TEST_ROOT/xyzzy/default.nix
[[ $(nix eval --impure --expr "import $TEST_ROOT/foo/bar") = 123 ]]