absfs

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Dec 12, 2025 License: MIT Imports: 9 Imported by: 16

README

absfs - Abstract File System for go

Go Reference Go Report Card CI License

absfs is a go package that defines an abstract filesystem interface.

The design goal of absfs is to support a system of composable filesystem implementations for various uses from testing to complex storage management.

Implementors can create stand alone filesystems that implement the absfs.Filer interface providing new components that can easily be added to existing compositions and data pipelines.

Features

  • 📦 Composable - Layer filesystems for complex behaviors
  • 🧪 Testable - Mock filesystems for unit testing
  • 🔧 Extensible - Easy to implement new filesystem types
  • 🚀 Performant - Minimal overhead wrapper pattern
  • 📚 Well-documented - Comprehensive docs and examples
  • Well-tested - 89% code coverage
  • 🌍 Cross-platform - Unix-style paths work on Windows, macOS, and Linux

Install

$ go get github.com/absfs/absfs

The Interfaces

absfs defines 4 interfaces.

  1. Filer - The minimum set of methods that an implementation must define.
  2. FileSystem - The complete set of Abstract FileSystem methods, including convenience functions.
  3. SymLinker - Methods for implementing symbolic links.
  4. SymlinkFileSystem - A FileSystem that supports symbolic links.
Filer - Interface

The minimum set of methods that an implementation must define.

type Filer interface {

    Mkdir(name string, perm os.FileMode) error
    OpenFile(name string, flag int, perm os.FileMode) (File, error)
    Remove(name string) error
    Rename(oldname, newname string) error
    Stat(name string) (os.FileInfo, error)
    Chmod(name string, mode os.FileMode) error
    Chtimes(name string, atime time.Time, mtime time.Time) error
    Chown(name string, uid, gid int) error

    // io/fs compatibility methods
    ReadDir(name string) ([]fs.DirEntry, error)
    ReadFile(name string) ([]byte, error)
    Sub(dir string) (fs.FS, error)

}
FileSystem - Interface

The complete set of Abstract FileSystem methods, including convenience functions.

type FileSystem interface {

    // Filer interface
    OpenFile(name string, flag int, perm os.FileMode) (File, error)
    Mkdir(name string, perm os.FileMode) error
    Remove(name string) error
    Rename(oldpath, newpath string) error
    Stat(name string) (os.FileInfo, error)
    Chmod(name string, mode os.FileMode) error
    Chtimes(name string, atime time.Time, mtime time.Time) error
    Chown(name string, uid, gid int) error

    Chdir(dir string) error
    Getwd() (dir string, err error)
    TempDir() string
    Open(name string) (File, error)
    Create(name string) (File, error)
    MkdirAll(name string, perm os.FileMode) error
    RemoveAll(path string) (err error)
    Truncate(name string, size int64) error

}
SymLinker - Interface

Additional methods for implementing symbolic links.

type SymLinker interface {

    Lstat(name string) (os.FileInfo, error)
    Lchown(name string, uid, gid int) error
    Readlink(name string) (string, error)
    Symlink(oldname, newname string) error

}

Windows Note: When using osfs on Windows, symlinks require elevated privileges or Developer Mode. See the osfs SYMLINKS.md for configuration instructions. Virtual filesystems (memfs, boltfs, etc.) support symlinks on all platforms without special configuration.

SymlinkFileSystem - Interface

A FileSystem that supports symbolic links.

type SymlinkFileSystem interface {

    // Filer interface
    OpenFile(name string, flag int, perm os.FileMode) (File, error)
    Mkdir(name string, perm os.FileMode) error
    Remove(name string) error
    Stat(name string) (os.FileInfo, error)
    Chmod(name string, mode os.FileMode) error
    Chtimes(name string, atime time.Time, mtime time.Time) error
    Chown(name string, uid, gid int) error

    // FileSystem interface
    Chdir(dir string) error
    Getwd() (dir string, err error)
    TempDir() string
    Open(name string) (File, error)
    Create(name string) (File, error)
    MkdirAll(name string, perm os.FileMode) error
    RemoveAll(path string) (err error)
    Truncate(name string, size int64) error

    // SymLinker interface
    Lstat(name string) (os.FileInfo, error)
    Lchown(name string, uid, gid int) error
    Readlink(name string) (string, error)
    Symlink(oldname, newname string) error
}

File Systems


OsFs

