go-fuse: github.com/hanwen/go-fuse/fs Index | Examples | Files

package fs

import "github.com/hanwen/go-fuse/fs"

Package fs provides infrastructure to build tree-organized filesystems.

Structure of a file system implementation

To create a file system, you should first define types for the nodes of the file system tree.

struct myNode {
   fs.Inode
}

// Node types must be InodeEmbedders
var _ = (fs.InodeEmbedder)((*myNode)(nil))

// Node types should implement some file system operations, eg. Lookup
var _ = (fs.NodeLookuper)((*myNode)(nil))

func (n *myNode) Lookup(ctx context.Context, name string,  ... ) (*Inode, syscall.Errno) {
  ops := myNode{}
  return n.NewInode(ctx, &ops, fs.StableAttr{Mode: syscall.S_IFDIR}), 0
}

The method names are inspired on the system call names, so we have Listxattr rather than ListXAttr.

the file system is mounted by calling mount on the root of the tree,

server, err := fs.Mount("/tmp/mnt", &myNode{}, &fs.Options{})
..
// start serving the file system
server.Wait()

Error handling

All error reporting must use the syscall.Errno type. This is an integer with predefined error codes, where the value 0 (`OK`) should be used to indicate success.

File system concepts

The FUSE API is very similar to Linux' internal VFS API for defining file systems in the kernel. It is therefore useful to understand some terminology.

File content: the raw bytes that we store inside regular files.

Path: a /-separated string path that describes location of a node in the file system tree. For example

dir1/file

describes path root → dir1 → file.

There can be several paths leading from tree root to a particular node, known as hard-linking, for example

  root
  /  \
dir1 dir2
  \  /
  file

Inode: ("index node") points to the file content, and stores metadata (size, timestamps) about a file or directory. Each inode has a type (directory, symlink, regular file, etc.) and an identity (a 64-bit number, unique to the file system). Directories can have children.

The inode in the kernel is represented in Go-FUSE as the Inode type.

While common OS APIs are phrased in terms of paths (strings), the precise semantics of a file system are better described in terms of Inodes. This allows us to specify what happens in corner cases, such as writing data to deleted files.

File descriptor: a handle returned to opening a file. File descriptors always refer to a single inode.

Dirent: a dirent maps (parent inode number, name string) tuple to child inode, thus representing a parent/child relation (or the absense thereof). Dirents do not have an equivalent type inside Go-FUSE, but the result of Lookup operation essentially is a dirent, which the kernel puts in a cache.

Kernel caching

The kernel caches several pieces of information from the FUSE process:

1. File contents: enabled with the fuse.FOPEN_KEEP_CACHE return flag in Open, manipulated with ReadCache and WriteCache, and invalidated with Inode.NotifyContent

2. File Attributes (size, mtime, etc.): controlled with the attribute timeout fields in fuse.AttrOut and fuse.EntryOut, which get be populated from Getattr and Lookup

3. Directory entries (parent/child relations in the FS tree): controlled with the timeout fields in fuse.EntryOut, and invalidated with Inode.NotifyEntry and Inode.NotifyDelete.

Without Directory Entry timeouts, every operation on file "a/b/c" must first do lookups for "a", "a/b" and "a/b/c", which is expensive because of context switches between the kernel and the FUSE process.

Unsuccessful entry lookups can also be cached by setting an entry timeout when Lookup returns ENOENT.

The libfuse C library specifies 1 second timeouts for both attribute and directory entries, but no timeout for negative entries. by default. This can be achieve in go-fuse by setting options on mount, eg.

sec := time.Second
opts := fs.Options{
  EntryTimeout: &sec,
  AttrTimeout: &sec,
}

Locking

Locks for networked filesystems are supported through the suite of Getlk, Setlk and Setlkw methods. They alllow locks on regions of regular files.

Parallelism

The VFS layer in the kernel is optimized to be highly parallel, and this parallelism also affects FUSE file systems: many FUSE operations can run in parallel, and this invites race conditions. It is strongly recommended to test your FUSE file system issuing file operations in parallel, and using the race detector to weed out data races.

Dynamically discovered file systems

File system data usually cannot fit all in RAM, so the kernel must discover the file system dynamically: as you are entering and list directory contents, the kernel asks the FUSE server about the files and directories you are busy reading/writing, and forgets parts of your file system when it is low on memory.

The two important operations for dynamic file systems are: 1. Lookup, part of the NodeLookuper interface for discovering individual children of directories, and 2. Readdir, part of the NodeReaddirer interface for listing the contents of a directory.

Static in-memory file systems

For small, read-only file systems, getting the locking mechanics of Lookup correct is tedious, so Go-FUSE provides a feature to simplify building such file systems.

Instead of discovering the FS tree on the fly, you can construct the entire tree from an OnAdd method. Then, that in-memory tree structure becomes the source of truth. This means you Go-FUSE must remember Inodes even if the kernel is no longer interested in them. This is done by instantiating "persistent" inodes from the OnAdd method of the root node. See the ZipFS example for a runnable example of how to do this.

This demonstrates how to build a file system in memory. The read/write logic for the file is provided by the MemRegularFile type.

Code:

package main

import (
    "context"
    "io/ioutil"
    "log"
    "path/filepath"
    "strings"
    "syscall"

    "github.com/hanwen/go-fuse/v2/fs"
    "github.com/hanwen/go-fuse/v2/fuse"
)

// files contains the files we will expose as a file system
var files = map[string]string{
    "file":              "content",
    "subdir/other-file": "other-content",
}

// inMemoryFS is the root of the tree
type inMemoryFS struct {
    fs.Inode
}

// Ensure that we implement NodeOnAdder
var _ = (fs.NodeOnAdder)((*inMemoryFS)(nil))

