Skip to content
Merged
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
2 changes: 1 addition & 1 deletion doc/manual/source/SUMMARY.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
- [Deriving Path](protocols/json/deriving-path.md)
- [Serving Tarball Flakes](protocols/tarball-fetcher.md)
- [Store Path Specification](protocols/store-path.md)
- [Nix Archive (NAR) Format](protocols/nix-archive.md)
- [Nix Archive (NAR) Format](protocols/nix-archive/index.md)
- [Derivation "ATerm" file format](protocols/derivation-aterm.md)
- [C API](c-api.md)
- [Glossary](glossary.md)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,15 @@ The `str` function / parameterized rule is defined as follows:
- `int(n)` = the 64-bit little endian representation of the number `n`

- `pad(s)` = the byte sequence `s`, padded with 0s to a multiple of 8 byte

## Kaitai Struct Specification

The Nix Archive (NAR) format is also formally described using [Kaitai Struct](https://kaitai.io/), an Interface Description Language (IDL) for defining binary data structures.

> Kaitai Struct provides a language-agnostic, machine-readable specification that can be compiled into parsers for various programming languages (e.g., C++, Python, Java, Rust).

```yaml
{{#include nar.ksy}}
```

The source of the spec can be found [here](https://github.com/nixos/nix/blob/master/src/nix-manual/source/protocols/nix-archive/nar.ksy). Contributions and improvements to the spec are welcomed.
169 changes: 169 additions & 0 deletions doc/manual/source/protocols/nix-archive/nar.ksy
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
meta:
id: nix_nar
title: Nix Archive (NAR)
file-extension: nar
endian: le
doc: |
Nix Archive (NAR) format. A simple, reproducible binary archive
format used by the Nix package manager to serialize file system objects.
doc-ref: 'https://nixos.org/manual/nix/stable/command-ref/nix-store.html#nar-format'

seq:
- id: magic
type: padded_str
doc: "Magic string, must be 'nix-archive-1'."
valid:
expr: _.body == 'nix-archive-1'
- id: root_node
type: node
doc: "The root of the archive, which is always a single node."

types:
padded_str:
doc: |
A string, prefixed with its length (u8le) and
padded with null bytes to the next 8-byte boundary.
seq:
- id: len_str
type: u8
- id: body
type: str
size: len_str
encoding: 'ascii'
- id: padding
size: (8 - (len_str % 8)) % 8

node:
doc: "A single filesystem node (file, directory, or symlink)."
seq:
- id: open_paren
type: padded_str
doc: "Must be '(', a token starting the node definition."
valid:
expr: _.body == '('
- id: type_key
type: padded_str
doc: "Must be 'type'."
valid:
expr: _.body == 'type'
- id: type_val
type: padded_str
doc: "The type of the node: 'regular', 'directory', or 'symlink'."
- id: body
type:
switch-on: type_val.body
cases:
"'directory'": type_directory
"'regular'": type_regular
"'symlink'": type_symlink
- id: close_paren
type: padded_str
valid:
expr: _.body == ')'
if: "type_val.body != 'directory'"
doc: "Must be ')', a token ending the node definition."

type_directory:
doc: "A directory node, containing a list of entries. Entries must be ordered by their names."
seq:
- id: entries
type: dir_entry
repeat: until
repeat-until: _.kind.body == ')'
types:
dir_entry:
doc: "A single entry within a directory, or a terminator."
seq:
- id: kind
type: padded_str
valid:
expr: _.body == 'entry' or _.body == ')'
doc: "Must be 'entry' (for a child node) or '' (for terminator)."
- id: open_paren
type: padded_str
valid:
expr: _.body == '('
if: 'kind.body == "entry"'
- id: name_key
type: padded_str
valid:
expr: _.body == 'name'
if: 'kind.body == "entry"'
- id: name
type: padded_str
if: 'kind.body == "entry"'
- id: node_key
type: padded_str
valid:
expr: _.body == 'node'
if: 'kind.body == "entry"'
- id: node
type: node
if: 'kind.body == "entry"'
doc: "The child node, present only if kind is 'entry'."
- id: close_paren
type: padded_str
valid:
expr: _.body == ')'
if: 'kind.body == "entry"'
instances:
is_terminator:
value: kind.body == ')'

type_regular:
doc: "A regular file node."
seq:
# Read attributes (like 'executable') until we hit 'contents'
- id: attributes
type: reg_attribute
repeat: until
repeat-until: _.key.body == "contents"
# After the 'contents' token, read the file data
- id: file_data
type: file_content
instances:
is_executable:
value: 'attributes[0].key.body == "executable"'
doc: "True if the file has the 'executable' attribute."
types:
reg_attribute:
doc: "An attribute of the file, e.g., 'executable' or 'contents'."
seq:
- id: key
type: padded_str
doc: "Attribute key, e.g., 'executable' or 'contents'."
valid:
expr: _.body == 'executable' or _.body == 'contents'
- id: value
type: padded_str
if: 'key.body == "executable"'
valid:
expr: _.body == ''
doc: "Must be '' if key is 'executable'."
file_content:
doc: "The raw data of the file, prefixed by length."
seq:
- id: len_contents
type: u8
# # This relies on the property of instances that they are lazily evaluated and cached.
- size: 0
if: nar_offset < 0
- id: contents
size: len_contents
- id: padding
size: (8 - (len_contents % 8)) % 8
instances:
nar_offset:
value: _io.pos

type_symlink:
doc: "A symbolic link node."
seq:
- id: target_key
type: padded_str
doc: "Must be 'target'."
valid:
expr: _.body == 'target'
- id: target_val
type: padded_str
doc: "The destination path of the symlink."
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ be many different serialisations.
For these reasons, Nix has its very own archive format—the Nix Archive (NAR) format,
which is carefully designed to avoid the problems described above.

The exact specification of the Nix Archive format is in [specified here](../../protocols/nix-archive.md).
The exact specification of the Nix Archive format is in [specified here](../../protocols/nix-archive/index.md).

## Content addressing File System Objects beyond a single serialisation pass

Expand Down
4 changes: 4 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,10 @@
supportsCross = false;
};

"nix-kaitai-struct-checks" = {
supportsCross = false;
};

"nix-perl-bindings" = {
supportsCross = false;
};
Expand Down
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ if get_option('unit-tests')
endif
subproject('nix-functional-tests')
subproject('json-schema-checks')
subproject('kaitai-struct-checks')
5 changes: 5 additions & 0 deletions packaging/components.nix
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,11 @@ in
*/
nix-json-schema-checks = callPackage ../src/json-schema-checks/package.nix { };

/**
Kaitai struct schema validation checks
*/
nix-kaitai-struct-checks = callPackage ../src/kaitai-struct-checks/package.nix { };

nix-perl-bindings = callPackage ../src/perl/package.nix { };

/**
Expand Down
4 changes: 3 additions & 1 deletion packaging/dev-shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
++ pkgs.nixComponents2.nix-external-api-docs.nativeBuildInputs
++ pkgs.nixComponents2.nix-functional-tests.externalNativeBuildInputs
++ pkgs.nixComponents2.nix-json-schema-checks.externalNativeBuildInputs
++ pkgs.nixComponents2.nix-kaitai-struct-checks.externalNativeBuildInputs
++ lib.optional (
!buildCanExecuteHost
# Hack around https://github.com/nixos/nixpkgs/commit/bf7ad8cfbfa102a90463433e2c5027573b462479
Expand Down Expand Up @@ -148,6 +149,7 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
++ pkgs.nixComponents2.nix-expr.externalPropagatedBuildInputs
++ pkgs.nixComponents2.nix-cmd.buildInputs
++ lib.optionals havePerl pkgs.nixComponents2.nix-perl-bindings.externalBuildInputs
++ lib.optional havePerl pkgs.perl;
++ lib.optional havePerl pkgs.perl
++ pkgs.nixComponents2.nix-kaitai-struct-checks.externalBuildInputs;
}
)
1 change: 1 addition & 0 deletions packaging/hydra.nix
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ let
"nix-cli"
"nix-functional-tests"
"nix-json-schema-checks"
"nix-kaitai-struct-checks"
]
++ lib.optionals enableBindings [
"nix-perl-bindings"
Expand Down
1 change: 1 addition & 0 deletions src/kaitai-struct-checks/.version
77 changes: 77 additions & 0 deletions src/kaitai-struct-checks/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Run with:
# meson test --suite kaitai-struct
# Run with: (without shell / configure)
# nix build .#nix-kaitai-struct-checks

project(
'nix-kaitai-struct-checks',
'cpp',
version : files('.version'),
default_options : [
'cpp_std=c++23',
# TODO(Qyriad): increase the warning level
'warning_level=1',
'errorlogs=true', # Please print logs for tests that fail
],
meson_version : '>= 1.1',
license : 'LGPL-2.1-or-later',
)

kaitai_runtime_dep = dependency('kaitai-struct-cpp-stl-runtime', required : true)
gtest_dep = dependency('gtest')
gtest_main_dep = dependency('gtest_main', required : true)

# Find the Kaitai Struct compiler
ksc = find_program('ksc', required : true)

kaitai_generated_srcs = custom_target(
'kaitai-generated-sources',
input : [ 'nar.ksy' ],
output : [ 'nix_nar.cpp', 'nix_nar.h' ],
command : [
ksc,
'@INPUT@',
'--target', 'cpp_stl',
'--outdir',
meson.current_build_dir(),
],
)

nar_kaitai_lib = library(
'nix-nar-kaitai-lib',
kaitai_generated_srcs,
dependencies : [ kaitai_runtime_dep ],
install : true,
)

nar_kaitai_dep = declare_dependency(
link_with : nar_kaitai_lib,
sources : kaitai_generated_srcs[1],
)

# The nar directory is a committed symlink to the actual nars location
nars_dir = meson.current_source_dir() / 'nars'

# Get all example files
nars = [
'dot.nar',
]

test_deps = [
nar_kaitai_dep,
kaitai_runtime_dep,
gtest_main_dep,
]

this_exe = executable(
meson.project_name(),
'test-parse-nar.cc',
dependencies : test_deps,
)

test(
meson.project_name(),
this_exe,
env : [ 'NIX_NARS_DIR=' + nars_dir ],
protocol : 'gtest',
)
1 change: 1 addition & 0 deletions src/kaitai-struct-checks/nar.ksy
1 change: 1 addition & 0 deletions src/kaitai-struct-checks/nars
1 change: 1 addition & 0 deletions src/kaitai-struct-checks/nix-meson-build-support
Loading
Loading