memfs

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: 13 Imported by: 3

README

memfs - In Memory File System

CI Go Report Card codecov GoDoc

The memfs package implements the absfs.FileSystem interface as a RAM-backed filesystem.

Care has been taken to ensure that memfs returns identical errors both in text and type as the os package. This makes memfs particularly suited for use in testing.

Features

  • POSIX-like File Operations: Create, Open, Read, Write, Seek, Truncate, Close
  • Directory Operations: Mkdir, MkdirAll, Remove, RemoveAll, Chdir, Getwd, Readdir
  • Symbolic Links: Full symlink support with automatic cycle detection
  • File Permissions: Chmod, Chown, Lchown with standard Unix-style permissions
  • File Metadata: Stat, Lstat, modification times, ownership, and size tracking
  • Directory Traversal: Walk and FastWalk for efficient tree traversal
  • Path Operations: Rename, relative and absolute path support
  • Error Compatibility: Returns errors identical to the os package for easy testing
  • absfs Interface: Full implementation of absfs.FileSystem and absfs.SymlinkFileSystem

Install

$ go get github.com/absfs/memfs

Example Usage

package main

import(
    "fmt"
    "os"

    "github.com/absfs/memfs"
)

func main() {
    fs, _ := memfs.NewFS() // remember kids don't ignore errors

    // Opens a file with read/write permissions in the current directory
    f, _ := fs.Create("/example.txt")

    f.Write([]byte("Hello, world!"))
    f.Close()

    fs.Remove("example.txt")
}

Thread Safety

memfs is NOT thread-safe by design. This is an intentional choice to maximize performance for single-threaded use cases.

Performance: Raw vs Concurrent
Operation Raw memfs With lockfs Overhead
Create+Write+Close+Remove 379 ns 439 ns +16%
Open+Read+Close 59 ns 97 ns +64%
Stat 41 ns 46 ns +12%
Mkdir+Remove 73 ns 88 ns +20%
When to Use Raw memfs (Single-Threaded)

Use memfs directly without locking when:

  • Unit tests running sequentially
  • Single-goroutine applications where only one goroutine accesses the filesystem
  • Performance-critical code where every nanosecond matters
  • Isolated filesystem instances where each goroutine has its own memfs.FileSystem
// Fast single-threaded usage
fs, _ := memfs.NewFS()
f, _ := fs.Create("/file.txt")
f.Write([]byte("data"))
f.Close()
When to Use lockfs (Multi-Threaded)

Wrap with lockfs when multiple goroutines need concurrent access to the same filesystem instance:

import "github.com/absfs/lockfs"

// Thread-safe concurrent usage
raw, _ := memfs.NewFS()
fs, _ := lockfs.NewFS(raw)

// Now safe for concurrent access
go func() { fs.Create("/file1.txt") }()
go func() { fs.Create("/file2.txt") }()

While lockfs adds 7-64% overhead per operation, concurrent workloads may achieve higher total throughput by utilizing multiple CPU cores. The trade-off:

  • Single-threaded: Raw memfs is faster per operation
  • Multi-threaded: lockfs enables parallelism that can outweigh the per-operation overhead
Alternative: Separate Instances

For embarrassingly parallel workloads, consider giving each goroutine its own filesystem instance:

// No locking needed - each goroutine has isolated state
go func() {
    fs, _ := memfs.NewFS()
    // ... work with fs
}()
go func() {
    fs, _ := memfs.NewFS()
    // ... work with fs
}()

Limitations

Persistence

memfs provides no persistence. All data is stored in memory and will be lost when:

  • The FileSystem instance is garbage collected
  • Your application terminates
  • The system restarts

This is by design - memfs is intended for temporary storage, testing, and scenarios where persistence is not required.

Memory Constraints
  • Memory grows linearly with the number and size of files created
  • No automatic cleanup: Deleted file data is set to nil but the underlying slice capacity remains
  • Large files: Creating very large files (> 1GB) can cause memory pressure
  • No memory limits: memfs does not enforce any quota or size limits