// OnAdd is called on mounting the file system. Use it to populate
// the file system tree.
func (root *inMemoryFS) OnAdd(ctx context.Context) {
    for name, content := range files {
        dir, base := filepath.Split(name)

        p := &root.Inode

        // Add directories leading up to the file.
        for _, component := range strings.Split(dir, "/") {
            if len(component) == 0 {
                continue
            }
            ch := p.GetChild(component)
            if ch == nil {
                // Create a directory
                ch = p.NewPersistentInode(ctx, &fs.Inode{},
                    fs.StableAttr{Mode: syscall.S_IFDIR})
                // Add it
                p.AddChild(component, ch, true)
            }

            p = ch
        }

        // Make a file out of the content bytes. This type
        // provides the open/read/flush methods.
        embedder := &fs.MemRegularFile{
            Data: []byte(content),
        }

        // Create the file. The Inode must be persistent,
        // because its life time is not under control of the
        // kernel.
        child := p.NewPersistentInode(ctx, embedder, fs.StableAttr{})

        // And add it
        p.AddChild(base, child, true)
    }
}

// This demonstrates how to build a file system in memory. The
// read/write logic for the file is provided by the MemRegularFile type.
func main() {
    // This is where we'll mount the FS
    mntDir, _ := ioutil.TempDir("", "")

    root := &inMemoryFS{}
    server, err := fs.Mount(mntDir, root, &fs.Options{
        MountOptions: fuse.MountOptions{Debug: true},
    })
    if err != nil {
        log.Panic(err)
    }

    log.Printf("Mounted on %s", mntDir)
    log.Printf("Unmount by calling 'fusermount -u %s'", mntDir)

    // Wait until unmount before exiting
    server.Wait()
}

ExampleDirectIO shows how to create a file whose contents change on every read.

Code:

package main

import (
    "context"
    "fmt"
    "log"
    "syscall"
    "time"

    "github.com/hanwen/go-fuse/v2/fs"
    "github.com/hanwen/go-fuse/v2/fuse"
)

// bytesFileHandle is a file handle that carries separate content for
// each Open call
type bytesFileHandle struct {
    content []byte
}

// bytesFileHandle allows reads
var _ = (fs.FileReader)((*bytesFileHandle)(nil))

func (fh *bytesFileHandle) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
    end := off + int64(len(dest))
    if end > int64(len(fh.content)) {
        end = int64(len(fh.content))
    }

    // We could copy to the `dest` buffer, but since we have a
    // []byte already, return that.
    return fuse.ReadResultData(fh.content[off:end]), 0
}

// timeFile is a file that contains the wall clock time as ASCII.
type timeFile struct {
    fs.Inode
}

// timeFile implements Open
var _ = (fs.NodeOpener)((*timeFile)(nil))

func (f *timeFile) Open(ctx context.Context, openFlags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
    // disallow writes
    if fuseFlags&(syscall.O_RDWR|syscall.O_WRONLY) != 0 {
        return nil, 0, syscall.EROFS
    }

    // capture open time
    now := time.Now().Format(time.StampNano) + "\n"
    fh = &bytesFileHandle{
        content: []byte(now),
    }

    // Return FOPEN_DIRECT_IO so content is not cached.
    return fh, fuse.FOPEN_DIRECT_IO, 0
}

// ExampleDirectIO shows how to create a file whose contents change on
// every read.
func main() {
    mntDir := "/tmp/x"
    root := &fs.Inode{}

    // Mount the file system
    server, err := fs.Mount(mntDir, root, &fs.Options{
        MountOptions: fuse.MountOptions{Debug: false},

        // Setup the clock file.
        OnAdd: func(ctx context.Context) {
            ch := root.NewPersistentInode(
                ctx,
                &timeFile{},
                fs.StableAttr{Mode: syscall.S_IFREG})
            root.AddChild("clock", ch, true)
        },
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("cat %s/clock to see the time\n", mntDir)
    fmt.Printf("Unmount by calling 'fusermount -u %s'\n", mntDir)

    // Serve the file system, until unmounted by calling fusermount -u
    server.Wait()
}

ExampleDynamic is a whimsical example of a dynamically discovered file system.

Code:

package main

import (
    "context"
    "log"
    "os"
    "strconv"
    "syscall"

    "github.com/hanwen/go-fuse/v2/fs"
    "github.com/hanwen/go-fuse/v2/fuse"
)

// numberNode is a filesystem node representing an integer. Prime
// numbers are regular files, while composite numbers are directories
// containing all smaller numbers, eg.
//
//   $ ls -F  /tmp/x/6
//   2  3  4/  5
//
// the file system nodes are deduplicated using inode numbers. The
// number 2 appears in many directories, but it is actually the represented
// by the same numberNode{} object, with inode number 2.
//
//   $ ls -i1  /tmp/x/2  /tmp/x/8/6/4/2
//   2 /tmp/x/2
//   2 /tmp/x/8/6/4/2
//
type numberNode struct {
    // Must embed an Inode for the struct to work as a node.
    fs.Inode

    // num is the integer represented in this file/directory
    num int
}

// isPrime returns whether n is prime
func isPrime(n int) bool {
    for i := 2; i*i <= n; i++ {
        if n%i == 0 {
            return false
        }
    }
    return true
}

func numberToMode(n int) uint32 {
    // prime numbers are files
    if isPrime(n) {
        return fuse.S_IFREG
    }
    // composite numbers are directories
    return fuse.S_IFDIR
}

// Ensure we are implementing the NodeReaddirer interface
var _ = (fs.NodeReaddirer)((*numberNode)(nil))

// Readdir is part of the NodeReaddirer interface
func (n *numberNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
    r := make([]fuse.DirEntry, 0, n.num)
    for i := 2; i < n.num; i++ {
        d := fuse.DirEntry{
            Name: strconv.Itoa(i),
            Ino:  uint64(i),
            Mode: numberToMode(i),
        }
        r = append(r, d)
    }
    return fs.NewListDirStream(r), 0
}

// Ensure we are implementing the NodeLookuper interface
var _ = (fs.NodeLookuper)((*numberNode)(nil))

// Lookup is part of the NodeLookuper interface
func (n *numberNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
    i, err := strconv.Atoi(name)
    if err != nil {
        return nil, syscall.ENOENT
    }

    if i >= n.num || i <= 1 {
        return nil, syscall.ENOENT
    }

    stable := fs.StableAttr{
        Mode: numberToMode(i),
        // The child inode is identified by its Inode number.
        // If multiple concurrent lookups try to find the same
        // inode, they are deduplicated on this key.
        Ino: uint64(i),
    }
    operations := &numberNode{num: i}

    // The NewInode call wraps the `operations` object into an Inode.
    child := n.NewInode(ctx, operations, stable)

    // In case of concurrent lookup requests, it can happen that operations !=
    // child.Operations().
    return child, 0
}

// ExampleDynamic is a whimsical example of a dynamically discovered
// file system.
func main() {
    // This is where we'll mount the FS
    mntDir := "/tmp/x"
    os.Mkdir(mntDir, 0755)
    root := &numberNode{num: 10}
    server, err := fs.Mount(mntDir, root, &fs.Options{
        MountOptions: fuse.MountOptions{
            // Set to true to see how the file system works.
            Debug: true,
        },
    })
    if err != nil {
        log.Panic(err)
    }

    log.Printf("Mounted on %s", mntDir)
    log.Printf("Unmount by calling 'fusermount -u %s'", mntDir)

    // Wait until unmount before exiting
    server.Wait()
}

ExampleHandleLess shows how to create a file that can be read or written by implementing Read/Write directly on the nodes.

Code:

package main

import (
    "context"
    "fmt"
    "log"
    "sync"
    "syscall"
    "time"

    "github.com/hanwen/go-fuse/v2/fs"
    "github.com/hanwen/go-fuse/v2/fuse"
)

// bytesNode is a file that can be read and written
type bytesNode struct {
    fs.Inode

    // When file systems are mutable, all access must use
    // synchronization.
    mu      sync.Mutex
    content []byte
    mtime   time.Time
}

// Implement GetAttr to provide size and mtime
var _ = (fs.NodeGetattrer)((*bytesNode)(nil))

func (bn *bytesNode) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
    bn.mu.Lock()
    defer bn.mu.Unlock()
    bn.getattr(out)
    return 0
}

func (bn *bytesNode) getattr(out *fuse.AttrOut) {
    out.Size = uint64(len(bn.content))
    out.SetTimes(nil, &bn.mtime, nil)
}

func (bn *bytesNode) resize(sz uint64) {
    if sz > uint64(cap(bn.content)) {
        n := make([]byte, sz)
        copy(n, bn.content)
        bn.content = n
    } else {
        bn.content = bn.content[:sz]
    }
    bn.mtime = time.Now()
}

// Implement Setattr to support truncation
var _ = (fs.NodeSetattrer)((*bytesNode)(nil))

func (bn *bytesNode) Setattr(ctx context.Context, fh fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
    bn.mu.Lock()
    defer bn.mu.Unlock()

    if sz, ok := in.GetSize(); ok {
        bn.resize(sz)
    }
    bn.getattr(out)
    return 0
}

// Implement handleless read.
var _ = (fs.NodeReader)((*bytesNode)(nil))

func (bn *bytesNode) Read(ctx context.Context, fh fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
    bn.mu.Lock()
    defer bn.mu.Unlock()

    end := off + int64(len(dest))
    if end > int64(len(bn.content)) {
        end = int64(len(bn.content))
    }

    // We could copy to the `dest` buffer, but since we have a
    // []byte already, return that.
    return fuse.ReadResultData(bn.content[off:end]), 0
}

// Implement handleless write.
var _ = (fs.NodeWriter)((*bytesNode)(nil))

func (bn *bytesNode) Write(ctx context.Context, fh fs.FileHandle, buf []byte, off int64) (uint32, syscall.Errno) {
    bn.mu.Lock()
    defer bn.mu.Unlock()

    sz := int64(len(buf))
    if off+sz > int64(len(bn.content)) {
        bn.resize(uint64(off + sz))
    }
    copy(bn.content[off:], buf)
    bn.mtime = time.Now()
    return uint32(sz), 0
}

// Implement (handleless) Open
var _ = (fs.NodeOpener)((*bytesNode)(nil))

func (f *bytesNode) Open(ctx context.Context, openFlags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
    return nil, 0, 0
}

// ExampleHandleLess shows how to create a file that can be read or written
// by implementing Read/Write directly on the nodes.
func main() {
    mntDir := "/tmp/x"
    root := &fs.Inode{}

    // Mount the file system
    server, err := fs.Mount(mntDir, root, &fs.Options{
        MountOptions: fuse.MountOptions{Debug: false},

        // Setup the file.
        OnAdd: func(ctx context.Context) {
            ch := root.NewPersistentInode(
                ctx,
                &bytesNode{},
                fs.StableAttr{
                    Mode: syscall.S_IFREG,
                    // Make debug output readable.
                    Ino: 2,
                })
            root.AddChild("bytes", ch, true)
        },
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf(`Try:

  cd %s
  ls -l bytes
  echo hello > bytes
  ls -l bytes
  cat bytes
  cd -

`, mntDir)
    fmt.Printf("Unmount by calling 'fusermount -u %s'\n", mntDir)

    // Serve the file system, until unmounted by calling fusermount -u
    server.Wait()
}

ExampleMount shows how to create a loopback file system, and mounting it onto a directory

Code:

mntDir, _ := ioutil.TempDir("", "")
home := os.Getenv("HOME")
// Make $HOME available on a mount dir under /tmp/ . Caution:
// write operations are also mirrored.
root, err := fs.NewLoopbackRoot(home)
if err != nil {
    log.Fatal(err)
}

// Mount the file system
server, err := fs.Mount(mntDir, root, &fs.Options{
    MountOptions: fuse.MountOptions{Debug: true},
})
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Mounted %s as loopback on %s\n", home, mntDir)
fmt.Printf("\n\nCAUTION:\nwrite operations on %s will also affect $HOME (%s)\n\n", mntDir, home)
fmt.Printf("Unmount by calling 'fusermount -u %s'\n", mntDir)

// Serve the file system, until unmounted by calling fusermount -u
server.Wait()

ExampleZipFS shows an in-memory, static file system

Code:

package main

import (
    "archive/zip"
    "context"
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "path/filepath"
    "strings"
    "sync"
    "syscall"

    "github.com/hanwen/go-fuse/v2/fs"
    "github.com/hanwen/go-fuse/v2/fuse"
)

// zipFile is a file read from a zip archive.
type zipFile struct {
    fs.Inode
    file *zip.File

    mu   sync.Mutex
    data []byte
}

// We decompress the file on demand in Open
var _ = (fs.NodeOpener)((*zipFile)(nil))

// Getattr sets the minimum, which is the size. A more full-featured
// FS would also set timestamps and permissions.
var _ = (fs.NodeGetattrer)((*zipFile)(nil))

func (zf *zipFile) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
    out.Size = zf.file.UncompressedSize64
    return 0
}

// Open lazily unpacks zip data
func (zf *zipFile) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) {
    zf.mu.Lock()
    defer zf.mu.Unlock()
    if zf.data == nil {
        rc, err := zf.file.Open()
        if err != nil {
            return nil, 0, syscall.EIO
        }
        content, err := ioutil.ReadAll(rc)
        if err != nil {
            return nil, 0, syscall.EIO
        }

        zf.data = content
    }

    // We don't return a filehandle since we don't really need
    // one.  The file content is immutable, so hint the kernel to
    // cache the data.
    return nil, fuse.FOPEN_KEEP_CACHE, fs.OK
}

// Read simply returns the data that was already unpacked in the Open call
func (zf *zipFile) Read(ctx context.Context, f fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
    end := int(off) + len(dest)
    if end > len(zf.data) {
        end = len(zf.data)
    }
    return fuse.ReadResultData(zf.data[off:end]), fs.OK
}

// zipRoot is the root of the Zip filesystem. Its only functionality
// is populating the filesystem.
type zipRoot struct {
    fs.Inode

    zr  *zip.Reader
}

// The root populates the tree in its OnAdd method
var _ = (fs.NodeOnAdder)((*zipRoot)(nil))

func (zr *zipRoot) OnAdd(ctx context.Context) {
    // OnAdd is called once we are attached to an Inode. We can
    // then construct a tree.  We construct the entire tree, and
    // we don't want parts of the tree to disappear when the
    // kernel is short on memory, so we use persistent inodes.
    for _, f := range zr.zr.File {
        dir, base := filepath.Split(f.Name)

        p := &zr.Inode
        for _, component := range strings.Split(dir, "/") {
            if len(component) == 0 {
                continue
            }
            ch := p.GetChild(component)
            if ch == nil {
                ch = p.NewPersistentInode(ctx, &fs.Inode{},
                    fs.StableAttr{Mode: fuse.S_IFDIR})
                p.AddChild(component, ch, true)
            }

            p = ch
        }
        ch := p.NewPersistentInode(ctx, &zipFile{file: f}, fs.StableAttr{})
        p.AddChild(base, ch, true)
    }
}

// ExampleZipFS shows an in-memory, static file system
func main() {
    flag.Parse()
    if len(flag.Args()) != 1 {
        log.Fatal("usage: zipmount ZIP-FILE")
    }
    zfile, err := zip.OpenReader(flag.Arg(0))
    if err != nil {
        log.Fatal(err)
    }

    root := &zipRoot{zr: &zfile.Reader}
    mnt := "/tmp/x"
    os.Mkdir(mnt, 0755)
    server, err := fs.Mount(mnt, root, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("zip file mounted")
    fmt.Printf("to unmount: fusermount -u %s\n", mnt)
    server.Wait()
}

Index

Examples

Package Files

api.go bridge.go constants.go constants_linux.go default.go dirstream.go dirstream_linux.go files.go files_linux.go inode.go loopback.go loopback_linux.go mem.go mount.go syscall_linux.go

Constants

const RENAME_EXCHANGE = 0x2

RENAME_EXCHANGE is a flag argument for renameat2()

Variables

var ENOATTR = syscall.ENODATA

ENOATTR indicates that an extended attribute was not present.

var OK = syscall.Errno(0)

OK is the Errno return value to indicate absense of errors.

func Mount Uses

func Mount(dir string, root InodeEmbedder, options *Options) (*fuse.Server, error)

Mount mounts the given NodeFS on the directory, and starts serving requests. This is a convenience wrapper around NewNodeFS and fuse.NewServer. If nil is given as options, default settings are applied, which are 1 second entry and attribute timeout.

func NewNodeFS Uses

func NewNodeFS(root InodeEmbedder, opts *Options) fuse.RawFileSystem

NewNodeFS creates a node based filesystem based on the InodeEmbedder instance for the root of the tree.

func ToErrno Uses

func ToErrno(err error) syscall.Errno

ToErrno exhumes the syscall.Errno error from wrapped error values.

type DirStream Uses

type DirStream interface {
    // HasNext indicates if there are further entries. HasNext
    // might be called on already closed streams.
    HasNext() bool

    // Next retrieves the next entry. It is only called if HasNext
    // has previously returned true.  The Errno return may be used to
    // indicate I/O errors
    Next() (fuse.DirEntry, syscall.Errno)

    // Close releases resources related to this directory
    // stream.
    Close()
}

DirStream lists directory entries.

func NewListDirStream Uses

func NewListDirStream(list []fuse.DirEntry) DirStream

NewListDirStream wraps a slice of DirEntry as a DirStream.

func NewLoopbackDirStream Uses

func NewLoopbackDirStream(name string) (DirStream, syscall.Errno)

NewLoopbackDirStream open a directory for reading as a DirStream

type FileAllocater Uses

type FileAllocater interface {
    Allocate(ctx context.Context, off uint64, size uint64, mode uint32) syscall.Errno
}

See NodeAllocater.

type FileFlusher Uses

type FileFlusher interface {
    Flush(ctx context.Context) syscall.Errno
}

See NodeFlusher.

type FileFsyncer Uses

type FileFsyncer interface {
    Fsync(ctx context.Context, flags uint32) syscall.Errno
}

See NodeFsync.

type FileGetattrer Uses

type FileGetattrer interface {
    Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno
}

See NodeGetattrer.

type FileGetlker Uses

type FileGetlker interface {
    Getlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) syscall.Errno
}

See NodeGetlker.

type FileHandle Uses

type FileHandle interface {
}

FileHandle is a resource identifier for opened files. Usually, a FileHandle should implement some of the FileXxxx interfaces.

All of the FileXxxx operations can also be implemented at the InodeEmbedder level, for example, one can implement NodeReader instead of FileReader.

FileHandles are useful in two cases: First, if the underlying storage systems needs a handle for reading/writing. This is the case with Unix system calls, which need a file descriptor (See also the function `NewLoopbackFile`). Second, it is useful for implementing files whose contents are not tied to an inode. For example, a file like `/proc/interrupts` has no fixed content, but changes on each open call. This means that each file handle must have its own view of the content; this view can be tied to a FileHandle. Files that have such dynamic content should return the FOPEN_DIRECT_IO flag from their `Open` method. See directio_test.go for an example.

func NewLoopbackFile Uses

func NewLoopbackFile(fd int) FileHandle

NewLoopbackFile creates a FileHandle out of a file descriptor. All operations are implemented.

type FileLseeker Uses

type FileLseeker interface {
    Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno)
}