An absfs.FileSystem implementation that wraps the equivalent os standard library filesystem functions. Using osfs is essentially identical to using the os package except that it implements a per object current working directory.

MemFs

The memfs filesystem implements a memory only filesystem.

NilFs

The Nil FileSystem is a no-op implementation. Methods have no effect and most return no errors. The Read and ReadAt methods always return io.EOF to avoid infinite loops.

PTFS

The Pass Through FileSystem (ptfs) takes any object that implements absfs.FileSystem and passes all methods through unaltered to that object.

ROFS

The rofs is a read-only filesystem that can wrap any other filesystem implementation and make it read only.

BaseFS

The basefs filesystem takes a absfs.FileSystem and an absolute path within that filesystem, and provides a absfs.FileSystem interface that is rooted at that path. Relative paths are handled correctly such that it is not possible to 'escape' the base path constraint (for example by using a path like "/.."). In addition errors are rewritten to accurately obscure the base path.

HttpFS

A filesystem that can convert a absfs.FileSystem to a http.FileSystem interface for use with http.FileServer.

CorFS (cache on read fs)

An absfs.Filer that wraps two absfs.Filers when data is read if it doesn't exist in the second tier filesytem the filer copies data from the underlying file systems into the second tier filesystem causing it to function like a cache.

CowFS (copy on write fs)

An absfs.Filer that wraps two absfs.Filers the first of which may be a read only filesystem. Any attempt to modify or write data to a CowFS causes the Filer to write those changes to the secondary fs leaving the underlying filesystem unmodified.

BoltFS

An absfs.Filer that provides and absfs.Filer interface for the popular embeddable key/value store called bolt.

S3FS

An absfs.Filer that provides and absfs. Filer interface to any S3 compatible object storage API.

SftpFs

A filesystem which reads and writes securely between computers across networks using the SFTP interface.


Using absfs

See the User Guide for comprehensive documentation on using absfs in your applications, including:

  • Working with files and directories
  • Path handling across platforms
  • Thread safety patterns
  • Composition and testing strategies
Cross-Platform OS Filesystem Pattern

For applications using the OS filesystem across Windows, macOS, and Linux, use this simple pattern:

// filesystem_windows.go
//go:build windows
func NewFS(drive string) absfs.FileSystem {
    if drive == "" { drive = "C:" }
    return osfs.NewWindowsDriveMapper(osfs.NewFS(), drive)
}

// filesystem_unix.go
//go:build !windows
func NewFS(drive string) absfs.FileSystem {
    return osfs.NewFS()
}

Then use Unix-style paths everywhere:

fs := NewFS("")
fs.Create("/config/app.json")  // Works on all platforms!

See PATH_HANDLING.md and examples/cross-platform for details.

Implementing a Custom Filesystem

Want to create your own filesystem implementation? See the Implementer Guide for complete instructions on implementing the Filer interface, including:

  • Method-by-method requirements
  • Security best practices
  • Testing strategies
  • Performance considerations

Thread Safety

⚠️ FileSystem instances are NOT goroutine-safe by default.

Each FileSystem object created by ExtendFiler maintains its own current working directory (cwd) state, which can be modified by Chdir. Concurrent access from multiple goroutines can cause race conditions.

Safe Usage Patterns
  1. One FileSystem per goroutine (Recommended)

    func worker(id int) {
        fs := absfs.ExtendFiler(filer) // Each goroutine gets its own instance
        fs.Chdir(fmt.Sprintf("/worker%d", id))
        // ... do work ...
    }
    
  2. Use absolute paths only

    // Shared filesystem is safe if you only use absolute paths
    sharedFS := absfs.ExtendFiler(filer)
    // Safe: no dependency on cwd
    sharedFS.Open("/absolute/path/to/file.txt")
    
  3. External synchronization

    var mu sync.Mutex
    sharedFS := absfs.ExtendFiler(filer)
    
    mu.Lock()
    sharedFS.Chdir("/some/directory")
    file, _ := sharedFS.Open("relative/file.txt")
    mu.Unlock()
    

See SECURITY.md for more details.

Path Handling

The absfs package provides consistent path semantics across all platforms. Paths starting with / or \ are treated as virtual absolute paths, allowing Unix-style paths to work on Windows while maintaining support for OS-native paths.

