Skip to content

Commit 53b4ea6

Browse files
fzakariaEricson2314
authored andcommitted
Add documentation for NAR spec in kaitai
* Add a new flake check * Add unit tests * Add Kaitai spec * Updated documentation
1 parent 89fa8c0 commit 53b4ea6

File tree

17 files changed

+402
-4
lines changed

17 files changed

+402
-4
lines changed

doc/manual/source/SUMMARY.md.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125
- [Deriving Path](protocols/json/deriving-path.md)
126126
- [Serving Tarball Flakes](protocols/tarball-fetcher.md)
127127
- [Store Path Specification](protocols/store-path.md)
128-
- [Nix Archive (NAR) Format](protocols/nix-archive.md)
128+
- [Nix Archive (NAR) Format](protocols/nix-archive/index.md)
129129
- [Derivation "ATerm" file format](protocols/derivation-aterm.md)
130130
- [C API](c-api.md)
131131
- [Glossary](glossary.md)

doc/manual/source/protocols/nix-archive.md renamed to doc/manual/source/protocols/nix-archive/index.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,15 @@ The `str` function / parameterized rule is defined as follows:
4141
- `int(n)` = the 64-bit little endian representation of the number `n`
4242

4343
- `pad(s)` = the byte sequence `s`, padded with 0s to a multiple of 8 byte
44+
45+
## Kaitai Struct Specification
46+
47+
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.
48+
49+
> 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).
50+
51+
```yaml
52+
{{#include nar.ksy}}
53+
```
54+
55+
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.
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
meta:
2+
id: nix_nar
3+
title: Nix Archive (NAR)
4+
file-extension: nar
5+
endian: le
6+
doc: |
7+
Nix Archive (NAR) format. A simple, reproducible binary archive
8+
format used by the Nix package manager to serialize file system objects.
9+
doc-ref: 'https://nixos.org/manual/nix/stable/command-ref/nix-store.html#nar-format'
10+
11+
seq:
12+
- id: magic
13+
type: padded_str
14+
doc: "Magic string, must be 'nix-archive-1'."
15+
valid:
16+
expr: _.body == 'nix-archive-1'
17+
- id: root_node
18+
type: node
19+
doc: "The root of the archive, which is always a single node."
20+
21+
types:
22+
padded_str:
23+
doc: |
24+
A string, prefixed with its length (u8le) and
25+
padded with null bytes to the next 8-byte boundary.
26+
seq:
27+
- id: len_str
28+
type: u8
29+
- id: body
30+
type: str
31+
size: len_str
32+
encoding: 'ascii'
33+
- id: padding
34+
size: (8 - (len_str % 8)) % 8
35+
36+
node:
37+
doc: "A single filesystem node (file, directory, or symlink)."
38+
seq:
39+
- id: open_paren
40+
type: padded_str
41+
doc: "Must be '(', a token starting the node definition."
42+
valid:
43+
expr: _.body == '('
44+
- id: type_key
45+
type: padded_str
46+
doc: "Must be 'type'."
47+
valid:
48+
expr: _.body == 'type'
49+
- id: type_val
50+
type: padded_str
51+
doc: "The type of the node: 'regular', 'directory', or 'symlink'."
52+
- id: body
53+
type:
54+
switch-on: type_val.body
55+
cases:
56+
"'directory'": type_directory
57+
"'regular'": type_regular
58+
"'symlink'": type_symlink
59+
- id: close_paren
60+
type: padded_str
61+
valid:
62+
expr: _.body == ')'
63+
if: "type_val.body != 'directory'"
64+
doc: "Must be ')', a token ending the node definition."
65+
66+
type_directory:
67+
doc: "A directory node, containing a list of entries. Entries must be ordered by their names."
68+
seq:
69+
- id: entries
70+
type: dir_entry
71+
repeat: until
72+
repeat-until: _.kind.body == ')'
73+
types:
74+
dir_entry:
75+
doc: "A single entry within a directory, or a terminator."
76+
seq:
77+
- id: kind
78+
type: padded_str
79+
valid:
80+
expr: _.body == 'entry' or _.body == ')'
81+
doc: "Must be 'entry' (for a child node) or '' (for terminator)."
82+
- id: open_paren
83+
type: padded_str
84+
valid:
85+
expr: _.body == '('
86+
if: 'kind.body == "entry"'
87+
- id: name_key
88+
type: padded_str
89+
valid:
90+
expr: _.body == 'name'
91+
if: 'kind.body == "entry"'
92+
- id: name
93+
type: padded_str
94+
if: 'kind.body == "entry"'
95+
- id: node_key
96+
type: padded_str
97+
valid:
98+
expr: _.body == 'node'
99+
if: 'kind.body == "entry"'
100+
- id: node
101+
type: node
102+
if: 'kind.body == "entry"'
103+
doc: "The child node, present only if kind is 'entry'."
104+
- id: close_paren
105+
type: padded_str
106+
valid:
107+
expr: _.body == ')'
108+
if: 'kind.body == "entry"'
109+
instances:
110+
is_terminator:
111+
value: kind.body == ')'
112+
113+
type_regular:
114+
doc: "A regular file node."
115+
seq:
116+
# Read attributes (like 'executable') until we hit 'contents'
117+
- id: attributes
118+
type: reg_attribute
119+
repeat: until
120+
repeat-until: _.key.body == "contents"
121+
# After the 'contents' token, read the file data
122+
- id: file_data
123+
type: file_content
124+
instances:
125+
is_executable:
126+
value: 'attributes[0].key.body == "executable"'
127+
doc: "True if the file has the 'executable' attribute."
128+
types:
129+
reg_attribute:
130+
doc: "An attribute of the file, e.g., 'executable' or 'contents'."
131+
seq:
132+
- id: key
133+
type: padded_str
134+
doc: "Attribute key, e.g., 'executable' or 'contents'."
135+
valid:
136+
expr: _.body == 'executable' or _.body == 'contents'
137+
- id: value
138+
type: padded_str
139+
if: 'key.body == "executable"'
140+
valid:
141+
expr: _.body == ''
142+
doc: "Must be '' if key is 'executable'."
143+
file_content:
144+
doc: "The raw data of the file, prefixed by length."
145+
seq:
146+
- id: len_contents
147+
type: u8
148+
# # This relies on the property of instances that they are lazily evaluated and cached.
149+
- size: 0
150+
if: nar_offset < 0
151+
- id: contents
152+
size: len_contents
153+
- id: padding
154+
size: (8 - (len_contents % 8)) % 8
155+
instances:
156+
nar_offset:
157+
value: _io.pos
158+
159+
type_symlink:
160+
doc: "A symbolic link node."
161+
seq:
162+
- id: target_key
163+
type: padded_str
164+
doc: "Must be 'target'."
165+
valid:
166+
expr: _.body == 'target'
167+
- id: target_val
168+
type: padded_str
169+
doc: "The destination path of the symlink."