See NodeLseeker.

type FileReader Uses

type FileReader interface {
    Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno)
}

See NodeReader.

type FileReleaser Uses

type FileReleaser interface {
    Release(ctx context.Context) syscall.Errno
}

See NodeReleaser.

type FileSetattrer Uses

type FileSetattrer interface {
    Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
}

See NodeFsync.

type FileSetlker Uses

type FileSetlker interface {
    Setlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
}

See NodeSetlker.

type FileSetlkwer Uses

type FileSetlkwer interface {
    Setlkw(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
}

See NodeSetlkwer.

type FileWriter Uses

type FileWriter interface {
    Write(ctx context.Context, data []byte, off int64) (written uint32, errno syscall.Errno)
}

See NodeWriter.

type Inode Uses

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

Inode is a node in VFS tree. Inodes are one-to-one mapped to Operations instances, which is the extension interface for file systems. One can create fully-formed trees of Inodes ahead of time by creating "persistent" Inodes.

The Inode struct contains a lock, so it should not be copied. Inodes should be obtained by calling Inode.NewInode() or Inode.NewPersistentInode().

func (*Inode) AddChild Uses

func (n *Inode) AddChild(name string, ch *Inode, overwrite bool) (success bool)

AddChild adds a child to this node. If overwrite is false, fail if the destination already exists.

func (*Inode) Children Uses

func (n *Inode) Children() map[string]*Inode

Children returns the list of children of this directory Inode.

func (*Inode) EmbeddedInode Uses

func (n *Inode) EmbeddedInode() *Inode

func (*Inode) ExchangeChild Uses

func (n *Inode) ExchangeChild(oldName string, newParent *Inode, newName string)

ExchangeChild swaps the entries at (n, oldName) and (newParent, newName).

func (*Inode) ForgetPersistent Uses

func (n *Inode) ForgetPersistent()

ForgetPersistent manually marks the node as no longer important. If it has no children, and if the kernel as no references, the nodes gets removed from the tree.

func (*Inode) Forgotten Uses

func (n *Inode) Forgotten() bool

Forgotten returns true if the kernel holds no references to this inode. This can be used for background cleanup tasks, since the kernel has no way of reviving forgotten nodes by its own initiative.

func (*Inode) GetChild Uses

func (n *Inode) GetChild(name string) *Inode

GetChild returns a child node with the given name, or nil if the directory has no child by that name.

func (*Inode) IsDir Uses

func (n *Inode) IsDir() bool

func (*Inode) IsRoot Uses

func (n *Inode) IsRoot() bool

Returns whether this is the root of the tree

func (*Inode) Mode Uses

func (n *Inode) Mode() uint32

Mode returns the filetype

func (*Inode) MvChild Uses

func (n *Inode) MvChild(old string, newParent *Inode, newName string, overwrite bool) bool

MvChild executes a rename. If overwrite is set, a child at the destination will be overwritten, should it exist. It returns false if 'overwrite' is false, and the destination exists.

func (*Inode) NewInode Uses

func (n *Inode) NewInode(ctx context.Context, node InodeEmbedder, id StableAttr) *Inode

NewInode returns an inode for the given InodeEmbedder. The mode should be standard mode argument (eg. S_IFDIR). The inode number in id.Ino argument is used to implement hard-links. If it is given, and another node with the same ID is known, that will node will be returned, and the passed-in `node` is ignored.

func (*Inode) NewPersistentInode Uses

func (n *Inode) NewPersistentInode(ctx context.Context, node InodeEmbedder, id StableAttr) *Inode

NewPersistentInode returns an Inode whose lifetime is not in control of the kernel.

When the kernel is short on memory, it will forget cached file system information (directory entries and inode metadata). This is announced with FORGET messages. There are no guarantees if or when this happens. When it happens, these are handled transparently by go-fuse: all Inodes created with NewInode are released automatically. NewPersistentInode creates inodes that go-fuse keeps in memory, even if the kernel is not interested in them. This is convenient for building static trees up-front.

func (*Inode) NotifyContent Uses

func (n *Inode) NotifyContent(off, sz int64) syscall.Errno

NotifyContent notifies the kernel that content under the given inode should be flushed from buffers.

func (*Inode) NotifyDelete Uses

func (n *Inode) NotifyDelete(name string, child *Inode) syscall.Errno

NotifyDelete notifies the kernel that the given inode was removed from this directory as entry under the given name. It is equivalent to NotifyEntry, but also sends an event to inotify watchers.

func (*Inode) NotifyEntry Uses

func (n *Inode) NotifyEntry(name string) syscall.Errno

NotifyEntry notifies the kernel that data for a (directory, name) tuple should be invalidated. On next access, a LOOKUP operation will be started.

func (*Inode) Operations Uses

func (n *Inode) Operations() InodeEmbedder

Operations returns the object implementing the file system operations.

func (*Inode) Parent Uses

func (n *Inode) Parent() (string, *Inode)

Parents returns a parent of this Inode, or nil if this Inode is deleted or is the root

func (*Inode) Path Uses

func (n *Inode) Path(root *Inode) string

Path returns a path string to the inode relative to `root`. Pass nil to walk the hierarchy as far up as possible.

If you set `root`, Path() warns if it finds an orphaned Inode, i.e. if it does not end up at `root` after walking the hierarchy.

func (*Inode) ReadCache Uses

func (n *Inode) ReadCache(offset int64, dest []byte) (count int, errno syscall.Errno)

ReadCache reads data from the kernel cache.

func (*Inode) RmAllChildren Uses

func (n *Inode) RmAllChildren()

RmAllChildren recursively drops a tree, forgetting all persistent nodes.

func (*Inode) RmChild Uses

func (n *Inode) RmChild(names ...string) (success, live bool)

RmChild removes multiple children. Returns whether the removal succeeded and whether the node is still live afterward. The removal is transactional: it only succeeds if all names are children, and if they all were removed successfully. If the removal was successful, and there are no children left, the node may be removed from the FS tree. In that case, RmChild returns live==false.

func (*Inode) Root Uses

func (n *Inode) Root() *Inode

Returns the root of the tree

func (*Inode) StableAttr Uses

func (n *Inode) StableAttr() StableAttr

StableAttr returns the (Ino, Gen) tuple for this node.

func (*Inode) String Uses

func (n *Inode) String() string

debugString is used for debugging. Racy.

func (*Inode) WriteCache Uses

func (n *Inode) WriteCache(offset int64, data []byte) syscall.Errno

WriteCache stores data in the kernel cache.

type InodeEmbedder Uses

type InodeEmbedder interface {

    // EmbeddedInode returns a pointer to the embedded inode.
    EmbeddedInode() *Inode
    // contains filtered or unexported methods
}

InodeEmbedder is an interface for structs that embed Inode.

InodeEmbedder objects usually should implement some of the NodeXxxx interfaces, to provide user-defined file system behaviors.

In general, if an InodeEmbedder does not implement specific filesystem methods, the filesystem will react as if it is a read-only filesystem with a predefined tree structure.

func NewLoopbackRoot Uses

func NewLoopbackRoot(root string) (InodeEmbedder, error)

NewLoopbackRoot returns a root node for a loopback file system whose root is at the given root. This node implements all NodeXxxxer operations available.

type MemRegularFile Uses

type MemRegularFile struct {
    Inode

    Data []byte
    Attr fuse.Attr
    // contains filtered or unexported fields
}

MemRegularFile is a filesystem node that holds a read-only data slice in memory.

func (*MemRegularFile) Flush Uses

func (f *MemRegularFile) Flush(ctx context.Context, fh FileHandle) syscall.Errno

func (*MemRegularFile) Getattr Uses

func (f *MemRegularFile) Getattr(ctx context.Context, fh FileHandle, out *fuse.AttrOut) syscall.Errno

func (*MemRegularFile) Open Uses

func (f *MemRegularFile) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno)