// These work identically on Unix, macOS, AND Windows:
fs := absfs.ExtendFiler(virtualFS)
fs.Create("/config/app.json")
fs.MkdirAll("/var/log/app", 0755)

Important for Windows users: Paths like /config/file.txt are treated as virtual-absolute for portability. For true OS operations on Windows, use drive letters (C:\path) or UNC paths (\\server\share).

See PATH_HANDLING.md for comprehensive cross-platform path behavior documentation.

Documentation

User Documentation
Developer Documentation
Reference

Contributing

We strongly encourage contributions, please fork and submit Pull Requests, and publish any FileSystems you implement.

New FileSystem types do not need to be added to this repo, but we'd be happy to link to yours so please open an issue or better yet add a Pull Request with the updated Readme.

LICENSE

This project is governed by the MIT License. See LICENSE

Documentation

Overview

Package absfs provides file and file system interfaces for abstract file system primitives. It's purpose is to define a filesystem abstraction that supports common file system operations.

Example

Example demonstrates basic usage of creating a filesystem

package main

import (
	"fmt"
)

func main() {
	// Create your Filer implementation (shown simplified)
	// filer := &MyFiler{}

	// Extend it to get full FileSystem interface
	// fs := absfs.ExtendFiler(filer)

	fmt.Println("See individual examples for specific operations")
}
Output:

See individual examples for specific operations
Example (PermissionConstants)

Example_permissionConstants demonstrates using permission constants

package main

import (
	"fmt"

	"github.com/absfs/absfs"
)

func main() {
	// Use permission constants for clarity
	// fs.Mkdir("/public", absfs.OS_ALL_RWX) // 0777
	// fs.Mkdir("/private", absfs.OS_USER_RWX) // 0700

	fmt.Printf("OS_ALL_RWX = 0%o\n", absfs.OS_ALL_RWX)
}
Output:

OS_ALL_RWX = 0777

Index

Examples

Constants

View Source
const (
	OS_READ        = 04
	OS_WRITE       = 02
	OS_EX          = 01
	OS_USER_SHIFT  = 6
	OS_GROUP_SHIFT = 3
	OS_OTH_SHIFT   = 0

	OS_USER_R   = OS_READ << OS_USER_SHIFT
	OS_USER_W   = OS_WRITE << OS_USER_SHIFT
	OS_USER_X   = OS_EX << OS_USER_SHIFT
	OS_USER_RW  = OS_USER_R | OS_USER_W
	OS_USER_RWX = OS_USER_RW | OS_USER_X

	OS_GROUP_R   = OS_READ << OS_GROUP_SHIFT
	OS_GROUP_W   = OS_WRITE << OS_GROUP_SHIFT
	OS_GROUP_X   = OS_EX << OS_GROUP_SHIFT
	OS_GROUP_RW  = OS_GROUP_R | OS_GROUP_W
	OS_GROUP_RWX = OS_GROUP_RW | OS_GROUP_X

	OS_OTH_R   = OS_READ << OS_OTH_SHIFT
	OS_OTH_W   = OS_WRITE << OS_OTH_SHIFT
	OS_OTH_X   = OS_EX << OS_OTH_SHIFT
	OS_OTH_RW  = OS_OTH_R | OS_OTH_W
	OS_OTH_RWX = OS_OTH_RW | OS_OTH_X

	OS_ALL_R   = OS_USER_R | OS_GROUP_R | OS_OTH_R
	OS_ALL_W   = OS_USER_W | OS_GROUP_W | OS_OTH_W
	OS_ALL_X   = OS_USER_X | OS_GROUP_X | OS_OTH_X
	OS_ALL_RW  = OS_ALL_R | OS_ALL_W
	OS_ALL_RWX = OS_ALL_RW | OS_ALL_X
)

Permission flags not provided by the standard library.

View Source
const (
	Separator     = '/'
	ListSeparator = ':'
)

Path separators for absfs virtual filesystems. All absfs filesystems use Unix-style paths regardless of host OS.

View Source
const (
	O_ACCESS = 0x3 // masks the access mode (O_RDONLY, O_WRONLY, or O_RDWR)

	// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
	O_RDONLY = os.O_RDONLY // open the file read-only.
	O_WRONLY = os.O_WRONLY // open the file write-only.
	O_RDWR   = os.O_RDWR   // open the file read-write.

	// The remaining values may be or'ed in to control behavior.
	O_APPEND = os.O_APPEND // append data to the file when writing.
	O_CREATE = os.O_CREATE // create a new file if none exists.
	O_EXCL   = os.O_EXCL   // used with O_CREATE, file must not exist.
	O_SYNC   = os.O_SYNC   // open for synchronous I/O.
	O_TRUNC  = os.O_TRUNC  // if possible, truncate file when opened.
)