For applications with many files or large datasets, monitor memory usage carefully.

Performance Considerations
  • Fast for small to medium datasets: Operations are O(1) to O(n) depending on the operation
  • Path resolution: Nested directory lookups are O(depth × entries)
  • Directory listings: O(n) where n is the number of entries in the directory
  • No disk I/O overhead: All operations are in-memory and very fast for typical use cases
Special Files Not Supported

The following special file types are not supported:

  • Device files (block or character devices)
  • Named pipes (FIFOs)
  • Unix domain sockets
  • Hard links (only symbolic links are supported)

Attempting to create these will result in either an error or undefined behavior.

Performance Characteristics

Benchmarks

Based on the included benchmarks, typical performance characteristics are:

  • File Creation: ~1-2 µs per file
  • Sequential Read/Write:
    • 1KB: ~500 MB/s
    • 64KB: ~2 GB/s (memory bandwidth limited)
  • Random Access: ~200-500 ns per operation
  • Directory Operations:
    • Mkdir: ~500 ns
    • MkdirAll: ~2 µs for 3-level nesting
    • Readdir (100 files): ~5 µs
  • Metadata Operations:
    • Stat: ~200 ns
    • Chmod/Chown: ~100 ns

These numbers are approximate and will vary based on hardware and system load.

Best Practices
  • Use MkdirAll for creating nested directory structures
  • Batch operations when possible to reduce overhead
  • For large directory listings, use Readdirnames instead of Readdir if you only need names
  • Close files promptly to trigger Sync and update metadata
  • Use RemoveAll for recursive deletion rather than manual traversal

Comparison with Alternatives

vs os package
  • memfs: No disk I/O, data lost on exit, much faster, ideal for testing
  • os: Persistent storage, slower due to disk I/O, production-ready
vs afero
  • memfs: Focused implementation of absfs interface, simpler, smaller API surface
  • afero: Larger ecosystem, more features, different abstraction layer
vs testing.TB.TempDir()
  • memfs: Pure in-memory, no filesystem pollution, faster cleanup
  • TempDir: Uses real filesystem, automatically cleaned up by testing framework
vs map[string][]byte
  • memfs: Full filesystem semantics (directories, permissions, metadata)
  • map: Simple key-value storage, no directory structure, no permissions
Use memfs when you need:
  • Fast, isolated filesystem for unit tests
  • Temporary in-memory storage with filesystem semantics
  • Filesystem abstraction that works identically across platforms
  • No disk I/O overhead
  • Easy cleanup (just discard the FileSystem instance)

Troubleshooting

Common Issues
"no such file or directory" errors
  • Ensure parent directories exist before creating files
  • Use MkdirAll to create nested directory structures
  • Check that paths are absolute (start with /) or relative to the current working directory
"directory not empty" when removing
  • Use RemoveAll instead of Remove for directories with contents
  • Ensure you're not trying to remove the root directory /
Memory leaks or high memory usage
  • Check for files that are created but never deleted
  • Large files consume memory proportional to their size
  • Deleted files still occupy slice capacity until garbage collection
  • Consider creating a new FileSystem instance periodically to reset memory
Permission errors
  • Check file permissions with Stat() and verify the mode
  • Ensure files are opened with appropriate flags (O_RDONLY, O_WRONLY, O_RDWR)
  • Remember that fs.Umask affects newly created file permissions
  • Stat() will return an error with "too many levels of symbolic links"
  • Use Lstat() to inspect symlinks without following them
  • Use Readlink() to check where a symlink points
Race conditions and crashes
  • See Thread Safety above
  • Run tests with -race flag: go test -race
  • Wrap with lockfs for concurrent access, or ensure only one goroutine accesses the FileSystem at a time
Path separator issues
  • memfs always uses / as the path separator, regardless of OS
  • Use path.Join() (not filepath.Join()) or construct paths with / directly
  • Avoid using \ (backslash) in paths
Debugging Tips
// Check current working directory
cwd, _ := fs.Getwd()
fmt.Println("Current directory:", cwd)

// List directory contents
dir, _ := fs.Open("/")
entries, _ := dir.Readdir(-1)
for _, entry := range entries {
    fmt.Printf("%s (dir: %v)\n", entry.Name(), entry.IsDir())
}

// Inspect file metadata
info, _ := fs.Stat("/myfile.txt")
fmt.Printf("Size: %d, Mode: %v, ModTime: %v\n",
    info.Size(), info.Mode(), info.ModTime())

// Check if error is a specific type
if pathErr, ok := err.(*os.PathError); ok {
    fmt.Printf("Operation: %s, Path: %s, Err: %v\n",
        pathErr.Op, pathErr.Path, pathErr.Err)
}

absfs

Check out the absfs repo for more information about the abstract filesystem interface and features like filesystem composition.

LICENSE

This project is governed by the MIT License. See LICENSE

Documentation

Overview

Package memfs provides an in-memory file system implementation that conforms to the absfs.FileSystem interface.

This package implements a complete virtual file system stored entirely in memory, supporting standard file operations, directories, symbolic links, permissions, and file metadata. It is particularly useful for testing, temporary storage, and scenarios where a full filesystem is needed without disk I/O.

Example (BasicFileOperations)

Example_basicFileOperations demonstrates basic file creation, writing, and reading.

package main

import (
	"fmt"

	"github.com/absfs/memfs"
)

func main() {
	fs, _ := memfs.NewFS()

	// Create a new file
	file, err := fs.Create("/hello.txt")
	if err != nil {
		panic(err)
	}

	// Write data to the file
	_, err = file.Write([]byte("Hello, World!"))
	if err != nil {
		panic(err)
	}
	file.Close()

	// Open and read the file
	file, err = fs.Open("/hello.txt")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	data := make([]byte, 13)
	n, err := file.Read(data)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Read %d bytes: %s\n", n, string(data))

}
Output:

Read 13 bytes: Hello, World!
Example (ChownAndChtimes)

Example_chownAndChtimes demonstrates changing file ownership and times.

package main

import (
	"fmt"

	"github.com/absfs/memfs"
)

func main() {
	fs, _ := memfs.NewFS()

	// Create a file
	file, _ := fs.Create("/test.txt")
	file.Write([]byte("test data"))
	file.Close()

	// Change ownership
	fs.Chown("/test.txt", 1000, 1000)

	// Note: Could also change access/modification times with fs.Chtimes()

	info, _ := fs.Stat("/test.txt")
	fmt.Printf("File: %s, Size: %d bytes\n", info.Name(), info.Size())

}
Output:

File: test.txt, Size: 9 bytes
Example (ConcurrentAccess)

Example_concurrentAccess demonstrates file system access with goroutines.

package main

import (
	"fmt"

	"github.com/absfs/memfs"
)

func main() {
	fs, _ := memfs.NewFS()

	// Create files sequentially to avoid race conditions
	// (Note: memfs is not designed for concurrent writes to fs.data)
	for i := 0; i < 3; i++ {
		filename := fmt.Sprintf("/file%d.txt", i)
		file, _ := fs.Create(filename)
		file.Write([]byte(fmt.Sprintf("data from file %d", i)))
		file.Close()
	}

	// List all files
	dir, _ := fs.Open("/")
	entries, _ := dir.Readdir(-1)
	dir.Close()

	for _, entry := range entries {
		if entry.Name() != "." && entry.Name() != ".." && !entry.IsDir() {
			fmt.Println(entry.Name())
		}
	}

}
Output:

file0.txt
file1.txt
file2.txt
Example (CopyFile)

Example_copyFile demonstrates copying a file within the file system.

package main

import (
	"fmt"
	"io"

	"github.com/absfs/memfs"
)

func main() {
	fs, _ := memfs.NewFS()

	// Create source file
	src, _ := fs.Create("/source.txt")
	src.Write([]byte("content to copy"))
	src.Close()

	// Open source for reading
	src, _ = fs.Open("/source.txt")

	// Create destination file
	dst, _ := fs.Create("/destination.txt")

	// Copy content
	bytes, _ := io.Copy(dst, src)
	fmt.Printf("Copied %d bytes\n", bytes)

	// Close both files
	src.Close()
	dst.Close()

	// Verify
	file, _ := fs.Open("/destination.txt")
	data, _ := io.ReadAll(file)
	file.Close()
	fmt.Println(string(data))

}
Output:

Copied 15 bytes
content to copy
Example (DirectoryOperations)

Example_directoryOperations demonstrates creating and navigating directories.

package main

import (
	"fmt"

	"github.com/absfs/memfs"
)

func main() {
	fs, _ := memfs.NewFS()

	// Create a directory
	err := fs.Mkdir("/home", 0755)
	if err != nil {
		panic(err)
	}

	// Create nested directories
	err = fs.MkdirAll("/home/user/documents", 0755)
	if err != nil {
		panic(err)
	}

	// Change to the new directory
	err = fs.Chdir("/home/user")
	if err != nil {
		panic(err)
	}

	// Get current working directory
	cwd, _ := fs.Getwd()
	fmt.Println(cwd)

}
Output:

/home/user
Example (FileManipulation)

Example_fileManipulation demonstrates renaming, removing, and truncating files.

package main

import (
	"fmt"
	"io"

	"github.com/absfs/memfs"
)

func main() {
	fs, _ := memfs.NewFS()

	// Create a file
	file, _ := fs.Create("/temp.txt")
	file.Write([]byte("temporary data"))
	file.Close()

	// Rename the file
	fs.Rename("/temp.txt", "/permanent.txt")

	// Truncate the file
	fs.Truncate("/permanent.txt", 4)

	// Read the truncated file
	file, _ = fs.Open("/permanent.txt")
	data, _ := io.ReadAll(file)
	file.Close()
	fmt.Println(string(data))

	// Remove the file
	fs.Remove("/permanent.txt")

	// Try to open the removed file
	_, err := fs.Open("/permanent.txt")
	fmt.Println(err != nil)

}
Output:

temp
true
Example (FilePermissions)

Example_filePermissions demonstrates working with file permissions.

package main

import (
	"fmt"
	"os"

	"github.com/absfs/memfs"
)

func main() {
	fs, _ := memfs.NewFS()

	// Create a file with specific permissions
	file, _ := fs.OpenFile("/secret.txt", os.O_CREATE|os.O_RDWR, 0600)
	file.Write([]byte("secret data"))
	file.Close()

	// Check file permissions
	info, _ := fs.Stat("/secret.txt")
	fmt.Printf("Permissions: %o\n", info.Mode().Perm())

	// Change permissions
	fs.Chmod("/secret.txt", 0644)

	info, _ = fs.Stat("/secret.txt")
	fmt.Printf("New permissions: %o\n", info.Mode().Perm())

}
Output:

Permissions: 600
New permissions: 644
Example (ListDirectory)

Example_listDirectory demonstrates listing directory contents.

package main

import (
	"fmt"

	"github.com/absfs/memfs"
)

func main() {
	fs, _ := memfs.NewFS()

	// Create some files and directories
	fs.MkdirAll("/app/src", 0755)
	fs.Create("/app/README.md")
	fs.Create("/app/main.go")
	fs.Create("/app/src/utils.go")

	// Open the directory
	dir, _ := fs.Open("/app")
	defer dir.Close()

	// Read directory entries
	entries, _ := dir.Readdir(-1)
	for _, entry := range entries {
		// Skip . and .. entries
		if entry.Name() == "." || entry.Name() == ".." {
			continue
		}
		fmt.Printf("%s (dir: %v)\n", entry.Name(), entry.IsDir())
	}

}
Output:

README.md (dir: false)
main.go (dir: false)
src (dir: true)
Example (RemoveAll)

Example_removeAll demonstrates recursively removing directories.

package main

import (
	"fmt"

	"github.com/absfs/memfs"
)

func main() {
	fs, _ := memfs.NewFS()

	// Create a directory structure with files
	fs.MkdirAll("/data/logs/2024", 0755)
	fs.Create("/data/logs/2024/app.log")
	fs.Create("/data/logs/2024/error.log")
	fs.Create("/data/config.json")

	// Remove entire directory tree
	fs.RemoveAll("/data/logs")

	// Verify logs directory is gone
	_, err := fs.Stat("/data/logs")
	fmt.Println(err != nil)

	// But parent still exists
	_, err = fs.Stat("/data")
	fmt.Println(err == nil)

}
Output:

true
true
Example (TemporaryFiles)

Example_temporaryFiles demonstrates working with temporary files.

package main

import (
	"fmt"
	"io"
	"path"

	"github.com/absfs/memfs"
)

func main() {
	fs, _ := memfs.NewFS()

	// Get temp directory
	tmpDir := fs.TempDir()
	fmt.Println(tmpDir)

	// Create temp directory if it doesn't exist
	fs.MkdirAll(tmpDir, 0755)

	// Create a temp file
	tmpFile := path.Join(tmpDir, "temp-12345.txt")
	file, _ := fs.Create(tmpFile)
	file.Write([]byte("temporary data"))
	file.Close()

	// Use the temp file
	file, _ = fs.Open(tmpFile)
	data, _ := io.ReadAll(file)
	file.Close()

	fmt.Println(string(data))

	// Clean up
	fs.Remove(tmpFile)

}
Output:

/tmp
temporary data
Example (WalkFileTree)

Example_walkFileTree demonstrates traversing a file tree.

package main

import (
	"fmt"
	"os"

	"github.com/absfs/fstools"
	"github.com/absfs/memfs"
)

func main() {
	fs, _ := memfs.NewFS()

	// Create a directory structure
	fs.MkdirAll("/project/src/main", 0755)
	fs.MkdirAll("/project/src/utils", 0755)
	fs.Create("/project/README.md")
	fs.Create("/project/src/main/app.go")
	fs.Create("/project/src/utils/helpers.go")

	// Walk the file tree
	fstools.Walk(fs, "/project", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			fmt.Printf("[DIR]  %s\n", path)
		} else {
			fmt.Printf("[FILE] %s\n", path)
		}
		return nil
	})

}
Output:

[DIR]  /project
[FILE] /project/README.md
[DIR]  /project/src
[DIR]  /project/src/main
[FILE] /project/src/main/app.go
[DIR]  /project/src/utils
[FILE] /project/src/utils/helpers.go
Example (WriteAndRead)

Example_writeAndRead demonstrates writing and reading from files.

package main

import (
	"fmt"
	"io"

	"github.com/absfs/memfs"
)

func main() {
	fs, _ := memfs.NewFS()

	// Create and write to a file
	file, _ := fs.Create("/data.txt")
	file.Write([]byte("line 1\n"))
	file.Write([]byte("line 2\n"))
	file.Write([]byte("line 3\n"))
	file.Close()

	// Open and read the entire file
	file, _ = fs.Open("/data.txt")
	defer file.Close()

	data, _ := io.ReadAll(file)
	fmt.Print(string(data))

}
Output:

line 1
line 2
line 3

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type File

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

File represents an open file in the in-memory file system.

It maintains the file's state including the current read/write offset, access flags, and a reference to the underlying inode. File implements the absfs.File interface and provides standard file operations.

File operations use the FileSystem's ByteStore for all data access, which provides thread-safe read and write operations.

func (*File) Close

func (f *File) Close() error

Close closes the file, making it unusable for I/O.

This releases the file handle. Subsequent operations on the file will return errors. Note: Since we use ByteStore directly, there's no buffering to sync.

func (*File) Name

func (f *File) Name() string

Name returns the name of the file as provided to Open or Create.

func (*File) Read

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

Read reads up to len(p) bytes from the file into p.

Returns the number of bytes read and any error encountered. Returns io.EOF when the end of the file is reached. Returns an error if the file was not opened for reading or if the file handle is invalid.

func (*File) ReadAt

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

ReadAt reads len(b) bytes from the file starting at byte offset off.

Returns the number of bytes read and any error encountered. Unlike Read, ReadAt does not update the file's current offset. Returns an error if the file was not opened for reading.

func (*File) ReadDir

func (f *File) ReadDir(n int) ([]fs.DirEntry, 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.

func (*File) Readdir

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

Readdir reads the contents of the directory associated with the file.

Returns up to n FileInfo values. If n <= 0, returns all remaining entries. Subsequent calls continue from where the previous call left off. Returns io.EOF when no more entries remain. Returns an error if the file is not a directory or was not opened for reading.

func (*File) Readdirnames

func (f *File) Readdirnames(n int) ([]string, error)

Readdirnames reads directory entries and returns their names.

Returns up to n entry names. If n <= 0, returns all remaining names. Subsequent calls continue from where the previous call left off. Returns io.EOF when no more entries remain. Returns an error if the file is not a directory or was not opened for reading.

func (*File) Seek

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

Seek sets the offset for the next Read or Write on the file.

The whence parameter determines the reference point: io.SeekStart (beginning), io.SeekCurrent (current position), or io.SeekEnd (end of file). Returns the new offset from the beginning of the file.

func (*File) Stat

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

Stat returns file information about the file.

Returns an error if the file handle is invalid.

func (*File) Sync

func (f *File) Sync() error

Sync commits the current contents of the file to the file system.

Since we use ByteStore directly without buffering, this is a no-op. All writes are immediately visible.

func (*File) Truncate

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

Truncate changes the size of the file.

If the file is larger than size, it is truncated. If it is smaller, it is extended with zero bytes. Returns an error if the file was not opened for writing.

func (*File) Write

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

Write writes len(p) bytes from p to the file.

Returns the number of bytes written and any error encountered. The file's data is automatically expanded if necessary. Returns an error if the file was not opened for writing. If the file was opened with O_APPEND, writes always append to the end of the file regardless of the current offset.

func (*File) WriteAt

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

WriteAt writes len(b) bytes to the file starting at byte offset off.

Returns the number of bytes written and any error encountered. Unlike Write, WriteAt does not update the file's current offset.

func (*File) WriteString

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

WriteString writes the contents of string s to the file.

Returns the number of bytes written and any error encountered. This is a convenience method equivalent to Write([]byte(s)).

type FileSystem

type FileSystem struct {
	Umask   os.FileMode
	Tempdir string
	// contains filtered or unexported fields
}

FileSystem represents an in-memory file system.

It maintains a hierarchical structure of inodes representing files and directories, along with their associated data and metadata. The file system supports standard POSIX-like operations including file creation, deletion, permissions, symbolic links, and directory traversal.

Thread Safety: FileSystem uses a thread-safe ByteStore for file data and a sync.Map for symlinks, making it safe for concurrent use by multiple goroutines. The ByteStore handles all file data operations with its own internal synchronization.

func NewFS

func NewFS() (*FileSystem, error)

NewFS creates and initializes a new in-memory file system.

The file system is created with a root directory ("/") and default settings including a umask of 0755 and a temp directory at "/tmp". Returns a pointer to the initialized FileSystem and nil error.

Example

ExampleNewFS demonstrates creating a new in-memory file system.

package main

import (
	"fmt"

	"github.com/absfs/memfs"
)

func main() {
	fs, err := memfs.NewFS()
	if err != nil {
		panic(err)
	}

	// Get the current working directory
	cwd, _ := fs.Getwd()
	fmt.Println(cwd)

}
Output:

/

func (*FileSystem) Chdir

func (fs *FileSystem) Chdir(name string) (err error)

Chdir changes the current working directory to the named directory.

The directory must exist and be accessible. Both absolute and relative paths are supported. Returns an error if the path does not exist or is not a directory.

func (*FileSystem) Chmod

func (fs *FileSystem) Chmod(name string, mode os.FileMode) error

Chmod changes the mode of the named file to mode.

func (*FileSystem) Chown

func (fs *FileSystem) Chown(name string, uid, gid int) error

Chown changes the owner and group ids of the named file

func (*FileSystem) Chtimes

func (fs *FileSystem) Chtimes(name string, atime time.Time, mtime time.Time) error

Chtimes changes the access and modification times of the named file

func (*FileSystem) Create

func (fs *FileSystem) Create(name string) (absfs.File, error)

Create creates or truncates the named file for writing.

This is equivalent to OpenFile(name, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644). If the file already exists, it is truncated. Returns an error if the file cannot be created.

func (*FileSystem) Getwd

func (fs *FileSystem) Getwd() (dir string, err error)

Getwd returns the current working directory path.

The returned path is always an absolute path. This method never returns an error in the current implementation.

func (*FileSystem) Lchown

func (fs *FileSystem) Lchown(name string, uid, gid int) error

Lchown changes the owner and group of the named file without following symbolic links.

Unlike Chown, if the file is a symbolic link, Lchown changes the ownership of the link itself rather than the file it points to.

func (*FileSystem) Lstat

func (fs *FileSystem) Lstat(name string) (os.FileInfo, error)

Lstat returns file information for the named file without following symbolic links.

Unlike Stat, if the file is a symbolic link, Lstat returns information about the link itself. Returns an error if the file does not exist.

func (*FileSystem) Mkdir

func (fs *FileSystem) Mkdir(name string, perm os.FileMode) error

Mkdir creates a new directory with the specified name and permissions.

The parent directory must already exist. Returns an error if the directory already exists, the parent directory does not exist, or if the operation fails.

func (*FileSystem) MkdirAll

func (fs *FileSystem) MkdirAll(name string, perm os.FileMode) error

MkdirAll creates a directory and all necessary parent directories.

If the directory already exists, MkdirAll does nothing and returns nil. This is similar to the 'mkdir -p' command in Unix.

func (*FileSystem) Open

func (fs *FileSystem) Open(name string) (absfs.File, error)

Open opens the named file for reading.

This is equivalent to OpenFile(name, os.O_RDONLY, 0). Returns an error if the file does not exist or cannot be opened.

func (*FileSystem) OpenFile

func (fs *FileSystem) OpenFile(name string, flag int, perm os.FileMode) (absfs.File, error)

OpenFile opens the named file with specified flags and permissions.

Supported flags include os.O_RDONLY, os.O_WRONLY, os.O_RDWR, os.O_CREATE, os.O_EXCL, and os.O_TRUNC. The perm argument specifies the file permissions to use if a new file is created. Both absolute and relative paths are supported. Symlinks are followed automatically when opening files. Returns an error if the operation fails due to permission issues, missing parent directories, or flag conflicts.

func (*FileSystem) ReadDir

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

ReadDir reads the named directory and returns a list of directory entries sorted by filename. This is compatible with io/fs.ReadDirFS.

func (*FileSystem) ReadFile

func (fs *FileSystem) ReadFile(name string) ([]byte, error)

ReadFile reads the named file and returns its contents. This is compatible with io/fs.ReadFileFS.

func (fs *FileSystem) Readlink(name string) (string, error)

Readlink returns the target of the named symbolic link.

Returns an error if the file does not exist or is not a symbolic link.

func (*FileSystem) Remove

func (fs *FileSystem) Remove(name string) (err error)

Remove deletes the named file or empty directory.

Returns an error if the file does not exist, if it is a non-empty directory, or if the operation fails. Use RemoveAll to delete non-empty directories.

func (*FileSystem) RemoveAll

func (fs *FileSystem) RemoveAll(name string) error

RemoveAll removes the named file or directory and all its contents.

Unlike Remove, RemoveAll will recursively delete directories and their contents. Returns an error if the file does not exist or if the operation fails.

func (*FileSystem) Rename

func (fs *FileSystem) Rename(oldpath, newpath string) error

Rename moves or renames a file or directory from oldpath to newpath.

Both relative and absolute paths are supported. Relative paths are resolved relative to the current working directory. The root directory cannot be renamed. Returns an error if oldpath does not exist, newpath already exists, or if the operation violates file system constraints.

func (*FileSystem) Stat

func (fs *FileSystem) Stat(name string) (os.FileInfo, error)

Stat returns file information for the named file, following symbolic links.

If the file is a symbolic link, Stat returns information about the file the link points to. Returns an error if the file does not exist or if a symbolic link loop is detected.

func (*FileSystem) Sub

func (fs *FileSystem) Sub(dir string) (fs.FS, error)

Sub returns an fs.FS corresponding to the subtree rooted at dir. This is compatible with io/fs.SubFS.

func (fs *FileSystem) Symlink(oldname, newname string) error

Symlink creates a symbolic link at newname pointing to oldname.

The symlink stores oldname exactly as provided (it can be absolute or relative). Returns an error if newname already exists or if the parent directory of newname does not exist. Note: Unlike some implementations, the target (oldname) does NOT need to exist - broken symlinks are valid.

func (*FileSystem) TempDir

func (fs *FileSystem) TempDir() string

TempDir returns the path to the temporary directory.

This directory is typically used for temporary file storage. The default value is "/tmp", but can be configured via the Tempdir field.

func (*FileSystem) Truncate

func (fs *FileSystem) Truncate(name string, size int64) error

Truncate changes the size of the named file.

If the file is larger than size, it is truncated. If it is smaller, it is extended with zero bytes. Returns an error if the file does not exist.

type MemByteStore

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

MemByteStore implements a thread-safe in-memory ByteStore.

This implementation uses a map of atomic pointers to byte slices, enabling lock-free reads while ensuring thread-safe writes through a mutex. Each file's data is stored as an atomic.Pointer[[]byte], allowing concurrent readers to access file data without blocking each other or blocking writers.

The store supports sparse files by filling gaps with zeros when writing beyond the current file size.

Thread Safety:

  • Reads (ReadAt, Stat) use atomic loads for lock-free access
  • Writes (WriteAt, Truncate) use a mutex to prevent concurrent modifications
  • Remove operations use a mutex to safely delete entries

This design makes MemByteStore fully thread-safe and suitable for use in concurrent environments without requiring external synchronization.

func NewMemByteStore

func NewMemByteStore() *MemByteStore

NewMemByteStore creates a new thread-safe in-memory ByteStore.

func (*MemByteStore) ReadAt

func (s *MemByteStore) ReadAt(ino uint64, p []byte, off int64) (int, error)

ReadAt reads len(p) bytes from the file at the given offset.

Returns the number of bytes read and any error encountered. Returns io.EOF when offset is at or beyond the end of file.

This operation is lock-free for better concurrency. It uses atomic loads to read the current file data without blocking other readers or writers.

func (*MemByteStore) Remove

func (s *MemByteStore) Remove(ino uint64) error

Remove deletes all data associated with the inode.

After Remove, operations on this inode will behave as if the file doesn't exist. Calling Remove on a non-existent inode is a no-op.

This operation uses a mutex to ensure thread-safe removal.

func (*MemByteStore) Stat

func (s *MemByteStore) Stat(ino uint64) (int64, error)

Stat returns the current size of the file in bytes.

Returns (0, nil) for empty or non-existent files.

This operation uses atomic loads for lock-free access.

func (*MemByteStore) Truncate

func (s *MemByteStore) Truncate(ino uint64, size int64) error

Truncate changes the file size.

If the file is larger than size, it is truncated to size. If the file is smaller, it is extended with zero bytes to size.

This operation uses a mutex to ensure thread-safe modifications.

func (*MemByteStore) WriteAt

func (s *MemByteStore) WriteAt(ino uint64, p []byte, off int64) (int, error)

WriteAt writes len(p) bytes to the file at the given offset.

Extends the file if necessary, filling any gaps with zeros to support sparse file semantics. Returns the number of bytes written and any error.

This operation uses a mutex to ensure thread-safe modifications. The implementation uses atomic compare-and-swap to update the file data, ensuring consistency even in the face of concurrent operations.

Jump to

Keyboard shortcuts

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