Help us shape the future of Go! Take the 2026 Go Developer Survey

atomicfile

package
v1.36.5-stratum.0 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package atomicfile is stratum's one durable single-file writer: a file is written to a unique hidden temp file in its own directory, labelled for its destination, fsynced at its final mode, renamed into place, and the directory is fsynced so the rename survives a power loss. WriteFile replaces whatever is at path; CreateExclusive refuses to. Both report how a failure left the filesystem through ErrNotDurable and ErrStrayCopy.

The package is a leaf: it may import internal/selinux, and nothing that imports internal/host.

Index

Constants

This section is empty.

Variables

View Source
var ErrNotDurable = errors.New("the file was replaced, but the replacement could not be made durable")

ErrNotDurable matches a WriteFile whose rename already put the new file at path, but whose directory sync failed: the new file is in place, and a power loss could still bring back the old one.

View Source
var ErrStrayCopy = errors.New("the temporary name could not be removed, and still holds the file's contents")

ErrStrayCopy matches a write whose temp name could not be removed. After CreateExclusive's link, it is a second name for the created file; before the link, or before WriteFile's rename, it is a file holding all or part of the contents.

Functions

func CreateExclusive

func CreateExclusive(path string, data []byte, perm os.FileMode) (os.FileInfo, error)

CreateExclusive is WriteFile for a file that must never replace one already at path. The at-rest key is minted on an observation, made earlier, that no key exists; a rename would silently replace a key that appeared since -- one an operator put there, whose Secrets would then be unreadable. A hard link of the finished temp file is create-only: it fails with an error matching fs.ErrExist instead. The temp name is removed on every path but one, a removal that fails after the link (ErrStrayCopy, below), and once it is gone the directory is synced, so the new entry is durable before this returns.

It returns the created file's FileInfo once the link exists -- even when the directory sync then fails -- so a caller that has to take the file back off can remove exactly the inode this call created, and nothing that replaced it. After the link the temp name is a second name for that file, so if it cannot be removed the error matches ErrStrayCopy and names it: taking path back off would not remove what the file holds.

func CreateExclusiveWith

func CreateExclusiveWith(path string, data []byte, perm os.FileMode, seams Seams) (os.FileInfo, error)

CreateExclusiveWith is CreateExclusive with the caller's seams, matching WriteFileWith.

func SyncDir

func SyncDir(dir string) error

SyncDir fsyncs dir, so an entry added to or removed from it survives a power loss.

func WriteFile

func WriteFile(path string, data []byte, perm os.FileMode) error

WriteFile writes data to path by creating a temp file in the SAME directory, fsyncing it, then renaming it into place. The temp file is created 0600 and only chmod'ed to perm before the rename, so it is never readable at a wider mode than path will have, even mid-write. A same-directory rename is atomic on the same filesystem: whatever fails along the way — the write, the fsync, a crash mid-write — the file already at path is untouched until the rename itself, so a failed write can never leave a corrupt or truncated file in place of a working one. A symlink at path is replaced, not written through: the rename puts a regular file at path and leaves the link's target untouched, so a caller that must keep something at path special (a masked unit is a symlink to /dev/null) checks for it before writing, never after — the rename would silently undo it.

The files written here are labelled for their destination path while still under the temp name: a file renamed over its destination otherwise keeps the label of the temp name, and a label that cannot be set stops the write before the rename, so a file never reaches path with the wrong label.

The rename is then made durable by fsyncing the directory: a rename lives in the directory's entry, and until the directory is synced a power loss can undo it after this function has returned success, bringing back the old file that the caller has already acted on replacing.

A failure of that sync is returned matching ErrNotDurable: the new file is already at path. A failure before the rename whose temp file cannot be removed is returned matching ErrStrayCopy, naming the temp file.

func WriteFileWith

func WriteFileWith(path string, data []byte, perm os.FileMode, seams Seams) error

WriteFileWith is WriteFile with the caller's seams for the writer's filesystem side effects; nil fields mean the real implementation for that step. It exists for a delegating package whose own tests hold seams for these effects and keep pinning them through the delegation.

Types

type Seams

type Seams struct {
	// SyncFile is (*os.File).Sync, the file fsync before the rename.
	SyncFile func(*os.File) error
	// LabelLike gives the temp file the SELinux label of its destination.
	LabelLike func(tmp, dst string) error
	// RemoveTempName removes the temp file's name.
	RemoveTempName func(string) error
	// SyncDir fsyncs a directory.
	SyncDir func(string) error
}

Seams are the writer's filesystem side effects, overridable per call. A package that delegates here and keeps its own test seams for these effects hands them in with WriteFileWith and CreateExclusiveWith, so its tests keep pinning the same behavior through the delegation. Every nil field means the real implementation for that step.

Jump to

Keyboard shortcuts

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