mirror of
https://github.com/NixOS/nix.git
synced 2025-12-02 23:20:59 +01:00
Add nix nario command
This replaces `nix-store --export` and `nix-store --import`.
This commit is contained in:
parent
377b60ee9b
commit
c8633ce4f8
10 changed files with 353 additions and 71 deletions
|
|
@ -5,58 +5,70 @@
|
|||
#include "nix/store/common-protocol.hh"
|
||||
#include "nix/store/common-protocol-impl.hh"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace nix {
|
||||
|
||||
static void exportPath(Store & store, const StorePath & path, Sink & sink)
|
||||
{
|
||||
auto info = store.queryPathInfo(path);
|
||||
static const uint32_t exportMagicV1 = 0x4558494e;
|
||||
|
||||
void exportPaths(Store & store, const StorePathSet & paths, Sink & sink, unsigned int version)
|
||||
{
|
||||
auto sorted = store.topoSortPaths(paths);
|
||||
std::reverse(sorted.begin(), sorted.end());
|
||||
|
||||
auto dumpNar = [&](const ValidPathInfo & info) {
|
||||
HashSink hashSink(HashAlgorithm::SHA256);
|
||||
TeeSink teeSink(sink, hashSink);
|
||||
|
||||
store.narFromPath(path, teeSink);
|
||||
store.narFromPath(info.path, teeSink);
|
||||
|
||||
/* Refuse to export paths that have changed. This prevents
|
||||
filesystem corruption from spreading to other machines.
|
||||
Don't complain if the stored hash is zero (unknown). */
|
||||
Hash hash = hashSink.currentHash().hash;
|
||||
if (hash != info->narHash && info->narHash != Hash(info->narHash.algo))
|
||||
if (hash != info.narHash && info.narHash != Hash(info.narHash.algo))
|
||||
throw Error(
|
||||
"hash of path '%s' has changed from '%s' to '%s'!",
|
||||
store.printStorePath(path),
|
||||
info->narHash.to_string(HashFormat::Nix32, true),
|
||||
store.printStorePath(info.path),
|
||||
info.narHash.to_string(HashFormat::Nix32, true),
|
||||
hash.to_string(HashFormat::Nix32, true));
|
||||
};
|
||||
|
||||
teeSink << exportMagic << store.printStorePath(path);
|
||||
CommonProto::write(store, CommonProto::WriteConn{.to = teeSink}, info->references);
|
||||
teeSink << (info->deriver ? store.printStorePath(*info->deriver) : "") << 0;
|
||||
}
|
||||
|
||||
void exportPaths(Store & store, const StorePathSet & paths, Sink & sink)
|
||||
{
|
||||
auto sorted = store.topoSortPaths(paths);
|
||||
std::reverse(sorted.begin(), sorted.end());
|
||||
switch (version) {
|
||||
|
||||
case 1:
|
||||
for (auto & path : sorted) {
|
||||
sink << 1;
|
||||
exportPath(store, path, sink);
|
||||
auto info = store.queryPathInfo(path);
|
||||
dumpNar(*info);
|
||||
sink << exportMagicV1 << store.printStorePath(path);
|
||||
CommonProto::write(store, CommonProto::WriteConn{.to = sink}, info->references);
|
||||
sink << (info->deriver ? store.printStorePath(*info->deriver) : "") << 0;
|
||||
}
|
||||
|
||||
sink << 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("unsupported nario version %d", version);
|
||||
}
|
||||
}
|
||||
|
||||
StorePaths importPaths(Store & store, Source & source, CheckSigsFlag checkSigs)
|
||||
{
|
||||
StorePaths res;
|
||||
while (true) {
|
||||
auto n = readNum<uint64_t>(source);
|
||||
if (n == 0)
|
||||
break;
|
||||
if (n != 1)
|
||||
throw Error("input doesn't look like something created by 'nix-store --export'");
|
||||
|
||||
auto version = readNum<uint64_t>(source);
|
||||
|
||||
/* Note: nario version 1 lacks an explicit header. The first
|
||||
integer denotes whether a store path follows or not. So look
|
||||
for 0 or 1. */
|
||||
switch (version) {
|
||||
|
||||
case 0:
|
||||
/* Empty version 1 nario, nothing to do. */
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* Non-empty version 1 nario. */
|
||||
while (true) {
|
||||
/* Extract the NAR from the source. */
|
||||
StringSink saved;
|
||||
TeeSource tee{source, saved};
|
||||
|
|
@ -64,13 +76,11 @@ StorePaths importPaths(Store & store, Source & source, CheckSigsFlag checkSigs)
|
|||
parseDump(ether, tee);
|
||||
|
||||
uint32_t magic = readInt(source);
|
||||
if (magic != exportMagic)
|
||||
throw Error("Nix archive cannot be imported; wrong format");
|
||||
if (magic != exportMagicV1)
|
||||
throw Error("nario cannot be imported; wrong format");
|
||||
|
||||
auto path = store.parseStorePath(readString(source));
|
||||
|
||||
// Activity act(*logger, lvlInfo, "importing path '%s'", info.path);
|
||||
|
||||
auto references = CommonProto::Serialise<StorePathSet>::read(store, CommonProto::ReadConn{.from = source});
|
||||
auto deriver = readString(source);
|
||||
auto narHash = hashString(HashAlgorithm::SHA256, saved.s);
|
||||
|
|
@ -85,11 +95,22 @@ StorePaths importPaths(Store & store, Source & source, CheckSigsFlag checkSigs)
|
|||
if (readInt(source) == 1)
|
||||
readString(source);
|
||||
|
||||
// Can't use underlying source, which would have been exhausted
|
||||
auto source = StringSource(saved.s);
|
||||
store.addToStore(info, source, NoRepair, checkSigs);
|
||||
// Can't use underlying source, which would have been exhausted.
|
||||
auto source2 = StringSource(saved.s);
|
||||
store.addToStore(info, source2, NoRepair, checkSigs);
|
||||
|
||||
res.push_back(info.path);
|
||||
|
||||
auto n = readNum<uint64_t>(source);
|
||||
if (n == 0)
|
||||
break;
|
||||
if (n != 1)
|
||||
throw Error("input doesn't look like a nario");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("input doesn't look like a nario");
|
||||
}
|
||||
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -4,16 +4,11 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Magic header of exportPath() output (obsolete).
|
||||
*/
|
||||
const uint32_t exportMagic = 0x4558494e;
|
||||
|
||||
/**
|
||||
* Export multiple paths in the format expected by `nix-store
|
||||
* --import`. The paths will be sorted topologically.
|
||||
*/
|
||||
void exportPaths(Store & store, const StorePathSet & paths, Sink & sink);
|
||||
void exportPaths(Store & store, const StorePathSet & paths, Sink & sink, unsigned int version);
|
||||
|
||||
/**
|
||||
* Import a sequence of NAR dumps created by `exportPaths()` into the
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ nix_sources = [ config_priv_h ] + files(
|
|||
'make-content-addressed.cc',
|
||||
'man-pages.cc',
|
||||
'nar.cc',
|
||||
'nario.cc',
|
||||
'optimise-store.cc',
|
||||
'path-from-hash-part.cc',
|
||||
'path-info.cc',
|
||||
|
|
|
|||
28
src/nix/nario-export.md
Normal file
28
src/nix/nario-export.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
R""(
|
||||
|
||||
# Examples
|
||||
|
||||
* Export the closure of building `nixpkgs#hello`:
|
||||
|
||||
```console
|
||||
# nix nario export --format 1 -r nixpkgs#hello > dump
|
||||
```
|
||||
|
||||
It can be imported in another store:
|
||||
|
||||
```console
|
||||
# nix nario import < dump
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
This command prints on standard output a serialization of the specified store paths in `nario` format. This serialization can be imported into another store using `nix nario import`.
|
||||
|
||||
References of a path are not exported by default; use `-r` to export a complete closure.
|
||||
Paths are exported in topographically sorted order (i.e. if path `X` refers to `Y`, then `Y` appears before `X`).
|
||||
|
||||
You must specify the desired `nario` version. Currently the following versions are supported:
|
||||
|
||||
* `1`: This version is compatible with the legacy `nix-store --export` and `nix-store --import` commands.
|
||||
|
||||
)""
|
||||
15
src/nix/nario-import.md
Normal file
15
src/nix/nario-import.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
R""(
|
||||
|
||||
# Examples
|
||||
|
||||
* Import store paths from the file named `dump`:
|
||||
|
||||
```console
|
||||
# nix nario import < dump
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
This command reads from standard input a serialization of store paths produced by `nix nario export` and adds them to the Nix store.
|
||||
|
||||
)""
|
||||
18
src/nix/nario-list.md
Normal file
18
src/nix/nario-list.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
R""(
|
||||
|
||||
# Examples
|
||||
|
||||
* List the contents of a nario file:
|
||||
|
||||
```console
|
||||
# nix nario list < dump
|
||||
/nix/store/4y1jj6cwvslmfh1bzkhbvhx77az6yf00-xgcc-14.2.1.20250322-libgcc: 201856 bytes
|
||||
/nix/store/d8hnbm5hvbg2vza50garppb63y724i94-libunistring-1.3: 2070240 bytes
|
||||
…
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
This command lists the contents of a nario file read from standard input.
|
||||
|
||||
)""
|
||||
189
src/nix/nario.cc
Normal file
189
src/nix/nario.cc
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
#include "nix/cmd/command.hh"
|
||||
#include "nix/main/shared.hh"
|
||||
#include "nix/store/store-api.hh"
|
||||
#include "nix/store/export-import.hh"
|
||||
#include "nix/util/callback.hh"
|
||||
#include "nix/util/fs-sink.hh"
|
||||
#include "nix/util/archive.hh"
|
||||
|
||||
using namespace nix;
|
||||
|
||||
struct CmdNario : NixMultiCommand
|
||||
{
|
||||
CmdNario()
|
||||
: NixMultiCommand("nario", RegisterCommand::getCommandsFor({"nario"}))
|
||||
{
|
||||
}
|
||||
|
||||
std::string description() override
|
||||
{
|
||||
return "operations for manipulating nario files";
|
||||
}
|
||||
|
||||
Category category() override
|
||||
{
|
||||
return catUtility;
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdNario = registerCommand<CmdNario>("nario");
|
||||
|
||||
struct CmdNarioExport : StorePathsCommand
|
||||
{
|
||||
unsigned int version = 0;
|
||||
|
||||
CmdNarioExport()
|
||||
{
|
||||
addFlag({
|
||||
.longName = "format",
|
||||
.description = "Version of the nario format to use. Must be `1`.",
|
||||
.labels = {"nario-format"},
|
||||
.handler = {&version},
|
||||
});
|
||||
}
|
||||
|
||||
std::string description() override
|
||||
{
|
||||
return "serialize store paths to standard output in nario format";
|
||||
}
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "nario-export.md"
|
||||
;
|
||||
}
|
||||
|
||||
void run(ref<Store> store, StorePaths && storePaths) override
|
||||
{
|
||||
if (!version)
|
||||
throw UsageError("`nix nario export` requires `--format` argument");
|
||||
|
||||
FdSink sink(getStandardOutput());
|
||||
exportPaths(*store, StorePathSet(storePaths.begin(), storePaths.end()), sink, version);
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdNarioExport = registerCommand2<CmdNarioExport>({"nario", "export"});
|
||||
|
||||
struct CmdNarioImport : StoreCommand
|
||||
{
|
||||
std::string description() override
|
||||
{
|
||||
return "import store paths from a nario file on standard input";
|
||||
}
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "nario-import.md"
|
||||
;
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override
|
||||
{
|
||||
FdSource source(getStandardInput());
|
||||
importPaths(*store, source, NoCheckSigs); // FIXME
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdNarioImport = registerCommand2<CmdNarioImport>({"nario", "import"});
|
||||
|
||||
struct CmdNarioList : Command
|
||||
{
|
||||
std::string description() override
|
||||
{
|
||||
return "list the contents of a nario file";
|
||||
}
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "nario-list.md"
|
||||
;
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
struct Config : StoreConfig
|
||||
{
|
||||
Config(const Params & params)
|
||||
: StoreConfig(params)
|
||||
{
|
||||
}
|
||||
|
||||
ref<Store> openStore() const override
|
||||
{
|
||||
abort();
|
||||
}
|
||||
};
|
||||
|
||||
struct ListingStore : Store
|
||||
{
|
||||
ListingStore(ref<const Config> config)
|
||||
: Store{*config}
|
||||
{
|
||||
}
|
||||
|
||||
void queryPathInfoUncached(
|
||||
const StorePath & path, Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept override
|
||||
{
|
||||
callback(nullptr);
|
||||
}
|
||||
|
||||
std::optional<TrustedFlag> isTrustedClient() override
|
||||
{
|
||||
return Trusted;
|
||||
}
|
||||
|
||||
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void
|
||||
addToStore(const ValidPathInfo & info, Source & source, RepairFlag repair, CheckSigsFlag checkSigs) override
|
||||
{
|
||||
logger->cout(fmt("%s: %d bytes", printStorePath(info.path), info.narSize));
|
||||
// Discard the NAR.
|
||||
NullFileSystemObjectSink parseSink;
|
||||
parseDump(parseSink, source);
|
||||
}
|
||||
|
||||
StorePath addToStoreFromDump(
|
||||
Source & dump,
|
||||
std::string_view name,
|
||||
FileSerialisationMethod dumpMethod,
|
||||
ContentAddressMethod hashMethod,
|
||||
HashAlgorithm hashAlgo,
|
||||
const StorePathSet & references,
|
||||
RepairFlag repair) override
|
||||
{
|
||||
unsupported("addToStoreFromDump");
|
||||
}
|
||||
|
||||
void narFromPath(const StorePath & path, Sink & sink) override
|
||||
{
|
||||
unsupported("narFromPath");
|
||||
}
|
||||
|
||||
void queryRealisationUncached(
|
||||
const DrvOutput &, Callback<std::shared_ptr<const Realisation>> callback) noexcept override
|
||||
{
|
||||
callback(nullptr);
|
||||
}
|
||||
|
||||
ref<SourceAccessor> getFSAccessor(bool requireValidPath) override
|
||||
{
|
||||
return makeEmptySourceAccessor();
|
||||
}
|
||||
};
|
||||
|
||||
FdSource source(getStandardInput());
|
||||
auto config = make_ref<Config>(StoreConfig::Params());
|
||||
ListingStore lister(config);
|
||||
importPaths(lister, source, NoCheckSigs);
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdNarioList = registerCommand2<CmdNarioList>({"nario", "list"});
|
||||
|
|
@ -775,7 +775,7 @@ static void opExport(Strings opFlags, Strings opArgs)
|
|||
paths.insert(store->followLinksToStorePath(i));
|
||||
|
||||
FdSink sink(getStandardOutput());
|
||||
exportPaths(*store, paths, sink);
|
||||
exportPaths(*store, paths, sink, 1);
|
||||
sink.flush();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ StoreWrapper::exportPaths(int fd, ...)
|
|||
StorePathSet paths;
|
||||
for (int n = 2; n < items; ++n) paths.insert(THIS->store->parseStorePath(SvPV_nolen(ST(n))));
|
||||
FdSink sink(fd);
|
||||
exportPaths(*THIS->store, paths, sink);
|
||||
exportPaths(*THIS->store, paths, sink, 1);
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,14 @@ clearStore
|
|||
outPath=$(nix-build dependencies.nix --no-out-link)
|
||||
|
||||
nix-store --export $outPath > $TEST_ROOT/exp
|
||||
nix nario export --format 1 "$outPath" > $TEST_ROOT/exp2
|
||||
cmp "$TEST_ROOT/exp" "$TEST_ROOT/exp2"
|
||||
|
||||
nix-store --export $(nix-store -qR $outPath) > $TEST_ROOT/exp_all
|
||||
|
||||
nix nario export --format 1 -r "$outPath" > $TEST_ROOT/exp_all2
|
||||
cmp "$TEST_ROOT/exp_all" "$TEST_ROOT/exp_all2"
|
||||
|
||||
if nix-store --export $outPath >/dev/full ; then
|
||||
echo "exporting to a bad file descriptor should fail"
|
||||
exit 1
|
||||
|
|
@ -38,3 +43,13 @@ clearStore
|
|||
# Regression test: the derivers in exp_all2 are empty, which shouldn't
|
||||
# cause a failure.
|
||||
nix-store --import < $TEST_ROOT/exp_all2
|
||||
|
||||
|
||||
# Test `nix nario import` on files created by `nix-store --export`.
|
||||
clearStore
|
||||
nix nario import < $TEST_ROOT/exp_all
|
||||
nix path-info "$outPath"
|
||||
|
||||
|
||||
# Test `nix nario list`.
|
||||
nix nario list < $TEST_ROOT/exp_all | grepQuiet "dependencies-input-0: .* bytes"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue