From 9951e14ae085c818a11e28c0d530a111c1e2ab86 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 9 May 2024 19:33:09 +0200 Subject: [PATCH] Handle zip files containing symlinks In streaming mode, libarchive doesn't handle symlinks in zip files correctly. So write the entire file to disk so libarchive can access it in random-access mode. Fixes #10649. This was broken in cabee9815239af426cece729cb765810b8a716ce. --- src/libfetchers/tarball.cc | 20 +++++++++++++++++++- tests/functional/flakes/prefetch.sh | 6 ++++++ tests/functional/flakes/tree.zip | Bin 0 -> 455 bytes tests/functional/local.mk | 1 + 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/functional/flakes/prefetch.sh create mode 100644 tests/functional/flakes/tree.zip 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 0000000000000000000000000000000000000000..f9e4d225fe04d51a8fed588cb94352bece51efe9 GIT binary patch literal 455 zcmWIWW@h1H0D(ufD3m;6LW?X)e0GSR10t{~*K{V87tPr1J_zGeac7H*PVgwqpq|qK`6xf$Q t(?Gt&VH(I