diff --git a/src/libfetchers/input-accessor.cc b/src/libfetchers/input-accessor.cc index f9909c218..f37a8058b 100644 --- a/src/libfetchers/input-accessor.cc +++ b/src/libfetchers/input-accessor.cc @@ -75,22 +75,28 @@ SourcePath SourcePath::resolveSymlinks() const int linksAllowed = 1024; - for (auto & component : path) { - res.path.push(component); - while (true) { - if (auto st = res.maybeLstat()) { + std::list todo; + for (auto & c : path) + todo.push_back(std::string(c)); + + 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--) throw Error("infinite symlink recursion in path '%s'", path); - if (st->type != InputAccessor::tSymlink) break; auto target = res.readLink(); + res.path.pop(); if (hasPrefix(target, "/")) - res = CanonPath(target); - else { - res.path.pop(); - res.path.extend(CanonPath(target)); - } - } else - break; + res.path = CanonPath::root; + todo.splice(todo.begin(), tokenizeString>(target, "/")); + } } } diff --git a/tests/eval.sh b/tests/eval.sh index 066d8fc36..b81bb1e2c 100644 --- a/tests/eval.sh +++ b/tests/eval.sh @@ -35,3 +35,9 @@ nix-instantiate --eval -E 'assert 1 + 2 == 3; true' # Check that symlink cycles don't cause a hang. ln -sfn cycle.nix $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 ]]