filesystem

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2026 License: BSD-3-Clause Imports: 3 Imported by: 0

README

go-filesystems/interface

filesystem (interface)

Go Reference CI

Shared, minimal filesystem interfaces used by the concrete filesystem implementations in this repository.

Module

github.com/go-filesystems/interface

Purpose

This package defines a small, stable contract that filesystem drivers implement so higher-level tools can operate on different filesystem images without depending on concrete types. The interface intentionally focuses on common file and directory operations needed by tooling and tests.

API (summary)

  • Filesystem — minimal filesystem API implemented by concrete packages:
type Filesystem interface {
	Close() error
	ReadFile(path string) ([]byte, error)
	ListDir(path string) ([]DirEntry, error)
	Stat(path string) (Stat, error)
	WriteFile(path string, data []byte, perm os.FileMode) error
	ReadLink(path string) (string, error)
	MkDir(path string, perm os.FileMode) error
	DeleteFile(path string) error
	DeleteDir(path string) error
	Rename(oldPath, newPath string) error
}
  • Labeller — optional interface for filesystems that expose a volume label. Implementations cap the label at their own length limit (ext2/3/4: 16 bytes; FAT: 11 bytes). Probe via type assertion; not part of Filesystem because some filesystems genuinely have no label concept or where label mutation is non-trivial.
type Labeller interface {
    Label() string
    SetLabel(label string) error
}

if l, ok := fs.(filesystem.Labeller); ok {
    l.SetLabel("rootfs")
}
  • LabelReader — optional read-only counterpart to Labeller, for drivers that can decode a volume label but can't yet rewrite it through their regular commit machinery. Every Labeller embeds this, so type-assert LabelReader when only reading is needed:
type LabelReader interface {
    Label() string
}
  • Symlinker — optional interface for creating symbolic links (ReadLink is already part of Filesystem; this gates the write side):
type Symlinker interface {
    Symlink(target, linkPath string) error
}
  • HardLinker — optional interface for POSIX hardlinks (directories cannot be hardlinked; implementations must reject that case):
type HardLinker interface {
    Link(oldPath, newPath string) error
}
  • MetadataSetter — optional interface bundling the POSIX metadata mutators (chmod / chown / utimes):
type MetadataSetter interface {
    Chmod(path string, perm os.FileMode) error
    Chown(path string, uid, gid uint32) error
    Chtimes(path string, atime, mtime time.Time) error
}
  • Truncater — optional interface for resizing a regular file in place (grow zero-fills, shrink drops trailing data):
type Truncater interface {
    Truncate(path string, newSize int64) error
}
  • Grower / Resizer — optional interfaces for changing a filesystem's on-disk size. Grower.GrowTo is grow-only; Resizer.Resize is the newer, uniform entry point that also handles shrink where the format allows it, returning the sentinel ErrShrinkUnsupported when it doesn't:
type Grower interface {
    GrowTo(newSizeBytes int64) error
}

type Resizer interface {
    Resize(newSize int64) error
}

var ErrShrinkUnsupported = errors.New("filesystem: shrink not supported")

if r, ok := fs.(filesystem.Resizer); ok {
    if err := r.Resize(newSize); errors.Is(err, filesystem.ErrShrinkUnsupported) {
        // driver only grows — caller decides how to handle
    }
}
  • DirEntry — accessor interface for directory entries:
type DirEntry interface {
	Inode() uint64
	Name() string
	FileType() uint8
}
  • Stat — file metadata accessor:
type Stat interface {
	Mode() uint16
	Size() uint64
	Inode() uint64
}

Constructors NewDirEntry(inode, name, fileType) and NewStat(mode,size,inode) are provided for convenience.

Implementations

Known implementations, across the go-filesystems org:

  • github.com/go-filesystems/apfs
  • github.com/go-filesystems/btrfs
  • github.com/go-filesystems/exfat
  • github.com/go-filesystems/ext4
  • github.com/go-filesystems/fat32
  • github.com/go-filesystems/ffs (re-export of ufs)
  • github.com/go-filesystems/hfsplus
  • github.com/go-filesystems/iso9660
  • github.com/go-filesystems/ntfs
  • github.com/go-filesystems/oci
  • github.com/go-filesystems/squashfs
  • github.com/go-filesystems/uefi
  • github.com/go-filesystems/ufs
  • github.com/go-filesystems/xfs
  • github.com/go-filesystems/zfs

See each implementor's README for format-specific details, examples, and which optional interfaces above it satisfies; or see go-filesystems.github.io/docs/drivers for a capability matrix. github.com/go-filesystems/detect composes over this interface too, as a type-probing dispatch registry rather than a driver.

Usage example

The interface can be used as a programming abstraction so callers accept a filesystem.Filesystem regardless of the concrete implementation:

import (
	filesystem "github.com/go-filesystems/interface"
	fsx "github.com/go-filesystems/xfs"
)

func example() error {
	f, err := fsx.Open("image.img", -1)
	if err != nil {
		return err
	}
	defer f.Close()

	// Use as the generic interface
	var fs filesystem.Filesystem = f
	data, err := fs.ReadFile("/hello.txt")
	if err != nil {
		return err
	}
	_ = data
	return nil
}

Notes

Keep the interface minimal; add helpers in implementor packages when format-specific functionality is required.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrShrinkUnsupported = errors.New("filesystem: shrink not supported")

ErrShrinkUnsupported is returned by Resizer.Resize when the driver cannot shrink its on-disk format and the caller passed a newSize smaller than the current size. Filesystems whose layout precludes shrink entirely (e.g. XFS, ZFS) return this sentinel rather than attempting a destructive resize.

Functions

This section is empty.

Types

type DirEntry

type DirEntry interface {
	Inode() uint64
	Name() string
	FileType() uint8
}

DirEntry describes a directory entry. Implementations must provide accessors for the inode number, name and file type.

func NewDirEntry

func NewDirEntry(inode uint64, name string, fileType uint8) DirEntry

NewDirEntry constructs a DirEntry implementation backed by an unexported struct. Returning the interface enforces encapsulation.

type Filesystem

type Filesystem interface {
	Close() error
	ReadFile(path string) ([]byte, error)
	ListDir(path string) ([]DirEntry, error)
	Stat(path string) (Stat, error)
	WriteFile(path string, data []byte, perm os.FileMode) error
	ReadLink(path string) (string, error)
	MkDir(path string, perm os.FileMode) error
	DeleteFile(path string) error
	DeleteDir(path string) error
	Rename(oldPath, newPath string) error
}

Filesystem defines a minimal common API implemented by concrete filesystem packages (ext4, xfs, btrfs).

type Grower

type Grower interface {
	// GrowTo resizes the underlying image and the filesystem
	// metadata so that it spans newSizeBytes. The implementation may
	// require that the new size is strictly larger than the current
	// size and/or aligned to a filesystem-specific boundary.
	GrowTo(newSizeBytes int64) error
}

Grower is the optional interface for filesystems that can expand in place to fill a larger backing image. The opposite direction (shrink) is intentionally not part of this surface — most filesystems' shrink paths are either unsupported or far more invasive than grow.

type HardLinker

type HardLinker interface {
	// Link adds a new directory entry at newPath that points at the
	// same inode as oldPath. The source must not be a directory and
	// newPath must not already exist. The source inode's nlink count
	// is bumped.
	Link(oldPath, newPath string) error
}

HardLinker is the optional interface for filesystems that support POSIX hardlinks (multiple directory entries pointing at the same inode). Directories cannot be hardlinked — implementations must reject that case.

type LabelReader

type LabelReader interface {
	// Label returns the current volume label, decoded from the
	// implementation's on-disk metadata. An empty string means the
	// filesystem has no label set (not an error).
	Label() string
}

LabelReader is the optional read-only interface for filesystems that can decode an on-disk volume label. Implementations that can also mutate the label additionally satisfy Labeller (which embeds this).

Some drivers (notably ones with transactional / COW write models) can read the label cheaply but cannot yet rewrite it through their regular commit machinery. Exposing the read capability separately lets generic tools (`diskimage exec <fs> label get`, status displays) work everywhere while keeping the write surface honest.

if r, ok := fs.(filesystem.LabelReader); ok {
    fmt.Println(r.Label())
}