func (*MemRegularFile) Read Uses

func (f *MemRegularFile) Read(ctx context.Context, fh FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno)

func (*MemRegularFile) Setattr Uses

func (f *MemRegularFile) Setattr(ctx context.Context, fh FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno

func (*MemRegularFile) Write Uses

func (f *MemRegularFile) Write(ctx context.Context, fh FileHandle, data []byte, off int64) (uint32, syscall.Errno)
type MemSymlink struct {
    Inode
    Attr fuse.Attr
    Data []byte
}

MemSymlink is an inode holding a symlink in memory.

func (*MemSymlink) Getattr Uses

func (l *MemSymlink) Getattr(ctx context.Context, fh FileHandle, out *fuse.AttrOut) syscall.Errno
func (l *MemSymlink) Readlink(ctx context.Context) ([]byte, syscall.Errno)

type NodeAccesser Uses

type NodeAccesser interface {
    Access(ctx context.Context, mask uint32) syscall.Errno
}

Access should return if the caller can access the file with the given mode. This is used for two purposes: to determine if a user may enter a directory, and to answer to implement the access system call. In the latter case, the context has data about the real UID. For example, a root-SUID binary called by user susan gets the UID and GID for susan here.

If not defined, a default implementation will check traditional unix permissions of the Getattr result agains the caller. If so, it is necessary to either return permissions from GetAttr/Lookup or set Options.DefaultPermissions in order to allow chdir into the FUSE mount.

type NodeAllocater Uses

type NodeAllocater interface {
    Allocate(ctx context.Context, f FileHandle, off uint64, size uint64, mode uint32) syscall.Errno
}

Allocate preallocates space for future writes, so they will never encounter ESPACE.

type NodeCopyFileRanger Uses

type NodeCopyFileRanger interface {
    CopyFileRange(ctx context.Context, fhIn FileHandle,
        offIn uint64, out *Inode, fhOut FileHandle, offOut uint64,
        len uint64, flags uint64) (uint32, syscall.Errno)
}

CopyFileRange copies data between sections of two files, without the data having to pass through the calling process.

type NodeCreater Uses

type NodeCreater interface {
    Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (node *Inode, fh FileHandle, fuseFlags uint32, errno syscall.Errno)
}

Create is similar to Lookup, but should create a new child. It typically also returns a FileHandle as a reference for future reads/writes. Default is to return EROFS.

type NodeFlusher Uses

type NodeFlusher interface {
    Flush(ctx context.Context, f FileHandle) syscall.Errno
}

Flush is called for the close(2) call on a file descriptor. In case of a descriptor that was duplicated using dup(2), it may be called more than once for the same FileHandle. The default implementation forwards to the FileHandle, or if the handle does not support FileFlusher, returns OK.

type NodeFsyncer Uses

type NodeFsyncer interface {
    Fsync(ctx context.Context, f FileHandle, flags uint32) syscall.Errno
}

Fsync is a signal to ensure writes to the Inode are flushed to stable storage.

type NodeGetattrer Uses

type NodeGetattrer interface {
    Getattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno
}

GetAttr reads attributes for an Inode. The library will ensure that Mode and Ino are set correctly. For files that are not opened with FOPEN_DIRECTIO, Size should be set so it can be read correctly. If returning zeroed permissions, the default behavior is to change the mode of 0755 (directory) or 0644 (files). This can be switched off with the Options.NullPermissions setting. If blksize is unset, 4096 is assumed, and the 'blocks' field is set accordingly.

type NodeGetlker Uses

type NodeGetlker interface {
    Getlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) syscall.Errno
}

Getlk returns locks that would conflict with the given input lock. If no locks conflict, the output has type L_UNLCK. See fcntl(2) for more information. If not defined, returns ENOTSUP

type NodeGetxattrer Uses

type NodeGetxattrer interface {
    Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno)
}

Getxattr should read data for the given attribute into `dest` and return the number of bytes. If `dest` is too small, it should return ERANGE and the size of the attribute. If not defined, Getxattr will return ENOATTR.

type NodeLinker Uses

type NodeLinker interface {
    Link(ctx context.Context, target InodeEmbedder, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno)
}

Link is similar to Lookup, but must create a new link to an existing Inode. Default is to return EROFS.

type NodeListxattrer Uses

type NodeListxattrer interface {
    Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno)
}

Listxattr should read all attributes (null terminated) into `dest`. If the `dest` buffer is too small, it should return ERANGE and the correct size. If not defined, return an empty list and success.

type NodeLookuper Uses

type NodeLookuper interface {
    Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno)
}

type NodeLseeker Uses

type NodeLseeker interface {
    Lseek(ctx context.Context, f FileHandle, Off uint64, whence uint32) (uint64, syscall.Errno)
}

Lseek is used to implement holes: it should return the first offset beyond `off` where there is data (SEEK_DATA) or where there is a hole (SEEK_HOLE).

type NodeMkdirer Uses

type NodeMkdirer interface {
    Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, syscall.Errno)
}

Mkdir is similar to Lookup, but must create a directory entry and Inode. Default is to return EROFS.

type NodeMknoder Uses

type NodeMknoder interface {
    Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (*Inode, syscall.Errno)
}

Mknod is similar to Lookup, but must create a device entry and Inode. Default is to return EROFS.

type NodeOnAdder Uses

type NodeOnAdder interface {
    OnAdd(ctx context.Context)
}

OnAdd is called when this InodeEmbedder is initialized.

type NodeOpendirer Uses

type NodeOpendirer interface {
    Opendir(ctx context.Context) syscall.Errno
}

OpenDir opens a directory Inode for reading its contents. The actual reading is driven from ReadDir, so this method is just for performing sanity/permission checks. The default is to return success.

type NodeOpener Uses

type NodeOpener interface {
    Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno)
}

Open opens an Inode (of regular file type) for reading. It is optional but recommended to return a FileHandle.

type NodeReaddirer Uses

type NodeReaddirer interface {
    Readdir(ctx context.Context) (DirStream, syscall.Errno)
}

ReadDir opens a stream of directory entries.

Readdir essentiallly returns a list of strings, and it is allowed for Readdir to return different results from Lookup. For example, you can return nothing for Readdir ("ls my-fuse-mount" is empty), while still implementing Lookup ("ls my-fuse-mount/a-specific-file" shows a single file).

If a directory does not implement NodeReaddirer, a list of currently known children from the tree is returned. This means that static in-memory file systems need not implement NodeReaddirer.

type NodeReader Uses

type NodeReader interface {
    Read(ctx context.Context, f FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno)
}

Reads data from a file. The data should be returned as ReadResult, which may be constructed from the incoming `dest` buffer. If the file was opened without FileHandle, the FileHandle argument here is nil. The default implementation forwards to the FileHandle.

type NodeReadlinker Uses

type NodeReadlinker interface {
    Readlink(ctx context.Context) ([]byte, syscall.Errno)
}

Readlink reads the content of a symlink.

type NodeReleaser Uses

type NodeReleaser interface {
    Release(ctx context.Context, f FileHandle) syscall.Errno
}

This is called to before a FileHandle is forgotten. The kernel ignores the return value of this method, so any cleanup that requires specific synchronization or could fail with I/O errors should happen in Flush instead. The default implementation forwards to the FileHandle.

