Movatterモバイル変換


[0]ホーム

URL:


fs

package
v2.0.3Latest Latest
Warning

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

Go to latest
Published: Apr 3, 2020 License:BSD-3-ClauseImports:14Imported by:286

Details

Repository

github.com/hanwen/go-fuse

Links

README

Objective

A high-performance FUSE API that minimizes pitfalls with writingcorrect filesystems.

Decisions

  • Nodes contain references to their children. This is usefulbecause most filesystems will need to construct tree-likestructures.

  • Nodes contain references to their parents. As a result, we canderive the path for each Inode, and there is no need for aseparate PathFS.

  • Nodes can be "persistent", meaning their lifetime is not undercontrol of the kernel. This is useful for constructing FS treesin advance, rather than driven by LOOKUP.

  • The NodeID for FS tree node must be defined on creation and areimmutable. By contrast, reusing NodeIds (eg. rsc/bazil FUSE, aswell as old go-fuse/fuse/nodefs) needs extra synchronization toavoid races with notify and FORGET, and makes handling the inodeGeneration more complicated.

  • The mode of an Inode is defined on creation. Files cannot changetype during their lifetime. This also prevents the common errorof forgetting to return the filetype in Lookup/GetAttr.

  • The NodeID (used for communicating with kernel) is equal toAttr.Ino (value shown in Stat and Lstat return values.).

  • No global treelock, to ensure scalability.

  • Support for hard links. libfuse doesn't support this in thehigh-level API. Extra care for race conditions is needed whenlooking up the same file through different paths.

  • do not issue Notify{Entry,Delete} as part ofAddChild/RmChild/MvChild: because NodeIDs are unique andimmutable, there is no confusion about which nodes areinvalidated, and the notification doesn't have to happen underlock.

  • Directory reading uses the DirStream. Semantics for rewindingdirectory reads, and adding files after opening (but beforereading) are handled automatically. No support for directoryseeks.

  • Method names are based on syscall names. Where there is nosyscall (eg. "open directory"), we bias towards writingeverything together (Opendir)

To do/To decide

  • Symlink []byte vs string.

Documentation

Overview

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 thenodes of the file system tree.

struct myNode {   fs.Inode}// Node types must be InodeEmbeddersvar _ = (fs.InodeEmbedder)((*myNode)(nil))// Node types should implement some file system operations, eg. Lookupvar _ = (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 haveListxattr 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 systemserver.Wait()

Error handling

All error reporting must use the syscall.Errno type. This is aninteger 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 fordefining file systems in the kernel. It is therefore useful tounderstand some terminology.

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

Path: a /-separated string path that describes location of a nodein 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 storesmetadata (size, timestamps) about a file or directory. Eachinode has a type (directory, symlink, regular file, etc.) andan identity (a 64-bit number, unique to the filesystem). Directories can have children.

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

While common OS APIs are phrased in terms of paths (strings), theprecise semantics of a file system are better described in terms ofInodes. 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. Filedescriptors always refer to a single inode.

Dirent: a dirent maps (parent inode number, name string) tuple tochild inode, thus representing a parent/child relation (or theabsense thereof). Dirents do not have an equivalent type insideGo-FUSE, but the result of Lookup operation essentially is adirent, 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 flagin Open, manipulated with ReadCache and WriteCache, and invalidatedwith Inode.NotifyContent

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

3. Directory entries (parent/child relations in the FS tree):controlled with the timeout fields in fuse.EntryOut, andinvalidated 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 isexpensive because of context switches between the kernel and theFUSE process.

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

The libfuse C library specifies 1 second timeouts for bothattribute and directory entries, but no timeout for negativeentries. by default. This can be achieve in go-fuse by settingoptions on mount, eg.

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

Locking

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

Parallelism

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

Dynamically discovered file systems