Variables

View Source
var ErrNotImplemented = errors.New("not implemented")

Functions

func FilerToFS

func FilerToFS(filer Filer, root string) (fs.FS, error)

FilerToFS creates an fs.FS from any Filer, rooted at the given directory. This is the canonical way to create an fs.FS from absfs types. The returned fs.FS is read-only; all opens use O_RDONLY.

func ParseFileMode

func ParseFileMode(input string) (os.FileMode, error)

ParseFileMode - parses a unix style file mode string and returns an `os.FileMode` and an error.

Example

ExampleParseFileMode demonstrates parsing Unix file mode strings

package main

import (
	"fmt"

	"github.com/absfs/absfs"
)

func main() {
	mode, err := absfs.ParseFileMode("drwxr-xr-x")
	if err != nil {
		panic(err)
	}

	fmt.Printf("Parsed mode: %o\n", mode&0777)
}
Output:

Parsed mode: 755

Types

type File

type File interface {
	Seekable

	// ReadAt reads len(b) bytes from the File starting at byte offset off. It
	// returns the number of bytes read and the error, if any. ReadAt always
	// returns a non-nil error when n < len(b). At end of file, that error is
	// io.EOF.
	ReadAt(b []byte, off int64) (n int, err error)

	// WriteAt writes len(b) bytes to the File starting at byte offset off. It
	// returns the number of bytes written and an error, if any. WriteAt returns
	// a non-nil error when n != len(b).
	WriteAt(b []byte, off int64) (n int, err error)

	// WriteString is like Write, but writes the contents of string s rather than
	// a slice of bytes.
	WriteString(s string) (n int, err error)

	// Truncate - changes the size of the file. It does not change the I/O offset.
	// If there is an error, it should be of type `*os.PathError`.
	Truncate(size int64) error

	// Readdirnames - reads the contents of the directory associated with file and
	// returns a slice of up to n names. Subsequent calls on the same
	// file will yield further names.
	//
	// If n > 0, Readdirnames returns at most n names. In this case, if
	// Readdirnames returns an empty slice, it will return a non-nil error
	// explaining why. At the end of a directory, the error is io.EOF.
	//
	// If n <= 0, Readdirnames returns all the names from the directory in a single
	// slice. In this case, if Readdirnames succeeds (reads all the way to the end of
	// the directory), it returns the slice and a nil error. If it encounters an
	// error before the end of the directory, Readdirnames returns the names read
	// until that point and a non-nil error.
	Readdirnames(n int) (names []string, err error)

	// ReadDir reads the contents of the directory and returns a slice of up to n
	// DirEntry values in directory order. This is the modern Go 1.16+ equivalent
	// of Readdir that returns lightweight DirEntry values instead of full FileInfo.
	//
	// If n > 0, ReadDir returns at most n entries. In this case, if ReadDir
	// returns an empty slice, it will return a non-nil error explaining why.
	// At the end of a directory, the error is io.EOF.
	//
	// If n <= 0, ReadDir returns all entries from the directory in a single slice.
	// In this case, if ReadDir succeeds (reads all the way to the end of the
	// directory), it returns the slice and a nil error.
	ReadDir(n int) ([]fs.DirEntry, error)
}

File - is an interface for file handles that supports the most common file operations. Filesystem interfaces in this package have functions that return values of type `File`, but be aware that some implementations may only support a subset of these functions given the limitations particular file systems.

func ExtendSeekable

func ExtendSeekable(sf Seekable) File

ExtendSeekable - extends a `Seekable` interface implementation to a File interface implementation. First type assertion is used to check if the argument already supports the `File` interface.

If the argument's native type is not recognized, and cannot be type asserted to an implementation of `File` `ExtendSeekable` wraps the argument in a type that implements the additional `File` functions using only the functions in the `Seekable` interface. Functions that are identical between `File` and `Seekable` are passed through unaltered. This should would fine, but may have have performance implications.

Example

ExampleExtendSeekable shows how to extend a Seekable to a File

package main

import (
	"fmt"
)

