Documentation
¶
Overview ¶
Package wire is the tree codec: a step's directory as a tar stream, packed and unpacked the way every transfer of one agrees on, symlinks kept and never followed, and nothing written outside the tree on the way in. Stdlib-only, so nothing it depends on can change what those bytes mean.
Index ¶
- Variables
- func PackPaths(w io.Writer, root string, names []string) error
- func PackTree(w io.Writer, root string) error
- func PackTreeAs(w io.Writer, dir, name string) error
- func UnpackFetchedTree(r io.Reader, root string, opts UnpackOptions) error
- func UnpackTree(r io.Reader, root string, opts UnpackOptions) error
- type UnpackOptions
Constants ¶
This section is empty.
Variables ¶
var ErrTreeTooLarge = errors.New("archive exceeds the unpack limit")
ErrTreeTooLarge is an archive past its UnpackOptions. The entry that crossed the limit is refused before any of its bytes are written; the caller owns discarding what landed before it, as with any failed unpack.
var ErrUnsafeLink = errors.New("archive symlink points outside the tree")
ErrUnsafeLink is a symlink whose target leaves the tree.
var ErrUnsafePath = errors.New("archive entry names a path outside the tree")
ErrUnsafePath is an archive entry naming a path outside the tree. os.Root would refuse it anyway; this refuses it earlier and says which entry, since "path escapes from parent" alone does not name the archive that carried it.
var ErrUnsupportedEntry = errors.New("cannot transfer a socket, fifo or device node")
ErrUnsupportedEntry is a socket, fifo or device node in a tree being packed. See PackTree: dropping it would change what digestTree computes over the extracted copy, and a cache that quietly disagrees with itself is worse than a step that refuses to ship one.
Functions ¶
func PackPaths ¶
PackPaths is PackTree restricted to the named top-level entries, each packed under its own name so it extracts back to the same place.
A nil or empty names packs everything. Naming them is what keeps a step's declared outputs from dragging its inputs home: a two-gigabyte checkout does not need to travel back to prove it did not change.
A named entry that does not exist is not an error here. A step that declared an output and produced nothing is a pipeline-level fact, reported where the outputs are checked, and failing the transfer instead would replace that message with a worse one.
func PackTree ¶
PackTree writes root's contents to w as a tar stream carrying exactly what digestTree hashes.
Symlinks are stored as symlinks, never followed: following one would put the host's own files on the far end of the wire, which is the same reason digestTree hashes a link's target text rather than its destination.
An entry that is neither a directory, a regular file nor a symlink — a socket, a fifo, a device node — is refused BY NAME rather than skipped. Skipping would drop a path digestTree counts, which changes the tree's digest, which poisons the step cache silently. Refusing is the honest failure, and in practice names a leftover .sock the step did not mean to declare as output.
func PackTreeAs ¶
PackTreeAs writes dir to w as ONE artifact called name: the directory itself as the entry `name/`, mode and all, then its contents under it.
It is how a fetch-all travels once the tree is one artifact rather than a bag of top-level entries. A get's resource directory is exactly that — the next step names the whole of it — and carrying the directory as an entry is what lets the worker file it under the same digest that step's offer will name: PackPaths over a copy of this tree, placed under name, produces this stream byte for byte.
func UnpackFetchedTree ¶
func UnpackFetchedTree(r io.Reader, root string, opts UnpackOptions) error
UnpackFetchedTree is UnpackTree for a tree another machine produced: a symlink whose target leaves the tree is refused (ErrUnsafeLink).
Separate from UnpackTree rather than the rule everywhere, because the digest contract is that a tree round-trips verbatim, escaping links and all — a checkout can legitimately hold one, and a step's tree sent OUT to a worker must arrive as it was. What comes BACK is different: the link's target names a path on this machine, and the copy that materializes the tree into a later step, and whatever that step reads or archives, would follow it. A worker does not get to name files here.
func UnpackTree ¶
func UnpackTree(r io.Reader, root string, opts UnpackOptions) error
UnpackTree extracts a PackTree stream into root, which must already exist.
Every path is opened through os.Root, so no entry can write outside root — including through a symlink an earlier entry in the same archive created, which is the shape workspace.rejectSymlinkSrc refuses on the way out and which has to be refused on the way in too, or the protection sits on the wrong side of the trust boundary.
Types ¶
type UnpackOptions ¶
type UnpackOptions struct {
// MaxBytes caps the regular-file bytes written, summed over the tree.
MaxBytes int64
// MaxEntries caps the entries of every kind: a tree of empty files costs
// inodes and no bytes.
MaxEntries int
}
UnpackOptions bound what one unpack may write. Zero means no limit, for a tree this machine packed itself; a tree from anywhere else gets FetchLimits.
func FetchLimits ¶
func FetchLimits() UnpackOptions
FetchLimits is what a tree fetched from a bucket or a worker may cost this machine. Generous on purpose — a step's real outputs (a build tree, a node_modules) must never meet them — so they stop a bucket writer or a worker filling the orchestrator's disk without second-guessing real use.