diff --git a/vendor/bazil.org/fuse/fs/bench/bench_create_test.go b/vendor/bazil.org/fuse/fs/bench/bench_create_test.go new file mode 100644 index 000000000..1f78786b7 --- /dev/null +++ b/vendor/bazil.org/fuse/fs/bench/bench_create_test.go @@ -0,0 +1,54 @@ +package bench_test + +import ( + "fmt" + "os" + "testing" + + "bazil.org/fuse" + "bazil.org/fuse/fs" + "bazil.org/fuse/fs/fstestutil" + "golang.org/x/net/context" +) + +type dummyFile struct { + fstestutil.File +} + +type benchCreateDir struct { + fstestutil.Dir +} + +var _ fs.NodeCreater = (*benchCreateDir)(nil) + +func (f *benchCreateDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) { + child := &dummyFile{} + return child, child, nil +} + +func BenchmarkCreate(b *testing.B) { + f := &benchCreateDir{} + mnt, err := fstestutil.MountedT(b, fstestutil.SimpleFS{f}, nil) + if err != nil { + b.Fatal(err) + } + defer mnt.Close() + + // prepare file names to decrease test overhead + names := make([]string, 0, b.N) + for i := 0; i < b.N; i++ { + // zero-padded so cost stays the same on every iteration + names = append(names, mnt.Dir+"/"+fmt.Sprintf("%08x", i)) + } + b.ResetTimer() + + for i := 0; i < b.N; i++ { + f, err := os.Create(names[i]) + if err != nil { + b.Fatalf("WriteFile: %v", err) + } + f.Close() + } + + b.StopTimer() +} diff --git a/vendor/bazil.org/fuse/fs/bench/bench_lookup_test.go b/vendor/bazil.org/fuse/fs/bench/bench_lookup_test.go new file mode 100644 index 000000000..b22edc82e --- /dev/null +++ b/vendor/bazil.org/fuse/fs/bench/bench_lookup_test.go @@ -0,0 +1,42 @@ +package bench_test + +import ( + "os" + "testing" + + "golang.org/x/net/context" + + "bazil.org/fuse" + "bazil.org/fuse/fs" + "bazil.org/fuse/fs/fstestutil" +) + +type benchLookupDir struct { + fstestutil.Dir +} + +var _ fs.NodeRequestLookuper = (*benchLookupDir)(nil) + +func (f *benchLookupDir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) { + return nil, fuse.ENOENT +} + +func BenchmarkLookup(b *testing.B) { + f := &benchLookupDir{} + mnt, err := fstestutil.MountedT(b, fstestutil.SimpleFS{f}, nil) + if err != nil { + b.Fatal(err) + } + defer mnt.Close() + + name := mnt.Dir + "/does-not-exist" + b.ResetTimer() + + for i := 0; i < b.N; i++ { + if _, err := os.Stat(name); !os.IsNotExist(err) { + b.Fatalf("Stat: wrong error: %v", err) + } + } + + b.StopTimer() +} diff --git a/vendor/bazil.org/fuse/fs/bench/bench_test.go b/vendor/bazil.org/fuse/fs/bench/bench_readwrite_test.go similarity index 100% rename from vendor/bazil.org/fuse/fs/bench/bench_test.go rename to vendor/bazil.org/fuse/fs/bench/bench_readwrite_test.go diff --git a/vendor/bazil.org/fuse/fs/serve.go b/vendor/bazil.org/fuse/fs/serve.go index 34a33706e..e9fc56590 100644 --- a/vendor/bazil.org/fuse/fs/serve.go +++ b/vendor/bazil.org/fuse/fs/serve.go @@ -91,6 +91,13 @@ type FSInodeGenerator interface { // simple, read-only filesystem. type Node interface { // Attr fills attr with the standard metadata for the node. + // + // Fields with reasonable defaults are prepopulated. For example, + // all times are set to a fixed moment when the program started. + // + // If Inode is left as 0, a dynamic inode number is chosen. + // + // The result may be cached for the duration set in Valid. Attr(ctx context.Context, attr *fuse.Attr) error } @@ -1192,6 +1199,12 @@ func (c *Server) handleRequest(ctx context.Context, node Node, snode *serveNode, s := &fuse.ReadResponse{Data: make([]byte, 0, r.Size)} if r.Dir { if h, ok := handle.(HandleReadDirAller); ok { + // detect rewinddir(3) or similar seek and refresh + // contents + if r.Offset == 0 { + shandle.readData = nil + } + if shandle.readData == nil { dirs, err := h.ReadDirAll(ctx) if err != nil { diff --git a/vendor/bazil.org/fuse/fs/serve_test.go b/vendor/bazil.org/fuse/fs/serve_test.go index 0853afcc3..e7f9cfb09 100644 --- a/vendor/bazil.org/fuse/fs/serve_test.go +++ b/vendor/bazil.org/fuse/fs/serve_test.go @@ -1116,7 +1116,17 @@ func TestInterrupt(t *testing.T) { <-f.hanging // err = child.Process.Signal(os.Interrupt) - err = child.Process.Signal(syscall.SIGIO) + var sig os.Signal = syscall.SIGIO + if runtime.GOOS == "darwin" { + // I can't get OSXFUSE 3.2.0 to trigger EINTR return from + // read(2), at least in a Go application. Works on Linux. So, + // on OS X, we just check that the signal at least kills the + // child, aborting the read, so operations on hanging FUSE + // filesystems can be aborted. + sig = os.Interrupt + } + + err = child.Process.Signal(sig) if err != nil { t.Errorf("cannot interrupt child: %v", err) return @@ -1132,14 +1142,28 @@ func TestInterrupt(t *testing.T) { if ws.CoreDump() { t.Fatalf("interrupt: didn't expect child to dump core: %v", ws) } - if ws.Signaled() { - t.Fatalf("interrupt: didn't expect child to exit with a signal: %v", ws) - } - if !ws.Exited() { - t.Fatalf("interrupt: expected child to exit normally: %v", ws) - } - if status := ws.ExitStatus(); status != 0 { - t.Errorf("interrupt: child failed: exit status %d", status) + switch runtime.GOOS { + case "darwin": + // see comment above about EINTR on OS X + if ws.Exited() { + t.Fatalf("interrupt: expected child to die from signal, got exit status: %v", ws.ExitStatus()) + } + if !ws.Signaled() { + t.Fatalf("interrupt: expected child to die from signal: %v", ws) + } + if got := ws.Signal(); got != sig { + t.Errorf("interrupt: child failed: signal %d", got) + } + default: + if ws.Signaled() { + t.Fatalf("interrupt: didn't expect child to exit with a signal: %v", ws) + } + if !ws.Exited() { + t.Fatalf("interrupt: expected child to exit normally: %v", ws) + } + if status := ws.ExitStatus(); status != 0 { + t.Errorf("interrupt: child failed: exit status %d", status) + } } default: t.Logf("interrupt: this platform has no test coverage") @@ -1452,6 +1476,73 @@ func TestReadDirNotImplemented(t *testing.T) { } } +type readDirAllRewind struct { + fstestutil.Dir + entries atomic.Value +} + +func (d *readDirAllRewind) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { + entries := d.entries.Load().([]fuse.Dirent) + return entries, nil +} + +func TestReadDirAllRewind(t *testing.T) { + t.Parallel() + f := &readDirAllRewind{} + f.entries.Store([]fuse.Dirent{ + {Name: "one", Inode: 11, Type: fuse.DT_Dir}, + }) + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f}, nil) + if err != nil { + t.Fatal(err) + } + defer mnt.Close() + + fil, err := os.Open(mnt.Dir) + if err != nil { + t.Error(err) + return + } + defer fil.Close() + + { + names, err := fil.Readdirnames(100) + if err != nil { + t.Error(err) + return + } + t.Logf("Got readdir: %q", names) + if len(names) != 1 || + names[0] != "one" { + t.Errorf(`expected entry of "one", got: %q`, names) + return + } + } + + f.entries.Store([]fuse.Dirent{ + {Name: "two", Inode: 12, Type: fuse.DT_File}, + {Name: "one", Inode: 11, Type: fuse.DT_Dir}, + }) + if _, err := fil.Seek(0, os.SEEK_SET); err != nil { + t.Fatal(err) + } + + { + names, err := fil.Readdirnames(100) + if err != nil { + t.Error(err) + return + } + t.Logf("Got readdir: %q", names) + if len(names) != 2 || + names[0] != "two" || + names[1] != "one" { + t.Errorf(`expected 2 entries of "two", "one", got: %q`, names) + return + } + } +} + // Test Chmod. type chmod struct { diff --git a/vendor/bazil.org/fuse/fuse.go b/vendor/bazil.org/fuse/fuse.go index c6143bbb0..6db0ef293 100644 --- a/vendor/bazil.org/fuse/fuse.go +++ b/vendor/bazil.org/fuse/fuse.go @@ -235,7 +235,7 @@ func initMount(c *Conn, conf *mountConfig) error { s := &InitResponse{ Library: proto, MaxReadahead: conf.maxReadahead, - MaxWrite: 128 * 1024, + MaxWrite: maxWrite, Flags: InitBigWrites | conf.initFlags, } r.Respond(s) @@ -403,9 +403,6 @@ func (h *Header) RespondError(err error) { h.respond(buf) } -// Maximum file write size we are prepared to receive from the kernel. -const maxWrite = 16 * 1024 * 1024 - // All requests read from the kernel, without data, are shorter than // this. var maxRequestSize = syscall.Getpagesize() @@ -1326,7 +1323,7 @@ type Attr struct { Ctime time.Time // time of last inode change Crtime time.Time // time of creation (OS X only) Mode os.FileMode // file mode - Nlink uint32 // number of links + Nlink uint32 // number of links (usually 1) Uid uint32 // owner uid Gid uint32 // group gid Rdev uint32 // device numbers diff --git a/vendor/bazil.org/fuse/fuse_darwin.go b/vendor/bazil.org/fuse/fuse_darwin.go new file mode 100644 index 000000000..b58dca97d --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_darwin.go @@ -0,0 +1,9 @@ +package fuse + +// Maximum file write size we are prepared to receive from the kernel. +// +// This value has to be >=16MB or OSXFUSE (3.4.0 observed) will +// forcibly close the /dev/fuse file descriptor on a Setxattr with a +// 16MB value. See TestSetxattr16MB and +// https://github.com/bazil/fuse/issues/42 +const maxWrite = 16 * 1024 * 1024 diff --git a/vendor/bazil.org/fuse/fuse_freebsd.go b/vendor/bazil.org/fuse/fuse_freebsd.go new file mode 100644 index 000000000..4aa83a0d4 --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_freebsd.go @@ -0,0 +1,6 @@ +package fuse + +// Maximum file write size we are prepared to receive from the kernel. +// +// This number is just a guess. +const maxWrite = 128 * 1024 diff --git a/vendor/bazil.org/fuse/fuse_kernel.go b/vendor/bazil.org/fuse/fuse_kernel.go index af62d8ef1..87c5ca1dc 100644 --- a/vendor/bazil.org/fuse/fuse_kernel.go +++ b/vendor/bazil.org/fuse/fuse_kernel.go @@ -62,7 +62,7 @@ type kstatfs struct { Bsize uint32 Namelen uint32 Frsize uint32 - Padding uint32 + _ uint32 Spare [6]uint32 } @@ -419,14 +419,14 @@ type forgetIn struct { type getattrIn struct { GetattrFlags uint32 - dummy uint32 + _ uint32 Fh uint64 } type attrOut struct { AttrValid uint64 // Cache timeout for the attributes AttrValidNsec uint32 - Dummy uint32 + _ uint32 Attr attr } @@ -448,10 +448,10 @@ type getxtimesOut struct { } type mknodIn struct { - Mode uint32 - Rdev uint32 - Umask uint32 - padding uint32 + Mode uint32 + Rdev uint32 + Umask uint32 + _ uint32 // "filename\x00" follows. } @@ -498,7 +498,7 @@ type linkIn struct { type setattrInCommon struct { Valid uint32 - Padding uint32 + _ uint32 Fh uint64 Size uint64 LockOwner uint64 // unused on OS X? @@ -523,14 +523,14 @@ type openIn struct { type openOut struct { Fh uint64 OpenFlags uint32 - Padding uint32 + _ uint32 } type createIn struct { - Flags uint32 - Mode uint32 - Umask uint32 - padding uint32 + Flags uint32 + Mode uint32 + Umask uint32 + _ uint32 } func createInSize(p Protocol) uintptr { @@ -552,7 +552,7 @@ type releaseIn struct { type flushIn struct { Fh uint64 FlushFlags uint32 - Padding uint32 + _ uint32 LockOwner uint64 } @@ -563,7 +563,7 @@ type readIn struct { ReadFlags uint32 LockOwner uint64 Flags uint32 - padding uint32 + _ uint32 } func readInSize(p Protocol) uintptr { @@ -598,7 +598,7 @@ type writeIn struct { WriteFlags uint32 LockOwner uint64 Flags uint32 - padding uint32 + _ uint32 } func writeInSize(p Protocol) uintptr { @@ -611,8 +611,8 @@ func writeInSize(p Protocol) uintptr { } type writeOut struct { - Size uint32 - Padding uint32 + Size uint32 + _ uint32 } // The WriteFlags are passed in WriteRequest. @@ -642,7 +642,7 @@ type statfsOut struct { type fsyncIn struct { Fh uint64 FsyncFlags uint32 - Padding uint32 + _ uint32 } type setxattrInCommon struct { @@ -655,8 +655,8 @@ func (setxattrInCommon) position() uint32 { } type getxattrInCommon struct { - Size uint32 - Padding uint32 + Size uint32 + _ uint32 } func (getxattrInCommon) position() uint32 { @@ -664,8 +664,8 @@ func (getxattrInCommon) position() uint32 { } type getxattrOut struct { - Size uint32 - Padding uint32 + Size uint32 + _ uint32 } type lkIn struct { @@ -673,7 +673,7 @@ type lkIn struct { Owner uint64 Lk fileLock LkFlags uint32 - padding uint32 + _ uint32 } func lkInSize(p Protocol) uintptr { @@ -690,8 +690,8 @@ type lkOut struct { } type accessIn struct { - Mask uint32 - Padding uint32 + Mask uint32 + _ uint32 } type initIn struct { @@ -719,7 +719,7 @@ type interruptIn struct { type bmapIn struct { Block uint64 BlockSize uint32 - Padding uint32 + _ uint32 } type bmapOut struct { @@ -727,14 +727,14 @@ type bmapOut struct { } type inHeader struct { - Len uint32 - Opcode uint32 - Unique uint64 - Nodeid uint64 - Uid uint32 - Gid uint32 - Pid uint32 - Padding uint32 + Len uint32 + Opcode uint32 + Unique uint64 + Nodeid uint64 + Uid uint32 + Gid uint32 + Pid uint32 + _ uint32 } const inHeaderSize = int(unsafe.Sizeof(inHeader{})) @@ -770,5 +770,5 @@ type notifyInvalInodeOut struct { type notifyInvalEntryOut struct { Parent uint64 Namelen uint32 - padding uint32 + _ uint32 } diff --git a/vendor/bazil.org/fuse/fuse_linux.go b/vendor/bazil.org/fuse/fuse_linux.go new file mode 100644 index 000000000..5fb96f9ae --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_linux.go @@ -0,0 +1,7 @@ +package fuse + +// Maximum file write size we are prepared to receive from the kernel. +// +// Linux 4.2.0 has been observed to cap this value at 128kB +// (FUSE_MAX_PAGES_PER_REQ=32, 4kB pages). +const maxWrite = 128 * 1024 diff --git a/vendor/bazil.org/fuse/mount_darwin.go b/vendor/bazil.org/fuse/mount_darwin.go index d40aa19b5..c1c36e62b 100644 --- a/vendor/bazil.org/fuse/mount_darwin.go +++ b/vendor/bazil.org/fuse/mount_darwin.go @@ -113,7 +113,10 @@ func callMount(bin string, daemonVar string, dir string, conf *mountConfig, f *o ) cmd.ExtraFiles = []*os.File{f} cmd.Env = os.Environ() + // OSXFUSE <3.3.0 cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=") + // OSXFUSE >=3.3.0 + cmd.Env = append(cmd.Env, "MOUNT_OSXFUSE_CALL_BY_LIB=") daemon := os.Args[0] if daemonVar != "" { diff --git a/vendor/bazil.org/fuse/options.go b/vendor/bazil.org/fuse/options.go index a0d91f108..caee49538 100644 --- a/vendor/bazil.org/fuse/options.go +++ b/vendor/bazil.org/fuse/options.go @@ -83,6 +83,37 @@ func VolumeName(name string) MountOption { return volumeName(name) } +// NoAppleDouble makes OSXFUSE disallow files with names used by OS X +// to store extended attributes on file systems that do not support +// them natively. +// +// Such file names are: +// +// ._* +// .DS_Store +// +// OS X only. Others ignore this option. +func NoAppleDouble() MountOption { + return noAppleDouble +} + +// NoAppleXattr makes OSXFUSE disallow extended attributes with the +// prefix "com.apple.". This disables persistent Finder state and +// other such information. +// +// OS X only. Others ignore this option. +func NoAppleXattr() MountOption { + return noAppleXattr +} + +// DaemonTimeout sets the time in seconds between a request and a reply before +// the FUSE mount is declared dead. +// +// OS X and FreeBSD only. Others ignore this option. +func DaemonTimeout(name string) MountOption { + return daemonTimeout(name) +} + var ErrCannotCombineAllowOtherAndAllowRoot = errors.New("cannot combine AllowOther and AllowRoot") // AllowOther allows other users to access the file system. @@ -113,6 +144,24 @@ func AllowRoot() MountOption { } } +// AllowDev enables interpreting character or block special devices on the +// filesystem. +func AllowDev() MountOption { + return func(conf *mountConfig) error { + conf.options["dev"] = "" + return nil + } +} + +// AllowSUID allows set-user-identifier or set-group-identifier bits to take +// effect. +func AllowSUID() MountOption { + return func(conf *mountConfig) error { + conf.options["suid"] = "" + return nil + } +} + // DefaultPermissions makes the kernel enforce access control based on // the file mode (as in chmod). // diff --git a/vendor/bazil.org/fuse/options_daemon_timeout_test.go b/vendor/bazil.org/fuse/options_daemon_timeout_test.go new file mode 100644 index 000000000..1cd3eccff --- /dev/null +++ b/vendor/bazil.org/fuse/options_daemon_timeout_test.go @@ -0,0 +1,64 @@ +// Test for adjustable timeout between a FUSE request and the daemon's response. +// +// +build darwin freebsd + +package fuse_test + +import ( + "os" + "runtime" + "syscall" + "testing" + "time" + + "bazil.org/fuse" + "bazil.org/fuse/fs" + "bazil.org/fuse/fs/fstestutil" + "golang.org/x/net/context" +) + +type slowCreaterDir struct { + fstestutil.Dir +} + +var _ fs.NodeCreater = slowCreaterDir{} + +func (c slowCreaterDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) { + time.Sleep(10 * time.Second) + // pick a really distinct error, to identify it later + return nil, nil, fuse.Errno(syscall.ENAMETOOLONG) +} + +func TestMountOptionDaemonTimeout(t *testing.T) { + if runtime.GOOS != "darwin" && runtime.GOOS != "freebsd" { + return + } + if testing.Short() { + t.Skip("skipping time-based test in short mode") + } + t.Parallel() + + mnt, err := fstestutil.MountedT(t, + fstestutil.SimpleFS{slowCreaterDir{}}, + nil, + fuse.DaemonTimeout("2"), + ) + if err != nil { + t.Fatal(err) + } + defer mnt.Close() + + // This should fail by the kernel timing out the request. + f, err := os.Create(mnt.Dir + "/child") + if err == nil { + f.Close() + t.Fatal("expected an error") + } + perr, ok := err.(*os.PathError) + if !ok { + t.Fatalf("expected PathError, got %T: %v", err, err) + } + if perr.Err == syscall.ENAMETOOLONG { + t.Fatalf("expected other than ENAMETOOLONG, got %T: %v", err, err) + } +} diff --git a/vendor/bazil.org/fuse/options_darwin.go b/vendor/bazil.org/fuse/options_darwin.go index f71fa97eb..b53402466 100644 --- a/vendor/bazil.org/fuse/options_darwin.go +++ b/vendor/bazil.org/fuse/options_darwin.go @@ -11,3 +11,20 @@ func volumeName(name string) MountOption { return nil } } + +func daemonTimeout(name string) MountOption { + return func(conf *mountConfig) error { + conf.options["daemon_timeout"] = name + return nil + } +} + +func noAppleXattr(conf *mountConfig) error { + conf.options["noapplexattr"] = "" + return nil +} + +func noAppleDouble(conf *mountConfig) error { + conf.options["noappledouble"] = "" + return nil +} diff --git a/vendor/bazil.org/fuse/options_freebsd.go b/vendor/bazil.org/fuse/options_freebsd.go index 5a0b84806..2387124c2 100644 --- a/vendor/bazil.org/fuse/options_freebsd.go +++ b/vendor/bazil.org/fuse/options_freebsd.go @@ -7,3 +7,18 @@ func localVolume(conf *mountConfig) error { func volumeName(name string) MountOption { return dummyOption } + +func daemonTimeout(name string) MountOption { + return func(conf *mountConfig) error { + conf.options["timeout"] = name + return nil + } +} + +func noAppleXattr(conf *mountConfig) error { + return nil +} + +func noAppleDouble(conf *mountConfig) error { + return nil +} diff --git a/vendor/bazil.org/fuse/options_linux.go b/vendor/bazil.org/fuse/options_linux.go index 5a0b84806..77b46a500 100644 --- a/vendor/bazil.org/fuse/options_linux.go +++ b/vendor/bazil.org/fuse/options_linux.go @@ -7,3 +7,15 @@ func localVolume(conf *mountConfig) error { func volumeName(name string) MountOption { return dummyOption } + +func daemonTimeout(name string) MountOption { + return dummyOption +} + +func noAppleXattr(conf *mountConfig) error { + return nil +} + +func noAppleDouble(conf *mountConfig) error { + return nil +} diff --git a/vendor/bazil.org/fuse/options_test.go b/vendor/bazil.org/fuse/options_test.go index 2578e1f47..965c00c95 100644 --- a/vendor/bazil.org/fuse/options_test.go +++ b/vendor/bazil.org/fuse/options_test.go @@ -165,6 +165,7 @@ func TestMountOptionDefaultPermissions(t *testing.T) { t.Skip("FreeBSD does not support DefaultPermissions") } t.Parallel() + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{ &fstestutil.ChildMap{"child": unwritableFile{}}, @@ -172,7 +173,6 @@ func TestMountOptionDefaultPermissions(t *testing.T) { nil, fuse.DefaultPermissions(), ) - if err != nil { t.Fatal(err) } @@ -203,12 +203,12 @@ func (createrDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fus func TestMountOptionReadOnly(t *testing.T) { t.Parallel() + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{createrDir{}}, nil, fuse.ReadOnly(), ) - if err != nil { t.Fatal(err) }