doc/manual/source/store/file-system-object/content-address.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ be many different serialisations.
4646
For these reasons, Nix has its very own archive format—the Nix Archive (NAR) format,
4747
which is carefully designed to avoid the problems described above.
4848

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

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

flake.nix

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,10 @@
417417
supportsCross = false;
418418
};
419419

420+
"nix-kaitai-struct-checks" = {
421+
supportsCross = false;
422+
};
423+
420424
"nix-perl-bindings" = {
421425
supportsCross = false;
422426
};

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,4 @@ if get_option('unit-tests')
6161
endif
6262
subproject('nix-functional-tests')
6363
subproject('json-schema-checks')
64+
subproject('kaitai-struct-checks')

packaging/components.nix

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,11 @@ in
443443
*/
444444
nix-json-schema-checks = callPackage ../src/json-schema-checks/package.nix { };
445445

446+
/**
447+
Kaitai struct schema validation checks
448+
*/
449+
nix-kaitai-struct-checks = callPackage ../src/kaitai-struct-checks/package.nix { };
450+
446451
nix-perl-bindings = callPackage ../src/perl/package.nix { };
447452

448453
/**

packaging/dev-shell.nix

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
109109
++ pkgs.nixComponents2.nix-external-api-docs.nativeBuildInputs
110110
++ pkgs.nixComponents2.nix-functional-tests.externalNativeBuildInputs
111111
++ pkgs.nixComponents2.nix-json-schema-checks.externalNativeBuildInputs
112+
++ pkgs.nixComponents2.nix-kaitai-struct-checks.externalNativeBuildInputs
112113
++ lib.optional (
113114
!buildCanExecuteHost
114115
# Hack around https://github.com/nixos/nixpkgs/commit/bf7ad8cfbfa102a90463433e2c5027573b462479
@@ -148,6 +149,7 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
148149
++ pkgs.nixComponents2.nix-expr.externalPropagatedBuildInputs
149150
++ pkgs.nixComponents2.nix-cmd.buildInputs
150151
++ lib.optionals havePerl pkgs.nixComponents2.nix-perl-bindings.externalBuildInputs
151-
++ lib.optional havePerl pkgs.perl;
152+
++ lib.optional havePerl pkgs.perl
153+
++ pkgs.nixComponents2.nix-kaitai-struct-checks.externalBuildInputs;
152154
}
153155
)

packaging/hydra.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ let
6363
"nix-cli"
6464
"nix-functional-tests"
6565
"nix-json-schema-checks"
66+
"nix-kaitai-struct-checks"
6667
]
6768
++ lib.optionals enableBindings [
6869
"nix-perl-bindings"

src/kaitai-struct-checks/.version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../.version

0 commit comments

Comments
 (0)