Entries
An Entry is a canonical-CBOR map describing one directory member. The
member's unix metadata (the recorded subset of lstat, §7.2) lives here,
in the parent — content blocks are pure content:
a.txt and b.txt hold the same bytes with a different mode/owner ⇒ still
one content block. Every stat field lives in the parent, so:
- listing with metadata = 1 block read
chmod= rewrite this dir + spine, content untouched- dedupe = by content alone, metadata never interferes
#7.1 Type
Required field t — one of:
t | member type |
|---|---|
"f" | regular file |
"d" | directory |
"l" | symbolic link |
"c" | character device |
"b" | block device |
"p" | fifo |
"s" | socket |
#7.2 Stat fields
All optional, and valid on every type except as noted (ino is invalid on
"d"). There is no default-omission magic: an absent field means "not
recorded", never an implied default value. Writers MUST NOT invent
defaults; readers MUST preserve the distinction between absent and any
concrete value.
The recorded set is a deliberate subset of POSIX stat: access and
status-change times (atime, ctime), birth time, the containing
filesystem's device number, and link counts are volatile or derived and
are not recorded.
| field | type | rule |
|---|---|---|
m | integer | permission bits, 0 … 0o7777 (type bits live in t). |
mt | integer | mtime seconds; MAY be negative; §2.3 range. |
mtn | integer | mtime nanoseconds, [1, 10^9) on the wire. Zero nanoseconds is canonically absent: writers MUST omit the field when 0, and readers MUST reject an encoded 0 (one value, one encoding). MUST NOT appear without mt. |
uid, gid | integer | ≥ 0, ≤ 2^32 − 1. |
un, gn | text | owner/group names (tar style), ≤ 256 UTF-8 bytes each, no U+0000. |
x | map | xattrs: name (text, 1–255 UTF-8 bytes, no NUL) → byte string ≤ 65536 bytes. Binary-safe. |
ino | integer | ≥ 0; hardlink group id (see below). Invalid on "d" — POSIX forbids hardlinked directories. |
Sub-second times. mt/mtn follow the POSIX timespec convention:
mt is the floor of the timestamp in whole seconds and mtn the
non-negative nanosecond remainder — a time of −0.5 s is
mt: −1, mtn: 500000000.
Hardlink groups. The scope of an ino group is a single root's tree:
entries belong to one group when they share an ino value and are
reachable within the same tree. ino values carry no meaning across
roots or stores. All entries of one group MUST be identical maps — same
type, content (sz/d/r), mode, ownership, times, xattrs — differing
only in the name and directory that reach them: they are one inode, and an
inode has one stat. The link count is the group size, computed by
consumers, never stored. This is a whole-tree invariant, deliberately not
checkable block-locally (a shared subtree cannot see its siblings), so it
is checked only at materialization or full-tree validation. Consumers
MUST treat a violation as an error — never resolve it by picking one
variant.
#7.3 Per-type fields
"f"—sz(integer ≥ 0) REQUIRED. The content is exactly one of:d: inline bytes, withlength(d) == sz— used when the whole file is one chunk of length ≤ the inline threshold (§9);r: a Ref — raw-codec when the file is a single chunk (the referenced block's length MUST equalsz), dag-cbor-codec pointing at an"f"node (whose ownszMUST equal the entry'ssz) when multi-chunk;- neither
dnorr, if and only ifsz == 0(the empty file).
dandrtogether MUST be rejected."d"—rREQUIRED: a dag-cbor-codec Ref to a"d"node."l"—tgtREQUIRED: the link target, text, 1–4096 UTF-8 bytes, no U+0000. No block. A writer ingesting a native symlink whose target is not valid UTF-8 MUST reject it (§6); a consumer materializingtgtMUST treat it as an untrusted path (§16)."c","b"—rdevREQUIRED:[major, minor], a 2-element array of integers ≥ 0. No block."p","s"— no extra fields. No block.
A field that is not a stat field (§7.2) and not valid for the entry's type
(e.g. tgt on a file, sz on a symlink, any unknown key) MUST be rejected.