confinedfs

package
v0.40.0 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package confinedfs provides held-root, root-relative filesystem operations. It deliberately accepts only portable relative paths and plain directories or regular files at governed boundaries.

Index

Constants

This section is empty.

Variables

View Source
var ErrOutputLockBusy = errors.New("output lock busy")

ErrOutputLockBusy is matched by OutputLockBusyError when another process or transaction already owns the lock for the same held workspace and output root. Acquisition is deliberately non-blocking.

Functions

func OutputLockRootsEqual

func OutputLockRootsEqual(left, right string) (bool, error)

OutputLockRootsEqual reports whether two portable output-root spellings map to the same platform lock identity. Recovery admission must use this same comparison so a filesystem alias cannot acquire the shared lock and then bypass the journal belonging to that physical output.

func ValidatePortablePath

func ValidatePortablePath(value string) (string, error)

ValidatePortablePath returns the canonical spelling of a slash-separated relative path that is safe on every supported platform.

Types

type AtomicWriteResult

type AtomicWriteResult struct {
	Installed           bool
	FileSynced          bool
	DirectorySynced     bool
	PermissionsVerified bool
}

AtomicWriteResult distinguishes a pre-install failure from a failure after rename. DirectorySynced is false without error on platforms/filesystems that cannot durably flush directory metadata.

type Error

type Error struct {
	Code      ErrorCode
	Op        string
	Path      string
	Message   string
	Err       error
	Installed bool
}

Error is returned for every package-level validation or I/O failure. Installed is set only when an atomic write completed its rename before a later durability or root-identity check failed.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap exposes the underlying operating-system error.

type ErrorCode

type ErrorCode string

ErrorCode classifies a fail-closed confined filesystem decision.

const (
	ErrInvalidPath         ErrorCode = "invalid_path"
	ErrUnsafeEntry         ErrorCode = "unsafe_entry"
	ErrRootChanged         ErrorCode = "root_identity_changed"
	ErrIdentityUnsupported ErrorCode = "identity_unsupported"
	ErrClosed              ErrorCode = "root_closed"
	ErrIO                  ErrorCode = "io"
)

type Identity

type Identity struct {
	Scheme string `json:"scheme"`
	Volume uint64 `json:"volume"`
	File   uint64 `json:"file"`
}

Identity is a same-filesystem identity for a directory or regular file while a surrounding operation still holds and revalidates the object. Filesystems may reuse it after handle close, so it is neither a durable journal key nor an authorization credential.

func (Identity) String

func (i Identity) String() string

String returns a deterministic diagnostic representation. It must not be used as a durable lock, journal, or authorization identity on its own.

func (Identity) Valid

func (i Identity) Valid() bool

Valid reports whether the identity was populated by a supported platform.

type OutputLock

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

OutputLock owns one operating-system advisory lock. Lock files are retained after release: unlinking a lock file can split future contenders across different file objects while an existing owner still holds the old one.

func (*OutputLock) Borrow

func (l *OutputLock) Borrow(transaction *Transaction, expectedOutputRoot string) (*OutputLockLease, error)

Borrow proves that transaction belongs to the same held workspace object and expected output root as this still-live lock. The lease holds a read borrow on the lock until Close, so concurrent Release cannot invalidate an authorization between verification and consumption.

func (*OutputLock) Close

func (l *OutputLock) Close() error

Close is an alias for Release and is also idempotent.

func (*OutputLock) OutputRoot

func (l *OutputLock) OutputRoot() string

OutputRoot returns the canonical portable output root guarded by the lock.

func (*OutputLock) Release

func (l *OutputLock) Release() error

Release unlocks and closes the underlying file handle. It is safe to call concurrently or more than once; every call observes the same result.

type OutputLockBusyError

type OutputLockBusyError struct {
	OutputRoot string
	LockPath   string
}

OutputLockBusyError reports non-blocking lock contention without collapsing it into an I/O failure.

func (*OutputLockBusyError) Error

func (e *OutputLockBusyError) Error() string

func (*OutputLockBusyError) Is

func (e *OutputLockBusyError) Is(target error) bool

Is permits errors.Is(err, ErrOutputLockBusy).

type OutputLockLease

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

OutputLockLease is a copy-safe proof that one exact held transaction still owns the advisory lock for one output root. Copies share close state; closing any copy invalidates all of them and unblocks OutputLock.Release.

func (*OutputLockLease) Close

func (l *OutputLockLease) Close() error

Close releases only the in-process borrow. The caller that acquired the OutputLock still owns and must release the operating-system advisory lock.

func (*OutputLockLease) OutputRoot

