stree 0.1.0

A directory hierarchy mapping format, similar to mtree
Documentation
STree Specification
===================

Encoding
--------
- File paths and [field]#field-syntax values must be encoded according the rules outlined in [percent encoding]#percent-encoding
- The document **MUST** use US-ASCII/UTF-8 encoding.
- Anywhere in this specification that 'space' is used, this is referring strictly to the ASCII
  space character (decimal `32`, hex `20`,  Unicode codepoint `U+0020`), not
  a horizontal/vertical tab, form feed, carriage return or line feed.
- Anywhere in this specification that 'newline' is used, this is referring strictly to the ASCII
  line feed character (decimal `10`, hex `0A`, Unicode codepoint `U+000A`) **ONLY**.
- All lines must be terminated with line feeds (`\n`), carriage returns are not supported.

Percent Encoding
----------------
The following characters present in [field](#field-syntax) values and paths must be 'escaped'
using the following conversion rules:

| Character          | Replacement |
|--------------------|-------------|
| `=`                | `%3D`       |
| [space]#encoding | `%20`       |
| `%`                | `%25`       |

Manifest Anatomy
----------------
An STree manifest consists of:

- The first line, beginning with `#stree`, followed by a single [space]#encoding character and
  an integer denoting the number of seconds elapsed since the [Unix epoch]https://en.wikipedia.org/wiki/Unix_time at the time of capturing.
- The second line, referring to the *top*/*base* of the tree which **MUST** be represented as `.`
- A sequence of lines, each beginning with the path to the file (from the *top* of the tree), including
  **AT LEAST** the [base fields]#base-fields and any number of the [optional fields]#optional-fields.
  All of which must be separated by [space]#encoding characters. The line/entry (as with all lines) must
  be terminated by a [newline]#encoding.

Field syntax
------------
Every field consists of its' key and value separated by an `=` (equal sign), with no spacing of any kind between
the key, equal sign and value.
```
type=directory
device=7:0:7
```

Base Fields
-----------
These fields **MUST** be present in every document

| Field name    | Value format                                                                                                                                                  |
|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type`        | One of the [file types]#file-types                                                                                                                          |
| `mode`        | Exactly four digits, denoting the file mode in octal notation (e.g.: `0755`)                                                                                  |
| `user`        | The owning user account name and UID, separated by a colon (e.g.: `user:1000`). A sentinel value of `<unknown>` may be used for unresolvable users            |
| `group`       | The owning group name and GID, separated by a colon (e.g.: `users:99`). A sentinel value of `<unknown>` may be used for unresolvable groups                   |
| `time.modify` | The time of the file's last modification in [time notation]#time-notation. "modification" meaning the files' content, not its metadata as in `time.change` |
| `size`        | Only required for `type=regular` entries; The file size in bytes                                                                                              |

Optional Fields
---------------
These fields are completely optional

| Field name          | Value format                                                                                                                                                                                                      |
|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `inode`             | A 64-bit unsigned integer denoting the file's inode number                                                                                                                                                        |
| `nlink`             | A 64-bit unsigned integer denoting the amount of hard links to a file                                                                                                                                             |
| `time.access`       | The time of the file's last access in [time notation]#time-notation                                                                                                                                             |
| `time.change`       | The time of the file's last change, in [time notation]#time-notation                                                                                                                                            |
| `time.birth`        | The time of the file's creation in [time notation]#time-notation                                                                                                                                                |
| `device`            | Three unsigned integers denoting (in order) the combined major and minor identifiers, the major and the minor, separated by colons (e.g.: `7:0:7`)                                                                |
| `device.special`    | Only applicable to entries with `type=block`/`type=character`; Three unsigned integers denoting (in order) the combined major and minor identifiers, the major and the minor, separated by colons (e.g.: `7:0:7`) |
| `hash.<algorithm>`  | Only applicable to entries with `type=regular`; The hash of the file produced by [`<algorithm>`]#checksum-algorithms                                                                                            |
| `link.target`       | Only applicable to entries with `type=link`; The target which the symlink points to, regardless of whether the target exists or not                                                                               |
| `link.real`         | Only applicable to entries with `type=link`; The path a symlink resolves to after following any number of redirections. A sentinel value of `<broken>` may be used when the resolved destination does not exist.  |

Extension Fields
----------------
Unlike [optional fields](#optional-fields), these may be ignored by an implementations lacking the capability
to support them when parsing manifests and validating trees, and **SHOULD NOT** be relied upon for integrity.

| Field name      | Value format                                                                                                                                                                                                                   |
|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `mime.type`     | The mime type of the file, as produced by [`libmagic`]https://github.com/cdd/libmagic (e.g.: `inode/symlink`, `text/plain`). A sentinel value of `<unknown>` may be used if the information cannot be obtained from libmagic |
| `mime.encoding` | The mime encoding of the file, as produced by [`libmagic`]https://github.com/cdd/libmagic (e.g.: `binary`, `us-ascii`). A sentinel value of `<unknown>` may be used if the information cannot be obtained from libmagic      |

File Types
----------
- `regular`
- `directory`
- `link`
- `socket`
- `fifo`
- `block`
- `character`

Time Notation
-------------

Times are represented as two 64-bit unsigned integers, one for the number of seconds,
and one for nanoseconds. Due to the nature of the return values provided by the C `stat` family
of functions (with seconds being signed and nanoseconds being 32 *or* 64-bit unsigned integer), the value(s)
should be converted to unsigned 64-bit integers, clamping any negative value to `0`. Systems which
do not keep track of nanoseconds may simply use a value of `0` in its' slot.

If your usage would require validation of times before the Unix epoch, I suppose I apologize,
but it's just not a likely enough use case to bother with. Perhaps mtree would be a better solution
for you.

Times are written in the format `<seconds>:<nanoseconds>`, e.g.: `1791133798:884526192`.

Checksum Algorithms
-------------------
- `sha256`
- `sha512`
- `sha3-256`
- `sha3-512`
- `blake2b`
- `blake2s`
- `blake3`
- `crc32`
- `crc32c`
 
Comparison
----------

All fields which are present **MUST** be used when comparing/validating a tree with the exception of
- `time.access`: affected by the actions needed to produce the `STree` format.
- `mime.type`, `mime.encoding`: Extension fields, which are only required to validate if
  the implementation is capable of supporting them.