Commit 2332f049 authored by Kirill Smelkov's avatar Kirill Smelkov

Merge branch 'master' into t

* master:
  fs: rename from nodefs
  nodefs: make the examples package level examples
  nodefs: prefix Node interface names with Node
  zipfs: remove unused data type ZipFile
  Add TestParallelDiropsHang / emulate gvfs-udisks2-volume-monitor
  dirstream_linux: fix zero terminator search
parents 9abf7b8e df62cf81
...@@ -17,16 +17,16 @@ import ( ...@@ -17,16 +17,16 @@ import (
"testing" "testing"
"time" "time"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil" "github.com/hanwen/go-fuse/internal/testutil"
"github.com/hanwen/go-fuse/nodefs"
) )
func setupFs(fs nodefs.InodeEmbedder, N int) (string, func()) { func setupFs(node fs.InodeEmbedder, N int) (string, func()) {
opts := &nodefs.Options{} opts := &fs.Options{}
opts.Debug = testutil.VerboseTest() opts.Debug = testutil.VerboseTest()
mountPoint := testutil.TempDir() mountPoint := testutil.TempDir()
server, err := nodefs.Mount(mountPoint, fs, opts) server, err := fs.Mount(mountPoint, node, opts)
if err != nil { if err != nil {
log.Panicf("cannot mount %v", err) log.Panicf("cannot mount %v", err)
} }
......
...@@ -10,17 +10,17 @@ import ( ...@@ -10,17 +10,17 @@ import (
"strings" "strings"
"syscall" "syscall"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/nodefs"
) )
type StatFS struct { type StatFS struct {
nodefs.Inode fs.Inode
files map[string]fuse.Attr files map[string]fuse.Attr
} }
var _ = (nodefs.OnAdder)((*StatFS)(nil)) var _ = (fs.NodeOnAdder)((*StatFS)(nil))
func (r *StatFS) OnAdd(ctx context.Context) { func (r *StatFS) OnAdd(ctx context.Context) {
for nm, a := range r.files { for nm, a := range r.files {
...@@ -50,8 +50,8 @@ func (r *StatFS) addFile(name string, a fuse.Attr) { ...@@ -50,8 +50,8 @@ func (r *StatFS) addFile(name string, a fuse.Attr) {
ch := p.GetChild(component) ch := p.GetChild(component)
if ch == nil { if ch == nil {
// Create a directory // Create a directory
ch = p.NewPersistentInode(context.Background(), &nodefs.Inode{}, ch = p.NewPersistentInode(context.Background(), &fs.Inode{},
nodefs.StableAttr{Mode: syscall.S_IFDIR}) fs.StableAttr{Mode: syscall.S_IFDIR})
// Add it // Add it
p.AddChild(component, ch, true) p.AddChild(component, ch, true)
} }
...@@ -60,10 +60,10 @@ func (r *StatFS) addFile(name string, a fuse.Attr) { ...@@ -60,10 +60,10 @@ func (r *StatFS) addFile(name string, a fuse.Attr) {
} }
// Create the file // Create the file
child := p.NewPersistentInode(context.Background(), &nodefs.MemRegularFile{ child := p.NewPersistentInode(context.Background(), &fs.MemRegularFile{
Data: make([]byte, a.Size), Data: make([]byte, a.Size),
Attr: a, Attr: a,
}, nodefs.StableAttr{}) }, fs.StableAttr{})
// And add it // And add it
p.AddChild(base, child, true) p.AddChild(base, child, true)
......
...@@ -12,32 +12,32 @@ import ( ...@@ -12,32 +12,32 @@ import (
"log" "log"
"syscall" "syscall"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/nodefs"
) )
type HelloRoot struct { type HelloRoot struct {
nodefs.Inode fs.Inode
} }
func (r *HelloRoot) OnAdd(ctx context.Context) { func (r *HelloRoot) OnAdd(ctx context.Context) {
ch := r.NewPersistentInode( ch := r.NewPersistentInode(
ctx, &nodefs.MemRegularFile{ ctx, &fs.MemRegularFile{
Data: []byte("file.txt"), Data: []byte("file.txt"),
Attr: fuse.Attr{ Attr: fuse.Attr{
Mode: 0644, Mode: 0644,
}, },
}, nodefs.StableAttr{Ino: 2}) }, fs.StableAttr{Ino: 2})
r.AddChild("file.txt", ch, false) r.AddChild("file.txt", ch, false)
} }
func (r *HelloRoot) Getattr(ctx context.Context, fh nodefs.FileHandle, out *fuse.AttrOut) syscall.Errno { func (r *HelloRoot) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
out.Mode = 0755 out.Mode = 0755
return 0 return 0
} }
var _ = (nodefs.Getattrer)((*HelloRoot)(nil)) var _ = (fs.NodeGetattrer)((*HelloRoot)(nil))
var _ = (nodefs.OnAdder)((*HelloRoot)(nil)) var _ = (fs.NodeOnAdder)((*HelloRoot)(nil))
func main() { func main() {
debug := flag.Bool("debug", false, "print debug data") debug := flag.Bool("debug", false, "print debug data")
...@@ -45,9 +45,9 @@ func main() { ...@@ -45,9 +45,9 @@ func main() {
if len(flag.Args()) < 1 { if len(flag.Args()) < 1 {
log.Fatal("Usage:\n hello MOUNTPOINT") log.Fatal("Usage:\n hello MOUNTPOINT")
} }
opts := &nodefs.Options{} opts := &fs.Options{}
opts.Debug = *debug opts.Debug = *debug
server, err := nodefs.Mount(flag.Arg(0), &HelloRoot{}, opts) server, err := fs.Mount(flag.Arg(0), &HelloRoot{}, opts)
if err != nil { if err != nil {
log.Fatalf("Mount fail: %v\n", err) log.Fatalf("Mount fail: %v\n", err)
} }
......
...@@ -18,7 +18,7 @@ import ( ...@@ -18,7 +18,7 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/hanwen/go-fuse/nodefs" "github.com/hanwen/go-fuse/fs"
) )
func writeMemProfile(fn string, sigs <-chan os.Signal) { func writeMemProfile(fn string, sigs <-chan os.Signal) {
...@@ -75,13 +75,13 @@ func main() { ...@@ -75,13 +75,13 @@ func main() {
} }
orig := flag.Arg(1) orig := flag.Arg(1)
loopbackRoot, err := nodefs.NewLoopbackRoot(orig) loopbackRoot, err := fs.NewLoopbackRoot(orig)
if err != nil { if err != nil {
log.Fatalf("NewLoopbackRoot(%s): %v\n", orig, err) log.Fatalf("NewLoopbackRoot(%s): %v\n", orig, err)
} }
sec := time.Second sec := time.Second
opts := &nodefs.Options{ opts := &fs.Options{
// These options are to be compatible with libfuse defaults, // These options are to be compatible with libfuse defaults,
// making benchmarking easier. // making benchmarking easier.
AttrTimeout: &sec, AttrTimeout: &sec,
...@@ -89,7 +89,7 @@ func main() { ...@@ -89,7 +89,7 @@ func main() {
} }
opts.Debug = *debug opts.Debug = *debug
opts.AllowOther = *other opts.AllowOther = *other
server, err := nodefs.Mount(flag.Arg(0), loopbackRoot, opts) server, err := fs.Mount(flag.Arg(0), loopbackRoot, opts)
if err != nil { if err != nil {
log.Fatalf("Mount fail: %v\n", err) log.Fatalf("Mount fail: %v\n", err)
} }
......
...@@ -11,7 +11,7 @@ import ( ...@@ -11,7 +11,7 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/hanwen/go-fuse/nodefs" "github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/zipfs" "github.com/hanwen/go-fuse/zipfs"
) )
...@@ -25,15 +25,15 @@ func main() { ...@@ -25,15 +25,15 @@ func main() {
os.Exit(2) os.Exit(2)
} }
fs := &zipfs.MultiZipFs{} root := &zipfs.MultiZipFs{}
sec := time.Second sec := time.Second
opts := nodefs.Options{ opts := fs.Options{
EntryTimeout: &sec, EntryTimeout: &sec,
AttrTimeout: &sec, AttrTimeout: &sec,
DefaultPermissions: true, DefaultPermissions: true,
} }
opts.Debug = *debug opts.Debug = *debug
server, err := nodefs.Mount(flag.Arg(0), fs, &opts) server, err := fs.Mount(flag.Arg(0), root, &opts)
if err != nil { if err != nil {
fmt.Printf("Mount fail: %v\n", err) fmt.Printf("Mount fail: %v\n", err)
os.Exit(1) os.Exit(1)
......
...@@ -18,8 +18,8 @@ import ( ...@@ -18,8 +18,8 @@ import (
"time" "time"
"github.com/hanwen/go-fuse/benchmark" "github.com/hanwen/go-fuse/benchmark"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/nodefs"
) )
func main() { func main() {
...@@ -50,18 +50,18 @@ func main() { ...@@ -50,18 +50,18 @@ func main() {
} }
} }
fs := &benchmark.StatFS{} root := &benchmark.StatFS{}
lines := benchmark.ReadLines(flag.Arg(1)) lines := benchmark.ReadLines(flag.Arg(1))
for _, l := range lines { for _, l := range lines {
fs.AddFile(strings.TrimSpace(l), root.AddFile(strings.TrimSpace(l),
fuse.Attr{Mode: syscall.S_IFREG}) fuse.Attr{Mode: syscall.S_IFREG})
} }
opts := &nodefs.Options{ opts := &fs.Options{
AttrTimeout: ttl, AttrTimeout: ttl,
EntryTimeout: ttl, EntryTimeout: ttl,
} }
opts.Debug = *debug opts.Debug = *debug
server, err := nodefs.Mount(flag.Arg(0), fs, opts) server, err := fs.Mount(flag.Arg(0), root, opts)
if err != nil { if err != nil {
log.Fatalf("Mount fail: %v\n", err) log.Fatalf("Mount fail: %v\n", err)
} }
......
...@@ -16,7 +16,7 @@ import ( ...@@ -16,7 +16,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/hanwen/go-fuse/nodefs" "github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/zipfs" "github.com/hanwen/go-fuse/zipfs"
) )
...@@ -54,13 +54,13 @@ func main() { ...@@ -54,13 +54,13 @@ func main() {
os.Exit(1) os.Exit(1)
} }
opts := &nodefs.Options{ opts := &fs.Options{
AttrTimeout: ttl, AttrTimeout: ttl,
EntryTimeout: ttl, EntryTimeout: ttl,
DefaultPermissions: true, DefaultPermissions: true,
} }
opts.Debug = *debug opts.Debug = *debug
server, err := nodefs.Mount(flag.Arg(0), root, opts) server, err := fs.Mount(flag.Arg(0), root, opts)
if err != nil { if err != nil {
fmt.Printf("Mount fail: %v\n", err) fmt.Printf("Mount fail: %v\n", err)
os.Exit(1) os.Exit(1)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package nodefs provides infrastructure to build tree-organized filesystems. // Package fs provides infrastructure to build tree-organized filesystems.
// //
// A tree-organized filesystem is similar to UNIX or Plan 9 filesystem: it // A tree-organized filesystem is similar to UNIX or Plan 9 filesystem: it
// consists of nodes with each node being either a file or a directory. Files // consists of nodes with each node being either a file or a directory. Files
...@@ -68,8 +68,10 @@ ...@@ -68,8 +68,10 @@
// inspired on the system call names, so we have Listxattr rather than // inspired on the system call names, so we have Listxattr rather than
// ListXAttr. // ListXAttr.
// //
// Locks for networked filesystems are supported through the suite of
package nodefs // Getlk, Setlk and Setlkw methods. They alllow locks on regions of
// regular files.
package fs
import ( import (
"context" "context"
...@@ -86,8 +88,8 @@ import ( ...@@ -86,8 +88,8 @@ import (
// read-only filesystem with a predefined tree structure. See // read-only filesystem with a predefined tree structure. See
// zipfs_test.go for an example. A example is in zip_test.go // zipfs_test.go for an example. A example is in zip_test.go
type InodeEmbedder interface { type InodeEmbedder interface {
// populateInode and inode are used by nodefs internally to // populateInode and inode are used internally to link Inode
// link Inode to a Node. // to a Node.
// //
// See Inode() for the public API to retrieve an inode from Node. // See Inode() for the public API to retrieve an inode from Node.
embed() *Inode embed() *Inode
...@@ -100,7 +102,7 @@ type InodeEmbedder interface { ...@@ -100,7 +102,7 @@ type InodeEmbedder interface {
// Inode. If not defined, the `out` argument will zeroed with an OK // Inode. If not defined, the `out` argument will zeroed with an OK
// result. This is because OSX filesystems must Statfs, or the mount // result. This is because OSX filesystems must Statfs, or the mount
// will not work. // will not work.
type Statfser interface { type NodeStatfser interface {
Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno
} }
...@@ -116,24 +118,24 @@ type Statfser interface { ...@@ -116,24 +118,24 @@ type Statfser interface {
// is necessary to either return permissions from GetAttr/Lookup or // is necessary to either return permissions from GetAttr/Lookup or
// set Options.DefaultPermissions in order to allow chdir into the // set Options.DefaultPermissions in order to allow chdir into the
// FUSE mount. // FUSE mount.
type Accesser interface { type NodeAccesser interface {
Access(ctx context.Context, mask uint32) syscall.Errno Access(ctx context.Context, mask uint32) syscall.Errno
} }
// GetAttr reads attributes for an Inode. The library will // GetAttr reads attributes for an Inode. The library will
// ensure that Mode and Ino are set correctly. For regular // ensure that Mode and Ino are set correctly. For regular
// files, Size should be set so it can be read correctly. // files, Size should be set so it can be read correctly.
type Getattrer interface { type NodeGetattrer interface {
Getattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno Getattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno
} }
// SetAttr sets attributes for an Inode. // SetAttr sets attributes for an Inode.
type Setattrer interface { type NodeSetattrer interface {
Setattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno Setattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
} }
// OnAdd is called when this InodeEmbedder is initialized. // OnAdd is called when this InodeEmbedder is initialized.
type OnAdder interface { type NodeOnAdder interface {
OnAdd(ctx context.Context) OnAdd(ctx context.Context)
} }
...@@ -141,20 +143,20 @@ type OnAdder interface { ...@@ -141,20 +143,20 @@ type OnAdder interface {
// `dest` and return the number of bytes. If `dest` is too // `dest` and return the number of bytes. If `dest` is too
// small, it should return ERANGE and the size of the attribute. // small, it should return ERANGE and the size of the attribute.
// If not defined, Getxattr will return ENOATTR. // If not defined, Getxattr will return ENOATTR.
type Getxattrer interface { type NodeGetxattrer interface {
Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno)
} }
// Setxattr should store data for the given attribute. See // Setxattr should store data for the given attribute. See
// setxattr(2) for information about flags. // setxattr(2) for information about flags.
// If not defined, Setxattr will return ENOATTR. // If not defined, Setxattr will return ENOATTR.
type Setxattrer interface { type NodeSetxattrer interface {
Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno
} }
// Removexattr should delete the given attribute. // Removexattr should delete the given attribute.
// If not defined, Removexattr will return ENOATTR. // If not defined, Removexattr will return ENOATTR.
type Removexattrer interface { type NodeRemovexattrer interface {
Removexattr(ctx context.Context, attr string) syscall.Errno Removexattr(ctx context.Context, attr string) syscall.Errno
} }
...@@ -162,18 +164,18 @@ type Removexattrer interface { ...@@ -162,18 +164,18 @@ type Removexattrer interface {
// `dest`. If the `dest` buffer is too small, it should return ERANGE // `dest`. If the `dest` buffer is too small, it should return ERANGE
// and the correct size. If not defined, return an empty list and // and the correct size. If not defined, return an empty list and
// success. // success.
type Listxattrer interface { type NodeListxattrer interface {
Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno)
} }
// Readlink reads the content of a symlink. // Readlink reads the content of a symlink.
type Readlinker interface { type NodeReadlinker interface {
Readlink(ctx context.Context) ([]byte, syscall.Errno) Readlink(ctx context.Context) ([]byte, syscall.Errno)
} }
// Open opens an Inode (of regular file type) for reading. It // Open opens an Inode (of regular file type) for reading. It
// is optional but recommended to return a FileHandle. // is optional but recommended to return a FileHandle.
type Opener interface { type NodeOpener interface {
Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno)
} }
...@@ -182,20 +184,20 @@ type Opener interface { ...@@ -182,20 +184,20 @@ type Opener interface {
// `dest` buffer. If the file was opened without FileHandle, // `dest` buffer. If the file was opened without FileHandle,
// the FileHandle argument here is nil. The default // the FileHandle argument here is nil. The default
// implementation forwards to the FileHandle. // implementation forwards to the FileHandle.
type Reader interface { type NodeReader interface {
Read(ctx context.Context, f FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) Read(ctx context.Context, f FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno)
} }
// Writes the data into the file handle at given offset. After // Writes the data into the file handle at given offset. After
// returning, the data will be reused and may not referenced. // returning, the data will be reused and may not referenced.
// The default implementation forwards to the FileHandle. // The default implementation forwards to the FileHandle.
type Writer interface { type NodeWriter interface {
Write(ctx context.Context, f FileHandle, data []byte, off int64) (written uint32, errno syscall.Errno) Write(ctx context.Context, f FileHandle, data []byte, off int64) (written uint32, errno syscall.Errno)
} }
// Fsync is a signal to ensure writes to the Inode are flushed // Fsync is a signal to ensure writes to the Inode are flushed
// to stable storage. // to stable storage.
type Fsyncer interface { type NodeFsyncer interface {
Fsync(ctx context.Context, f FileHandle, flags uint32) syscall.Errno Fsync(ctx context.Context, f FileHandle, flags uint32) syscall.Errno
} }
...@@ -203,28 +205,28 @@ type Fsyncer interface { ...@@ -203,28 +205,28 @@ type Fsyncer interface {
// case of duplicated descriptor, it may be called more than // case of duplicated descriptor, it may be called more than
// once for a file. The default implementation forwards to the // once for a file. The default implementation forwards to the
// FileHandle. // FileHandle.
type Flusher interface { type NodeFlusher interface {
Flush(ctx context.Context, f FileHandle) syscall.Errno Flush(ctx context.Context, f FileHandle) syscall.Errno
} }
// This is called to before the file handle is forgotten. The // This is called to before a FileHandle is forgotten. The
// kernel ignores the return value of this method, // kernel ignores the return value of this method,
// so any cleanup that requires specific synchronization or // so any cleanup that requires specific synchronization or
// could fail with I/O errors should happen in Flush instead. // could fail with I/O errors should happen in Flush instead.
// The default implementation forwards to the FileHandle. // The default implementation forwards to the FileHandle.
type Releaser interface { type NodeReleaser interface {
Release(ctx context.Context, f FileHandle) syscall.Errno Release(ctx context.Context, f FileHandle) syscall.Errno
} }
// Allocate preallocates space for future writes, so they will // Allocate preallocates space for future writes, so they will
// never encounter ESPACE. // never encounter ESPACE.
type Allocater interface { type NodeAllocater interface {
Allocate(ctx context.Context, f FileHandle, off uint64, size uint64, mode uint32) syscall.Errno Allocate(ctx context.Context, f FileHandle, off uint64, size uint64, mode uint32) syscall.Errno
} }
// CopyFileRange copies data between sections of two files, // CopyFileRange copies data between sections of two files,
// without the data having to pass through the calling process. // without the data having to pass through the calling process.
type CopyFileRanger interface { type NodeCopyFileRanger interface {
CopyFileRange(ctx context.Context, fhIn FileHandle, CopyFileRange(ctx context.Context, fhIn FileHandle,
offIn uint64, out *Inode, fhOut FileHandle, offOut uint64, offIn uint64, out *Inode, fhOut FileHandle, offOut uint64,
len uint64, flags uint64) (uint32, syscall.Errno) len uint64, flags uint64) (uint32, syscall.Errno)
...@@ -233,29 +235,28 @@ type CopyFileRanger interface { ...@@ -233,29 +235,28 @@ type CopyFileRanger interface {
// Lseek is used to implement holes: it should return the // Lseek is used to implement holes: it should return the
// first offset beyond `off` where there is data (SEEK_DATA) // first offset beyond `off` where there is data (SEEK_DATA)
// or where there is a hole (SEEK_HOLE). // or where there is a hole (SEEK_HOLE).
type Lseeker interface { type NodeLseeker interface {
Lseek(ctx context.Context, f FileHandle, Off uint64, whence uint32) (uint64, syscall.Errno) Lseek(ctx context.Context, f FileHandle, Off uint64, whence uint32) (uint64, syscall.Errno)
} }
// LockOperations are operations for locking regions of regular files.
// Getlk returns locks that would conflict with the given input // Getlk returns locks that would conflict with the given input
// lock. If no locks conflict, the output has type L_UNLCK. See // lock. If no locks conflict, the output has type L_UNLCK. See
// fcntl(2) for more information. // fcntl(2) for more information.
// If not defined, returns ENOTSUP // If not defined, returns ENOTSUP
type Getlker interface { type NodeGetlker interface {
Getlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) syscall.Errno Getlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) syscall.Errno
} }
// Setlk obtains a lock on a file, or fail if the lock could not // Setlk obtains a lock on a file, or fail if the lock could not
// obtained. See fcntl(2) for more information. If not defined, // obtained. See fcntl(2) for more information. If not defined,
// returns ENOTSUP // returns ENOTSUP
type Setlker interface { type NodeSetlker interface {
Setlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno Setlk(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) // Setlkw obtains a lock on a file, waiting if necessary. See fcntl(2)
// for more information. If not defined, returns ENOTSUP // for more information. If not defined, returns ENOTSUP
type Setlkwer interface { type NodeSetlkwer interface {
Setlkw(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno Setlkw(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
} }
...@@ -285,7 +286,7 @@ type DirStream interface { ...@@ -285,7 +286,7 @@ type DirStream interface {
// //
// If not defined, we look for an existing child with the given name, // If not defined, we look for an existing child with the given name,
// or returns ENOENT. // or returns ENOENT.
type Lookuper interface { type NodeLookuper interface {
Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno)
} }
...@@ -293,7 +294,7 @@ type Lookuper interface { ...@@ -293,7 +294,7 @@ type Lookuper interface {
// contents. The actual reading is driven from ReadDir, so // contents. The actual reading is driven from ReadDir, so
// this method is just for performing sanity/permission // this method is just for performing sanity/permission
// checks. The default is to return success. // checks. The default is to return success.
type Opendirer interface { type NodeOpendirer interface {
Opendir(ctx context.Context) syscall.Errno Opendir(ctx context.Context) syscall.Errno
} }
...@@ -301,31 +302,31 @@ type Opendirer interface { ...@@ -301,31 +302,31 @@ type Opendirer interface {
// //
// The default ReadDir returns the list of currently known children // The default ReadDir returns the list of currently known children
// from the tree // from the tree
type Readdirer interface { type NodeReaddirer interface {
Readdir(ctx context.Context) (DirStream, syscall.Errno) Readdir(ctx context.Context) (DirStream, syscall.Errno)
} }
// Mkdir is similar to Lookup, but must create a directory entry and Inode. // Mkdir is similar to Lookup, but must create a directory entry and Inode.
// Default is to return EROFS. // Default is to return EROFS.
type Mkdirer interface { type NodeMkdirer interface {
Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, syscall.Errno)
} }
// Mknod is similar to Lookup, but must create a device entry and Inode. // Mknod is similar to Lookup, but must create a device entry and Inode.
// Default is to return EROFS. // Default is to return EROFS.
type Mknoder interface { type NodeMknoder interface {
Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (*Inode, syscall.Errno)
} }
// Link is similar to Lookup, but must create a new link to an existing Inode. // Link is similar to Lookup, but must create a new link to an existing Inode.
// Default is to return EROFS. // Default is to return EROFS.
type Linker interface { type NodeLinker interface {
Link(ctx context.Context, target InodeEmbedder, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno) Link(ctx context.Context, target InodeEmbedder, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno)
} }
// Symlink is similar to Lookup, but must create a new symbolic link. // Symlink is similar to Lookup, but must create a new symbolic link.
// Default is to return EROFS. // Default is to return EROFS.
type Symlinker interface { type NodeSymlinker interface {
Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno)
} }
...@@ -333,28 +334,27 @@ type Symlinker interface { ...@@ -333,28 +334,27 @@ type Symlinker interface {
// child. It typically also returns a FileHandle as a // child. It typically also returns a FileHandle as a
// reference for future reads/writes. // reference for future reads/writes.
// Default is to return EROFS. // Default is to return EROFS.
type Creater interface { 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(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (node *Inode, fh FileHandle, fuseFlags uint32, errno syscall.Errno)
} }
// Unlink should remove a child from this directory. If the // Unlink should remove a child from this directory. If the
// return status is OK, the Inode is removed as child in the // return status is OK, the Inode is removed as child in the
// FS tree automatically. Default is to return EROFS. // FS tree automatically. Default is to return EROFS.
type Unlinker interface { type NodeUnlinker interface {
Unlink(ctx context.Context, name string) syscall.Errno Unlink(ctx context.Context, name string) syscall.Errno
} }
// Rmdir is like Unlink but for directories. // Rmdir is like Unlink but for directories.
// Default is to return EROFS. // Default is to return EROFS.
type Rmdirer interface { type NodeRmdirer interface {
Rmdir(ctx context.Context, name string) syscall.Errno Rmdir(ctx context.Context, name string) syscall.Errno
} }
// Rename should move a child from one directory to a different // 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 // one. The change is effected in the FS tree if the return status is
// OK. // OK. Default is to return EROFS.
// Default is to return EROFS. type NodeRenamer interface {
type Renamer interface {
Rename(ctx context.Context, name string, newParent InodeEmbedder, newName string, flags uint32) syscall.Errno Rename(ctx context.Context, name string, newParent InodeEmbedder, newName string, flags uint32) syscall.Errno
} }
...@@ -372,63 +372,62 @@ type Renamer interface { ...@@ -372,63 +372,62 @@ type Renamer interface {
type FileHandle interface { type FileHandle interface {
} }
// Release is called when forgetting the file handle. Default is to // See NodeReleaser.
// call Release on the Inode.
type FileReleaser interface { type FileReleaser interface {
Release(ctx context.Context) syscall.Errno Release(ctx context.Context) syscall.Errno
} }
// See Getattrer. Default is to call Getattr on the Inode // See NodeGetattrer.
type FileGetattrer interface { type FileGetattrer interface {
Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno
} }
// See Reader. Default is to call Read on the Inode // See NodeReader.
type FileReader interface { type FileReader interface {
Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno)
} }
// See Writer. Default is to call Write on the Inode // See NodeWriter.
type FileWriter interface { type FileWriter interface {
Write(ctx context.Context, data []byte, off int64) (written uint32, errno syscall.Errno) Write(ctx context.Context, data []byte, off int64) (written uint32, errno syscall.Errno)
} }
// See Getlker. Default is to call Getlk on the Inode // See NodeGetlker.
type FileGetlker interface { type FileGetlker interface {
Getlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) syscall.Errno Getlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) syscall.Errno
} }
// See Setlker. Default is to call Setlk on the Inode // See NodeSetlker.
type FileSetlker interface { type FileSetlker interface {
Setlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno Setlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
} }
// See Setlkwer. Default is to call Setlkw on the Inode // See NodeSetlkwer.
type FileSetlkwer interface { type FileSetlkwer interface {
Setlkw(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno Setlkw(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
} }
// See Lseek. Default is to call Lseek on the Inode // See NodeLseeker.
type FileLseeker interface { type FileLseeker interface {
Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno)
} }
// See Flusher. Default is to call Flush on the Inode // See NodeFlusher.
type FileFlusher interface { type FileFlusher interface {
Flush(ctx context.Context) syscall.Errno Flush(ctx context.Context) syscall.Errno
} }
// See Fsync. Default is to call Fsync on the Inode // See NodeFsync.
type FileFsyncer interface { type FileFsyncer interface {
Fsync(ctx context.Context, flags uint32) syscall.Errno Fsync(ctx context.Context, flags uint32) syscall.Errno
} }
// See Fsync. Default is to call Setattr on the Inode // See NodeFsync.
type FileSetattrer interface { type FileSetattrer interface {
Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
} }
// See Allocater. Default is to call Allocate on the Inode // See NodeAllocater.
type FileAllocater interface { type FileAllocater interface {
Allocate(ctx context.Context, off uint64, size uint64, mode uint32) syscall.Errno Allocate(ctx context.Context, off uint64, size uint64, mode uint32) syscall.Errno
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"context" "context"
...@@ -107,7 +107,7 @@ func (b *rawBridge) newInode(ctx context.Context, ops InodeEmbedder, id StableAt ...@@ -107,7 +107,7 @@ func (b *rawBridge) newInode(ctx context.Context, ops InodeEmbedder, id StableAt
return ch return ch
} }
if oa, ok := ops.(OnAdder); ok { if oa, ok := ops.(NodeOnAdder); ok {
oa.OnAdd(ctx) oa.OnAdd(ctx)
} }
return ch return ch
...@@ -160,8 +160,8 @@ func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) { ...@@ -160,8 +160,8 @@ func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) {
} }
} }
// NewNodeFS creates a node based filesystem based on an Operations // NewNodeFS creates a node based filesystem based on the
// instance for the root. // InodeEmbedder instance for the root of the tree.
func NewNodeFS(root InodeEmbedder, opts *Options) fuse.RawFileSystem { func NewNodeFS(root InodeEmbedder, opts *Options) fuse.RawFileSystem {
bridge := &rawBridge{ bridge := &rawBridge{
automaticIno: opts.FirstAutomaticIno, automaticIno: opts.FirstAutomaticIno,
...@@ -201,7 +201,7 @@ func NewNodeFS(root InodeEmbedder, opts *Options) fuse.RawFileSystem { ...@@ -201,7 +201,7 @@ func NewNodeFS(root InodeEmbedder, opts *Options) fuse.RawFileSystem {
if opts.OnAdd != nil { if opts.OnAdd != nil {
opts.OnAdd(context.Background()) opts.OnAdd(context.Background())
} else if oa, ok := root.(OnAdder); ok { } else if oa, ok := root.(NodeOnAdder); ok {
oa.OnAdd(context.Background()) oa.OnAdd(context.Background())
} }
...@@ -242,7 +242,7 @@ func (b *rawBridge) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name s ...@@ -242,7 +242,7 @@ func (b *rawBridge) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name s
} }
func (b *rawBridge) lookup(ctx *fuse.Context, parent *Inode, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) { func (b *rawBridge) lookup(ctx *fuse.Context, parent *Inode, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
if lu, ok := parent.ops.(Lookuper); ok { if lu, ok := parent.ops.(NodeLookuper); ok {
return lu.Lookup(ctx, name, out) return lu.Lookup(ctx, name, out)
} }
...@@ -251,7 +251,7 @@ func (b *rawBridge) lookup(ctx *fuse.Context, parent *Inode, name string, out *f ...@@ -251,7 +251,7 @@ func (b *rawBridge) lookup(ctx *fuse.Context, parent *Inode, name string, out *f
return nil, syscall.ENOENT return nil, syscall.ENOENT
} }
if ga, ok := child.ops.(Getattrer); ok { if ga, ok := child.ops.(NodeGetattrer); ok {
var a fuse.AttrOut var a fuse.AttrOut
errno := ga.Getattr(ctx, nil, &a) errno := ga.Getattr(ctx, nil, &a)
if errno == 0 { if errno == 0 {
...@@ -265,7 +265,7 @@ func (b *rawBridge) lookup(ctx *fuse.Context, parent *Inode, name string, out *f ...@@ -265,7 +265,7 @@ func (b *rawBridge) lookup(ctx *fuse.Context, parent *Inode, name string, out *f
func (b *rawBridge) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status { func (b *rawBridge) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status {
parent, _ := b.inode(header.NodeId, 0) parent, _ := b.inode(header.NodeId, 0)
var errno syscall.Errno var errno syscall.Errno
if mops, ok := parent.ops.(Rmdirer); ok { if mops, ok := parent.ops.(NodeRmdirer); ok {
errno = mops.Rmdir(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name) errno = mops.Rmdir(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
} }
...@@ -278,7 +278,7 @@ func (b *rawBridge) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name st ...@@ -278,7 +278,7 @@ func (b *rawBridge) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name st
func (b *rawBridge) Unlink(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status { func (b *rawBridge) Unlink(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status {
parent, _ := b.inode(header.NodeId, 0) parent, _ := b.inode(header.NodeId, 0)
var errno syscall.Errno var errno syscall.Errno
if mops, ok := parent.ops.(Unlinker); ok { if mops, ok := parent.ops.(NodeUnlinker); ok {
errno = mops.Unlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name) errno = mops.Unlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
} }
...@@ -293,7 +293,7 @@ func (b *rawBridge) Mkdir(cancel <-chan struct{}, input *fuse.MkdirIn, name stri ...@@ -293,7 +293,7 @@ func (b *rawBridge) Mkdir(cancel <-chan struct{}, input *fuse.MkdirIn, name stri
var child *Inode var child *Inode
var errno syscall.Errno var errno syscall.Errno
if mops, ok := parent.ops.(Mkdirer); ok { if mops, ok := parent.ops.(NodeMkdirer); ok {
child, errno = mops.Mkdir(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, out) child, errno = mops.Mkdir(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, out)
} else { } else {
return fuse.ENOTSUP return fuse.ENOTSUP
...@@ -318,7 +318,7 @@ func (b *rawBridge) Mknod(cancel <-chan struct{}, input *fuse.MknodIn, name stri ...@@ -318,7 +318,7 @@ func (b *rawBridge) Mknod(cancel <-chan struct{}, input *fuse.MknodIn, name stri
var child *Inode var child *Inode
var errno syscall.Errno var errno syscall.Errno
if mops, ok := parent.ops.(Mknoder); ok { if mops, ok := parent.ops.(NodeMknoder); ok {
child, errno = mops.Mknod(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, input.Rdev, out) child, errno = mops.Mknod(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, input.Rdev, out)
} }
...@@ -340,7 +340,7 @@ func (b *rawBridge) Create(cancel <-chan struct{}, input *fuse.CreateIn, name st ...@@ -340,7 +340,7 @@ func (b *rawBridge) Create(cancel <-chan struct{}, input *fuse.CreateIn, name st
var errno syscall.Errno var errno syscall.Errno
var f FileHandle var f FileHandle
var flags uint32 var flags uint32
if mops, ok := parent.ops.(Creater); ok { if mops, ok := parent.ops.(NodeCreater); ok {
child, f, flags, errno = mops.Create(ctx, name, input.Flags, input.Mode, &out.EntryOut) child, f, flags, errno = mops.Create(ctx, name, input.Flags, input.Mode, &out.EntryOut)
} else { } else {
return fuse.EROFS return fuse.EROFS
...@@ -397,7 +397,7 @@ func (b *rawBridge) getattr(ctx context.Context, n *Inode, f FileHandle, out *fu ...@@ -397,7 +397,7 @@ func (b *rawBridge) getattr(ctx context.Context, n *Inode, f FileHandle, out *fu
fg, _ = f.(FileGetattrer) fg, _ = f.(FileGetattrer)
} }
if fops, ok := n.ops.(Getattrer); ok { if fops, ok := n.ops.(NodeGetattrer); ok {
errno = fops.Getattr(ctx, f, out) errno = fops.Getattr(ctx, f, out)
} else if fg != nil { } else if fg != nil {
errno = fg.Getattr(ctx, out) errno = fg.Getattr(ctx, out)
...@@ -423,7 +423,7 @@ func (b *rawBridge) SetAttr(cancel <-chan struct{}, in *fuse.SetAttrIn, out *fus ...@@ -423,7 +423,7 @@ func (b *rawBridge) SetAttr(cancel <-chan struct{}, in *fuse.SetAttrIn, out *fus
f = nil f = nil
} }
if fops, ok := n.ops.(Setattrer); ok { if fops, ok := n.ops.(NodeSetattrer); ok {
return errnoToStatus(fops.Setattr(ctx, f, in, out)) return errnoToStatus(fops.Setattr(ctx, f, in, out))
} }
if fops, ok := f.(FileSetattrer); ok { if fops, ok := f.(FileSetattrer); ok {
...@@ -437,7 +437,7 @@ func (b *rawBridge) Rename(cancel <-chan struct{}, input *fuse.RenameIn, oldName ...@@ -437,7 +437,7 @@ func (b *rawBridge) Rename(cancel <-chan struct{}, input *fuse.RenameIn, oldName
p1, _ := b.inode(input.NodeId, 0) p1, _ := b.inode(input.NodeId, 0)
p2, _ := b.inode(input.Newdir, 0) p2, _ := b.inode(input.Newdir, 0)
if mops, ok := p1.ops.(Renamer); ok { if mops, ok := p1.ops.(NodeRenamer); ok {
errno := mops.Rename(&fuse.Context{Caller: input.Caller, Cancel: cancel}, oldName, p2.ops, newName, input.Flags) errno := mops.Rename(&fuse.Context{Caller: input.Caller, Cancel: cancel}, oldName, p2.ops, newName, input.Flags)
if errno == 0 { if errno == 0 {
if input.Flags&RENAME_EXCHANGE != 0 { if input.Flags&RENAME_EXCHANGE != 0 {
...@@ -456,7 +456,7 @@ func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string ...@@ -456,7 +456,7 @@ func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string
parent, _ := b.inode(input.NodeId, 0) parent, _ := b.inode(input.NodeId, 0)
target, _ := b.inode(input.Oldnodeid, 0) target, _ := b.inode(input.Oldnodeid, 0)
if mops, ok := parent.ops.(Linker); ok { if mops, ok := parent.ops.(NodeLinker); ok {
child, errno := mops.Link(&fuse.Context{Caller: input.Caller, Cancel: cancel}, target.ops, name, out) child, errno := mops.Link(&fuse.Context{Caller: input.Caller, Cancel: cancel}, target.ops, name, out)
if errno != 0 { if errno != 0 {
return errnoToStatus(errno) return errnoToStatus(errno)
...@@ -473,7 +473,7 @@ func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string ...@@ -473,7 +473,7 @@ func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string
func (b *rawBridge) Symlink(cancel <-chan struct{}, header *fuse.InHeader, target string, name string, out *fuse.EntryOut) fuse.Status { func (b *rawBridge) Symlink(cancel <-chan struct{}, header *fuse.InHeader, target string, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(header.NodeId, 0) parent, _ := b.inode(header.NodeId, 0)
if mops, ok := parent.ops.(Symlinker); ok { if mops, ok := parent.ops.(NodeSymlinker); ok {
child, status := mops.Symlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, target, name, out) child, status := mops.Symlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, target, name, out)
if status != 0 { if status != 0 {
return errnoToStatus(status) return errnoToStatus(status)
...@@ -490,7 +490,7 @@ func (b *rawBridge) Symlink(cancel <-chan struct{}, header *fuse.InHeader, targe ...@@ -490,7 +490,7 @@ func (b *rawBridge) Symlink(cancel <-chan struct{}, header *fuse.InHeader, targe
func (b *rawBridge) Readlink(cancel <-chan struct{}, header *fuse.InHeader) (out []byte, status fuse.Status) { func (b *rawBridge) Readlink(cancel <-chan struct{}, header *fuse.InHeader) (out []byte, status fuse.Status) {
n, _ := b.inode(header.NodeId, 0) n, _ := b.inode(header.NodeId, 0)
if linker, ok := n.ops.(Readlinker); ok { if linker, ok := n.ops.(NodeReadlinker); ok {
result, errno := linker.Readlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}) result, errno := linker.Readlink(&fuse.Context{Caller: header.Caller, Cancel: cancel})
if errno != 0 { if errno != 0 {
return nil, errnoToStatus(errno) return nil, errnoToStatus(errno)
...@@ -507,7 +507,7 @@ func (b *rawBridge) Access(cancel <-chan struct{}, input *fuse.AccessIn) fuse.St ...@@ -507,7 +507,7 @@ func (b *rawBridge) Access(cancel <-chan struct{}, input *fuse.AccessIn) fuse.St
n, _ := b.inode(input.NodeId, 0) n, _ := b.inode(input.NodeId, 0)
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel} ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
if a, ok := n.ops.(Accesser); ok { if a, ok := n.ops.(NodeAccesser); ok {
return errnoToStatus(a.Access(ctx, input.Mask)) return errnoToStatus(a.Access(ctx, input.Mask))
} }
...@@ -530,7 +530,7 @@ func (b *rawBridge) Access(cancel <-chan struct{}, input *fuse.AccessIn) fuse.St ...@@ -530,7 +530,7 @@ func (b *rawBridge) Access(cancel <-chan struct{}, input *fuse.AccessIn) fuse.St
func (b *rawBridge) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string, data []byte) (uint32, fuse.Status) { func (b *rawBridge) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string, data []byte) (uint32, fuse.Status) {
n, _ := b.inode(header.NodeId, 0) n, _ := b.inode(header.NodeId, 0)
if xops, ok := n.ops.(Getxattrer); ok { if xops, ok := n.ops.(NodeGetxattrer); ok {
nb, errno := xops.Getxattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr, data) nb, errno := xops.Getxattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr, data)
return nb, errnoToStatus(errno) return nb, errnoToStatus(errno)
} }
...@@ -540,7 +540,7 @@ func (b *rawBridge) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr ...@@ -540,7 +540,7 @@ func (b *rawBridge) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr
func (b *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []byte) (sz uint32, status fuse.Status) { func (b *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []byte) (sz uint32, status fuse.Status) {
n, _ := b.inode(header.NodeId, 0) n, _ := b.inode(header.NodeId, 0)
if xops, ok := n.ops.(Listxattrer); ok { if xops, ok := n.ops.(NodeListxattrer); ok {
sz, errno := xops.Listxattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, dest) sz, errno := xops.Listxattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, dest)
return sz, errnoToStatus(errno) return sz, errnoToStatus(errno)
} }
...@@ -549,7 +549,7 @@ func (b *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, des ...@@ -549,7 +549,7 @@ func (b *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, des
func (b *rawBridge) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status { func (b *rawBridge) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status {
n, _ := b.inode(input.NodeId, 0) n, _ := b.inode(input.NodeId, 0)
if xops, ok := n.ops.(Setxattrer); ok { if xops, ok := n.ops.(NodeSetxattrer); ok {
return errnoToStatus(xops.Setxattr(&fuse.Context{Caller: input.Caller, Cancel: cancel}, attr, data, input.Flags)) return errnoToStatus(xops.Setxattr(&fuse.Context{Caller: input.Caller, Cancel: cancel}, attr, data, input.Flags))
} }
return fuse.ENOATTR return fuse.ENOATTR
...@@ -557,7 +557,7 @@ func (b *rawBridge) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, att ...@@ -557,7 +557,7 @@ func (b *rawBridge) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, att
func (b *rawBridge) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string) fuse.Status { func (b *rawBridge) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string) fuse.Status {
n, _ := b.inode(header.NodeId, 0) n, _ := b.inode(header.NodeId, 0)
if xops, ok := n.ops.(Removexattrer); ok { if xops, ok := n.ops.(NodeRemovexattrer); ok {
return errnoToStatus(xops.Removexattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr)) return errnoToStatus(xops.Removexattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr))
} }
return fuse.ENOATTR return fuse.ENOATTR
...@@ -566,7 +566,7 @@ func (b *rawBridge) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, a ...@@ -566,7 +566,7 @@ func (b *rawBridge) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, a
func (b *rawBridge) Open(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status { func (b *rawBridge) Open(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0) n, _ := b.inode(input.NodeId, 0)
if op, ok := n.ops.(Opener); ok { if op, ok := n.ops.(NodeOpener); ok {
f, flags, errno := op.Open(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Flags) f, flags, errno := op.Open(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Flags)
if errno != 0 { if errno != 0 {
return errnoToStatus(errno) return errnoToStatus(errno)
...@@ -607,7 +607,7 @@ func (b *rawBridge) registerFile(n *Inode, f FileHandle, flags uint32) uint32 { ...@@ -607,7 +607,7 @@ func (b *rawBridge) registerFile(n *Inode, f FileHandle, flags uint32) uint32 {
func (b *rawBridge) Read(cancel <-chan struct{}, input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.Status) { func (b *rawBridge) Read(cancel <-chan struct{}, input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if fops, ok := n.ops.(Reader); ok { if fops, ok := n.ops.(NodeReader); ok {
res, errno := fops.Read(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, buf, int64(input.Offset)) res, errno := fops.Read(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, buf, int64(input.Offset))
return res, errnoToStatus(errno) return res, errnoToStatus(errno)
} }
...@@ -622,7 +622,7 @@ func (b *rawBridge) Read(cancel <-chan struct{}, input *fuse.ReadIn, buf []byte) ...@@ -622,7 +622,7 @@ func (b *rawBridge) Read(cancel <-chan struct{}, input *fuse.ReadIn, buf []byte)
func (b *rawBridge) GetLk(cancel <-chan struct{}, input *fuse.LkIn, out *fuse.LkOut) fuse.Status { func (b *rawBridge) GetLk(cancel <-chan struct{}, input *fuse.LkIn, out *fuse.LkOut) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if lops, ok := n.ops.(Getlker); ok { if lops, ok := n.ops.(NodeGetlker); ok {
return errnoToStatus(lops.Getlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags, &out.Lk)) return errnoToStatus(lops.Getlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags, &out.Lk))
} }
if gl, ok := f.file.(FileGetlker); ok { if gl, ok := f.file.(FileGetlker); ok {
...@@ -633,7 +633,7 @@ func (b *rawBridge) GetLk(cancel <-chan struct{}, input *fuse.LkIn, out *fuse.Lk ...@@ -633,7 +633,7 @@ func (b *rawBridge) GetLk(cancel <-chan struct{}, input *fuse.LkIn, out *fuse.Lk
func (b *rawBridge) SetLk(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status { func (b *rawBridge) SetLk(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if lops, ok := n.ops.(Setlker); ok { if lops, ok := n.ops.(NodeSetlker); ok {
return errnoToStatus(lops.Setlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags)) return errnoToStatus(lops.Setlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags))
} }
if sl, ok := n.ops.(FileSetlker); ok { if sl, ok := n.ops.(FileSetlker); ok {
...@@ -643,7 +643,7 @@ func (b *rawBridge) SetLk(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status ...@@ -643,7 +643,7 @@ func (b *rawBridge) SetLk(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status
} }
func (b *rawBridge) SetLkw(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status { func (b *rawBridge) SetLkw(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if lops, ok := n.ops.(Setlkwer); ok { if lops, ok := n.ops.(NodeSetlkwer); ok {
return errnoToStatus(lops.Setlkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags)) return errnoToStatus(lops.Setlkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags))
} }
if sl, ok := n.ops.(FileSetlkwer); ok { if sl, ok := n.ops.(FileSetlkwer); ok {
...@@ -660,7 +660,7 @@ func (b *rawBridge) Release(cancel <-chan struct{}, input *fuse.ReleaseIn) { ...@@ -660,7 +660,7 @@ func (b *rawBridge) Release(cancel <-chan struct{}, input *fuse.ReleaseIn) {
f.wg.Wait() f.wg.Wait()
if r, ok := n.ops.(Releaser); ok { if r, ok := n.ops.(NodeReleaser); ok {
r.Release(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file) r.Release(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file)
} else if r, ok := f.file.(FileReleaser); ok { } else if r, ok := f.file.(FileReleaser); ok {
r.Release(&fuse.Context{Caller: input.Caller, Cancel: cancel}) r.Release(&fuse.Context{Caller: input.Caller, Cancel: cancel})
...@@ -705,7 +705,7 @@ func (b *rawBridge) releaseFileEntry(nid uint64, fh uint64) (*Inode, *fileEntry) ...@@ -705,7 +705,7 @@ func (b *rawBridge) releaseFileEntry(nid uint64, fh uint64) (*Inode, *fileEntry)
func (b *rawBridge) Write(cancel <-chan struct{}, input *fuse.WriteIn, data []byte) (written uint32, status fuse.Status) { func (b *rawBridge) Write(cancel <-chan struct{}, input *fuse.WriteIn, data []byte) (written uint32, status fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if wr, ok := n.ops.(Writer); ok { if wr, ok := n.ops.(NodeWriter); ok {
w, errno := wr.Write(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, data, int64(input.Offset)) w, errno := wr.Write(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, data, int64(input.Offset))
return w, errnoToStatus(errno) return w, errnoToStatus(errno)
} }
...@@ -719,7 +719,7 @@ func (b *rawBridge) Write(cancel <-chan struct{}, input *fuse.WriteIn, data []by ...@@ -719,7 +719,7 @@ func (b *rawBridge) Write(cancel <-chan struct{}, input *fuse.WriteIn, data []by
func (b *rawBridge) Flush(cancel <-chan struct{}, input *fuse.FlushIn) fuse.Status { func (b *rawBridge) Flush(cancel <-chan struct{}, input *fuse.FlushIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if fl, ok := n.ops.(Flusher); ok { if fl, ok := n.ops.(NodeFlusher); ok {
return errnoToStatus(fl.Flush(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file)) return errnoToStatus(fl.Flush(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file))
} }
if fl, ok := f.file.(FileFlusher); ok { if fl, ok := f.file.(FileFlusher); ok {
...@@ -731,7 +731,7 @@ func (b *rawBridge) Flush(cancel <-chan struct{}, input *fuse.FlushIn) fuse.Stat ...@@ -731,7 +731,7 @@ func (b *rawBridge) Flush(cancel <-chan struct{}, input *fuse.FlushIn) fuse.Stat
func (b *rawBridge) Fsync(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Status { func (b *rawBridge) Fsync(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if fs, ok := n.ops.(Fsyncer); ok { if fs, ok := n.ops.(NodeFsyncer); ok {
return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.FsyncFlags)) return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.FsyncFlags))
} }
if fs, ok := f.file.(FileFsyncer); ok { if fs, ok := f.file.(FileFsyncer); ok {
...@@ -742,7 +742,7 @@ func (b *rawBridge) Fsync(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Stat ...@@ -742,7 +742,7 @@ func (b *rawBridge) Fsync(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Stat
func (b *rawBridge) Fallocate(cancel <-chan struct{}, input *fuse.FallocateIn) fuse.Status { func (b *rawBridge) Fallocate(cancel <-chan struct{}, input *fuse.FallocateIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if a, ok := n.ops.(Allocater); ok { if a, ok := n.ops.(NodeAllocater); ok {
return errnoToStatus(a.Allocate(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Offset, input.Length, input.Mode)) return errnoToStatus(a.Allocate(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Offset, input.Length, input.Mode))
} }
if a, ok := n.ops.(FileAllocater); ok { if a, ok := n.ops.(FileAllocater); ok {
...@@ -754,7 +754,7 @@ func (b *rawBridge) Fallocate(cancel <-chan struct{}, input *fuse.FallocateIn) f ...@@ -754,7 +754,7 @@ func (b *rawBridge) Fallocate(cancel <-chan struct{}, input *fuse.FallocateIn) f
func (b *rawBridge) OpenDir(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status { func (b *rawBridge) OpenDir(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0) n, _ := b.inode(input.NodeId, 0)
if od, ok := n.ops.(Opendirer); ok { if od, ok := n.ops.(NodeOpendirer); ok {
errno := od.Opendir(&fuse.Context{Caller: input.Caller, Cancel: cancel}) errno := od.Opendir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
if errno != 0 { if errno != 0 {
return errnoToStatus(errno) return errnoToStatus(errno)
...@@ -786,7 +786,7 @@ func (b *rawBridge) setStream(cancel <-chan struct{}, input *fuse.ReadIn, inode ...@@ -786,7 +786,7 @@ func (b *rawBridge) setStream(cancel <-chan struct{}, input *fuse.ReadIn, inode
} }
func (b *rawBridge) getStream(ctx context.Context, inode *Inode) (DirStream, syscall.Errno) { func (b *rawBridge) getStream(ctx context.Context, inode *Inode) (DirStream, syscall.Errno) {
if rd, ok := inode.ops.(Readdirer); ok { if rd, ok := inode.ops.(NodeReaddirer); ok {
return rd.Readdir(ctx) return rd.Readdir(ctx)
} }
...@@ -885,7 +885,7 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out ...@@ -885,7 +885,7 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out
func (b *rawBridge) FsyncDir(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Status { func (b *rawBridge) FsyncDir(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Status {
n, _ := b.inode(input.NodeId, input.Fh) n, _ := b.inode(input.NodeId, input.Fh)
if fs, ok := n.ops.(Fsyncer); ok { if fs, ok := n.ops.(NodeFsyncer); ok {
return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, nil, input.FsyncFlags)) return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, nil, input.FsyncFlags))
} }
...@@ -894,7 +894,7 @@ func (b *rawBridge) FsyncDir(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.S ...@@ -894,7 +894,7 @@ func (b *rawBridge) FsyncDir(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.S
func (b *rawBridge) StatFs(cancel <-chan struct{}, input *fuse.InHeader, out *fuse.StatfsOut) fuse.Status { func (b *rawBridge) StatFs(cancel <-chan struct{}, input *fuse.InHeader, out *fuse.StatfsOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0) n, _ := b.inode(input.NodeId, 0)
if sf, ok := n.ops.(Statfser); ok { if sf, ok := n.ops.(NodeStatfser); ok {
return errnoToStatus(sf.Statfs(&fuse.Context{Caller: input.Caller, Cancel: cancel}, out)) return errnoToStatus(sf.Statfs(&fuse.Context{Caller: input.Caller, Cancel: cancel}, out))
} }
...@@ -908,7 +908,7 @@ func (b *rawBridge) Init(s *fuse.Server) { ...@@ -908,7 +908,7 @@ func (b *rawBridge) Init(s *fuse.Server) {
func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRangeIn) (size uint32, status fuse.Status) { func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRangeIn) (size uint32, status fuse.Status) {
n1, f1 := b.inode(in.NodeId, in.FhIn) n1, f1 := b.inode(in.NodeId, in.FhIn)
cfr, ok := n1.ops.(CopyFileRanger) cfr, ok := n1.ops.(NodeCopyFileRanger)
if !ok { if !ok {
return 0, fuse.ENOTSUP return 0, fuse.ENOTSUP
} }
...@@ -923,7 +923,7 @@ func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRange ...@@ -923,7 +923,7 @@ func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRange
func (b *rawBridge) Lseek(cancel <-chan struct{}, in *fuse.LseekIn, out *fuse.LseekOut) fuse.Status { func (b *rawBridge) Lseek(cancel <-chan struct{}, in *fuse.LseekIn, out *fuse.LseekOut) fuse.Status {
n, f := b.inode(in.NodeId, in.Fh) n, f := b.inode(in.NodeId, in.Fh)
ls, ok := n.ops.(Lseeker) ls, ok := n.ops.(NodeLseeker)
if ok { if ok {
off, errno := ls.Lseek(&fuse.Context{Caller: in.Caller, Cancel: cancel}, off, errno := ls.Lseek(&fuse.Context{Caller: in.Caller, Cancel: cancel},
f.file, in.Offset, in.Whence) f.file, in.Offset, in.Whence)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"bytes" "bytes"
...@@ -25,9 +25,9 @@ type keepCacheFile struct { ...@@ -25,9 +25,9 @@ type keepCacheFile struct {
count int count int
} }
var _ = (Reader)((*keepCacheFile)(nil)) var _ = (NodeReader)((*keepCacheFile)(nil))
var _ = (Opener)((*keepCacheFile)(nil)) var _ = (NodeOpener)((*keepCacheFile)(nil))
var _ = (Getattrer)((*keepCacheFile)(nil)) var _ = (NodeGetattrer)((*keepCacheFile)(nil))
func (f *keepCacheFile) setContent(delta int) { func (f *keepCacheFile) setContent(delta int) {
f.mu.Lock() f.mu.Lock()
...@@ -69,7 +69,7 @@ type keepCacheRoot struct { ...@@ -69,7 +69,7 @@ type keepCacheRoot struct {
keep, nokeep *keepCacheFile keep, nokeep *keepCacheFile
} }
var _ = (OnAdder)((*keepCacheRoot)(nil)) var _ = (NodeOnAdder)((*keepCacheRoot)(nil))
func (r *keepCacheRoot) OnAdd(ctx context.Context) { func (r *keepCacheRoot) OnAdd(ctx context.Context) {
i := &r.Inode i := &r.Inode
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"syscall" "syscall"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import "syscall" import "syscall"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import "syscall" import "syscall"
......
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"bytes" "bytes"
...@@ -46,7 +46,7 @@ type dioFile struct { ...@@ -46,7 +46,7 @@ type dioFile struct {
Inode Inode
} }
var _ = (Opener)((*dioFile)(nil)) var _ = (NodeOpener)((*dioFile)(nil))
func (f *dioFile) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) { func (f *dioFile) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
return &dioFH{}, fuse.FOPEN_DIRECT_IO, OK return &dioFH{}, fuse.FOPEN_DIRECT_IO, OK
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"syscall" "syscall"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"io" "io"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"sync" "sync"
...@@ -63,12 +63,14 @@ func (ds *loopbackDirStream) Next() (fuse.DirEntry, syscall.Errno) { ...@@ -63,12 +63,14 @@ func (ds *loopbackDirStream) Next() (fuse.DirEntry, syscall.Errno) {
nameBytes := ds.todo[unsafe.Offsetof(syscall.Dirent{}.Name):de.Reclen] nameBytes := ds.todo[unsafe.Offsetof(syscall.Dirent{}.Name):de.Reclen]
ds.todo = ds.todo[de.Reclen:] ds.todo = ds.todo[de.Reclen:]
for l := len(nameBytes); l > 0; l-- { // After the loop, l contains the index of the first '\0'.
if nameBytes[l-1] != 0 { l := 0
for l = range nameBytes {
if nameBytes[l] == 0 {
break break
} }
nameBytes = nameBytes[:l-1]
} }
nameBytes = nameBytes[:l]
result := fuse.DirEntry{ result := fuse.DirEntry{
Ino: de.Ino, Ino: de.Ino,
Mode: (uint32(de.Type) << 12), Mode: (uint32(de.Type) << 12),
......
...@@ -2,35 +2,49 @@ ...@@ -2,35 +2,49 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs_test
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
) )
// mountLoopback mounts dir under the given mountpoint
func mountLoopback(dir, mntPoint string) (*fuse.Server, error) {
root, err := fs.NewLoopbackRoot(dir)
if err != nil {
return nil, err
}
// Make the root available under mntDir
return fs.Mount(mntPoint, root, &fs.Options{
MountOptions: fuse.MountOptions{Debug: true},
})
}
// An example of creating a loopback file system, and mounting it onto // An example of creating a loopback file system, and mounting it onto
// a directory // a directory
func ExampleMount() { func Example_mountLoopback() {
mntDir, _ := ioutil.TempDir("", "") mntDir, _ := ioutil.TempDir("", "")
home := os.Getenv("HOME") home := os.Getenv("HOME")
root, err := NewLoopbackRoot(home)
if err != nil {
log.Panic(err)
}
server, err := Mount(mntDir, root, &Options{ // Make $HOME available on a mount dir under /tmp/ . Caution:
MountOptions: fuse.MountOptions{Debug: true}, // write operations are also mirrored.
}) server, err := mountLoopback(mntDir, home)
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
log.Printf("Mounted %s as loopback on %s", home, mntDir) fmt.Printf("Mounted %s as loopback on %s\n", home, mntDir)
log.Printf("Unmount by calling 'fusermount -u %s'", 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)
// Wait until the directory is unmounted
server.Wait() server.Wait()
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"context" "context"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"context" "context"
......
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs_test
import (
"context"
"io/ioutil"
"log"
"path/filepath"
"strings"
"syscall"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/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
}
// Create the file. The Inode must be persistent,
// because its life time is not under control of the
// kernel.
child := p.NewPersistentInode(ctx, &fs.MemRegularFile{
Data: []byte(content),
}, fs.StableAttr{})
// And add it
p.AddChild(base, child, true)
}
}
// This demonstrates how to build a file system in memory.
func Example() {
// 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},
// This adds read permissions to the files and
// directories, which is necessary for doing a chdir
// into the mount.
DefaultPermissions: 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()
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"context" "context"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"context" "context"
...@@ -19,7 +19,7 @@ type interruptRoot struct { ...@@ -19,7 +19,7 @@ type interruptRoot struct {
child interruptOps child interruptOps
} }
var _ = (Lookuper)((*interruptRoot)(nil)) var _ = (NodeLookuper)((*interruptRoot)(nil))
func (r *interruptRoot) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) { func (r *interruptRoot) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
if name != "file" { if name != "file" {
...@@ -37,7 +37,7 @@ type interruptOps struct { ...@@ -37,7 +37,7 @@ type interruptOps struct {
interrupted bool interrupted bool
} }
var _ = (Opener)((*interruptOps)(nil)) var _ = (NodeOpener)((*interruptOps)(nil))
func (o *interruptOps) Open(ctx context.Context, flags uint32) (FileHandle, uint32, syscall.Errno) { func (o *interruptOps) Open(ctx context.Context, flags uint32) (FileHandle, uint32, syscall.Errno) {
select { select {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"context" "context"
...@@ -24,26 +24,26 @@ type loopbackNode struct { ...@@ -24,26 +24,26 @@ type loopbackNode struct {
Inode Inode
} }
var _ = (Statfser)((*loopbackNode)(nil)) var _ = (NodeStatfser)((*loopbackNode)(nil))
var _ = (Statfser)((*loopbackNode)(nil)) var _ = (NodeStatfser)((*loopbackNode)(nil))
var _ = (Getattrer)((*loopbackNode)(nil)) var _ = (NodeGetattrer)((*loopbackNode)(nil))
var _ = (Getxattrer)((*loopbackNode)(nil)) var _ = (NodeGetxattrer)((*loopbackNode)(nil))
var _ = (Setxattrer)((*loopbackNode)(nil)) var _ = (NodeSetxattrer)((*loopbackNode)(nil))
var _ = (Removexattrer)((*loopbackNode)(nil)) var _ = (NodeRemovexattrer)((*loopbackNode)(nil))
var _ = (Listxattrer)((*loopbackNode)(nil)) var _ = (NodeListxattrer)((*loopbackNode)(nil))
var _ = (Readlinker)((*loopbackNode)(nil)) var _ = (NodeReadlinker)((*loopbackNode)(nil))
var _ = (Opener)((*loopbackNode)(nil)) var _ = (NodeOpener)((*loopbackNode)(nil))
var _ = (CopyFileRanger)((*loopbackNode)(nil)) var _ = (NodeCopyFileRanger)((*loopbackNode)(nil))
var _ = (Lookuper)((*loopbackNode)(nil)) var _ = (NodeLookuper)((*loopbackNode)(nil))
var _ = (Opendirer)((*loopbackNode)(nil)) var _ = (NodeOpendirer)((*loopbackNode)(nil))
var _ = (Readdirer)((*loopbackNode)(nil)) var _ = (NodeReaddirer)((*loopbackNode)(nil))
var _ = (Mkdirer)((*loopbackNode)(nil)) var _ = (NodeMkdirer)((*loopbackNode)(nil))
var _ = (Mknoder)((*loopbackNode)(nil)) var _ = (NodeMknoder)((*loopbackNode)(nil))
var _ = (Linker)((*loopbackNode)(nil)) var _ = (NodeLinker)((*loopbackNode)(nil))
var _ = (Symlinker)((*loopbackNode)(nil)) var _ = (NodeSymlinker)((*loopbackNode)(nil))
var _ = (Unlinker)((*loopbackNode)(nil)) var _ = (NodeUnlinker)((*loopbackNode)(nil))
var _ = (Rmdirer)((*loopbackNode)(nil)) var _ = (NodeRmdirer)((*loopbackNode)(nil))
var _ = (Renamer)((*loopbackNode)(nil)) var _ = (NodeRenamer)((*loopbackNode)(nil))
func (n *loopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno { func (n *loopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno {
s := syscall.Statfs_t{} s := syscall.Statfs_t{}
...@@ -181,7 +181,7 @@ func (r *loopbackRoot) idFromStat(st *syscall.Stat_t) StableAttr { ...@@ -181,7 +181,7 @@ func (r *loopbackRoot) idFromStat(st *syscall.Stat_t) StableAttr {
} }
} }
var _ = (Creater)((*loopbackNode)(nil)) var _ = (NodeCreater)((*loopbackNode)(nil))
func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (inode *Inode, fh FileHandle, fuseFlags uint32, errno syscall.Errno) { func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (inode *Inode, fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
p := filepath.Join(n.path(), name) p := filepath.Join(n.path(), name)
...@@ -298,7 +298,7 @@ func (n *loopbackNode) Getattr(ctx context.Context, f FileHandle, out *fuse.Attr ...@@ -298,7 +298,7 @@ func (n *loopbackNode) Getattr(ctx context.Context, f FileHandle, out *fuse.Attr
return OK return OK
} }
var _ = (Setattrer)((*loopbackNode)(nil)) var _ = (NodeSetattrer)((*loopbackNode)(nil))
func (n *loopbackNode) Setattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno { func (n *loopbackNode) Setattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
p := n.path() p := n.path()
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"context" "context"
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"context" "context"
......
...@@ -2,16 +2,20 @@ ...@@ -2,16 +2,20 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"reflect" "reflect"
"sync"
"syscall" "syscall"
"testing" "testing"
"time"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil"
"github.com/kylelemons/godebug/pretty" "github.com/kylelemons/godebug/pretty"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
...@@ -198,3 +202,136 @@ func TestCopyFileRange(t *testing.T) { ...@@ -198,3 +202,136 @@ func TestCopyFileRange(t *testing.T) {
} }
} }
// Wait for a change in /proc/self/mounts. Efficient through the use of
// unix.Poll().
func waitProcMountsChange() error {
fd, err := syscall.Open("/proc/self/mounts", syscall.O_RDONLY, 0)
defer syscall.Close(fd)
if err != nil {
return err
}
pollFds := []unix.PollFd{
{
Fd: int32(fd),
Events: unix.POLLPRI,
},
}
_, err = unix.Poll(pollFds, 1000)
return err
}
// Wait until mountpoint "mnt" shows up /proc/self/mounts
func waitMount(mnt string) error {
for {
err := waitProcMountsChange()
if err != nil {
return err
}
content, err := ioutil.ReadFile("/proc/self/mounts")
if err != nil {
return err
}
if bytes.Contains(content, []byte(mnt)) {
return nil
}
}
}
// There is a hang that appears when enabling CAP_PARALLEL_DIROPS on Linux
// 4.15.0: https://github.com/hanwen/go-fuse/issues/281
// The hang was originally triggered by gvfs-udisks2-volume-monitor. This
// test emulates what gvfs-udisks2-volume-monitor does.
func TestParallelDiropsHang(t *testing.T) {
// We do NOT want to use newTestCase() here because we need to know the
// mnt path before the filesystem is mounted
dir := testutil.TempDir()
orig := dir + "/orig"
mnt := dir + "/mnt"
if err := os.Mkdir(orig, 0755); err != nil {
t.Fatal(err)
}
if err := os.Mkdir(mnt, 0755); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
// Unblock the goroutines onces the mount shows up in /proc/self/mounts
wait := make(chan struct{})
go func() {
err := waitMount(mnt)
if err != nil {
t.Error(err)
}
// Unblock the goroutines regardless of an error. We don't want to hang
// the test.
close(wait)
}()
// gvfs-udisks2-volume-monitor hits the mount with three threads - we try to
// emulate exactly what it does acc. to an strace log.
var wg sync.WaitGroup
wg.Add(3)
// [pid 2117] lstat(".../mnt/autorun.inf", <unfinished ...>
go func() {
defer wg.Done()
<-wait
var st unix.Stat_t
unix.Lstat(mnt+"/autorun.inf", &st)
}()
// [pid 2116] open(".../mnt/.xdg-volume-info", O_RDONLY <unfinished ...>
go func() {
defer wg.Done()
<-wait
syscall.Open(mnt+"/.xdg-volume-info", syscall.O_RDONLY, 0)
}()
// 25 times this:
// [pid 1874] open(".../mnt", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC <unfinished ...>
// [pid 1874] fstat(11, {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
// [pid 1874] getdents(11, /* 2 entries */, 32768) = 48
// [pid 1874] close(11) = 0
go func() {
defer wg.Done()
<-wait
for i := 1; i <= 25; i++ {
f, err := os.Open(mnt)
if err != nil {
t.Error(err)
return
}
_, err = f.Stat()
if err != nil {
t.Error(err)
f.Close()
return
}
_, err = f.Readdirnames(-1)
if err != nil {
t.Errorf("iteration %d: fd %d: %v", i, f.Fd(), err)
return
}
f.Close()
}
}()
loopbackRoot, err := NewLoopbackRoot(orig)
if err != nil {
t.Fatalf("NewLoopbackRoot(%s): %v\n", orig, err)
}
sec := time.Second
opts := &Options{
AttrTimeout: &sec,
EntryTimeout: &sec,
}
opts.Debug = testutil.VerboseTest()
rawFS := NewNodeFS(loopbackRoot, opts)
server, err := fuse.NewServer(rawFS, mnt, &opts.MountOptions)
if err != nil {
t.Fatal(err)
}
go server.Serve()
wg.Wait()
server.Unmount()
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"context" "context"
...@@ -20,9 +20,9 @@ type MemRegularFile struct { ...@@ -20,9 +20,9 @@ type MemRegularFile struct {
Attr fuse.Attr Attr fuse.Attr
} }
var _ = (Opener)((*MemRegularFile)(nil)) var _ = (NodeOpener)((*MemRegularFile)(nil))
var _ = (Reader)((*MemRegularFile)(nil)) var _ = (NodeReader)((*MemRegularFile)(nil))
var _ = (Flusher)((*MemRegularFile)(nil)) var _ = (NodeFlusher)((*MemRegularFile)(nil))
func (f *MemRegularFile) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) { func (f *MemRegularFile) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
if flags&(syscall.O_RDWR) != 0 || flags&syscall.O_WRONLY != 0 { if flags&(syscall.O_RDWR) != 0 || flags&syscall.O_WRONLY != 0 {
...@@ -32,7 +32,7 @@ func (f *MemRegularFile) Open(ctx context.Context, flags uint32) (fh FileHandle, ...@@ -32,7 +32,7 @@ func (f *MemRegularFile) Open(ctx context.Context, flags uint32) (fh FileHandle,
return nil, fuse.FOPEN_KEEP_CACHE, OK return nil, fuse.FOPEN_KEEP_CACHE, OK
} }
var _ = (Getattrer)((*MemRegularFile)(nil)) var _ = (NodeGetattrer)((*MemRegularFile)(nil))
func (f *MemRegularFile) Getattr(ctx context.Context, fh FileHandle, out *fuse.AttrOut) syscall.Errno { func (f *MemRegularFile) Getattr(ctx context.Context, fh FileHandle, out *fuse.AttrOut) syscall.Errno {
out.Attr = f.Attr out.Attr = f.Attr
...@@ -59,13 +59,13 @@ type MemSymlink struct { ...@@ -59,13 +59,13 @@ type MemSymlink struct {
Data []byte Data []byte
} }
var _ = (Readlinker)((*MemSymlink)(nil)) var _ = (NodeReadlinker)((*MemSymlink)(nil))
func (l *MemSymlink) Readlink(ctx context.Context) ([]byte, syscall.Errno) { func (l *MemSymlink) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
return l.Data, OK return l.Data, OK
} }
var _ = (Getattrer)((*MemSymlink)(nil)) var _ = (NodeGetattrer)((*MemSymlink)(nil))
func (l *MemSymlink) Getattr(ctx context.Context, fh FileHandle, out *fuse.AttrOut) syscall.Errno { func (l *MemSymlink) Getattr(ctx context.Context, fh FileHandle, out *fuse.AttrOut) syscall.Errno {
out.Attr = l.Attr out.Attr = l.Attr
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"bytes" "bytes"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"time" "time"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"context" "context"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"fmt" "fmt"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs
import ( import (
"syscall" "syscall"
......
...@@ -2,14 +2,13 @@ ...@@ -2,14 +2,13 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package nodefs package fs_test
import ( import (
"archive/zip" "archive/zip"
"bytes" "bytes"
"context" "context"
"io/ioutil" "io/ioutil"
"log"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings" "strings"
...@@ -17,6 +16,7 @@ import ( ...@@ -17,6 +16,7 @@ import (
"syscall" "syscall"
"testing" "testing"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
) )
...@@ -62,8 +62,15 @@ func TestZipFS(t *testing.T) { ...@@ -62,8 +62,15 @@ func TestZipFS(t *testing.T) {
} }
root := &zipRoot{zr: r} root := &zipRoot{zr: r}
mntDir, _, clean := testMount(t, root, nil) mntDir, err := ioutil.TempDir("", "ZipFS")
defer clean() if err != nil {
t.Fatal(err)
}
server, err := fs.Mount(mntDir, root, nil)
if err != nil {
t.Fatal(err)
}
defer server.Unmount()
for k, v := range testData { for k, v := range testData {
c, err := ioutil.ReadFile(filepath.Join(mntDir, k)) c, err := ioutil.ReadFile(filepath.Join(mntDir, k))
...@@ -103,14 +110,22 @@ func TestZipFSOnAdd(t *testing.T) { ...@@ -103,14 +110,22 @@ func TestZipFSOnAdd(t *testing.T) {
zr := &zipRoot{zr: r} zr := &zipRoot{zr: r}
root := &Inode{} root := &fs.Inode{}
mnt, _, clean := testMount(t, root, &Options{ mnt, err := ioutil.TempDir("", "ZipFS")
if err != nil {
t.Fatal(err)
}
server, err := fs.Mount(mnt, root, &fs.Options{
OnAdd: func(ctx context.Context) { OnAdd: func(ctx context.Context) {
root.AddChild("sub", root.AddChild("sub",
root.NewPersistentInode(ctx, zr, StableAttr{Mode: syscall.S_IFDIR}), false) root.NewPersistentInode(ctx, zr, fs.StableAttr{Mode: syscall.S_IFDIR}), false)
}, },
}) })
defer clean() if err != nil {
t.Fatal(err)
}
defer server.Unmount()
c, err := ioutil.ReadFile(mnt + "/sub/dir/subdir/subfile") c, err := ioutil.ReadFile(mnt + "/sub/dir/subdir/subfile")
if err != nil { if err != nil {
t.Fatal("ReadFile", err) t.Fatal("ReadFile", err)
...@@ -122,25 +137,25 @@ func TestZipFSOnAdd(t *testing.T) { ...@@ -122,25 +137,25 @@ func TestZipFSOnAdd(t *testing.T) {
// zipFile is a file read from a zip archive. // zipFile is a file read from a zip archive.
type zipFile struct { type zipFile struct {
Inode fs.Inode
file *zip.File file *zip.File
mu sync.Mutex mu sync.Mutex
data []byte data []byte
} }
var _ = (Opener)((*zipFile)(nil)) var _ = (fs.NodeOpener)((*zipFile)(nil))
var _ = (Getattrer)((*zipFile)(nil)) var _ = (fs.NodeGetattrer)((*zipFile)(nil))
// Getattr sets the minimum, which is the size. A more full-featured // Getattr sets the minimum, which is the size. A more full-featured
// FS would also set timestamps and permissions. // FS would also set timestamps and permissions.
func (zf *zipFile) Getattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno { func (zf *zipFile) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
out.Size = zf.file.UncompressedSize64 out.Size = zf.file.UncompressedSize64
return OK return 0
} }
// Open lazily unpacks zip data // Open lazily unpacks zip data
func (zf *zipFile) Open(ctx context.Context, flags uint32) (FileHandle, uint32, syscall.Errno) { func (zf *zipFile) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) {
zf.mu.Lock() zf.mu.Lock()
defer zf.mu.Unlock() defer zf.mu.Unlock()
if zf.data == nil { if zf.data == nil {
...@@ -159,27 +174,27 @@ func (zf *zipFile) Open(ctx context.Context, flags uint32) (FileHandle, uint32, ...@@ -159,27 +174,27 @@ func (zf *zipFile) Open(ctx context.Context, flags uint32) (FileHandle, uint32,
// We don't return a filehandle since we don't really need // We don't return a filehandle since we don't really need
// one. The file content is immutable, so hint the kernel to // one. The file content is immutable, so hint the kernel to
// cache the data. // cache the data.
return nil, fuse.FOPEN_KEEP_CACHE, OK return nil, fuse.FOPEN_KEEP_CACHE, fs.OK
} }
// Read simply returns the data that was already unpacked in the Open call // Read simply returns the data that was already unpacked in the Open call
func (zf *zipFile) Read(ctx context.Context, f FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) { func (zf *zipFile) Read(ctx context.Context, f fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
end := int(off) + len(dest) end := int(off) + len(dest)
if end > len(zf.data) { if end > len(zf.data) {
end = len(zf.data) end = len(zf.data)
} }
return fuse.ReadResultData(zf.data[off:end]), OK return fuse.ReadResultData(zf.data[off:end]), fs.OK
} }
// zipRoot is the root of the Zip filesystem. Its only functionality // zipRoot is the root of the Zip filesystem. Its only functionality
// is populating the filesystem. // is populating the filesystem.
type zipRoot struct { type zipRoot struct {
Inode fs.Inode
zr *zip.Reader zr *zip.Reader
} }
var _ = (OnAdder)((*zipRoot)(nil)) var _ = (fs.NodeOnAdder)((*zipRoot)(nil))
func (zr *zipRoot) OnAdd(ctx context.Context) { func (zr *zipRoot) OnAdd(ctx context.Context) {
// OnAdd is called once we are attached to an Inode. We can // OnAdd is called once we are attached to an Inode. We can
...@@ -196,76 +211,14 @@ func (zr *zipRoot) OnAdd(ctx context.Context) { ...@@ -196,76 +211,14 @@ func (zr *zipRoot) OnAdd(ctx context.Context) {
} }
ch := p.GetChild(component) ch := p.GetChild(component)
if ch == nil { if ch == nil {
ch = p.NewPersistentInode(ctx, &Inode{}, ch = p.NewPersistentInode(ctx, &fs.Inode{},
StableAttr{Mode: fuse.S_IFDIR}) fs.StableAttr{Mode: fuse.S_IFDIR})
p.AddChild(component, ch, true) p.AddChild(component, ch, true)
} }
p = ch p = ch
} }
ch := p.NewPersistentInode(ctx, &zipFile{file: f}, StableAttr{}) ch := p.NewPersistentInode(ctx, &zipFile{file: f}, fs.StableAttr{})
p.AddChild(base, ch, true) p.AddChild(base, ch, true)
} }
} }
// Persistent inodes can be used to create an in-memory
// prefabricated file system tree.
func ExampleInode_NewPersistentInode() {
// This is where we'll mount the FS
mntDir, _ := ioutil.TempDir("", "")
files := map[string]string{
"file": "content",
"subdir/other-file": "other-content",
}
root := &Inode{}
populate := func(ctx context.Context) {
for name, content := range files {
dir, base := filepath.Split(name)
p := root
// 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, &Inode{},
StableAttr{Mode: syscall.S_IFDIR})
// Add it
p.AddChild(component, ch, true)
}
p = ch
}
// Create the file
child := p.NewPersistentInode(ctx, &MemRegularFile{
Data: []byte(content),
}, StableAttr{})
// And add it
p.AddChild(base, child, true)
}
}
server, err := Mount(mntDir, root, &Options{
MountOptions: fuse.MountOptions{Debug: true},
// This adds read permissions to the files and
// directories, which is necessary for doing a chdir
// into the mount.
DefaultPermissions: true,
OnAdd: populate,
})
if err != nil {
log.Panic(err)
}
log.Printf("Mounted on %s", mntDir)
log.Printf("Unmount by calling 'fusermount -u %s'", mntDir)
server.Wait()
}
...@@ -13,8 +13,8 @@ import ( ...@@ -13,8 +13,8 @@ import (
"path/filepath" "path/filepath"
"syscall" "syscall"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/nodefs"
) )
func filePathHash(path string) string { func filePathHash(path string) string {
...@@ -32,7 +32,7 @@ type unionFSRoot struct { ...@@ -32,7 +32,7 @@ type unionFSRoot struct {
} }
type unionFSNode struct { type unionFSNode struct {
nodefs.Inode fs.Inode
} }
const delDir = "DELETIONS" const delDir = "DELETIONS"
...@@ -42,7 +42,7 @@ var delDirHash = filePathHash(delDir) ...@@ -42,7 +42,7 @@ var delDirHash = filePathHash(delDir)
func (r *unionFSRoot) allMarkers(result map[string]struct{}) syscall.Errno { func (r *unionFSRoot) allMarkers(result map[string]struct{}) syscall.Errno {
dir := filepath.Join(r.roots[0], delDir) dir := filepath.Join(r.roots[0], delDir)
ds, errno := nodefs.NewLoopbackDirStream(dir) ds, errno := fs.NewLoopbackDirStream(dir)
if errno != 0 { if errno != 0 {
return errno return errno
} }
...@@ -85,7 +85,7 @@ func (r *unionFSRoot) writeMarker(name string) syscall.Errno { ...@@ -85,7 +85,7 @@ func (r *unionFSRoot) writeMarker(name string) syscall.Errno {
dest := r.markerPath(name) dest := r.markerPath(name)
err := ioutil.WriteFile(dest, []byte(name), 0644) err := ioutil.WriteFile(dest, []byte(name), 0644)
return nodefs.ToErrno(err) return fs.ToErrno(err)
} }
func (r *unionFSRoot) markerPath(name string) string { func (r *unionFSRoot) markerPath(name string) string {
...@@ -102,25 +102,25 @@ func (n *unionFSNode) root() *unionFSRoot { ...@@ -102,25 +102,25 @@ func (n *unionFSNode) root() *unionFSRoot {
return n.Root().Operations().(*unionFSRoot) return n.Root().Operations().(*unionFSRoot)
} }
var _ = (nodefs.Setattrer)((*unionFSNode)(nil)) var _ = (fs.NodeSetattrer)((*unionFSNode)(nil))
func (n *unionFSNode) Setattr(ctx context.Context, fh nodefs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno { func (n *unionFSNode) Setattr(ctx context.Context, fh fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
if errno := n.promote(); errno != 0 { if errno := n.promote(); errno != 0 {
return errno return errno
} }
if fh != nil { if fh != nil {
return fh.(nodefs.FileSetattrer).Setattr(ctx, in, out) return fh.(fs.FileSetattrer).Setattr(ctx, in, out)
} }
p := filepath.Join(n.root().roots[0], n.Path(nil)) p := filepath.Join(n.root().roots[0], n.Path(nil))
fsa, ok := fh.(nodefs.FileSetattrer) fsa, ok := fh.(fs.FileSetattrer)
if ok && fsa != nil { if ok && fsa != nil {
fsa.Setattr(ctx, in, out) fsa.Setattr(ctx, in, out)
} else { } else {
if m, ok := in.GetMode(); ok { if m, ok := in.GetMode(); ok {
if err := syscall.Chmod(p, m); err != nil { if err := syscall.Chmod(p, m); err != nil {
return nodefs.ToErrno(err) return fs.ToErrno(err)
} }
} }
...@@ -136,7 +136,7 @@ func (n *unionFSNode) Setattr(ctx context.Context, fh nodefs.FileHandle, in *fus ...@@ -136,7 +136,7 @@ func (n *unionFSNode) Setattr(ctx context.Context, fh nodefs.FileHandle, in *fus
sgid = int(gid) sgid = int(gid)
} }
if err := syscall.Chown(p, suid, sgid); err != nil { if err := syscall.Chown(p, suid, sgid); err != nil {
return nodefs.ToErrno(err) return fs.ToErrno(err)
} }
} }
...@@ -158,34 +158,34 @@ func (n *unionFSNode) Setattr(ctx context.Context, fh nodefs.FileHandle, in *fus ...@@ -158,34 +158,34 @@ func (n *unionFSNode) Setattr(ctx context.Context, fh nodefs.FileHandle, in *fus
ts[1] = fuse.UtimeToTimespec(mp) ts[1] = fuse.UtimeToTimespec(mp)
if err := syscall.UtimesNano(p, ts[:]); err != nil { if err := syscall.UtimesNano(p, ts[:]); err != nil {
return nodefs.ToErrno(err) return fs.ToErrno(err)
} }
} }
if sz, ok := in.GetSize(); ok { if sz, ok := in.GetSize(); ok {
if err := syscall.Truncate(p, int64(sz)); err != nil { if err := syscall.Truncate(p, int64(sz)); err != nil {
return nodefs.ToErrno(err) return fs.ToErrno(err)
} }
} }
} }
fga, ok := fh.(nodefs.FileGetattrer) fga, ok := fh.(fs.FileGetattrer)
if ok && fga != nil { if ok && fga != nil {
fga.Getattr(ctx, out) fga.Getattr(ctx, out)
} else { } else {
st := syscall.Stat_t{} st := syscall.Stat_t{}
err := syscall.Lstat(p, &st) err := syscall.Lstat(p, &st)
if err != nil { if err != nil {
return nodefs.ToErrno(err) return fs.ToErrno(err)
} }
out.FromStat(&st) out.FromStat(&st)
} }
return 0 return 0
} }
var _ = (nodefs.Creater)((*unionFSNode)(nil)) var _ = (fs.NodeCreater)((*unionFSNode)(nil))
func (n *unionFSNode) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (*nodefs.Inode, nodefs.FileHandle, uint32, syscall.Errno) { func (n *unionFSNode) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (*fs.Inode, fs.FileHandle, uint32, syscall.Errno) {
if n.IsRoot() && name == delDir { if n.IsRoot() && name == delDir {
return nil, nil, 0, syscall.EPERM return nil, nil, 0, syscall.EPERM
} }
...@@ -217,15 +217,15 @@ func (n *unionFSNode) Create(ctx context.Context, name string, flags uint32, mod ...@@ -217,15 +217,15 @@ func (n *unionFSNode) Create(ctx context.Context, name string, flags uint32, mod
return nil, nil, 0, err.(syscall.Errno) return nil, nil, 0, err.(syscall.Errno)
} }
ch := n.NewInode(ctx, &unionFSNode{}, nodefs.StableAttr{Mode: st.Mode, Ino: st.Ino}) ch := n.NewInode(ctx, &unionFSNode{}, fs.StableAttr{Mode: st.Mode, Ino: st.Ino})
out.FromStat(&st) out.FromStat(&st)
return ch, nodefs.NewLoopbackFile(fd), 0, 0 return ch, fs.NewLoopbackFile(fd), 0, 0
} }
var _ = (nodefs.Opener)((*unionFSNode)(nil)) var _ = (fs.NodeOpener)((*unionFSNode)(nil))
func (n *unionFSNode) Open(ctx context.Context, flags uint32) (nodefs.FileHandle, uint32, syscall.Errno) { func (n *unionFSNode) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) {
isWR := (flags&syscall.O_RDWR != 0) || (flags&syscall.O_WRONLY != 0) isWR := (flags&syscall.O_RDWR != 0) || (flags&syscall.O_WRONLY != 0)
var st syscall.Stat_t var st syscall.Stat_t
...@@ -242,12 +242,12 @@ func (n *unionFSNode) Open(ctx context.Context, flags uint32) (nodefs.FileHandle ...@@ -242,12 +242,12 @@ func (n *unionFSNode) Open(ctx context.Context, flags uint32) (nodefs.FileHandle
return nil, 0, err.(syscall.Errno) return nil, 0, err.(syscall.Errno)
} }
return nodefs.NewLoopbackFile(fd), 0, 0 return fs.NewLoopbackFile(fd), 0, 0
} }
var _ = (nodefs.Getattrer)((*unionFSNode)(nil)) var _ = (fs.NodeGetattrer)((*unionFSNode)(nil))
func (n *unionFSNode) Getattr(ctx context.Context, fh nodefs.FileHandle, out *fuse.AttrOut) syscall.Errno { func (n *unionFSNode) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
var st syscall.Stat_t var st syscall.Stat_t
_, idx := n.getBranch(&st) _, idx := n.getBranch(&st)
if idx < 0 { if idx < 0 {
...@@ -258,9 +258,9 @@ func (n *unionFSNode) Getattr(ctx context.Context, fh nodefs.FileHandle, out *fu ...@@ -258,9 +258,9 @@ func (n *unionFSNode) Getattr(ctx context.Context, fh nodefs.FileHandle, out *fu
return 0 return 0
} }
var _ = (nodefs.Lookuper)((*unionFSNode)(nil)) var _ = (fs.NodeLookuper)((*unionFSNode)(nil))
func (n *unionFSNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*nodefs.Inode, syscall.Errno) { func (n *unionFSNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
if n.IsRoot() && name == delDir { if n.IsRoot() && name == delDir {
return nil, syscall.ENOENT return nil, syscall.ENOENT
} }
...@@ -271,7 +271,7 @@ func (n *unionFSNode) Lookup(ctx context.Context, name string, out *fuse.EntryOu ...@@ -271,7 +271,7 @@ func (n *unionFSNode) Lookup(ctx context.Context, name string, out *fuse.EntryOu
idx := n.root().getBranch(p, &st) idx := n.root().getBranch(p, &st)
if idx >= 0 { if idx >= 0 {
// XXX use idx in Ino? // XXX use idx in Ino?
ch := n.NewInode(ctx, &unionFSNode{}, nodefs.StableAttr{Mode: st.Mode, Ino: st.Ino}) ch := n.NewInode(ctx, &unionFSNode{}, fs.StableAttr{Mode: st.Mode, Ino: st.Ino})
out.FromStat(&st) out.FromStat(&st)
out.Mode |= 0111 out.Mode |= 0111
return ch, 0 return ch, 0
...@@ -279,21 +279,21 @@ func (n *unionFSNode) Lookup(ctx context.Context, name string, out *fuse.EntryOu ...@@ -279,21 +279,21 @@ func (n *unionFSNode) Lookup(ctx context.Context, name string, out *fuse.EntryOu
return nil, syscall.ENOENT return nil, syscall.ENOENT
} }
var _ = (nodefs.Unlinker)((*unionFSNode)(nil)) var _ = (fs.NodeUnlinker)((*unionFSNode)(nil))
func (n *unionFSNode) Unlink(ctx context.Context, name string) syscall.Errno { func (n *unionFSNode) Unlink(ctx context.Context, name string) syscall.Errno {
return n.root().delPath(filepath.Join(n.Path(nil), name)) return n.root().delPath(filepath.Join(n.Path(nil), name))
} }
var _ = (nodefs.Rmdirer)((*unionFSNode)(nil)) var _ = (fs.NodeRmdirer)((*unionFSNode)(nil))
func (n *unionFSNode) Rmdir(ctx context.Context, name string) syscall.Errno { func (n *unionFSNode) Rmdir(ctx context.Context, name string) syscall.Errno {
return n.root().delPath(filepath.Join(n.Path(nil), name)) return n.root().delPath(filepath.Join(n.Path(nil), name))
} }
var _ = (nodefs.Symlinker)((*unionFSNode)(nil)) var _ = (fs.NodeSymlinker)((*unionFSNode)(nil))
func (n *unionFSNode) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (*nodefs.Inode, syscall.Errno) { func (n *unionFSNode) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
n.promote() n.promote()
path := filepath.Join(n.root().roots[0], n.Path(nil), name) path := filepath.Join(n.root().roots[0], n.Path(nil), name)
err := syscall.Symlink(target, path) err := syscall.Symlink(target, path)
...@@ -309,14 +309,14 @@ func (n *unionFSNode) Symlink(ctx context.Context, target, name string, out *fus ...@@ -309,14 +309,14 @@ func (n *unionFSNode) Symlink(ctx context.Context, target, name string, out *fus
out.FromStat(&st) out.FromStat(&st)
ch := n.NewInode(ctx, &unionFSNode{}, nodefs.StableAttr{ ch := n.NewInode(ctx, &unionFSNode{}, fs.StableAttr{
Mode: syscall.S_IFLNK, Mode: syscall.S_IFLNK,
Ino: st.Ino, Ino: st.Ino,
}) })
return ch, 0 return ch, 0
} }
var _ = (nodefs.Readlinker)((*unionFSNode)(nil)) var _ = (fs.NodeReadlinker)((*unionFSNode)(nil))
func (n *unionFSNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) { func (n *unionFSNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
nm, idx := n.getBranch(nil) nm, idx := n.getBranch(nil)
...@@ -330,9 +330,9 @@ func (n *unionFSNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) { ...@@ -330,9 +330,9 @@ func (n *unionFSNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
return buf[:count], 0 return buf[:count], 0
} }
var _ = (nodefs.Readdirer)((*unionFSNode)(nil)) var _ = (fs.NodeReaddirer)((*unionFSNode)(nil))
func (n *unionFSNode) Readdir(ctx context.Context) (nodefs.DirStream, syscall.Errno) { func (n *unionFSNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
root := n.root() root := n.root()
markers := map[string]struct{}{delDirHash: struct{}{}} markers := map[string]struct{}{delDirHash: struct{}{}}
...@@ -358,11 +358,11 @@ func (n *unionFSNode) Readdir(ctx context.Context) (nodefs.DirStream, syscall.Er ...@@ -358,11 +358,11 @@ func (n *unionFSNode) Readdir(ctx context.Context) (nodefs.DirStream, syscall.Er
}) })
} }
return nodefs.NewListDirStream(result), 0 return fs.NewListDirStream(result), 0
} }
func readRoot(root string, dir string, result map[string]uint32) { func readRoot(root string, dir string, result map[string]uint32) {
ds, errno := nodefs.NewLoopbackDirStream(filepath.Join(root, dir)) ds, errno := fs.NewLoopbackDirStream(filepath.Join(root, dir))
if errno != 0 { if errno != 0 {
return return
} }
...@@ -412,7 +412,7 @@ func (n *unionFSRoot) delPath(p string) syscall.Errno { ...@@ -412,7 +412,7 @@ func (n *unionFSRoot) delPath(p string) syscall.Errno {
if idx == 0 { if idx == 0 {
err := syscall.Unlink(filepath.Join(r.roots[idx], p)) err := syscall.Unlink(filepath.Join(r.roots[idx], p))
if err != nil { if err != nil {
return nodefs.ToErrno(err) return fs.ToErrno(err)
} }
idx = r.getBranch(p, &st) idx = r.getBranch(p, &st)
} }
......
...@@ -13,9 +13,9 @@ import ( ...@@ -13,9 +13,9 @@ import (
"syscall" "syscall"
"testing" "testing"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil" "github.com/hanwen/go-fuse/internal/testutil"
"github.com/hanwen/go-fuse/nodefs"
"github.com/hanwen/go-fuse/posixtest" "github.com/hanwen/go-fuse/posixtest"
) )
...@@ -50,7 +50,7 @@ func newTestCase(t *testing.T, populate bool) *testCase { ...@@ -50,7 +50,7 @@ func newTestCase(t *testing.T, populate bool) *testCase {
} }
} }
opts := nodefs.Options{} opts := fs.Options{}
opts.Debug = testutil.VerboseTest() opts.Debug = testutil.VerboseTest()
tc := &testCase{ tc := &testCase{
dir: dir, dir: dir,
...@@ -62,7 +62,7 @@ func newTestCase(t *testing.T, populate bool) *testCase { ...@@ -62,7 +62,7 @@ func newTestCase(t *testing.T, populate bool) *testCase {
roots: []string{tc.rw, tc.ro}, roots: []string{tc.rw, tc.ro},
} }
server, err := nodefs.Mount(tc.mnt, tc.root, &opts) server, err := fs.Mount(tc.mnt, tc.root, &opts)
if err != nil { if err != nil {
t.Fatal("Mount", err) t.Fatal("Mount", err)
} }
......
Objective
=========
A high-performance FUSE API that minimizes pitfalls with writing
correct filesystems.
Decisions
=========
* Nodes contain references to their children. This is useful
because most filesystems will need to construct tree-like
structures.
* Nodes contain references to their parents. As a result, we can
derive the path for each Inode, and there is no need for a
separate PathFS.
* Nodes can be "persistent", meaning their lifetime is not under
control of the kernel. This is useful for constructing FS trees
in advance, rather than driven by LOOKUP.
* The NodeID for FS tree node must be defined on creation and are
immutable. By contrast, reusing NodeIds (eg. rsc/bazil FUSE, as
well as old go-fuse/fuse/nodefs) needs extra synchronization to
avoid races with notify and FORGET, and makes handling the inode
Generation more complicated.
* The mode of an Inode is defined on creation. Files cannot change
type during their lifetime. This also prevents the common error
of forgetting to return the filetype in Lookup/GetAttr.
* The NodeID (used for communicating with kernel) is equal to
Attr.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 the
high-level API. Extra care for race conditions is needed when
looking up the same file through different paths.
* do not issue Notify{Entry,Delete} as part of
AddChild/RmChild/MvChild: because NodeIDs are unique and
immutable, there is no confusion about which nodes are
invalidated, and the notification doesn't have to happen under
lock.
* Directory reading uses the DirStream. Semantics for rewinding
directory reads, and adding files after opening (but before
reading) are handled automatically. No support for directory
seeks.
* Method names are based on syscall names. Where there is no
syscall (eg. "open directory"), we bias towards writing
everything together (Opendir)
To do/To decide
=========
* decide on a final package name
* handle less open/create.
* Symlink []byte vs string.
...@@ -19,27 +19,27 @@ import ( ...@@ -19,27 +19,27 @@ import (
"log" "log"
"syscall" "syscall"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/nodefs"
) )
// MultiZipFs is a filesystem that mounts zipfiles. // MultiZipFs is a filesystem that mounts zipfiles.
type MultiZipFs struct { type MultiZipFs struct {
nodefs.Inode fs.Inode
} }
func (fs *MultiZipFs) OnAdd(ctx context.Context) { func (root *MultiZipFs) OnAdd(ctx context.Context) {
n := fs.NewPersistentInode(ctx, &configRoot{}, nodefs.StableAttr{Mode: syscall.S_IFDIR}) n := root.NewPersistentInode(ctx, &configRoot{}, fs.StableAttr{Mode: syscall.S_IFDIR})
fs.AddChild("config", n, false) root.AddChild("config", n, false)
} }
type configRoot struct { type configRoot struct {
nodefs.Inode fs.Inode
} }
var _ = (nodefs.Unlinker)((*configRoot)(nil)) var _ = (fs.NodeUnlinker)((*configRoot)(nil))
var _ = (nodefs.Symlinker)((*configRoot)(nil)) var _ = (fs.NodeSymlinker)((*configRoot)(nil))
func (r *configRoot) Unlink(ctx context.Context, basename string) syscall.Errno { func (r *configRoot) Unlink(ctx context.Context, basename string) syscall.Errno {
if r.GetChild(basename) == nil { if r.GetChild(basename) == nil {
...@@ -64,7 +64,7 @@ func (r *configRoot) Unlink(ctx context.Context, basename string) syscall.Errno ...@@ -64,7 +64,7 @@ func (r *configRoot) Unlink(ctx context.Context, basename string) syscall.Errno
return 0 return 0
} }
func (r *configRoot) Symlink(ctx context.Context, target string, base string, out *fuse.EntryOut) (*nodefs.Inode, syscall.Errno) { func (r *configRoot) Symlink(ctx context.Context, target string, base string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
root, err := NewArchiveFileSystem(target) root, err := NewArchiveFileSystem(target)
if err != nil { if err != nil {
log.Println("NewZipArchiveFileSystem failed.", err) log.Println("NewZipArchiveFileSystem failed.", err)
...@@ -72,12 +72,12 @@ func (r *configRoot) Symlink(ctx context.Context, target string, base string, ou ...@@ -72,12 +72,12 @@ func (r *configRoot) Symlink(ctx context.Context, target string, base string, ou
} }
_, parent := r.Parent() _, parent := r.Parent()
ch := r.NewPersistentInode(ctx, root, nodefs.StableAttr{Mode: syscall.S_IFDIR}) ch := r.NewPersistentInode(ctx, root, fs.StableAttr{Mode: syscall.S_IFDIR})
parent.AddChild(base, ch, false) parent.AddChild(base, ch, false)
link := r.NewPersistentInode(ctx, &nodefs.MemSymlink{ link := r.NewPersistentInode(ctx, &fs.MemSymlink{
Data: []byte(target), Data: []byte(target),
}, nodefs.StableAttr{Mode: syscall.S_IFLNK}) }, fs.StableAttr{Mode: syscall.S_IFLNK})
r.AddChild(base, link, false) r.AddChild(base, link, false)
return link, 0 return link, 0
} }
...@@ -10,9 +10,9 @@ import ( ...@@ -10,9 +10,9 @@ import (
"testing" "testing"
"time" "time"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil" "github.com/hanwen/go-fuse/internal/testutil"
"github.com/hanwen/go-fuse/nodefs"
) )
const testTtl = 100 * time.Millisecond const testTtl = 100 * time.Millisecond
...@@ -22,13 +22,13 @@ func setupMzfs(t *testing.T) (mountPoint string, state *fuse.Server, cleanup fun ...@@ -22,13 +22,13 @@ func setupMzfs(t *testing.T) (mountPoint string, state *fuse.Server, cleanup fun
mountPoint = testutil.TempDir() mountPoint = testutil.TempDir()
dt := testTtl dt := testTtl
opts := &nodefs.Options{ opts := &fs.Options{
EntryTimeout: &dt, EntryTimeout: &dt,
AttrTimeout: &dt, AttrTimeout: &dt,
} }
opts.Debug = testutil.VerboseTest() opts.Debug = testutil.VerboseTest()
server, err := nodefs.Mount(mountPoint, root, opts) server, err := fs.Mount(mountPoint, root, opts)
if err != nil { if err != nil {
t.Fatalf("MountNodeFileSystem failed: %v", err) t.Fatalf("MountNodeFileSystem failed: %v", err)
} }
......
...@@ -17,8 +17,8 @@ import ( ...@@ -17,8 +17,8 @@ import (
"strings" "strings"
"syscall" "syscall"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/nodefs"
) )
// TODO - handle symlinks. // TODO - handle symlinks.
...@@ -32,7 +32,7 @@ func HeaderToFileInfo(out *fuse.Attr, h *tar.Header) { ...@@ -32,7 +32,7 @@ func HeaderToFileInfo(out *fuse.Attr, h *tar.Header) {
} }
type tarRoot struct { type tarRoot struct {
nodefs.Inode fs.Inode
rc io.ReadCloser rc io.ReadCloser
} }
...@@ -77,8 +77,8 @@ func (r *tarRoot) OnAdd(ctx context.Context) { ...@@ -77,8 +77,8 @@ func (r *tarRoot) OnAdd(ctx context.Context) {
ch := p.GetChild(comp) ch := p.GetChild(comp)
if ch == nil { if ch == nil {
ch = p.NewPersistentInode(ctx, ch = p.NewPersistentInode(ctx,
&nodefs.Inode{}, &fs.Inode{},
nodefs.StableAttr{Mode: syscall.S_IFDIR}) fs.StableAttr{Mode: syscall.S_IFDIR})
p.AddChild(comp, ch, false) p.AddChild(comp, ch, false)
} }
p = ch p = ch
...@@ -88,37 +88,37 @@ func (r *tarRoot) OnAdd(ctx context.Context) { ...@@ -88,37 +88,37 @@ func (r *tarRoot) OnAdd(ctx context.Context) {
HeaderToFileInfo(&attr, hdr) HeaderToFileInfo(&attr, hdr)
switch hdr.Typeflag { switch hdr.Typeflag {
case tar.TypeSymlink: case tar.TypeSymlink:
l := &nodefs.MemSymlink{ l := &fs.MemSymlink{
Data: []byte(hdr.Linkname), Data: []byte(hdr.Linkname),
} }
l.Attr = attr l.Attr = attr
p.AddChild(base, r.NewPersistentInode(ctx, l, nodefs.StableAttr{Mode: syscall.S_IFLNK}), false) p.AddChild(base, r.NewPersistentInode(ctx, l, fs.StableAttr{Mode: syscall.S_IFLNK}), false)
case tar.TypeLink: case tar.TypeLink:
log.Println("don't know how to handle Typelink") log.Println("don't know how to handle Typelink")
case tar.TypeChar: case tar.TypeChar:
rf := &nodefs.MemRegularFile{} rf := &fs.MemRegularFile{}
rf.Attr = attr rf.Attr = attr
p.AddChild(base, r.NewPersistentInode(ctx, rf, nodefs.StableAttr{Mode: syscall.S_IFCHR}), false) p.AddChild(base, r.NewPersistentInode(ctx, rf, fs.StableAttr{Mode: syscall.S_IFCHR}), false)
case tar.TypeBlock: case tar.TypeBlock:
rf := &nodefs.MemRegularFile{} rf := &fs.MemRegularFile{}
rf.Attr = attr rf.Attr = attr
p.AddChild(base, r.NewPersistentInode(ctx, rf, nodefs.StableAttr{Mode: syscall.S_IFBLK}), false) p.AddChild(base, r.NewPersistentInode(ctx, rf, fs.StableAttr{Mode: syscall.S_IFBLK}), false)
case tar.TypeDir: case tar.TypeDir:
rf := &nodefs.MemRegularFile{} rf := &fs.MemRegularFile{}
rf.Attr = attr rf.Attr = attr
p.AddChild(base, r.NewPersistentInode(ctx, rf, nodefs.StableAttr{Mode: syscall.S_IFDIR}), false) p.AddChild(base, r.NewPersistentInode(ctx, rf, fs.StableAttr{Mode: syscall.S_IFDIR}), false)
case tar.TypeFifo: case tar.TypeFifo:
rf := &nodefs.MemRegularFile{} rf := &fs.MemRegularFile{}
rf.Attr = attr rf.Attr = attr
p.AddChild(base, r.NewPersistentInode(ctx, rf, nodefs.StableAttr{Mode: syscall.S_IFIFO}), false) p.AddChild(base, r.NewPersistentInode(ctx, rf, fs.StableAttr{Mode: syscall.S_IFIFO}), false)
case tar.TypeReg, tar.TypeRegA: case tar.TypeReg, tar.TypeRegA:
df := &nodefs.MemRegularFile{ df := &fs.MemRegularFile{
Data: buf.Bytes(), Data: buf.Bytes(),
} }
df.Attr = attr df.Attr = attr
p.AddChild(base, r.NewPersistentInode(ctx, df, nodefs.StableAttr{}), false) p.AddChild(base, r.NewPersistentInode(ctx, df, fs.StableAttr{}), false)
default: default:
log.Printf("entry %q: unsupported type '%c'", hdr.Name, hdr.Typeflag) log.Printf("entry %q: unsupported type '%c'", hdr.Name, hdr.Typeflag)
} }
...@@ -134,7 +134,7 @@ func (rc *readCloser) Close() error { ...@@ -134,7 +134,7 @@ func (rc *readCloser) Close() error {
return rc.close() return rc.close()
} }
func NewTarCompressedTree(name string, format string) (nodefs.InodeEmbedder, error) { func NewTarCompressedTree(name string, format string) (fs.InodeEmbedder, error) {
f, err := os.Open(name) f, err := os.Open(name)
if err != nil { if err != nil {
return nil, err return nil, err
......
...@@ -16,8 +16,8 @@ import ( ...@@ -16,8 +16,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/internal/testutil" "github.com/hanwen/go-fuse/internal/testutil"
"github.com/hanwen/go-fuse/nodefs"
) )
var tarContents = map[string]string{ var tarContents = map[string]string{
...@@ -69,9 +69,9 @@ func TestTar(t *testing.T) { ...@@ -69,9 +69,9 @@ func TestTar(t *testing.T) {
mnt := testutil.TempDir() mnt := testutil.TempDir()
defer os.Remove(mnt) defer os.Remove(mnt)
opts := &nodefs.Options{} opts := &fs.Options{}
opts.Debug = true opts.Debug = testutil.VerboseTest()
s, err := nodefs.Mount(mnt, root, opts) s, err := fs.Mount(mnt, root, opts)
if err != nil { if err != nil {
t.Errorf("Mount: %v", err) t.Errorf("Mount: %v", err)
} }
......
...@@ -6,10 +6,8 @@ package zipfs ...@@ -6,10 +6,8 @@ package zipfs
import ( import (
"archive/zip" "archive/zip"
"bytes"
"context" "context"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
...@@ -17,39 +15,17 @@ import ( ...@@ -17,39 +15,17 @@ import (
"sync" "sync"
"syscall" "syscall"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/nodefs"
) )
type ZipFile struct {
*zip.File
}
func (f *ZipFile) Stat(out *fuse.Attr) {
}
func (f *ZipFile) Data() []byte {
zf := (*f)
rc, err := zf.Open()
if err != nil {
panic(err)
}
dest := bytes.NewBuffer(make([]byte, 0, f.UncompressedSize))
_, err = io.CopyN(dest, rc, int64(f.UncompressedSize))
if err != nil {
panic(err)
}
return dest.Bytes()
}
type zipRoot struct { type zipRoot struct {
nodefs.Inode fs.Inode
zr *zip.ReadCloser zr *zip.ReadCloser
} }
var _ = (nodefs.OnAdder)((*zipRoot)(nil)) var _ = (fs.NodeOnAdder)((*zipRoot)(nil))
func (zr *zipRoot) OnAdd(ctx context.Context) { func (zr *zipRoot) OnAdd(ctx context.Context) {
for _, f := range zr.zr.File { for _, f := range zr.zr.File {
...@@ -65,20 +41,20 @@ func (zr *zipRoot) OnAdd(ctx context.Context) { ...@@ -65,20 +41,20 @@ func (zr *zipRoot) OnAdd(ctx context.Context) {
} }
ch := p.GetChild(component) ch := p.GetChild(component)
if ch == nil { if ch == nil {
ch = p.NewPersistentInode(ctx, &nodefs.Inode{}, ch = p.NewPersistentInode(ctx, &fs.Inode{},
nodefs.StableAttr{Mode: fuse.S_IFDIR}) fs.StableAttr{Mode: fuse.S_IFDIR})
p.AddChild(component, ch, true) p.AddChild(component, ch, true)
} }
p = ch p = ch
} }
ch := p.NewPersistentInode(ctx, &zipFile{file: f}, nodefs.StableAttr{}) ch := p.NewPersistentInode(ctx, &zipFile{file: f}, fs.StableAttr{})
p.AddChild(base, ch, true) p.AddChild(base, ch, true)
} }
} }
// NewZipTree creates a new file-system for the zip file named name. // NewZipTree creates a new file-system for the zip file named name.
func NewZipTree(name string) (nodefs.InodeEmbedder, error) { func NewZipTree(name string) (fs.InodeEmbedder, error) {
r, err := zip.OpenReader(name) r, err := zip.OpenReader(name)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -89,19 +65,19 @@ func NewZipTree(name string) (nodefs.InodeEmbedder, error) { ...@@ -89,19 +65,19 @@ func NewZipTree(name string) (nodefs.InodeEmbedder, error) {
// zipFile is a file read from a zip archive. // zipFile is a file read from a zip archive.
type zipFile struct { type zipFile struct {
nodefs.Inode fs.Inode
file *zip.File file *zip.File
mu sync.Mutex mu sync.Mutex
data []byte data []byte
} }
var _ = (nodefs.Opener)((*zipFile)(nil)) var _ = (fs.NodeOpener)((*zipFile)(nil))
var _ = (nodefs.Getattrer)((*zipFile)(nil)) var _ = (fs.NodeGetattrer)((*zipFile)(nil))
// Getattr sets the minimum, which is the size. A more full-featured // Getattr sets the minimum, which is the size. A more full-featured
// FS would also set timestamps and permissions. // FS would also set timestamps and permissions.
func (zf *zipFile) Getattr(ctx context.Context, f nodefs.FileHandle, out *fuse.AttrOut) syscall.Errno { func (zf *zipFile) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
out.Mode = uint32(zf.file.Mode()) & 07777 out.Mode = uint32(zf.file.Mode()) & 07777
out.Nlink = 1 out.Nlink = 1
out.Mtime = uint64(zf.file.ModTime().Unix()) out.Mtime = uint64(zf.file.ModTime().Unix())
...@@ -113,7 +89,7 @@ func (zf *zipFile) Getattr(ctx context.Context, f nodefs.FileHandle, out *fuse.A ...@@ -113,7 +89,7 @@ func (zf *zipFile) Getattr(ctx context.Context, f nodefs.FileHandle, out *fuse.A
} }
// Open lazily unpacks zip data // Open lazily unpacks zip data
func (zf *zipFile) Open(ctx context.Context, flags uint32) (nodefs.FileHandle, uint32, syscall.Errno) { func (zf *zipFile) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) {
zf.mu.Lock() zf.mu.Lock()
defer zf.mu.Unlock() defer zf.mu.Unlock()
if zf.data == nil { if zf.data == nil {
...@@ -136,7 +112,7 @@ func (zf *zipFile) Open(ctx context.Context, flags uint32) (nodefs.FileHandle, u ...@@ -136,7 +112,7 @@ func (zf *zipFile) Open(ctx context.Context, flags uint32) (nodefs.FileHandle, u
} }
// Read simply returns the data that was already unpacked in the Open call // Read simply returns the data that was already unpacked in the Open call
func (zf *zipFile) Read(ctx context.Context, f nodefs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) { func (zf *zipFile) Read(ctx context.Context, f fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
end := int(off) + len(dest) end := int(off) + len(dest)
if end > len(zf.data) { if end > len(zf.data) {
end = len(zf.data) end = len(zf.data)
...@@ -144,9 +120,9 @@ func (zf *zipFile) Read(ctx context.Context, f nodefs.FileHandle, dest []byte, o ...@@ -144,9 +120,9 @@ func (zf *zipFile) Read(ctx context.Context, f nodefs.FileHandle, dest []byte, o
return fuse.ReadResultData(zf.data[off:end]), 0 return fuse.ReadResultData(zf.data[off:end]), 0
} }
var _ = (nodefs.OnAdder)((*zipRoot)(nil)) var _ = (fs.NodeOnAdder)((*zipRoot)(nil))
func NewArchiveFileSystem(name string) (root nodefs.InodeEmbedder, err error) { func NewArchiveFileSystem(name string) (root fs.InodeEmbedder, err error) {
switch { switch {
case strings.HasSuffix(name, ".zip"): case strings.HasSuffix(name, ".zip"):
root, err = NewZipTree(name) root, err = NewZipTree(name)
......
...@@ -13,9 +13,9 @@ import ( ...@@ -13,9 +13,9 @@ import (
"testing" "testing"
"time" "time"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil" "github.com/hanwen/go-fuse/internal/testutil"
"github.com/hanwen/go-fuse/nodefs"
) )
func testZipFile() string { func testZipFile() string {
...@@ -34,9 +34,9 @@ func setupZipfs(t *testing.T) (mountPoint string, cleanup func()) { ...@@ -34,9 +34,9 @@ func setupZipfs(t *testing.T) (mountPoint string, cleanup func()) {
} }
mountPoint = testutil.TempDir() mountPoint = testutil.TempDir()
opts := &nodefs.Options{} opts := &fs.Options{}
opts.Debug = testutil.VerboseTest() opts.Debug = testutil.VerboseTest()
server, err := nodefs.Mount(mountPoint, root, opts) server, err := fs.Mount(mountPoint, root, opts)
return mountPoint, func() { return mountPoint, func() {
server.Unmount() server.Unmount()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment