vfs

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Mar 18, 2023 License: MIT Imports: 9 Imported by: 0

README

vfs for golang Build Status GoDoc Coverage Status

vfs is library to support virtual filesystems. It provides basic abstractions of filesystems and implementations, like OS accessing the file system of the underlying OS and memfs a full filesystem in-memory.

Usage

$ go get github.com/3JoB/vfs

Note: Always vendor your dependencies or fix on a specific version tag.

import github.com/3JoB/vfs
// Create a vfs accessing the filesystem of the underlying OS
var osfs vfs.Filesystem = vfs.OS()
osfs.Mkdir("/tmp", 0777)

// Make the filesystem read-only:
osfs = vfs.ReadOnly(osfs) // Simply wrap filesystems to change its behaviour

// os.O_CREATE will fail and return vfs.ErrReadOnly
// os.O_RDWR is supported but Write(..) on the file is disabled
f, _ := osfs.OpenFile("/tmp/example.txt", os.O_RDWR, 0)

// Return vfs.ErrReadOnly
_, err := f.Write([]byte("Write on readonly fs?"))
if err != nil {
    fmt.Errorf("Filesystem is read only!\n")
}

// Create a fully writable filesystem in memory
mfs := memfs.Create()
mfs.Mkdir("/root", 0777)

// Create a vfs supporting mounts
// The root fs is accessing the filesystem of the underlying OS
fs := mountfs.Create(osfs)

// Mount a memfs inside /memfs
// /memfs may not exist
fs.Mount(mfs, "/memfs")

// This will create /testdir inside the memfs
fs.Mkdir("/memfs/testdir", 0777)

// This would create /tmp/testdir inside your OS fs
// But the rootfs `osfs` is read-only
fs.Mkdir("/tmp/testdir", 0777)

Check detailed examples below. Also check the GoDocs.

Why should I use this lib?

  • Only Stdlib
  • (Nearly) Fully tested (Coverage >90%)
  • Easy to create your own filesystem
  • Mock a full filesystem for testing (or use included memfs)
  • Compose/Wrap Filesystems ReadOnly(OS()) and write simple Wrappers
  • Many features, see GoDocs and examples below

Features and Examples

Current state: ALPHA

While the functionality is quite stable and heavily tested, interfaces are subject to change.

You need more/less abstraction? Let me know by creating a Issue, thank you.

Motivation

I simply couldn't find any lib supporting this wide range of variation and adaptability.

Contribution

Feel free to make a pull request. For bigger changes create a issue first to discuss about it.

License

See LICENSE file.

Documentation

Overview

Package vfs defines an abstract file system and various implementations.

Example
package main

import (
	"errors"
	"os"

	"github.com/3JoB/vfs"
	"github.com/3JoB/vfs/memfs"
	"github.com/3JoB/vfs/mountfs"
)

func main() {
	// Create a vfs accessing the filesystem of the underlying OS
	var osfs vfs.Filesystem = vfs.OS()
	osfs.Mkdir("/tmp", 0777)

	// Make the filesystem read-only:
	osfs = vfs.ReadOnly(osfs) // Simply wrap filesystems to change its behaviour

	// os.O_CREATE will fail and return vfs.ErrReadOnly
	// os.O_RDWR is supported but Write(..) on the file is disabled
	f, _ := osfs.OpenFile("/tmp/example.txt", os.O_RDWR, 0)

	// Return vfs.ErrReadOnly
	_, err := f.Write([]byte("Write on readonly fs?"))
	if err != nil {
		panic(errors.New("filesystem is read only!\n"))
	}

	// Create a fully writable filesystem in memory
	mfs := memfs.Create()
	mfs.Mkdir("/root", 0777)

	// Create a vfs supporting mounts
	// The root fs is accessing the filesystem of the underlying OS
	fs := mountfs.Create(osfs)

	// Mount a memfs inside /memfs
	// /memfs may not exist
	fs.Mount(mfs, "/memfs")

	// This will create /testdir inside the memfs
	fs.Mkdir("/memfs/testdir", 0777)

	// This would create /tmp/testdir inside your OS fs
	// But the rootfs `osfs` is read-only
	fs.Mkdir("/tmp/testdir", 0777)
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrIsDirectory is returned if a file is a directory
	ErrIsDirectory = errors.New("is directory")

	// ErrNotDirectory is returned if a file is not a directory
	ErrNotDirectory = errors.New("is not a directory")
)
View Source
var ErrReadOnly = errors.New("Filesystem is read-only")

ErrorReadOnly is returned on every disabled operation.

Functions

func MkdirAll

func MkdirAll(fs Filesystem, path string, perm os.FileMode) error