type Labeller

type Labeller interface {
	LabelReader
	// SetLabel writes a new volume label. Concrete implementations
	// document whether the call is safe with a live, actively-mutating
	// filesystem; the conservative assumption is "offline only".
	SetLabel(label string) error
}

Labeller is the optional interface implemented by filesystems that expose a read/write volume label. Embeds LabelReader so every Labeller is automatically a LabelReader too — generic code can downgrade the assertion when only read access is needed.

if l, ok := fs.(filesystem.Labeller); ok {
    l.SetLabel("rootfs")
}

Kept separate from Filesystem so implementations that genuinely have no concept of a label (or where label mutation is non-trivial) aren't forced to stub it. The label's encoding and length limit are filesystem-specific (e.g. ext2/3/4 caps at 16 bytes; FAT caps at 11). SetLabel must reject labels exceeding its filesystem's limit.

type MetadataSetter

type MetadataSetter interface {
	// Chmod replaces the permission bits at path, preserving the
	// file-type bits. ctime is refreshed.
	Chmod(path string, perm os.FileMode) error
	// Chown updates uid/gid at path. ctime is refreshed; mode, body,
	// and the other timestamps are left alone.
	Chown(path string, uid, gid uint32) error
	// Chtimes sets atime and mtime at path. ctime is refreshed to
	// "now" per POSIX. Birth time (if the filesystem records one) is
	// left untouched.
	Chtimes(path string, atime, mtime time.Time) error
}

MetadataSetter is the optional interface bundling the POSIX metadata mutators (chmod / chown / utimes). Filesystems that support any of these typically support all three, so they're bundled — drivers that only implement a subset can still expose the methods they have and return an error from the others. (The type assertion only proves they all compile, not that any one call must succeed.)

type Resizer

type Resizer interface {
	// Resize changes the filesystem's underlying size in bytes. The newSize
	// is the new total capacity of the on-disk image. Implementations may
	// reject sizes that would lose data; semantics per driver:
	//
	//   - grow (always allowed where the on-disk format supports it)
	//   - shrink (allowed only if the data fits in newSize; some FS forbid
	//     shrink entirely — e.g. XFS, ZFS — and return ErrShrinkUnsupported)
	//
	// Returns ErrShrinkUnsupported when the driver doesn't implement shrink
	// and newSize < current; returns wrapping I/O errors for failed grow.
	Resize(newSize int64) error
}

Resizer is the optional, uniform resize capability implemented by every driver that can change its on-disk size, in either direction. It supersedes the older grow-only Grower split by giving callers a single entry point and a clearly-typed sentinel for the one case where the semantics genuinely diverge (shrink-unsupported formats).

if r, ok := fs.(filesystem.Resizer); ok {
    if err := r.Resize(newSize); errors.Is(err, filesystem.ErrShrinkUnsupported) {
        // driver only grows — caller decides how to handle
    }
}

type Stat

type Stat interface {
	Mode() uint16
	Size() uint64
	Inode() uint64
}

Stat describes basic metadata for a filesystem path.

func NewStat

func NewStat(mode uint16, size uint64, inode uint64) Stat

NewStat constructs a Stat implementation backed by an unexported struct.

type Symlinker

type Symlinker interface {
	// Symlink creates a symbolic link at linkPath whose target is the
	// literal string `target`. The parent of linkPath must exist; the
	// path itself must not. Symlink targets are stored as-is and are
	// not resolved at creation time.
	Symlink(target, linkPath string) error
}

Symlinker is the optional interface implemented by filesystems that support creating symbolic links. ReadLink is already part of Filesystem; this capability gates the write side.

if s, ok := fs.(filesystem.Symlinker); ok {
    s.Symlink("/target", "/link")
}

type Truncater

type Truncater interface {
	// Truncate resizes the regular file at path to newSize bytes.
	// mtime and ctime are refreshed per POSIX truncate(2).
	Truncate(path string, newSize int64) error
}

Truncater is the optional interface for filesystems that support resizing a regular file in place. Growing extends the file with implicit zero-fill (sparse where the format allows); shrinking drops or trims the trailing data.

Jump to

Keyboard shortcuts

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