type NodeRemovexattrer Uses

type NodeRemovexattrer interface {
    Removexattr(ctx context.Context, attr string) syscall.Errno
}

Removexattr should delete the given attribute. If not defined, Removexattr will return ENOATTR.

type NodeRenamer Uses

type NodeRenamer interface {
    Rename(ctx context.Context, name string, newParent InodeEmbedder, newName string, flags uint32) syscall.Errno
}

Rename should move a child from one directory to a different one. The change is effected in the FS tree if the return status is OK. Default is to return EROFS.

type NodeRmdirer Uses

type NodeRmdirer interface {
    Rmdir(ctx context.Context, name string) syscall.Errno
}

Rmdir is like Unlink but for directories. Default is to return EROFS.

type NodeSetattrer Uses

type NodeSetattrer interface {
    Setattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
}

SetAttr sets attributes for an Inode.

type NodeSetlker Uses

type NodeSetlker interface {
    Setlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
}

Setlk obtains a lock on a file, or fail if the lock could not obtained. See fcntl(2) for more information. If not defined, returns ENOTSUP

type NodeSetlkwer Uses

type NodeSetlkwer interface {
    Setlkw(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
}

Setlkw obtains a lock on a file, waiting if necessary. See fcntl(2) for more information. If not defined, returns ENOTSUP

type NodeSetxattrer Uses

type NodeSetxattrer interface {
    Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno
}

Setxattr should store data for the given attribute. See setxattr(2) for information about flags. If not defined, Setxattr will return ENOATTR.

type NodeStatfser Uses

type NodeStatfser interface {
    Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno
}

Statfs implements statistics for the filesystem that holds this Inode. If not defined, the `out` argument will zeroed with an OK result. This is because OSX filesystems must Statfs, or the mount will not work.

type NodeSymlinker Uses

type NodeSymlinker interface {
    Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno)
}

Symlink is similar to Lookup, but must create a new symbolic link. Default is to return EROFS.

type NodeUnlinker Uses

type NodeUnlinker interface {
    Unlink(ctx context.Context, name string) syscall.Errno
}

Unlink should remove a child from this directory. If the return status is OK, the Inode is removed as child in the FS tree automatically. Default is to return EROFS.

type NodeWriter Uses

type NodeWriter interface {
    Write(ctx context.Context, f FileHandle, data []byte, off int64) (written uint32, errno syscall.Errno)
}

Writes the data into the file handle at given offset. After returning, the data will be reused and may not referenced. The default implementation forwards to the FileHandle.

type Options Uses

type Options struct {
    // MountOptions contain the options for mounting the fuse server
    fuse.MountOptions

    // If set to nonnil, this defines the overall entry timeout
    // for the file system. See fuse.EntryOut for more information.
    EntryTimeout *time.Duration

    // If set to nonnil, this defines the overall attribute
    // timeout for the file system. See fuse.EntryOut for more
    // information.
    AttrTimeout *time.Duration

    // If set to nonnil, this defines the overall entry timeout
    // for failed lookups (fuse.ENOENT). See fuse.EntryOut for
    // more information.
    NegativeTimeout *time.Duration

    // Automatic inode numbers are handed out sequentially
    // starting from this number. If unset, use 2^63.
    FirstAutomaticIno uint64

    // OnAdd is an alternative way to specify the OnAdd
    // functionality of the root node.
    OnAdd func(ctx context.Context)

    // NullPermissions if set, leaves null file permissions
    // alone. Otherwise, they are set to 755 (dirs) or 644 (other
    // files.), which is necessary for doing a chdir into the FUSE
    // directories.
    NullPermissions bool

    // If nonzero, replace default (zero) UID with the given UID
    UID uint32

    // If nonzero, replace default (zero) GID with the given GID
    GID uint32

    // ServerCallbacks can be provided to stub out notification
    // functions for testing a filesystem without mounting it.
    ServerCallbacks ServerCallbacks

    // Logger is a sink for diagnostic messages. Diagnostic
    // messages are printed under conditions where we cannot
    // return error, but want to signal something seems off
    // anyway. If unset, no messages are printed.
    Logger *log.Logger
}

Options sets options for the entire filesystem

type ServerCallbacks Uses

type ServerCallbacks interface {
    DeleteNotify(parent uint64, child uint64, name string) fuse.Status
    EntryNotify(parent uint64, name string) fuse.Status
    InodeNotify(node uint64, off int64, length int64) fuse.Status
    InodeRetrieveCache(node uint64, offset int64, dest []byte) (n int, st fuse.Status)
    InodeNotifyStoreCache(node uint64, offset int64, data []byte) fuse.Status
}

ServerCallbacks are calls into the kernel to manipulate the inode, entry and page cache. They are stubbed so filesystems can be unittested without mounting them.

type StableAttr Uses

type StableAttr struct {
    // Each Inode has a type, which does not change over the
    // lifetime of the inode, for example fuse.S_IFDIR. The default (0)
    // is interpreted as S_IFREG (regular file).
    Mode uint32

    // The inode number must be unique among the currently live
    // objects in the file system. It is used to communicate to
    // the kernel about this file object. The values uint64(-1),
    // and 1 are reserved. When using Ino==0, a unique, sequential
    // number is assigned (starting at 2^63 by default) on Inode creation.
    Ino uint64

    // When reusing a previously used inode number for a new
    // object, the new object must have a different Gen
    // number. This is irrelevant if the FS is not exported over
    // NFS
    Gen uint64
}

StableAttr holds immutable attributes of a object in the filesystem.

func (*StableAttr) Reserved Uses

func (i *StableAttr) Reserved() bool

Reserved returns if the StableAttr is using reserved Inode numbers.

Package fs imports 15 packages (graph). Updated 2020-04-04. Refresh now. Tools for package owners.