MkdirAll creates a directory named path on the given Filesystem, along with any necessary parents, and returns nil, or else returns an error. The permission bits perm are used for all directories that MkdirAll creates. If path is already a directory, MkdirAll does nothing and returns nil.

func ReadFile

func ReadFile(fs Filesystem, filename string) ([]byte, error)

ReadFile reads the file named by filename and returns the contents. A successful call returns err == nil, not err == EOF. Because ReadFile reads the whole file, it does not treat an EOF from Read as an error to be reported.

This is a port of the stdlib ioutil.ReadFile function.

func RemoveAll

func RemoveAll(fs Filesystem, path string) error

RemoveAll removes path and any children it contains. It removes everything it can but returns the first error it encounters. If the path does not exist, RemoveAll returns nil.

func SplitPath

func SplitPath(path string, sep string) []string

SplitPath splits the given path in segments:

"/" 				-> []string{""}
"./file" 			-> []string{".", "file"}
"file" 				-> []string{".", "file"}
"/usr/src/linux/" 	-> []string{"", "usr", "src", "linux"}

The returned slice of path segments consists of one more more segments.

func Walk

func Walk(fs Filesystem, root string, walkFunc filepath.WalkFunc) error

Walk walks the file tree rooted at root, calling walkFunc for each file or directory in the tree, including root. All errors that arise visiting files and directories are filtered by walkFn. The files are walked in lexical order, which makes the output deterministic but means that for very large directories Walk can be inefficient. Walk does not follow symbolic links.

func WriteFile

func WriteFile(fs Filesystem, filename string, data []byte, perm os.FileMode) error

WriteFile writes data to a file named by filename on the given Filesystem. If the file does not exist, WriteFile creates it with permissions perm; otherwise WriteFile truncates it before writing.

This is a port of the stdlib ioutil.WriteFile function.

Types

type DumFile

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

DumFile represents a dummy File

func DummyFile

func DummyFile(err error) *DumFile

DummyFile mocks a File returning an error on every operation To create a DummyFS returning a dummyFile instead of an error you can your own DummyFS:

type writeDummyFS struct {
	Filesystem
}

func (fs writeDummyFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
	return DummyFile(dummyError), nil
}

func (DumFile) Close

func (f DumFile) Close() error

Close returns dummy error

func (DumFile) Name

func (f DumFile) Name() string

Name returns "dummy"

func (DumFile) Read

func (f DumFile) Read(p []byte) (n int, err error)

Read returns dummy error

func (DumFile) ReadAt

func (f DumFile) ReadAt(p []byte, off int64) (n int, err error)

ReadAt returns dummy error

func (DumFile) Seek

func (f DumFile) Seek(offset int64, whence int) (int64, error)

Seek returns dummy error

func (DumFile) Sync

func (f DumFile) Sync() error

Sync returns dummy error

func (DumFile) Truncate

func (f DumFile) Truncate(size int64) error

Truncate returns dummy error

func (DumFile) Write

func (f DumFile) Write(p []byte) (n int, err error)

Write returns dummy error

type DumFileInfo

type DumFileInfo struct {
	IName    string
	ISize    int64
	IMode    os.FileMode
	IModTime time.Time
	IDir     bool
	ISys     any
}

DumFileInfo mocks a os.FileInfo returning default values on every operation Struct fields can be set.

func (DumFileInfo) IsDir

func (fi DumFileInfo) IsDir() bool

IsDir returns the field IDir

func (DumFileInfo) ModTime

func (fi DumFileInfo) ModTime() time.Time

ModTime returns the field IModTime

func (DumFileInfo) Mode

func (fi DumFileInfo) Mode() os.FileMode

Mode returns the field IMode

func (DumFileInfo) Name

func (fi DumFileInfo) Name() string

Name returns the field IName

func (DumFileInfo) Size

func (fi DumFileInfo) Size() int64

Size returns the field ISize

func (DumFileInfo) Sys

func (fi DumFileInfo) Sys() any

Sys returns the field ISys

type DummyFS

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

DummyFS is dummy filesystem which returns an error on every operation. It can be used to mock a full filesystem for testing or fs creation.

Example
package main

import (
	"errors"
	"fmt"
	"os"

	"github.com/3JoB/vfs"
)

type myFS struct {
	vfs.Filesystem // Embed the Filesystem interface and fill it with vfs.Dummy on creation
}

func MyFS() *myFS {
	return &myFS{
		Filesystem: vfs.Dummy(errors.New("Not implemented yet!")),
	}
}

func (fs myFS) Mkdir(name string, perm os.FileMode) error {
	// Create a directory
	// ...
	return nil
}

func main() {
	// Simply bootstrap your filesystem
	var fs vfs.Filesystem = MyFS()

	// Your mkdir implementation
	fs.Mkdir("/tmp", 0777)

	// All necessary methods like OpenFile (therefor Create) are stubbed
	// and return the dummys error
	_, err := vfs.Create(fs, "/tmp/vfs/example.txt")
	if err != nil {
		fmt.Print("Error will be: Not implemented yet!\n")
	}
}
Output:

func Dummy

func Dummy(err error) *DummyFS

Dummy creates a new dummy filesystem which returns the given error on every operation.

func (DummyFS) Lstat

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

Lstat returns dummy error

func (DummyFS) Mkdir

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

Mkdir returns dummy error

func (DummyFS) Open

func (fs DummyFS) Open(name string) (File, error)

Open returns dummy error

func (DummyFS) OpenFile

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

OpenFile returns dummy error

func (DummyFS) PathSeparator

func (fs DummyFS) PathSeparator() uint8

PathSeparator returns the path separator

func (DummyFS) ReadDir

func (fs DummyFS) ReadDir(path string) ([]os.FileInfo, error)

ReadDir returns dummy error

func (DummyFS) Remove

func (fs DummyFS) Remove(name string) error

Remove returns dummy error

func (DummyFS) Rename

func (fs DummyFS) Rename(oldpath, newpath string) error

Rename returns dummy error

func (DummyFS) Stat

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

Stat returns dummy error

func (fs DummyFS) Symlink(oldname, newname string) error

Symlink returns dummy error

type File

type File interface {
	Name() string
	Sync() error

	// Truncate shrinks or extends the size of the File to the specified size.
	Truncate(int64) error

	io.Reader
	io.ReaderAt
	io.Writer
	io.Seeker
	io.Closer
}

File represents a File with common operations. It differs from os.File so e.g. Stat() needs to be called from the Filesystem instead.

osfile.Stat() -> filesystem.Stat(file.Name())

func Create

func Create(fs Filesystem, name string) (File, error)

Create creates the named file mode 0666 (before umask) on the given Filesystem, truncating it if it already exists. The associated file descriptor has mode os.O_RDWR. If there is an error, it will be of type *os.PathError.

func Open

func Open(fs Filesystem, name string) (File, error)

Open opens the named file on the given Filesystem for reading. If successful, methods on the returned file can be used for reading. The associated file descriptor has mode os.O_RDONLY. If there is an error, it will be of type *PathError.

func ReadOnlyFile

func ReadOnlyFile(f File) File

ReadOnlyFile wraps the given file and disables Write(..) operation.

type Filesystem

type Filesystem interface {
	PathSeparator() uint8
	OpenFile(name string, flag int, perm os.FileMode) (File, error)
	Remove(name string) error

	// RemoveAll(path string) error
	Rename(oldpath, newpath string) error

	Mkdir(name string, perm os.FileMode) error

	Symlink(oldname, newname string) error

	// TempDir() string
	// Chmod(name string, mode FileMode) error
	// Chown(name string, uid, gid int) error
	Stat(name string) (os.FileInfo, error)

	Lstat(name string) (os.FileInfo, error)
	ReadDir(path string) ([]os.FileInfo, error)
}

Filesystem represents an abstract filesystem

type OsFS

type OsFS struct{}

OsFS represents a filesystem backed by the filesystem of the underlying OS.

Example
package main

import (
	"fmt"

	"github.com/3JoB/vfs"
)

func main() {
	// Create a vfs accessing the filesystem of the underlying OS
	osFS := vfs.OS()
	err := osFS.Mkdir("/tmp/vfs_example", 0777)
	if err != nil {
		fmt.Printf("Error creating directory: %s\n", err)
	}

	// Convenience method
	f, err := vfs.Create(osFS, "/tmp/vfs_example/example.txt")
	// f, err := osFS.OpenFile("/tmp/vfs/example.txt", os.O_CREATE|os.O_RDWR, 0666)
	if err != nil {
		fmt.Printf("Could not create file: %s\n", err)
	}
	defer f.Close()
	if _, err := f.Write([]byte("VFS working on your filesystem")); err != nil {
		fmt.Printf("Error writing to file: %s\n", err)
	}
}
Output:

Example (MyWrapper)
package main

import (
	"errors"
	"fmt"
	"os"

	"github.com/3JoB/vfs"
)

type noNewDirs struct {
	vfs.Filesystem
}

func NoNewDirs(fs vfs.Filesystem) *noNewDirs {
	return &noNewDirs{Filesystem: fs}
}

// Mkdir is disabled
func (fs *noNewDirs) Mkdir(name string, perm os.FileMode) error {
	return errors.New("Mkdir disabled!")
}

