From ab1e58e948ebcfc3ad619d0e4da02aad45946193 Mon Sep 17 00:00:00 2001 From: Victor Fuentes Date: Thu, 29 May 2025 18:01:09 -0700 Subject: [PATCH] feat(nix store ls): support reading nar listings from binary cache Remote binary caches support `write-nar-listing` options where they create a `HASH.ls` file for quick indexing without having to download the nar. This commit makes experimental `nix store ls` attempt to read these files instead of downloading the full nar. The difference is very obvious with large packages like stellarium: nix store ls --store "https://cache.nixos.org" /nix/store/ijpvwgs9zamqaax5dy2cd0kxgz7lr7an-stellarium-25.1 -R Change-Id: I6a37e0788b3a91c319331a8de69c51daf3efa955 --- doc/manual/change-authors.yml | 5 +++++ .../rl-next/nix-store-ls-read-nar-listings.md | 12 +++++++++++ lix/nix/ls.cc | 20 ++++++++++++++++++- tests/functional/nar-access.sh | 20 +++++++++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 doc/manual/rl-next/nix-store-ls-read-nar-listings.md diff --git a/doc/manual/change-authors.yml b/doc/manual/change-authors.yml index 193c087c7..ea4fdd938 100644 --- a/doc/manual/change-authors.yml +++ b/doc/manual/change-authors.yml @@ -236,6 +236,11 @@ vigress8: forgejo: vigress8 github: vigress8 +vlinkz: + display_name: Victor Fuentes + forgejo: vlinkz + github: vlinkz + winter: forgejo: winter github: winterqt diff --git a/doc/manual/rl-next/nix-store-ls-read-nar-listings.md b/doc/manual/rl-next/nix-store-ls-read-nar-listings.md new file mode 100644 index 000000000..e394670c4 --- /dev/null +++ b/doc/manual/rl-next/nix-store-ls-read-nar-listings.md @@ -0,0 +1,12 @@ +--- +synopsis: Allow `nix store ls` to read nar listings from binary cache stores. +issues: [] +cls: [3225] +category: Improvements +credits: [vlinkz] +--- + +The `nix store ls` command now supports reading `.ls` nar listings from binary cache stores. +If a listing is detected for the store path being queried, the nar is no longer downloaded. +These nar listings are available in binary cache stores where the `write-nar-listing` option is +enabled, such as cache.nixos.org. diff --git a/lix/nix/ls.cc b/lix/nix/ls.cc index dcdd035ae..4f6de63cd 100644 --- a/lix/nix/ls.cc +++ b/lix/nix/ls.cc @@ -1,4 +1,5 @@ #include "lix/libcmd/command.hh" +#include "lix/libstore/binary-cache-store.hh" #include "lix/libstore/store-api.hh" #include "lix/libstore/fs-accessor.hh" #include "lix/libstore/nar-accessor.hh" @@ -123,7 +124,24 @@ struct CmdLsStore : StoreCommand, MixLs void run(ref store) override { - list(store->getFSAccessor()); + auto accessor = store->getFSAccessor(); + + try { + auto binaryCacheStore = store.try_cast_shared(); + if (binaryCacheStore) { + const auto [storePath, restPath] = store->toStorePath(path); + auto file = binaryCacheStore->getFile(fmt("%s.ls", storePath.hashPart()))->drain(); + JSON j = json::parse(std::move(file), "a nar content listing"); + if (j["version"] == 1) { + path = restPath; + accessor = makeLazyNarAccessor(j["root"].dump(), [](uint64_t, uint64_t) -> std::string { + throw Error("attempted to read NAR content during listing"); + }); + } + } + } catch (NoSuchBinaryCacheFile &) { } + + list(accessor); } }; diff --git a/tests/functional/nar-access.sh b/tests/functional/nar-access.sh index 426068e68..af22a87eb 100644 --- a/tests/functional/nar-access.sh +++ b/tests/functional/nar-access.sh @@ -59,3 +59,23 @@ if nix-store --dump $storePath >/dev/full ; then echo "dumping to /dev/full should fail" exit -1 fi + + +# Test reading from remote nar listings if available + +nix copy --to "file://$cacheDir?write-nar-listing=true" $storePath + +export _NIX_FORCE_HTTP=1 + +diff -u \ + <(nix store ls --json $storePath --store "file://$cacheDir" | jq -S) \ + <(echo '{"type":"directory","entries":{"foo":{},"foo-x":{},"qux":{},"zyx":{}}}' | jq -S) +diff -u \ + <(nix store ls --json -R $storePath/foo/bar --store "file://$cacheDir" | jq -S) \ + <(echo '{"narOffset": 368,"type":"regular","size":0}' | jq -S) + +# Confirm that we are reading from ".ls" file by deleting the nar +rm -rf $cacheDir/nar +diff -u \ + <(nix store ls --json -R $storePath/foo/bar --store "file://$cacheDir" | jq -S) \ + <(echo '{"narOffset": 368,"type":"regular","size":0}' | jq -S)