func main() {
	// If you have a Seekable implementation that doesn't implement
	// ReadAt, WriteAt, Truncate, etc., you can extend it:

	// seekable := &MySeekable{}
	// file := absfs.ExtendSeekable(seekable)

	// Now file implements the full File interface with:
	// - ReadAt/WriteAt
	// - Truncate
	// - Readdirnames

	fmt.Println("Extended seekable to file")
}
Output:

Extended seekable to file

type FileSystem

type FileSystem interface {
	Filer

	Chdir(dir string) error
	Getwd() (dir string, err error)
	TempDir() string
	Open(name string) (File, error)
	Create(name string) (File, error)
	MkdirAll(name string, perm os.FileMode) error
	RemoveAll(path string) (err error)
	Truncate(name string, size int64) error
}

func ExtendFiler

func ExtendFiler(filer Filer) FileSystem

ExtendFiler adds the FileSystem convenience functions to any Filer implementation.

Path Semantics:

All absfs filesystems use Unix-style forward slash paths ("/") on all platforms. This design enables virtual filesystems (mocks, in-memory, archives) to work consistently across all platforms and compose seamlessly with each other.

Examples:

  • "/config/app.json" → absolute path on all platforms
  • "/home/user/data" → absolute path on all platforms
  • "relative/path" → relative on all platforms

For OS filesystem wrappers (like osfs), use the osfs.ToNative() and osfs.FromNative() helpers to convert between absfs paths and native OS paths.

See PATH_HANDLING.md for detailed cross-platform behavior documentation.

Example

ExampleExtendFiler shows how to extend a Filer to a full FileSystem

package main

import (
	"fmt"
)

func main() {
	// Create a Filer implementation
	// In practice, use an actual implementation like osfs, memfs, etc.
	// filer := &MyCustomFiler{}

	// Extend it to get all convenience methods
	// fs := absfs.ExtendFiler(filer)

	// Now you can use convenience methods like:
	// fs.Create("/file.txt")
	// fs.MkdirAll("/path/to/dir", 0755)
	// fs.Open("/file.txt")

	fmt.Println("Extended filer to FileSystem")
}
Output:

Extended filer to FileSystem

type Filer

type Filer interface {

	// OpenFile opens a file using the given flags and the given mode.
	OpenFile(name string, flag int, perm os.FileMode) (File, error)

	// Mkdir creates a directory in the filesystem, return an error if any
	// happens.
	Mkdir(name string, perm os.FileMode) error

	// Remove removes a file identified by name, returning an error, if any
	// happens.
	Remove(name string) error

	// Rename renames (moves) oldpath to newpath. If newpath already exists and
	// is not a directory, Rename replaces it. OS-specific restrictions may apply
	// when oldpath and newpath are in different directories. If there is an
	// error, it will be of type *LinkError.
	Rename(oldpath, newpath string) error

	// Stat returns the FileInfo structure describing file. If there is an error,
	// it will be of type *PathError.
	Stat(name string) (os.FileInfo, error)

	//Chmod changes the mode of the named file to mode.
	Chmod(name string, mode os.FileMode) error

	//Chtimes changes the access and modification times of the named file
	Chtimes(name string, atime time.Time, mtime time.Time) error

	//Chown changes the owner and group ids of the named file
	Chown(name string, uid, gid int) error

	// ReadDir reads the named directory and returns a list of directory entries
	// sorted by filename. This is compatible with io/fs.ReadDirFS.
	ReadDir(name string) ([]fs.DirEntry, error)

	// ReadFile reads the named file and returns its contents.
	// This is compatible with io/fs.ReadFileFS.
	ReadFile(name string) ([]byte, error)

	// Sub returns an fs.FS corresponding to the subtree rooted at dir.
	// This is compatible with io/fs.SubFS interface.
	// The returned fs.FS is read-only; for writable subdirectory access, use basefs.
	Sub(dir string) (fs.FS, error)
}

type Flags

type Flags int

Flags - represents access and permission flags for use with file opening functions.

func ParseFlags

func ParseFlags(input string) (Flags, error)

ParseFlags - parses a string of flags separated by "|" and returns a `Flags` value and an error.

Following is the list of recognized flags, "O_RDONLY", "O_RDWR", "O_WRONLY", "O_APPEND", "O_CREATE", "O_EXCL", "O_SYNC", "O_TRUNC". The access mode values ("O_RDONLY", "O_RDWR", "O_WRONLY") are mutually exclusive, only one may be specified. If no access mode is specified "O_RDONLY" is the default. All other flags may appear more than once, but subsequent occurrences have no effect.

Example

ExampleParseFlags demonstrates parsing file open flags

package main

import (
	"fmt"

	"github.com/absfs/absfs"
)

func main() {
	flags, err := absfs.ParseFlags("O_RDWR|O_CREATE|O_TRUNC")
	if err != nil {
		panic(err)
	}

	fmt.Printf("Flags: %s\n", flags)
}
Output:

Flags: O_RDWR|O_CREATE|O_TRUNC

func (Flags) String

func (f Flags) String() string

String - returns the list of values set in a `Flag` separated by "|".

Example

ExampleFlags_String demonstrates converting flags to string

package main

import (
	"fmt"

	"github.com/absfs/absfs"
)

func main() {
	flags := absfs.Flags(absfs.O_RDWR | absfs.O_APPEND)
	fmt.Println(flags.String())
}
Output:

O_RDWR|O_APPEND

type InvalidFile

type InvalidFile struct {

	// Path should be set to the file path provided to the function opening the
	// file (i.e. OpenFile, Open, Create).
	Path string
}

InvalidFile is a no-op implementation of File that should be returned from any file open methods when an error occurs. InvalidFile mimics the behavior of file handles returnd by the `os` package when there is an error.

func (*InvalidFile) Close

func (f *InvalidFile) Close() error

Close - does nothing and does not return an error.

func (*InvalidFile) Name

func (f *InvalidFile) Name() string

Name - returns the name of the file path provided to the function that attempted to open the file.

func (*InvalidFile) Read

func (f *InvalidFile) Read(p []byte) (int, error)

Read - returns an *os.PathError indicating a bad file handle.

func (*InvalidFile) ReadAt

func (f *InvalidFile) ReadAt(b []byte, off int64) (n int, err error)

ReadAt - returns an *os.PathError indicating a bad file handle.

func (*InvalidFile) ReadDir

func (f *InvalidFile) ReadDir(n int) ([]fs.DirEntry, error)

ReadDir - returns an *os.PathError indicating a bad file handle.

func (*InvalidFile) Readdir

func (f *InvalidFile) Readdir(int) ([]os.FileInfo, error)

Readdir - returns an *os.PathError indicating a bad file handle.

func (*InvalidFile) Readdirnames

func (f *InvalidFile) Readdirnames(n int) (names []string, err error)

Readdirnames - returns an *os.PathError indicating a bad file handle.

func (*InvalidFile) Seek

func (f *InvalidFile) Seek(offset int64, whence int) (ret int64, err error)

Seek - returns an *os.PathError indicating a bad file handle.

func (*InvalidFile) Stat

func (f *InvalidFile) Stat() (os.FileInfo, error)

Stat - returns an *os.PathError indicating a bad file handle.

func (*InvalidFile) Sync

func (f *InvalidFile) Sync() error

Sync - returns an *os.PathError indicating a bad file handle.

func (*InvalidFile) Truncate

func (f *InvalidFile) Truncate(size int64) error

Truncate - returns an *os.PathError indicating a bad file handle.

func (*InvalidFile) Write

func (f *InvalidFile) Write(p []byte) (int, error)

Write - returns an *os.PathError indicating a bad file handle.

func (*InvalidFile) WriteAt

func (f *InvalidFile) WriteAt(b []byte, off int64) (n int, err error)

WriteAt - returns an *os.PathError indicating a bad file handle.

func (*InvalidFile) WriteString

func (f *InvalidFile) WriteString(s string) (n int, err error)

WriteString - returns an *os.PathError indicating a bad file handle.

type NoSymlinks struct {
	FileSystem
}

NoSymlinks wraps a FileSystem and actively blocks all symlink operations. Use this when your application must not allow symlinks under any circumstances.

Operations blocked:

  • Symlink: returns ENOTSUP
  • Readlink: returns ENOTSUP
  • Lstat: falls through to Stat (always follows symlinks)
  • Lchown: falls through to Chown (always follows symlinks)

Note: This doesn't prevent symlinks from existing if the underlying filesystem has them. It only prevents creation of new symlinks and reading existing ones through this wrapper.

func (*NoSymlinks) Lchown

