Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 93 additions & 72 deletions src/libstore/export-import.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,91 +5,112 @@
#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);

HashSink hashSink(HashAlgorithm::SHA256);
TeeSink teeSink(sink, hashSink);

store.narFromPath(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))
throw Error(
"hash of path '%s' has changed from '%s' to '%s'!",
store.printStorePath(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;
}
static const uint32_t exportMagicV1 = 0x4558494e;

void exportPaths(Store & store, const StorePathSet & paths, Sink & sink)
void exportPaths(Store & store, const StorePathSet & paths, Sink & sink, unsigned int version)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could version be an enum instead? That would be slightly more self-documenting.

{
auto sorted = store.topoSortPaths(paths);
std::reverse(sorted.begin(), sorted.end());

for (auto & path : sorted) {
sink << 1;
exportPath(store, path, sink);
auto dumpNar = [&](const ValidPathInfo & info) {
HashSink hashSink(HashAlgorithm::SHA256);
TeeSink teeSink(sink, hashSink);

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))
throw Error(
"hash of path '%s' has changed from '%s' to '%s'!",
store.printStorePath(info.path),
info.narHash.to_string(HashFormat::Nix32, true),
hash.to_string(HashFormat::Nix32, true));
};

switch (version) {

case 1:
for (auto & path : sorted) {
sink << 1;
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);
}

sink << 0;
}

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'");

/* Extract the NAR from the source. */
StringSink saved;
TeeSource tee{source, saved};
NullFileSystemObjectSink ether;
parseDump(ether, tee);

uint32_t magic = readInt(source);
if (magic != exportMagic)
throw Error("Nix archive 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);

ValidPathInfo info{path, narHash};
if (deriver != "")
info.deriver = store.parseStorePath(deriver);
info.references = references;
info.narSize = saved.s.size();

// Ignore optional legacy signature.
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);

res.push_back(info.path);

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};
NullFileSystemObjectSink ether;
parseDump(ether, tee);

uint32_t magic = readInt(source);
if (magic != exportMagicV1)
throw Error("nario cannot be imported; wrong format");

auto path = store.parseStorePath(readString(source));

auto references = CommonProto::Serialise<StorePathSet>::read(store, CommonProto::ReadConn{.from = source});
auto deriver = readString(source);
auto narHash = hashString(HashAlgorithm::SHA256, saved.s);

ValidPathInfo info{path, narHash};
if (deriver != "")
info.deriver = store.parseStorePath(deriver);
info.references = references;
info.narSize = saved.s.size();

// Ignore optional legacy signature.
if (readInt(source) == 1)
readString(source);

// 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;
Expand Down
7 changes: 1 addition & 6 deletions src/libstore/include/nix/store/export-import.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/nix/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
28 changes: 28 additions & 0 deletions src/nix/nario-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
R""(

# Examples

* Export the closure of building `nixpkgs#hello`:

```console
# nix nario export --format 1 -r nixpkgs#hello > dump
Copy link
Member

@Mic92 Mic92 Sep 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe useful?

Suggested change
# nix nario export --format 1 -r nixpkgs#hello > dump
# nix nario export --format 1 -r nixpkgs#hello > dump
# nix nario export --format 1 -r nixpkgs#hello | zstd > dump.zst

```

It can be imported in another store:

```console
# nix nario import < dump
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# nix nario import < dump
# nix nario import < dump
# zstd -d < dump | nix nario import

```

# 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 changes: 15 additions & 0 deletions src/nix/nario-import.md
Original file line number Diff line number Diff line change
@@ -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 changes: 18 additions & 0 deletions src/nix/nario-list.md
Original file line number Diff line number Diff line change
@@ -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.

)""
Loading
Loading