diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index e19b18505..d03ff82ce 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -145,9 +145,27 @@ DownloadTarballResult downloadTarball( // TODO: fall back to cached value if download fails. + AutoDelete cleanupTemp; + /* Note: if the download is cached, `importTarball()` will receive no data, which causes it to import an empty tarball. */ - TarArchive archive { *source }; + auto archive = + hasSuffix(toLower(parseURL(url).path), ".zip") + ? ({ + /* In streaming mode, libarchive doesn't handle + symlinks in zip files correctly (#10649). So write + the entire file to disk so libarchive can access it + in random-access mode. */ + auto [fdTemp, path] = createTempFile("nix-zipfile"); + cleanupTemp.reset(path); + debug("downloading '%s' into '%s'...", url, path); + { + FdSink sink(fdTemp.get()); + source->drainInto(sink); + } + TarArchive{path}; + }) + : TarArchive{*source}; auto parseSink = getTarballCache()->getFileSystemObjectSink(); auto lastModified = unpackTarfileToSink(archive, *parseSink); diff --git a/tests/functional/flakes/prefetch.sh b/tests/functional/flakes/prefetch.sh new file mode 100644 index 000000000..bfd0533f9 --- /dev/null +++ b/tests/functional/flakes/prefetch.sh @@ -0,0 +1,6 @@ +source common.sh + +# Test symlinks in zip files (#10649). +path=$(nix flake prefetch --json file://$(pwd)/tree.zip | jq -r .storePath) +[[ $(cat $path/foo) = foo ]] +[[ $(readlink $path/bar) = foo ]] diff --git a/tests/functional/flakes/tree.zip b/tests/functional/flakes/tree.zip new file mode 100644 index 000000000..f9e4d225f Binary files /dev/null and b/tests/functional/flakes/tree.zip differ diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 021af096c..18b920f7d 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -15,6 +15,7 @@ nix_tests = \ flakes/absolute-attr-paths.sh \ flakes/build-paths.sh \ flakes/flake-in-submodule.sh \ + flakes/prefetch.sh \ gc.sh \ nix-collect-garbage-d.sh \ remote-store.sh \