func (l *OutputLockLease) OutputRoot() string

func (*OutputLockLease) Verify

func (l *OutputLockLease) Verify(transaction *Transaction, expectedOutputRoot string) error

Verify re-proves the transaction path identity and exact output root while the underlying advisory lock remains borrowed.

type Root

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

Root owns one held os.Root for the lifetime of a governed filesystem operation. Relative Views borrow this root and never open a weaker path-based security boundary.

func Open

func Open(name string) (*Root, error)

Open validates and opens an existing plain-directory root. The before/open/ after identity checks detect replacement of the named root while it is opened. Every ancestor must also be a plain directory; Windows junctions are therefore rejected even when Go reports them as ModeIrregular rather than ModeSymlink.

func (*Root) BeginTransaction

func (r *Root) BeginTransaction() (*Transaction, error)

BeginTransaction borrows the held root until Transaction.Close. The named root must still identify the held directory when the borrow begins.

func (*Root) Close

func (r *Root) Close() error

Close releases the held root. Calling Close more than once is harmless.

func (*Root) Name

func (r *Root) Name() string

Name returns the absolute name originally passed to the held root.

func (*Root) RootIdentity

func (r *Root) RootIdentity() Identity

RootIdentity returns the platform identity captured from the held root.

func (*Root) VerifyPathIdentity

func (r *Root) VerifyPathIdentity() error

VerifyPathIdentity proves that the original root name still resolves through plain ancestors to the exact directory held by this Root. Operations remain confined to the held object even if this check fails; callers receive a typed failure rather than treating a renamed workspace as committed.

func (*Root) View

func (r *Root) View(prefix string) (View, error)

View returns a portable root-relative view. Prefix may be "." for the root itself. A View never owns or closes the Root.

type Transaction

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

Transaction is a borrowed, handle-relative mutation boundary beneath one held Root. It never exposes the underlying os.Root or converts governed operations back to the Root's original pathname. Close releases the borrow; it does not close the owning Root.

The named root may be checked explicitly with VerifyPathIdentity. Other methods deliberately continue to address the held directory object after a rename so rollback and cleanup cannot be redirected into a replacement at the old pathname. TryAcquireOutputLock provides the cross-process primitive, but callers must acquire it explicitly; other Transaction methods do not implicitly serialize an uncooperative writer inside the same held root.

func (*Transaction) Close

func (t *Transaction) Close() error

Close ends the transaction borrow. It is safe to call more than once.

func (*Transaction) CreatePrivateDirectory

func (t *Transaction) CreatePrivateDirectory(prefix string) (string, error)

CreatePrivateDirectory creates a unique 0700 directory directly beneath the held root. Prefix must be one portable path segment.

func (*Transaction) Exists

func (t *Transaction) Exists(relative string) (bool, os.FileInfo, error)

Exists reports whether a plain directory or regular file exists. Unsafe entries are errors rather than successful existence observations.

func (*Transaction) Lstat

func (t *Transaction) Lstat(relative string) (os.FileInfo, error)

Lstat observes a plain directory or regular file without following the final component. Parent components must be plain directories.

func (*Transaction) MkdirAll

func (t *Transaction) MkdirAll(relative string, mode os.FileMode) error

MkdirAll creates a portable directory chain beneath the held root and rejects links or non-directory components before and after each creation.

func (*Transaction) Name

func (t *Transaction) Name() string

Name returns the original absolute display name of the held root. It is never used as an operating-system input by Transaction methods.

func (*Transaction) ReadStable

func (t *Transaction) ReadStable(relative string) ([]byte, os.FileInfo, error)

ReadStable reads one plain regular file and proves that both its held handle and root-relative name remained bound to the same object for the read.

func (*Transaction) ReadStableBounded

func (t *Transaction) ReadStableBounded(relative string, maxBytes int64) ([]byte, os.FileInfo, error)

ReadStableBounded preserves ReadStable's held-file identity checks while rejecting files larger than maxBytes before reading their content. The read itself is also bounded if an uncooperative writer grows the file after open.

func (*Transaction) RemoveRegularFile

func (t *Transaction) RemoveRegularFile(relative string, expected os.FileInfo) error

RemoveRegularFile removes one previously observed regular entry. It never recursively removes a replacement directory. Callers serialize governed writers with the existing output lock; this observation is not a lock against arbitrary filesystem writers.

func (*Transaction) RemoveTree

func (t *Transaction) RemoveTree(relative string) error

RemoveTree removes a private plain tree beneath the held root. It never follows links and verifies each directory handle against its name before removing that name. The root itself (.) cannot be removed.

func (*Transaction) Rename