func main() {
	// Disable Mkdirs on the OS Filesystem
	var fs vfs.Filesystem = NoNewDirs(vfs.OS())

	err := fs.Mkdir("/tmp", 0777)
	if err != nil {
		fmt.Print("Mkdir disabled!\n")
	}
}
Output:

func OS

func OS() *OsFS

OS returns a filesystem backed by the filesystem of the os. It wraps os.* stdlib operations.

func (OsFS) Lstat

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

Lstat wraps os.Lstat

func (OsFS) Mkdir

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

Mkdir wraps os.Mkdir

func (OsFS) Open

func (fs OsFS) Open(name string) (File, error)

Open wraps os.Open

func (OsFS) OpenFile

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

OpenFile wraps os.OpenFile

func (OsFS) PathSeparator

func (fs OsFS) PathSeparator() uint8

PathSeparator returns the path separator

func (OsFS) ReadDir

func (fs OsFS) ReadDir(path string) ([]os.FileInfo, error)

ReadDir wraps ioutil.ReadDir

func (OsFS) Remove

func (fs OsFS) Remove(name string) error

Remove wraps os.Remove

func (OsFS) RemoveAll

func (fs OsFS) RemoveAll(name string) error

RemoveAll removes path and any children it contains. It removes everything it can but returns the first error it encounters. If the path does not exist, RemoveAll returns nil (no error). If there is an error, it will be of type *PathError.

func (OsFS) Rename

func (fs OsFS) Rename(oldpath, newpath string) error

Rename wraps os.Rename

func (OsFS) Stat

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

Stat wraps os.Stat

func (fs OsFS) Symlink(oldname, newname string) error

Symlink wraps os.Symlink

type RoFS

type RoFS struct {
	Filesystem
}

RoFS represents a read-only filesystem and works as a wrapper around existing filesystems.

Example

Every vfs.Filesystem could be easily wrapped

package main

import (
	"fmt"
	"os"

	"github.com/3JoB/vfs"
)

func main() {
	// Create a readonly vfs accessing the filesystem of the underlying OS
	roFS := vfs.ReadOnly(vfs.OS())

	// Mkdir is disabled on ReadOnly vfs, will return vfs.ErrReadOnly
	// See vfs.ReadOnly for all disabled operations
	err := roFS.Mkdir("/tmp/vfs_example", 0777)
	if err != nil {
		fmt.Printf("Error creating directory: %s\n", err)
		return
	}

	// OpenFile is controlled to support read-only functionality. os.O_CREATE or os.O_APPEND will fail.
	// Flags like os.O_RDWR are supported but the returned file is protected e.g. from Write(..).
	f, err := roFS.OpenFile("/tmp/vfs_example/example.txt", os.O_RDWR, 0)
	if err != nil {
		fmt.Printf("Could not create file: %s\n", err)
		return
	}
	defer f.Close()

	// Will fail and return vfs.ErrReadOnly
	_, err = f.Write([]byte("VFS working on your filesystem"))
	if err != nil {
		fmt.Printf("Could not write file on read only filesystem: %s", err)
		return
	}
}
Output:

func ReadOnly

func ReadOnly(fs Filesystem) *RoFS

ReadOnly creates a readonly wrapper around the given filesystem. It disables the following operations:

  • Create
  • Remove
  • Rename
  • Mkdir

And disables OpenFile flags: os.O_CREATE, os.O_APPEND, os.O_WRONLY

OpenFile returns a File with disabled Write() method otherwise.

func (RoFS) Mkdir

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

Mkdir is disabled and returns ErrorReadOnly

func (RoFS) Open

func (fs RoFS) Open(name string) (File, error)

Open opens the named file on the given Filesystem for reading. If successful, methods on the returned file can be used for reading. The associated file descriptor has mode os.O_RDONLY. If there is an error, it will be of type *PathError.

func (RoFS) OpenFile

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

OpenFile returns ErrorReadOnly if flag contains os.O_CREATE, os.O_APPEND, os.O_WRONLY. Otherwise it returns a read-only File with disabled Write(..) operation.

func (RoFS) Remove

func (fs RoFS) Remove(name string) error

Remove is disabled and returns ErrorReadOnly

func (RoFS) Rename

func (fs RoFS) Rename(oldpath, newpath string) error

Rename is disabled and returns ErrorReadOnly

func (fs RoFS) Symlink(oldname, newname string) error

Directories

Path Synopsis
Package memfs defines an in-memory filesystem
Package memfs defines an in-memory filesystem
Package mountfs defines a filesystem supporting the composition of multiple filesystems by mountpoints.
Package mountfs defines a filesystem supporting the composition of multiple filesystems by mountpoints.

Jump to

Keyboard shortcuts

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