func (n *NoSymlinks) Lchown(name string, uid, gid int) error

Lchown follows symlinks (same as Chown) - symlinks are invisible

func (*NoSymlinks) Lstat

func (n *NoSymlinks) Lstat(name string) (os.FileInfo, error)

Lstat follows symlinks (same as Stat) - symlinks are invisible

func (n *NoSymlinks) Readlink(name string) (string, error)

Readlink always returns ENOTSUP

func (n *NoSymlinks) Symlink(oldname, newname string) error

Symlink always returns ENOTSUP

type ReadOnlyFiler

type ReadOnlyFiler interface {
	Open(name string) (io.ReadCloser, error)
}

type Seekable

type Seekable interface {
	UnSeekable
	io.Seeker
}

Seekable - is an interface for file handles that can perform reads and/or writes and can seek to specific locations within a file. A Seekable is also an io.ReadWriteCloser, and an io.Seeker.

type SymLinker

type SymLinker interface {

	// Lstat returns a FileInfo describing the named file. If the file is a
	// symbolic link, the returned FileInfo describes the symbolic link. Lstat
	// makes no attempt to follow the link. If there is an error, it will be of type *PathError.
	Lstat(name string) (os.FileInfo, error)

	// Lchown changes the numeric uid and gid of the named file. If the file is a
	// symbolic link, it changes the uid and gid of the link itself. If there is
	// an error, it will be of type *PathError.
	//
	// On Windows, it always returns the syscall.EWINDOWS error, wrapped in
	// *PathError.
	Lchown(name string, uid, gid int) error

	// Readlink returns the destination of the named symbolic link. If there is an
	// error, it will be of type *PathError.
	Readlink(name string) (string, error)

	// Symlink creates newname as a symbolic link to oldname. If there is an
	// error, it will be of type *LinkError.
	Symlink(oldname, newname string) error
}

type SymlinkFileSystem

type SymlinkFileSystem interface {
	FileSystem
	SymLinker
}
func BlockSymlinks(fs FileSystem) SymlinkFileSystem

BlockSymlinks wraps a FileSystem to block all symlink operations. This is a convenience function that returns a SymlinkFileSystem where all symlink operations fail with ENOTSUP.

func ExtendSymlinkFiler

func ExtendSymlinkFiler(filer Filer) SymlinkFileSystem

ExtendSymlinkFiler returns a SymlinkFileSystem from any Filer.

This function intelligently handles different capabilities:

  • If filer already implements SymlinkFileSystem, returns it directly
  • If filer implements both FileSystem and SymLinker, combines them
  • If filer implements SymLinker but not FileSystem, extends and combines
  • If filer has no symlink support, wraps with SymlinkOverlay

This is the recommended way to get a SymlinkFileSystem from any Filer. Calling it multiple times is safe - it won't double-wrap.

Example:

filer := myFS{}                            // Implement Filer (9 methods)
fs := absfs.ExtendFiler(filer)             // Get FileSystem
sfs := absfs.ExtendSymlinkFiler(filer)    // Get SymlinkFileSystem

type SymlinkOverlay

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

SymlinkOverlay adds symlink support to filesystems that don't have native symlink capabilities. Symlinks are stored as regular files with:

  • Mode: os.ModeSymlink | 0777
  • Content: magic header + target path

This allows any filesystem that stores file modes to support symlinks.

func NewSymlinkOverlay

func NewSymlinkOverlay(base FileSystem) *SymlinkOverlay

NewSymlinkOverlay creates a SymlinkFileSystem by adding overlay-based symlink support to a FileSystem that doesn't have native symlinks.

Note: Prefer using ExtendSymlinkFiler which automatically detects whether the underlying filesystem has native symlink support.

func (*SymlinkOverlay) Base

func (o *SymlinkOverlay) Base() FileSystem

Base returns the underlying FileSystem

func (*SymlinkOverlay) Chdir

func (o *SymlinkOverlay) Chdir(dir string) error

func (*SymlinkOverlay) Chmod

func (o *SymlinkOverlay) Chmod(name string, mode os.FileMode) error

func (*SymlinkOverlay) Chown

func (o *SymlinkOverlay) Chown(name string, uid, gid int) error

func (*SymlinkOverlay) Chtimes

func (o *SymlinkOverlay) Chtimes(name string, atime, mtime time.Time) error