func (t *Transaction) Rename(oldRelative, newRelative string) (installed bool, returnErr error)

Rename moves one plain entry between two root-relative names. The destination must not exist, and the platform operation enforces that constraint atomically. installed is true once the confined rename succeeded, including when a later identity check fails. Rename finally re-proves that the original root pathname still names the held directory, so a caller that publishes with this rename observes a replaced root.

func (*Transaction) RenameHeld

func (t *Transaction) RenameHeld(oldRelative, newRelative string) (installed bool, returnErr error)

RenameHeld applies Rename's handle-relative, no-replace and entry identity guarantees but does not re-prove the root pathname. Multi-step transactions use it so journal, rollback and cleanup renames keep operating on the held directory after its pathname was replaced; such callers must perform their own explicit VerifyPathIdentity at the commit point.

func (*Transaction) SyncDirectory

func (t *Transaction) SyncDirectory(relative string) (supported bool, returnErr error)

SyncDirectory flushes one held root-relative directory after a metadata mutation. Supported is false, without weakening confinement, when the platform/filesystem cannot provide a directory durability primitive.

func (*Transaction) TryAcquireOutputLock

func (t *Transaction) TryAcquireOutputLock(outputRoot string) (*OutputLock, error)

TryAcquireOutputLock acquires an exclusive advisory lock for outputRoot without waiting. Its identity is derived from both the held workspace object and the canonical output root, so different output roots remain independent.

The persistent lock file lives beneath a fixed private workspace control directory, never beneath the output tree that an installer may swap.

func (*Transaction) VerifyPathIdentity

func (t *Transaction) VerifyPathIdentity() error

VerifyPathIdentity proves that the original pathname still identifies the held root. A failure does not weaken the handle boundary: callers can still use the transaction to perform rollback and cleanup before Close.

func (*Transaction) Walk

func (t *Transaction) Walk(relative string) ([]TreeEntry, error)

Walk returns a deterministic point-in-time traversal rooted at relative. It rejects symlinks, reparse-like irregular entries, devices, and sockets.

func (*Transaction) WriteFileExclusive

func (t *Transaction) WriteFileExclusive(relative string, data []byte, mode os.FileMode) error

WriteFileExclusive creates, writes, syncs, chmods, and closes one new plain regular file beneath an existing directory chain. It never truncates or replaces an existing entry.

func (*Transaction) WriteFileExclusiveStream

func (t *Transaction) WriteFileExclusiveStream(relative string, mode os.FileMode, write func(io.Writer) error) error

WriteFileExclusiveStream applies the same held-root and exclusive-file guarantees to streamed data, without buffering an archive in memory. The callback must return only after all encoders have been closed.

type TreeEntry

type TreeEntry struct {
	Path string
	Info os.FileInfo
}

TreeEntry is one point-in-time observation from Walk. Path is portable and relative to the held root. Info always describes a plain directory or plain regular file; links and irregular entries fail the walk closed.

type View

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

View is a portable relative prefix borrowed from one held Root.

func (View) Identity

func (v View) Identity(relative string) (Identity, error)

Identity returns a diagnostic, short-lived platform identity for a plain directory or regular file. The target handle is closed before return, so the value must not become a journal, CAS, or authorization identity.

func (View) Lstat

func (v View) Lstat(relative string) (os.FileInfo, error)

Lstat returns a point-in-time observation without following the final link. It is not a tree snapshot or authorization proof. All parent components must be plain directories while this operation holds its root lease.

func (View) Open

func (v View) Open(relative string) (*os.File, error)

Open opens a plain regular file for reading beneath the held Root.

func (View) Prefix

func (v View) Prefix() string

Prefix returns the portable root-relative prefix represented by the View.

func (View) Sub

func (v View) Sub(relative string) (View, error)

Sub returns another View beneath the same held Root.

func (View) WriteAtomic0600

func (v View) WriteAtomic0600(relative string, data []byte) (result AtomicWriteResult, returnErr error)

WriteAtomic0600 writes bytes through an exclusive temporary file, syncs and closes its handle, and replaces the target relative to one held parent. The parent must already exist. Until the transaction layer adds locking and CAS recovery, callers may use this only in a private staging tree without concurrent writers.

func (View) WriteAtomic0600NoReplace

func (v View) WriteAtomic0600NoReplace(relative string, data []byte) (result AtomicWriteResult, returnErr error)

WriteAtomic0600NoReplace publishes a fully written 0600 file through a same-parent hard link. The link is the atomic commit point and fails if the destination appeared concurrently; an existing file is never replaced.

Jump to

Keyboard shortcuts

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