publish

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 23, 2026 License: AGPL-3.0, AGPL-3.0-or-later Imports: 15 Imported by: 0

Documentation

Overview

Package publish is the descriptor-rooted bounded reader and transactional output publisher for CEM artifacts.

Every path is repository-root-relative and validated before any directory is created; ancestors are walked with no-follow checks so an output can never escape its root through a symlinked directory, an absolute path, or a dot-dot segment. Two-output publication is transactional: if the second output cannot land, the first's exact prior file is restored by atomic rename (or the fresh file removed when none existed) and no temporary or backup residue remains.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Publish

func Publish(output Output) error

Publish atomically writes one output with owner-only permissions, refusing a symlink as the final path.

func PublishPair

func PublishPair(first, second Output) error

PublishPair publishes two outputs transactionally: both land, or neither does. Both outputs are fully staged before either final path is touched, the first's prior file is set aside by atomic rename so a failed pair restores the exact prior inode, and a restoration failure is reported — never swallowed. The two targets must be distinct files. A crash after the prior file is set aside but before the first output lands leaves only the backup; callers holding the output's Lock repair that with RecoverPairBackup before treating the first output as missing.

func PublishSecure

func PublishSecure(ctx context.Context, output Output, createParents bool) error

PublishSecure publishes through held directory descriptors. It rejects symlinks in every traversed component, locks the parent directory, syncs the file before promotion, and syncs the directory after the atomic rename.

func ReadBoundedFile

func ReadBoundedFile(path string, bound int, code string) ([]byte, error)

ReadBoundedFile reads one absolute-path regular file with the same bounds. The opened descriptor's identity must match the pre-open metadata, so a path swapped for a symlink between the checks reads nothing else, and all bytes flow through a bound-plus-one limiter so growth after the size check can never allocate beyond the bound.

Types

type Output

type Output struct {
	Root     *Root
	Relative string
	Data     []byte
}

Output names one root-relative publication target.

type Root

type Root struct {
	// contains filtered or unexported fields
}

Root is one validated publication root.

func OpenRoot

func OpenRoot(path string) (*Root, error)

OpenRoot validates one existing directory as a publication root.

func (*Root) Lock

func (r *Root) Lock(relative string) (func(), error)

Lock serializes one root-relative output's read-modify-write under an exclusive advisory lock on the sibling file "<output>.lock". A non-empty file at that path is refused, never adopted. The returned release removes the lock file before unlocking, but only while the path still names the held inode; a waiter that then acquires the unlinked inode sees it no longer names the path and retries. Expiry of the wait refuses with map-locked rather than risking a lost update.

func (*Root) Path

func (r *Root) Path() string

Path returns the root's resolved absolute path.

func (*Root) ReadBounded

func (r *Root) ReadBounded(relative string, bound int, code string) ([]byte, error)

ReadBounded reads one root-relative regular file without following a symlink at any component, rejecting content above bound bytes. An ancestor that is not a real directory is the caller's read refusal, not a publication one.

func (*Root) RecoverPairBackup

func (r *Root) RecoverPairBackup(relative string) error

RecoverPairBackup repairs the one crash window of PublishPair that loses the first output: its prior file was set aside as "<output>.bak-<hex>" and the process died before the new file landed. With the output missing, exactly one such backup is restored by atomic rename; several are refused because the intended prior file is ambiguous. A present output is left untouched. Callers must hold the output's Lock so an in-flight pair is never mistaken for an interrupted one.

func (*Root) RemoveValidatedPairBackup

func (r *Root) RemoveValidatedPairBackup(relative string) error

RemoveValidatedPairBackup removes the one recognized backup left when a pair publication crashed after its new first output landed. The caller must already have validated the present final output while holding its Lock. Multiple backups remain an ambiguous, fail-closed condition.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL