| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
A filesystem abstraction for Go that extends io/fs with write operations and context support. The same code reads and writes local disks, in-memory filesystems, and remote systems reached over SSH, S3, SMB, or WebDAV.
// Copying between filesystems is io.Copy of standard interfaces.
w, err := fs.Create(ctx, remote, "backup.tar.gz")
if err != nil {
return err
}
defer w.Close()
r, err := fs.Open(ctx, local, "backup.tar.gz")
if err != nil {
return err
}
defer r.Close()
_, err = io.Copy(w, r)| Capability | io/fs | os | lesiw.io/fs |
|---|---|---|---|
| Read files | ✅ | ✅ | ✅ |
| Write files | ❌ | ✅ | ✅ |
| Create/remove directories | ❌ | ✅ | ✅ |
| Read metadata (stat) | ✅ | ✅ | ✅ |
| Write metadata (chmod, chtimes) | ❌ | ✅ | ✅ |
| Create symbolic links | ❌ | ✅ | ✅ |
| io interfaces, not File types | ❌ | ❌ | ✅ |
| Fallback implementations | ✅ | ❌ | ✅ |
| Context support | ❌ | ❌ | ✅ |
| Bulk operations (tar) | ❌ | ❌ | ✅ |
| Virtual directories | ❌ | ❌ | ✅ |
| Range-over-func iterators | ❌ | ❌ | ✅ |
go get lesiw.io/fsRequires Go 1.24.2 or later.
package main
import (
"context"
"fmt"
"log"
"lesiw.io/fs"
"lesiw.io/fs/memfs"
)
func main() {
ctx, fsys := context.Background(), memfs.New()
defer fs.Close(fsys)
err := fs.WriteFile(ctx, fsys, "hello.txt", []byte("Hello, world!"))
if err != nil {
log.Fatal(err)
}
content, err := fs.ReadFile(ctx, fsys, "hello.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(content))
}Tip
The in-memory filesystem runs in the Go Playground, so the package can be tried without installing anything: run an example.
The core interface requires one method.
type FS interface {
Open(ctx context.Context, name string) (io.ReadCloser, error)
}Everything else is an optional interface an implementation may add — CreateFS for writing, MkdirFS for directories, StatFS for metadata, two dozen in all. Helper functions probe for the capability and report fs.ErrUnsupported when an operation has no path forward.
w, err := fs.Create(ctx, fsys, "file.txt")
if errors.Is(err, fs.ErrUnsupported) {
// Filesystem is read-only.
}Many helpers carry fallbacks, so an implementation supplies the operations it can do natively and inherits the rest. Append falls back to read-and-rewrite, Rename to copy-and-delete, Walk and ReadDir to each other, and directory-as-tar operations to archive/tar over a filesystem walk.
Cancellation and deadlines work the way they do everywhere else in Go, which matters most when the filesystem is on the far side of a network.
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
data, err := fs.ReadFile(ctx, remote, "large-file.dat")File and directory modes travel on the context too, where they apply to every operation in a chain — including parent directories created implicitly along the way.
ctx = fs.WithFileMode(ctx, 0600)
ctx = fs.WithDirMode(ctx, 0700)
err := fs.WriteFile(ctx, fsys, "logs/2026/app.log", data)That call creates logs/2026/ with mode 0700 when it doesn't exist, then writes the file with mode 0600. Object stores treat the nested path as a key and skip directory creation; traditional filesystems create the parents. The calling code is the same for both.
A trailing slash names a directory, and directories read and write as tar archives with the same verbs as files.
// Read a directory as an archive, like reading a file.
r, err := fs.Open(ctx, fsys, "project/")
// Add files to a directory, like appending to a file.
w, err := fs.Append(ctx, fsys, "project/")
// Empty a directory, like truncating a file.
err = fs.Truncate(ctx, fsys, "project/", 0)
// Replace a directory's contents, like creating a file.
w, err = fs.Create(ctx, fsys, "restore/")Implementations with native tar support (DirFS, AppendDirFS, TruncateDirFS) move one stream instead of one request per file; without them, the helpers fall back to a filesystem walk with archive/tar.
Walk and ReadDir are range-over-func iterators, so errors are handled in the loop and early termination costs nothing.
for entry, err := range fs.Walk(ctx, fsys, "project", 3) {
if err != nil {
return err
}
if entry.Name() == "target.txt" {
return process(entry)
}
}Maintained in this module:
Reference implementations demonstrating the abstraction across diverse backends, with Docker-based tests:
lesiw.io/command builds on this package: its command.FS derives a filesystem for any machine it can run commands on, ssh hosts and containers included.
The fstest package validates filesystem implementations.
func TestMyFS(t *testing.T) {
fsys := myfs.New()
t.Cleanup(func() { _ = fs.Close(fsys) })
fstest.TestFS(t.Context(), t, fsys)
}The suite detects capabilities through type assertions and exercises every operation the implementation supports, fallbacks included.
| Back | FazBrowse Home | New Git URL |