safewrite

package
v0.3.3 Latest Latest
Warning

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

Go to latest
Published: Aug 31, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package safewrite is the single way this tool replaces a file on disk.

Every write -- the target's new content and the .bak holding its prior content alike -- is staged in a temp file beside the target, fsynced, optionally validated, and only then renamed into place, so a crash or an error partway through can never leave a truncated or half-written file. The containing directory is fsynced after the rename where the platform supports it, so the rename itself is durable and not just the bytes behind it. A target that already exists with different bytes is refused unless the caller passes Force -- a tool that authors files into a user's repository must not silently destroy the user's edits. Paths are confined to a caller-supplied root with symlinks resolved, so a symlink planted in a project can never redirect a write outside it.

CANARY: REQ=ENG-4330; FEATURE="SafeWrite"; ASPECT=Storage; STATUS=TESTED; TEST=TestWriteNewFile,TestWriteIdenticalNoop,TestWriteRefusesWithoutForce,TestWriteForceBacksUpAndPreservesMode,TestWriteFailureLeavesOriginal,TestConfineRejectsSymlinkEscape,TestConfineAcceptsPathInsideRoot; UPDATED=2026-08-30

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrWouldReplace reports that the target exists with different content
	// and Force was not set. Callers typically print a notice and skip.
	ErrWouldReplace = errors.New("target exists with different content; pass force")
	// ErrRootEscape reports that the target, with symlinks resolved, lies
	// outside the confinement root.
	ErrRootEscape = errors.New("path resolves outside root")
)

Functions

func Confine

func Confine(root, path string) (string, error)

Confine resolves symlinks in path and its parents and returns the resolved path, or ErrRootEscape when it does not lie within the resolved root. Components that do not exist yet are appended verbatim: a file about to be created cannot be resolved, but every existing directory leading to it can.

Note the ordering: filepath.Abs cleans the path lexically -- collapsing "." and ".." textually -- before any symlink is resolved. So "root/link/../x" is evaluated as "root/x", not as "<link's target>/../x". That is strictly more restrictive than resolving ".." against the link's real parent: it can reject a path that a symlink-first walk would have allowed inside the root, but it can never let one out of it, which is the only direction that matters for confinement.

Types

type Options

type Options struct {
	// Root confines the write. Required: an unset root is an error rather
	// than an implicit "anywhere".
	Root string
	// Force allows replacing an existing file whose bytes differ.
	Force bool
	// Backup writes the prior bytes to <path>.bak before replacing.
	Backup bool
	// Validate, when set, inspects the staged bytes before the rename. A
	// non-nil error aborts the write with the original file untouched.
	Validate func([]byte) error
}

Options configures Write.

type Result

type Result struct {
	// Written is false when the target already held exactly these bytes.
	Written bool
	// Replaced is true when an existing file's content was replaced.
	Replaced bool
	// BackupPath is the .bak file written, when Options.Backup asked for one.
	BackupPath string
	// Diff summarizes old-to-new line changes. It is set whenever an
	// existing file differs -- including on the ErrWouldReplace refusal, so
	// a caller can show what it declined to overwrite.
	Diff string
}

Result describes what Write did.

func Write

func Write(path string, data []byte, mode fs.FileMode, o Options) (Result, error)

Write replaces path's content with data, atomically and confined to Options.Root.

The target is left byte-identical on every failure path. When the target already exists its own permissions are preserved and mode is ignored; mode applies to newly created files only.

Jump to

Keyboard shortcuts

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