func (*SymlinkOverlay) Create

func (o *SymlinkOverlay) Create(name string) (File, error)

func (*SymlinkOverlay) Getwd

func (o *SymlinkOverlay) Getwd() (string, error)

func (*SymlinkOverlay) Lchown

func (o *SymlinkOverlay) Lchown(name string, uid, gid int) error

Lchown changes ownership of a symlink without following it.

func (*SymlinkOverlay) Lstat

func (o *SymlinkOverlay) Lstat(name string) (os.FileInfo, error)

Lstat returns file info without following symlinks.

func (*SymlinkOverlay) Mkdir

func (o *SymlinkOverlay) Mkdir(name string, perm os.FileMode) error

func (*SymlinkOverlay) MkdirAll

func (o *SymlinkOverlay) MkdirAll(p string, perm os.FileMode) error

func (*SymlinkOverlay) Open

func (o *SymlinkOverlay) Open(name string) (File, error)

func (*SymlinkOverlay) OpenFile

func (o *SymlinkOverlay) OpenFile(name string, flag int, perm os.FileMode) (File, error)

func (*SymlinkOverlay) ReadDir

func (o *SymlinkOverlay) ReadDir(name string) ([]fs.DirEntry, error)

func (*SymlinkOverlay) ReadFile

func (o *SymlinkOverlay) ReadFile(name string) ([]byte, error)
func (o *SymlinkOverlay) Readlink(name string) (string, error)

Readlink returns the destination of the named symbolic link.

func (*SymlinkOverlay) Remove

func (o *SymlinkOverlay) Remove(name string) error

func (*SymlinkOverlay) RemoveAll

func (o *SymlinkOverlay) RemoveAll(name string) error

func (*SymlinkOverlay) Rename

func (o *SymlinkOverlay) Rename(oldpath, newpath string) error

func (*SymlinkOverlay) Stat

func (o *SymlinkOverlay) Stat(name string) (os.FileInfo, error)

func (*SymlinkOverlay) Sub

func (o *SymlinkOverlay) Sub(dir string) (fs.FS, error)
func (o *SymlinkOverlay) Symlink(oldname, newname string) error

Symlink creates newname as a symbolic link to oldname.

func (*SymlinkOverlay) TempDir

func (o *SymlinkOverlay) TempDir() string

func (*SymlinkOverlay) Truncate

func (o *SymlinkOverlay) Truncate(name string, size int64) error

type UnSeekable

type UnSeekable interface {

	// Name returns the name of the file as presented to Open.
	Name() string

	// Read reads up to len(b) bytes from the File. It returns the number of bytes
	// read and any error encountered. At end of file, Read returns 0, io.EOF.
	Read(b []byte) (int, error)

	// Write writes len(b) bytes to the File. It returns the number of bytes
	// written and an error, if any. Write returns a non-nil error when
	// n != len(b).
	Write(b []byte) (int, error)

	// Close - closes the File, rendering it unusable for I/O. It returns an error,
	// if any.
	Close() error

	// Sync - commits the current contents of the file to stable storage.
	Sync() error

	// Stat - returns the FileInfo structure describing file. If there is an
	// error, it should be of type `*os.PathError`.
	Stat() (os.FileInfo, error)

	// Readdir - reads the contents of the directory associated with file and
	// returns a slice of up to n `os.FileInfo` values, as would be returned by
	// Stat or Lstat in directory order. Subsequent calls on the same file will
	// yield further `os.FileInfos`.
	//
	// If n > 0, Readdir returns at most n FileInfo structures. In this case, if
	// Readdir returns an empty slice, it will return a non-nil error explaining
	// why. At the end of a directory, the error is io.EOF.
	//
	// If n <= 0, Readdir returns all the FileInfo from the directory in a single
	// slice. In this case, if Readdir succeeds (reads all the way to the end of
	// the directory), it returns the slice and a nil error. If it encounters an
	// error before the end of the directory, Readdir returns the FileInfo read
	// until that point and a non-nil error.
	Readdir(int) ([]os.FileInfo, error)
}

UnSeekable - is an interface for file handles that can perform reads and/or writes but cannot perform seek operations. An UnSeekable is also an io.ReadWriteCloser.

type WriteOnlyFiler

type WriteOnlyFiler interface {
	Open(name string) (io.WriteCloser, error)
}

Jump to

Keyboard shortcuts

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