fs

package
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Jun 9, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package fs provides filesystem helpers for the agent-memory project:

  • Atomic writes (atomic.go): write a file such that readers always see either the pre-write or post-write contents, never a partial state.
  • Path validation (paths.go): refuse paths that escape the .agent-memory/ root or target server-managed derived files.

See docs/patterns/atomic-writes.md and docs/patterns/path-validation.md.

Example (WriteAtomic)

Example_writeAtomic shows the canonical use of WriteAtomic.

dir, _ := os.MkdirTemp("", "example-")
defer os.RemoveAll(dir)

path := filepath.Join(dir, "hello.txt")
if err := WriteAtomic(path, []byte("hello, world\n"), 0644); err != nil {
	fmt.Println("error:", err)
	return
}
got, _ := os.ReadFile(path)
fmt.Print(string(got))
Output:
hello, world

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrSymlinkNotAllowed = errors.New("symlink not allowed in store")

ErrSymlinkNotAllowed is returned by CopyDirValidated when it encounters a symlink anywhere in the source tree. Synced external stores must not contain symlinks — they could escape the store root or point at host files — so we reject rather than follow them.

Functions

func CopyDirValidated added in v0.5.0

func CopyDirValidated(src, dst string) error

CopyDirValidated copies the directory tree at src to dst, enforcing a sandbox suitable for untrusted external content (a referenced store being synced):

  • symlinks are rejected and never followed (ErrSymlinkNotAllowed);
  • only directories and regular files are copied (devices/pipes/sockets are rejected);
  • every destination path is verified to stay under dst.

dst must not already exist; its parent must exist.

func IsDerivedPath

func IsDerivedPath(rel string) bool

IsDerivedPath reports whether rel refers to a server-managed derived file that agents must never write through ValidateMemoryPath.

rel is expected to use forward slashes (the canonical form used in the design doc and manifest examples). Callers that have OS-separator paths should pass them through filepath.ToSlash first.

The list:

  • meta/index.sqlite and its WAL/SHM companions (the FTS5 shadow index).
  • meta/lock (the advisory-lock file).

Files that are server-interpreted but stored as canonical Markdown or YAML (manifest.yaml, schema.yaml, index.md) are NOT derived in this sense — agents reach them through the normal staged/applied write machinery.

func PathExists

func PathExists(path string) bool

PathExists is a small convenience used by callers and tests.

func SwapDir added in v0.5.0

func SwapDir(staging, dest string) error

SwapDir replaces dest with staging via a two-step rename (both must be on the same filesystem). Windows-safe: a rename onto an existing directory fails there, so the existing dest is moved aside first and removed only after the swap succeeds; on failure the original is restored best-effort.

This is NOT fully atomic: there is a brief window between the two renames where dest is absent. A concurrent reader tolerates this — PR5's fetch reads cached files directly and treats a transiently-missing/half-swapped file as a read error (that section is omitted), so no shared lock is required.

func ValidateMemoryPath

func ValidateMemoryPath(root, rel string) (string, error)

ValidateMemoryPath validates a relative path intended to reference content inside the .agent-memory/ directory of a repository.

  • root must be the absolute path to .agent-memory/.
  • rel is a path relative to root. It may use either forward slashes or the OS path separator; the result uses the OS separator.

On success, returns the cleaned absolute path inside root.

Rejects:

  • non-absolute root (programmer error).
  • absolute rel paths.
  • rel paths containing ".." segments (including those that Clean resolves to leaving rel above root).
  • rel paths that target server-managed derived files (see IsDerivedPath).

Symlink resolution is intentionally not performed here; callers that dereference symlinks should re-validate the resolved path before any filesystem access.

func WriteAtomic

func WriteAtomic(path string, data []byte, perm fs.FileMode) error

WriteAtomic writes data to path atomically using the canonical write-temp-then-rename pattern:

  1. Create a temp file in the same directory as path (cross-device renames are not atomic on POSIX).
  2. Write all of data, then fsync the temp file.
  3. Rename the temp file to path. Both NTFS and POSIX guarantee rename is atomic when source and destination are on the same filesystem.
  4. On POSIX, fsync the parent directory so the rename survives power loss. Windows skips this step (NTFS handles rename durability internally and directory handles do not support Sync there).

Readers of path always see either the previous contents or the new contents, never a partial write. If any step fails, the temp file is removed and the original target (if any) is left untouched.

Constraints:

  • path must be absolute.
  • The parent directory must exist.
  • perm is applied to both the temp file (briefly) and the final file.

Types

This section is empty.

Jump to

Keyboard shortcuts

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