File system data usually cannot fit all in RAM, so the kernel mustdiscover the file system dynamically: as you are entering and listdirectory contents, the kernel asks the FUSE server about the filesand directories you are busy reading/writing, and forgets parts ofyour 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 discoveringindividual children of directories, and 2. Readdir, part of theNodeReaddirer interface for listing the contents of a directory.

Static in-memory file systems

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

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

Example

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

package mainimport ("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 systemvar files = map[string]string{"file":              "content","subdir/other-file": "other-content",}// inMemoryFS is the root of the treetype inMemoryFS struct {fs.Inode}// Ensure that we implement NodeOnAddervar _ = (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 directorych = p.NewPersistentInode(ctx, &fs.Inode{},fs.StableAttr{Mode: syscall.S_IFDIR})// Add itp.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 itp.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 FSmntDir, _ := 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 exitingserver.Wait()}

Example (DirectIO)

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

package mainimport ("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 calltype bytesFileHandle struct {content []byte}// bytesFileHandle allows readsvar _ = (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 Openvar _ = (fs.NodeOpener)((*timeFile)(nil))func (f *timeFile) Open(ctx context.Context, openFlags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {// disallow writesif fuseFlags&(syscall.O_RDWR|syscall.O_WRONLY) != 0 {return nil, 0, syscall.EROFS}// capture open timenow := 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 systemserver, 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 -userver.Wait()}

Example (Dynamic)

ExampleDynamic is a whimsical example of a dynamically discoveredfile system.

package mainimport ("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/2type numberNode struct {// Must embed an Inode for the struct to work as a node.fs.Inode// num is the integer represented in this file/directorynum int}// isPrime returns whether n is primefunc 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 filesif isPrime(n) {return fuse.S_IFREG}// composite numbers are directoriesreturn fuse.S_IFDIR}// Ensure we are implementing the NodeReaddirer interfacevar _ = (fs.NodeReaddirer)((*numberNode)(nil))// Readdir is part of the NodeReaddirer interfacefunc (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 interfacevar _ = (fs.NodeLookuper)((*numberNode)(nil))// Lookup is part of the NodeLookuper interfacefunc (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 FSmntDir := "/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 exitingserver.Wait()}

Example (HandleLess)

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

package mainimport ("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 writtentype bytesNode struct {fs.Inode// When file systems are mutable, all access must use// synchronization.mu      sync.Mutexcontent []bytemtime   time.Time}// Implement GetAttr to provide size and mtimevar _ = (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 truncationvar _ = (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) Openvar _ = (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 systemserver, 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 -userver.Wait()}

Example (Mount)

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

package mainimport ("fmt""io/ioutil""log""os""github.com/hanwen/go-fuse/v2/fs""github.com/hanwen/go-fuse/v2/fuse")func main() {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 systemserver, 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 -userver.Wait()}

Example (ZipFS)

ExampleZipFS shows an in-memory, static file system

package mainimport ("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.Inodefile *zip.Filemu   sync.Mutexdata []byte}// We decompress the file on demand in Openvar _ = (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.UncompressedSize64return 0}// Open lazily unpacks zip datafunc (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 callfunc (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.Inodezr *zip.Reader}// The root populates the tree in its OnAdd methodvar _ = (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.Inodefor _, 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 systemfunc 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

Constants

View Source
const RENAME_EXCHANGE = 0x2

RENAME_EXCHANGE is a flag argument for renameat2()

Variables

ENOATTR indicates that an extended attribute was not present.

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

Functions

funcMount

func Mount(dirstring, rootInodeEmbedder, options *Options) (*fuse.Server,error)

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

funcNewNodeFS

func NewNodeFS(rootInodeEmbedder, opts *Options)fuse.RawFileSystem

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

funcToErrno

func ToErrno(errerror)syscall.Errno

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

Types

typeDirStream

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 errorsNext() (fuse.DirEntry,syscall.Errno)// Close releases resources related to this directory// stream.Close()}

DirStream lists directory entries.

funcNewListDirStream

func NewListDirStream(list []fuse.DirEntry)DirStream

NewListDirStream wraps a slice of DirEntry as a DirStream.

funcNewLoopbackDirStream

func NewLoopbackDirStream(namestring) (DirStream,syscall.Errno)

NewLoopbackDirStream open a directory for reading as a DirStream

typeFileAllocater

type FileAllocater interface {Allocate(ctxcontext.Context, offuint64, sizeuint64, modeuint32)syscall.Errno}

See NodeAllocater.

typeFileFlusher

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

See NodeFlusher.

typeFileFsyncer

type FileFsyncer interface {Fsync(ctxcontext.Context, flagsuint32)syscall.Errno}

See NodeFsync.

typeFileGetattrer

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

See NodeGetattrer.

typeFileGetlker

type FileGetlker interface {Getlk(ctxcontext.Context, owneruint64, lk *fuse.FileLock, flagsuint32, out *fuse.FileLock)syscall.Errno}

See NodeGetlker.

typeFileHandle

type FileHandle interface {}

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

All of the FileXxxx operations can also be implemented at theInodeEmbedder level, for example, one can implement NodeReaderinstead of FileReader.

FileHandles are useful in two cases: First, if the underlyingstorage systems needs a handle for reading/writing. This is thecase with Unix system calls, which need a file descriptor (See alsothe function `NewLoopbackFile`). Second, it is useful forimplementing files whose contents are not tied to an inode. Forexample, a file like `/proc/interrupts` has no fixed content, butchanges on each open call. This means that each file handle musthave its own view of the content; this view can be tied to aFileHandle. Files that have such dynamic content should return theFOPEN_DIRECT_IO flag from their `Open` method. See directio_test.gofor an example.

funcNewLoopbackFile

func NewLoopbackFile(fdint)FileHandle

NewLoopbackFile creates a FileHandle out of a file descriptor. Alloperations are implemented.

typeFileLseeker

type FileLseeker interface {Lseek(ctxcontext.Context, offuint64, whenceuint32) (uint64,syscall.Errno)}

See NodeLseeker.

typeFileReader

type FileReader interface {Read(ctxcontext.Context, dest []byte, offint64) (fuse.ReadResult,syscall.Errno)}

See NodeReader.

typeFileReleaser

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

See NodeReleaser.

typeFileSetattrer

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

See NodeFsync.

typeFileSetlker

type FileSetlker interface {Setlk(ctxcontext.Context, owneruint64, lk *fuse.FileLock, flagsuint32)syscall.Errno}

See NodeSetlker.

typeFileSetlkwer

type FileSetlkwer interface {Setlkw(ctxcontext.Context, owneruint64, lk *fuse.FileLock, flagsuint32)syscall.Errno}

See NodeSetlkwer.

typeFileWriter

type FileWriter interface {Write(ctxcontext.Context, data []byte, offint64) (writtenuint32, errnosyscall.Errno)}

See NodeWriter.

typeInode

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

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

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

func (*Inode)AddChild

func (n *Inode) AddChild(namestring, ch *Inode, overwritebool) (successbool)

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

func (*Inode)Children

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

Children returns the list of children of this directory Inode.

func (*Inode)EmbeddedInode

func (n *Inode) EmbeddedInode() *Inode

func (*Inode)ExchangeChild

func (n *Inode) ExchangeChild(oldNamestring, newParent *Inode, newNamestring)

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

func (*Inode)ForgetPersistent

func (n *Inode) ForgetPersistent()

ForgetPersistent manually marks the node as no longer important. Ifit has no children, and if the kernel as no references, the nodesgets removed from the tree.

func (*Inode)Forgotten

func (n *Inode) Forgotten()bool

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

func (*Inode)GetChild

func (n *Inode) GetChild(namestring) *Inode

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

func (*Inode)IsDir

func (n *Inode) IsDir()bool

func (*Inode)IsRoot

func (n *Inode) IsRoot()bool

Returns whether this is the root of the tree

func (*Inode)Mode

func (n *Inode) Mode()uint32

Mode returns the filetype

func (*Inode)MvChild

func (n *Inode) MvChild(oldstring, newParent *Inode, newNamestring, overwritebool)bool

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

func (*Inode)NewInode

func (n *Inode) NewInode(ctxcontext.Context, nodeInodeEmbedder, idStableAttr) *Inode

NewInode returns an inode for the given InodeEmbedder. The modeshould be standard mode argument (eg. S_IFDIR). The inode number inid.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 bereturned, and the passed-in `node` is ignored.

func (*Inode)NewPersistentInode

func (n *Inode) NewPersistentInode(ctxcontext.Context, nodeInodeEmbedder, idStableAttr) *Inode

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

When the kernel is short on memory, it will forget cached filesystem information (directory entries and inode metadata). This isannounced with FORGET messages. There are no guarantees if or whenthis happens. When it happens, these are handled transparently bygo-fuse: all Inodes created with NewInode are releasedautomatically. NewPersistentInode creates inodes that go-fuse keepsin memory, even if the kernel is not interested in them. This isconvenient for building static trees up-front.

func (*Inode)NotifyContent

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

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

func (*Inode)NotifyDelete

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

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

func (*Inode)NotifyEntry

func (n *Inode) NotifyEntry(namestring)syscall.Errno

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

func (*Inode)Operations

func (n *Inode) Operations()InodeEmbedder

Operations returns the object implementing the file systemoperations.

func (*Inode)Parent

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

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

func (*Inode)Path

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

func (n *Inode) ReadCache(offsetint64, dest []byte) (countint, errnosyscall.Errno)

ReadCache reads data from the kernel cache.

func (*Inode)RmAllChildren

func (n *Inode) RmAllChildren()

RmAllChildren recursively drops a tree, forgetting all persistentnodes.

func (*Inode)RmChild

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

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

func (*Inode)Root

func (n *Inode) Root() *Inode

Returns the root of the tree

func (*Inode)StableAttr

func (n *Inode) StableAttr()StableAttr

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

func (*Inode)String

func (n *Inode) String()string

debugString is used for debugging. Racy.

func (*Inode)WriteCache

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

WriteCache stores data in the kernel cache.

typeInodeEmbedder

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 NodeXxxxinterfaces, to provide user-defined file system behaviors.

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

funcNewLoopbackRoot

func NewLoopbackRoot(rootstring) (InodeEmbedder,error)

NewLoopbackRoot returns a root node for a loopback file system whoseroot is at the given root. This node implements all NodeXxxxeroperations available.

typeMemRegularFile

type MemRegularFile struct {InodeData []byteAttrfuse.Attr// contains filtered or unexported fields}

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

func (*MemRegularFile)Flush

func (*MemRegularFile)Getattr

func (*MemRegularFile)Open

func (f *MemRegularFile) Open(ctxcontext.Context, flagsuint32) (fhFileHandle, fuseFlagsuint32, errnosyscall.Errno)

func (*MemRegularFile)Read

func (*MemRegularFile)Setattradded inv2.0.3

func (*MemRegularFile)Writeadded inv2.0.3

func (f *MemRegularFile) Write(ctxcontext.Context, fhFileHandle, data []byte, offint64) (uint32,syscall.Errno)

typeMemSymlink

type MemSymlink struct {InodeAttrfuse.AttrData []byte}

MemSymlink is an inode holding a symlink in memory.

func (*MemSymlink)Getattr

func (*MemSymlink)Readlink

func (l *MemSymlink) Readlink(ctxcontext.Context) ([]byte,syscall.Errno)

typeNodeAccesser

type NodeAccesser interface {Access(ctxcontext.Context, maskuint32)syscall.Errno}

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

If not defined, a default implementation will check traditionalunix permissions of the Getattr result agains the caller. If so, itis necessary to either return permissions from GetAttr/Lookup orset Options.DefaultPermissions in order to allow chdir into theFUSE mount.

typeNodeAllocater

type NodeAllocater interface {Allocate(ctxcontext.Context, fFileHandle, offuint64, sizeuint64, modeuint32)syscall.Errno}

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

typeNodeCopyFileRanger

type NodeCopyFileRanger interface {CopyFileRange(ctxcontext.Context, fhInFileHandle,offInuint64, out *Inode, fhOutFileHandle, offOutuint64,lenuint64, flagsuint64) (uint32,syscall.Errno)}

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

typeNodeCreater

type NodeCreater interface {Create(ctxcontext.Context, namestring, flagsuint32, modeuint32, out *fuse.EntryOut) (node *Inode, fhFileHandle, fuseFlagsuint32, errnosyscall.Errno)}

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

typeNodeFlusher

type NodeFlusher interface {Flush(ctxcontext.Context, fFileHandle)syscall.Errno}

Flush is called for the close(2) call on a file descriptor. In caseof a descriptor that was duplicated using dup(2), it may be calledmore than once for the same FileHandle. The default implementationforwards to the FileHandle, or if the handle does not supportFileFlusher, returns OK.

typeNodeFsyncer

type NodeFsyncer interface {Fsync(ctxcontext.Context, fFileHandle, flagsuint32)syscall.Errno}

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

typeNodeGetattrer

type NodeGetattrer interface {Getattr(ctxcontext.Context, fFileHandle, out *fuse.AttrOut)syscall.Errno}

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

typeNodeGetlker

type NodeGetlker interface {Getlk(ctxcontext.Context, fFileHandle, owneruint64, lk *fuse.FileLock, flagsuint32, out *fuse.FileLock)syscall.Errno}

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

typeNodeGetxattrer

type NodeGetxattrer interface {Getxattr(ctxcontext.Context, attrstring, dest []byte) (uint32,syscall.Errno)}

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

typeNodeLinker

type NodeLinker interface {Link(ctxcontext.Context, targetInodeEmbedder, namestring, out *fuse.EntryOut) (node *Inode, errnosyscall.Errno)}

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

typeNodeListxattrer

type NodeListxattrer interface {Listxattr(ctxcontext.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 ERANGEand the correct size. If not defined, return an empty list andsuccess.

typeNodeLookuper

type NodeLookuper interface {Lookup(ctxcontext.Context, namestring, out *fuse.EntryOut) (*Inode,syscall.Errno)}

typeNodeLseeker

type NodeLseeker interface {Lseek(ctxcontext.Context, fFileHandle, Offuint64, whenceuint32) (uint64,syscall.Errno)}

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

typeNodeMkdirer

type NodeMkdirer interface {Mkdir(ctxcontext.Context, namestring, modeuint32, out *fuse.EntryOut) (*Inode,syscall.Errno)}

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

typeNodeMknoder

type NodeMknoder interface {Mknod(ctxcontext.Context, namestring, modeuint32, devuint32, out *fuse.EntryOut) (*Inode,syscall.Errno)}

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

typeNodeOnAdder

type NodeOnAdder interface {OnAdd(ctxcontext.Context)}

OnAdd is called when this InodeEmbedder is initialized.

typeNodeOpendirer

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

OpenDir opens a directory Inode for reading itscontents. The actual reading is driven from ReadDir, sothis method is just for performing sanity/permissionchecks. The default is to return success.

typeNodeOpener

type NodeOpener interface {Open(ctxcontext.Context, flagsuint32) (fhFileHandle, fuseFlagsuint32, errnosyscall.Errno)}

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

typeNodeReaddirer

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

ReadDir opens a stream of directory entries.

Readdir essentiallly returns a list of strings, and it is allowedfor 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 ofcurrently known children from the tree is returned. This means thatstatic in-memory file systems need not implement NodeReaddirer.

typeNodeReader

type NodeReader interface {Read(ctxcontext.Context, fFileHandle, dest []byte, offint64) (fuse.ReadResult,syscall.Errno)}

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

typeNodeReadlinker

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

Readlink reads the content of a symlink.

typeNodeReleaser

type NodeReleaser interface {Release(ctxcontext.Context, fFileHandle)syscall.Errno}

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

typeNodeRemovexattrer

type NodeRemovexattrer interface {Removexattr(ctxcontext.Context, attrstring)syscall.Errno}

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

typeNodeRenamer

type NodeRenamer interface {Rename(ctxcontext.Context, namestring, newParentInodeEmbedder, newNamestring, flagsuint32)syscall.Errno}

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

typeNodeRmdirer

type NodeRmdirer interface {Rmdir(ctxcontext.Context, namestring)syscall.Errno}

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

typeNodeSetattrer

type NodeSetattrer interface {Setattr(ctxcontext.Context, fFileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut)syscall.Errno}

SetAttr sets attributes for an Inode.

typeNodeSetlker

type NodeSetlker interface {Setlk(ctxcontext.Context, fFileHandle, owneruint64, lk *fuse.FileLock, flagsuint32)syscall.Errno}

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

typeNodeSetlkwer

type NodeSetlkwer interface {Setlkw(ctxcontext.Context, fFileHandle, owneruint64, lk *fuse.FileLock, flagsuint32)syscall.Errno}

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

typeNodeSetxattrer

type NodeSetxattrer interface {Setxattr(ctxcontext.Context, attrstring, data []byte, flagsuint32)syscall.Errno}

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

typeNodeStatfser

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

Statfs implements statistics for the filesystem that holds thisInode. If not defined, the `out` argument will zeroed with an OKresult. This is because OSX filesystems must Statfs, or the mountwill not work.

typeNodeSymlinker

type NodeSymlinker interface {Symlink(ctxcontext.Context, target, namestring, out *fuse.EntryOut) (node *Inode, errnosyscall.Errno)}

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

typeNodeUnlinker

type NodeUnlinker interface {Unlink(ctxcontext.Context, namestring)syscall.Errno}

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

typeNodeWriter

type NodeWriter interface {Write(ctxcontext.Context, fFileHandle, data []byte, offint64) (writtenuint32, errnosyscall.Errno)}

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

typeOptions

type Options struct {// MountOptions contain the options for mounting the fuse serverfuse.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.FirstAutomaticInouint64// OnAdd is an alternative way to specify the OnAdd// functionality of the root node.OnAdd func(ctxcontext.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.NullPermissionsbool// If nonzero, replace default (zero) UID with the given UIDUIDuint32// If nonzero, replace default (zero) GID with the given GIDGIDuint32// ServerCallbacks can be provided to stub out notification// functions for testing a filesystem without mounting it.ServerCallbacksServerCallbacks// 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

typeServerCallbacksadded inv2.0.3

type ServerCallbacks interface {DeleteNotify(parentuint64, childuint64, namestring)fuse.StatusEntryNotify(parentuint64, namestring)fuse.StatusInodeNotify(nodeuint64, offint64, lengthint64)fuse.StatusInodeRetrieveCache(nodeuint64, offsetint64, dest []byte) (nint, stfuse.Status)InodeNotifyStoreCache(nodeuint64, offsetint64, data []byte)fuse.Status}

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

typeStableAttr

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).Modeuint32// 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.Inouint64// 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// NFSGenuint64}

StableAttr holds immutable attributes of a object in the filesystem.

func (*StableAttr)Reserved

func (i *StableAttr) Reserved()bool

Reserved returns if the StableAttr is using reserved Inode numbers.

Source Files

View all Source files

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f orF : Jump to
y orY : Canonical URL
go.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.Learn more.

[8]ページ先頭

©2009-2025 Movatter.jp