forked from TrueCloudLab/restic
Update bazil.org/fuse
This commit is contained in:
parent
24398d2b9d
commit
cb80a70aca
28 changed files with 1831 additions and 471 deletions
4
vendor/manifest
vendored
4
vendor/manifest
vendored
|
@ -4,8 +4,8 @@
|
||||||
{
|
{
|
||||||
"importpath": "bazil.org/fuse",
|
"importpath": "bazil.org/fuse",
|
||||||
"repository": "https://github.com/bazil/fuse",
|
"repository": "https://github.com/bazil/fuse",
|
||||||
"revision": "18419ee53958df28fcfc9490fe6123bd59e237bb",
|
"revision": "371fbbdaa8987b715bdd21d6adc4c9b20155f748",
|
||||||
"branch": "HEAD"
|
"branch": "master"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"importpath": "github.com/elithrar/simple-scrypt",
|
"importpath": "github.com/elithrar/simple-scrypt",
|
||||||
|
|
|
@ -24,16 +24,7 @@ func usage() {
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func run(mountpoint string) error {
|
||||||
flag.Usage = usage
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if flag.NArg() != 1 {
|
|
||||||
usage()
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
mountpoint := flag.Arg(0)
|
|
||||||
|
|
||||||
c, err := fuse.Mount(
|
c, err := fuse.Mount(
|
||||||
mountpoint,
|
mountpoint,
|
||||||
fuse.FSName("clock"),
|
fuse.FSName("clock"),
|
||||||
|
@ -42,10 +33,14 @@ func main() {
|
||||||
fuse.VolumeName("Clock filesystem"),
|
fuse.VolumeName("Clock filesystem"),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return err
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
|
if p := c.Protocol(); !p.HasInvalidate() {
|
||||||
|
return fmt.Errorf("kernel FUSE support is too old to have invalidations: version %v", p)
|
||||||
|
}
|
||||||
|
|
||||||
srv := fs.New(c, nil)
|
srv := fs.New(c, nil)
|
||||||
filesys := &FS{
|
filesys := &FS{
|
||||||
// We pre-create the clock node so that it's always the same
|
// We pre-create the clock node so that it's always the same
|
||||||
|
@ -61,12 +56,28 @@ func main() {
|
||||||
// This goroutine never exits. That's fine for this example.
|
// This goroutine never exits. That's fine for this example.
|
||||||
go filesys.clockFile.update()
|
go filesys.clockFile.update()
|
||||||
if err := srv.Serve(filesys); err != nil {
|
if err := srv.Serve(filesys); err != nil {
|
||||||
log.Fatal(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mount process has an error to report.
|
// Check if the mount process has an error to report.
|
||||||
<-c.Ready
|
<-c.Ready
|
||||||
if err := c.MountError; err != nil {
|
if err := c.MountError; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Usage = usage
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if flag.NArg() != 1 {
|
||||||
|
usage()
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
mountpoint := flag.Arg(0)
|
||||||
|
|
||||||
|
if err := run(mountpoint); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,18 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Usage = func() {
|
func usage() {
|
||||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||||
fmt.Fprintf(os.Stderr, " %s MOUNTPOINT\n", os.Args[0])
|
fmt.Fprintf(os.Stderr, " %s MOUNTPOINT\n", os.Args[0])
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Usage = Usage
|
flag.Usage = usage
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if flag.NArg() != 1 {
|
if flag.NArg() != 1 {
|
||||||
Usage()
|
usage()
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
mountpoint := flag.Arg(0)
|
mountpoint := flag.Arg(0)
|
||||||
|
|
54
vendor/src/bazil.org/fuse/fs/bench/bench_create_test.go
vendored
Normal file
54
vendor/src/bazil.org/fuse/fs/bench/bench_create_test.go
vendored
Normal file
|
@ -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()
|
||||||
|
}
|
42
vendor/src/bazil.org/fuse/fs/bench/bench_lookup_test.go
vendored
Normal file
42
vendor/src/bazil.org/fuse/fs/bench/bench_lookup_test.go
vendored
Normal file
|
@ -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()
|
||||||
|
}
|
70
vendor/src/bazil.org/fuse/fs/fstestutil/checkdir.go
vendored
Normal file
70
vendor/src/bazil.org/fuse/fs/fstestutil/checkdir.go
vendored
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package fstestutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileInfoCheck is a function that validates an os.FileInfo according
|
||||||
|
// to some criteria.
|
||||||
|
type FileInfoCheck func(fi os.FileInfo) error
|
||||||
|
|
||||||
|
type checkDirError struct {
|
||||||
|
missing map[string]struct{}
|
||||||
|
extra map[string]os.FileMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *checkDirError) Error() string {
|
||||||
|
return fmt.Sprintf("wrong directory contents: missing %v, extra %v", e.missing, e.extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckDir checks the contents of the directory at path, making sure
|
||||||
|
// every directory entry listed in want is present. If the check is
|
||||||
|
// not nil, it must also pass.
|
||||||
|
//
|
||||||
|
// If want contains the impossible filename "", unexpected files are
|
||||||
|
// checked with that. If the key is not in want, unexpected files are
|
||||||
|
// an error.
|
||||||
|
//
|
||||||
|
// Missing entries, that are listed in want but not seen, are an
|
||||||
|
// error.
|
||||||
|
func CheckDir(path string, want map[string]FileInfoCheck) error {
|
||||||
|
problems := &checkDirError{
|
||||||
|
missing: make(map[string]struct{}, len(want)),
|
||||||
|
extra: make(map[string]os.FileMode),
|
||||||
|
}
|
||||||
|
for k := range want {
|
||||||
|
if k == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
problems.missing[k] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fis, err := ioutil.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot read directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fi := range fis {
|
||||||
|
check, ok := want[fi.Name()]
|
||||||
|
if !ok {
|
||||||
|
check, ok = want[""]
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
problems.extra[fi.Name()] = fi.Mode()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
delete(problems.missing, fi.Name())
|
||||||
|
if check != nil {
|
||||||
|
if err := check(fi); err != nil {
|
||||||
|
return fmt.Errorf("check failed: %v: %v", fi.Name(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(problems.missing) > 0 || len(problems.extra) > 0 {
|
||||||
|
return problems
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -50,13 +50,15 @@ func (mnt *Mount) Close() {
|
||||||
os.Remove(mnt.Dir)
|
os.Remove(mnt.Dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mounted mounts the fuse.Server at a temporary directory.
|
// MountedFunc mounts a filesystem at a temporary directory. The
|
||||||
|
// filesystem used is constructed by calling a function, to allow
|
||||||
|
// storing fuse.Conn and fs.Server in the FS.
|
||||||
//
|
//
|
||||||
// It also waits until the filesystem is known to be visible (OS X
|
// It also waits until the filesystem is known to be visible (OS X
|
||||||
// workaround).
|
// workaround).
|
||||||
//
|
//
|
||||||
// After successful return, caller must clean up by calling Close.
|
// After successful return, caller must clean up by calling Close.
|
||||||
func Mounted(filesys fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Mount, error) {
|
func MountedFunc(fn func(*Mount) fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Mount, error) {
|
||||||
dir, err := ioutil.TempDir("", "fusetest")
|
dir, err := ioutil.TempDir("", "fusetest")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -75,6 +77,7 @@ func Mounted(filesys fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Moun
|
||||||
Error: serveErr,
|
Error: serveErr,
|
||||||
done: done,
|
done: done,
|
||||||
}
|
}
|
||||||
|
filesys := fn(mnt)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(done)
|
defer close(done)
|
||||||
serveErr <- server.Serve(filesys)
|
serveErr <- server.Serve(filesys)
|
||||||
|
@ -95,14 +98,25 @@ func Mounted(filesys fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Moun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MountedT mounts the filesystem at a temporary directory,
|
// Mounted mounts the fuse.Server at a temporary directory.
|
||||||
|
//
|
||||||
|
// It also waits until the filesystem is known to be visible (OS X
|
||||||
|
// workaround).
|
||||||
|
//
|
||||||
|
// After successful return, caller must clean up by calling Close.
|
||||||
|
func Mounted(filesys fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Mount, error) {
|
||||||
|
fn := func(*Mount) fs.FS { return filesys }
|
||||||
|
return MountedFunc(fn, conf, options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountedFuncT mounts a filesystem at a temporary directory,
|
||||||
// directing it's debug log to the testing logger.
|
// directing it's debug log to the testing logger.
|
||||||
//
|
//
|
||||||
// See Mounted for usage.
|
// See MountedFunc for usage.
|
||||||
//
|
//
|
||||||
// The debug log is not enabled by default. Use `-fuse.debug` or call
|
// The debug log is not enabled by default. Use `-fuse.debug` or call
|
||||||
// DebugByDefault to enable.
|
// DebugByDefault to enable.
|
||||||
func MountedT(t testing.TB, filesys fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Mount, error) {
|
func MountedFuncT(t testing.TB, fn func(*Mount) fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Mount, error) {
|
||||||
if conf == nil {
|
if conf == nil {
|
||||||
conf = &fs.Config{}
|
conf = &fs.Config{}
|
||||||
}
|
}
|
||||||
|
@ -111,5 +125,17 @@ func MountedT(t testing.TB, filesys fs.FS, conf *fs.Config, options ...fuse.Moun
|
||||||
t.Logf("FUSE: %s", msg)
|
t.Logf("FUSE: %s", msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Mounted(filesys, conf, options...)
|
return MountedFunc(fn, conf, options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountedT mounts the filesystem at a temporary directory,
|
||||||
|
// directing it's debug log to the testing logger.
|
||||||
|
//
|
||||||
|
// See Mounted for usage.
|
||||||
|
//
|
||||||
|
// The debug log is not enabled by default. Use `-fuse.debug` or call
|
||||||
|
// DebugByDefault to enable.
|
||||||
|
func MountedT(t testing.TB, filesys fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Mount, error) {
|
||||||
|
fn := func(*Mount) fs.FS { return filesys }
|
||||||
|
return MountedFuncT(t, fn, conf, options...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -382,3 +382,28 @@ func (r *Removexattrs) RecordedRemovexattr() fuse.RemovexattrRequest {
|
||||||
}
|
}
|
||||||
return *(val.(*fuse.RemovexattrRequest))
|
return *(val.(*fuse.RemovexattrRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates records a Create request and its fields.
|
||||||
|
type Creates struct {
|
||||||
|
rec RequestRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = fs.NodeCreater(&Creates{})
|
||||||
|
|
||||||
|
// Create records the request and returns an error. Most callers should
|
||||||
|
// wrap this call in a function that returns a more useful result.
|
||||||
|
func (r *Creates) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
|
||||||
|
tmp := *req
|
||||||
|
r.rec.RecordRequest(&tmp)
|
||||||
|
return nil, nil, fuse.EIO
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordedCreate returns information about the Create request.
|
||||||
|
// If no request was seen, returns a zero value.
|
||||||
|
func (r *Creates) RecordedCreate() fuse.CreateRequest {
|
||||||
|
val := r.rec.Recorded()
|
||||||
|
if val == nil {
|
||||||
|
return fuse.CreateRequest{}
|
||||||
|
}
|
||||||
|
return *(val.(*fuse.CreateRequest))
|
||||||
|
}
|
||||||
|
|
447
vendor/src/bazil.org/fuse/fs/serve.go
vendored
447
vendor/src/bazil.org/fuse/fs/serve.go
vendored
|
@ -18,6 +18,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
"bazil.org/fuse/fuseutil"
|
"bazil.org/fuse/fuseutil"
|
||||||
)
|
)
|
||||||
|
@ -89,6 +91,13 @@ type FSInodeGenerator interface {
|
||||||
// simple, read-only filesystem.
|
// simple, read-only filesystem.
|
||||||
type Node interface {
|
type Node interface {
|
||||||
// Attr fills attr with the standard metadata for the node.
|
// 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
|
Attr(ctx context.Context, attr *fuse.Attr) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,9 +114,7 @@ type NodeSetattrer interface {
|
||||||
// Setattr sets the standard metadata for the receiver.
|
// Setattr sets the standard metadata for the receiver.
|
||||||
//
|
//
|
||||||
// Note, this is also used to communicate changes in the size of
|
// Note, this is also used to communicate changes in the size of
|
||||||
// the file. Not implementing Setattr causes writes to be unable
|
// the file, outside of Writes.
|
||||||
// to grow the file (except with OpenDirectIO, which bypasses that
|
|
||||||
// mechanism).
|
|
||||||
//
|
//
|
||||||
// req.Valid is a bitmask of what fields are actually being set.
|
// req.Valid is a bitmask of what fields are actually being set.
|
||||||
// For example, the method should not change the mode of the file
|
// For example, the method should not change the mode of the file
|
||||||
|
@ -297,16 +304,17 @@ type HandleReader interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type HandleWriter interface {
|
type HandleWriter interface {
|
||||||
// Write requests to write data into the handle.
|
// Write requests to write data into the handle at the given offset.
|
||||||
|
// Store the amount of data written in resp.Size.
|
||||||
//
|
//
|
||||||
// There is a writeback page cache in the kernel that normally submits
|
// There is a writeback page cache in the kernel that normally submits
|
||||||
// only page-aligned writes spanning one or more pages. However,
|
// only page-aligned writes spanning one or more pages. However,
|
||||||
// you should not rely on this. To see individual requests as
|
// you should not rely on this. To see individual requests as
|
||||||
// submitted by the file system clients, set OpenDirectIO.
|
// submitted by the file system clients, set OpenDirectIO.
|
||||||
//
|
//
|
||||||
// Note that file size changes are communicated through Setattr.
|
// Writes that grow the file are expected to update the file size
|
||||||
// Writes beyond the size of the file as reported by Attr are not
|
// (as seen through Attr). Note that file size changes are
|
||||||
// even attempted (except in OpenDirectIO mode).
|
// communicated also through Setattr.
|
||||||
Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error
|
Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,11 +330,13 @@ type Config struct {
|
||||||
// See fuse.Debug for the rules that log functions must follow.
|
// See fuse.Debug for the rules that log functions must follow.
|
||||||
Debug func(msg interface{})
|
Debug func(msg interface{})
|
||||||
|
|
||||||
// Function to create new contexts. If nil, use
|
// Function to put things into context for processing the request.
|
||||||
// context.Background.
|
// The returned context must have ctx as its parent.
|
||||||
//
|
//
|
||||||
// Note that changing this may not affect existing calls to Serve.
|
// Note that changing this may not affect existing calls to Serve.
|
||||||
GetContext func() context.Context
|
//
|
||||||
|
// Must not retain req.
|
||||||
|
WithContext func(ctx context.Context, req fuse.Request) context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new FUSE server ready to serve this kernel FUSE
|
// New returns a new FUSE server ready to serve this kernel FUSE
|
||||||
|
@ -342,14 +352,11 @@ func New(conn *fuse.Conn, config *Config) *Server {
|
||||||
}
|
}
|
||||||
if config != nil {
|
if config != nil {
|
||||||
s.debug = config.Debug
|
s.debug = config.Debug
|
||||||
s.context = config.GetContext
|
s.context = config.WithContext
|
||||||
}
|
}
|
||||||
if s.debug == nil {
|
if s.debug == nil {
|
||||||
s.debug = fuse.Debug
|
s.debug = fuse.Debug
|
||||||
}
|
}
|
||||||
if s.context == nil {
|
|
||||||
s.context = context.Background
|
|
||||||
}
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +364,7 @@ type Server struct {
|
||||||
// set in New
|
// set in New
|
||||||
conn *fuse.Conn
|
conn *fuse.Conn
|
||||||
debug func(msg interface{})
|
debug func(msg interface{})
|
||||||
context func() context.Context
|
context func(ctx context.Context, req fuse.Request) context.Context
|
||||||
|
|
||||||
// set once at Serve time
|
// set once at Serve time
|
||||||
fs FS
|
fs FS
|
||||||
|
@ -494,7 +501,7 @@ func (c *Server) saveNode(inode uint64, node Node) (id fuse.NodeID, gen uint64)
|
||||||
}
|
}
|
||||||
sn.generation = c.nodeGen
|
sn.generation = c.nodeGen
|
||||||
c.nodeRef[node] = id
|
c.nodeRef[node] = id
|
||||||
return
|
return id, sn.generation
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Server) saveHandle(handle Handle, nodeID fuse.NodeID) (id fuse.HandleID) {
|
func (c *Server) saveHandle(handle Handle, nodeID fuse.NodeID) (id fuse.HandleID) {
|
||||||
|
@ -601,7 +608,7 @@ type logResponseHeader struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m logResponseHeader) String() string {
|
func (m logResponseHeader) String() string {
|
||||||
return fmt.Sprintf("ID=%#x", m.ID)
|
return fmt.Sprintf("ID=%v", m.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
type response struct {
|
type response struct {
|
||||||
|
@ -626,21 +633,21 @@ func (r response) errstr() string {
|
||||||
func (r response) String() string {
|
func (r response) String() string {
|
||||||
switch {
|
switch {
|
||||||
case r.Errno != "" && r.Out != nil:
|
case r.Errno != "" && r.Out != nil:
|
||||||
return fmt.Sprintf("-> %s error=%s %s", r.Request, r.errstr(), r.Out)
|
return fmt.Sprintf("-> [%v] %v error=%s", r.Request, r.Out, r.errstr())
|
||||||
case r.Errno != "":
|
case r.Errno != "":
|
||||||
return fmt.Sprintf("-> %s error=%s", r.Request, r.errstr())
|
return fmt.Sprintf("-> [%v] %s error=%s", r.Request, r.Op, r.errstr())
|
||||||
case r.Out != nil:
|
case r.Out != nil:
|
||||||
// make sure (seemingly) empty values are readable
|
// make sure (seemingly) empty values are readable
|
||||||
switch r.Out.(type) {
|
switch r.Out.(type) {
|
||||||
case string:
|
case string:
|
||||||
return fmt.Sprintf("-> %s %q", r.Request, r.Out)
|
return fmt.Sprintf("-> [%v] %s %q", r.Request, r.Op, r.Out)
|
||||||
case []byte:
|
case []byte:
|
||||||
return fmt.Sprintf("-> %s [% x]", r.Request, r.Out)
|
return fmt.Sprintf("-> [%v] %s [% x]", r.Request, r.Op, r.Out)
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("-> %s %s", r.Request, r.Out)
|
return fmt.Sprintf("-> [%v] %v", r.Request, r.Out)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("-> %s", r.Request)
|
return fmt.Sprintf("-> [%v] %s", r.Request, r.Op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -652,20 +659,23 @@ type notification struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n notification) String() string {
|
func (n notification) String() string {
|
||||||
switch {
|
var buf bytes.Buffer
|
||||||
case n.Out != nil:
|
fmt.Fprintf(&buf, "=> %s %v", n.Op, n.Node)
|
||||||
|
if n.Out != nil {
|
||||||
// make sure (seemingly) empty values are readable
|
// make sure (seemingly) empty values are readable
|
||||||
switch n.Out.(type) {
|
switch n.Out.(type) {
|
||||||
case string:
|
case string:
|
||||||
return fmt.Sprintf("=> %s %d %q Err:%v", n.Op, n.Node, n.Out, n.Err)
|
fmt.Fprintf(&buf, " %q", n.Out)
|
||||||
case []byte:
|
case []byte:
|
||||||
return fmt.Sprintf("=> %s %d [% x] Err:%v", n.Op, n.Node, n.Out, n.Err)
|
fmt.Fprintf(&buf, " [% x]", n.Out)
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("=> %s %d %s Err:%v", n.Op, n.Node, n.Out, n.Err)
|
fmt.Fprintf(&buf, " %s", n.Out)
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
return fmt.Sprintf("=> %s %d Err:%v", n.Op, n.Node, n.Err)
|
|
||||||
}
|
}
|
||||||
|
if n.Err != "" {
|
||||||
|
fmt.Fprintf(&buf, " Err:%v", n.Err)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
type logMissingNode struct {
|
type logMissingNode struct {
|
||||||
|
@ -685,7 +695,7 @@ type logLinkRequestOldNodeNotFound struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *logLinkRequestOldNodeNotFound) String() string {
|
func (m *logLinkRequestOldNodeNotFound) String() string {
|
||||||
return fmt.Sprintf("In LinkRequest (request %#x), node %d not found", m.Request.Hdr().ID, m.In.OldNode)
|
return fmt.Sprintf("In LinkRequest (request %v), node %d not found", m.Request.Hdr().ID, m.In.OldNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
type renameNewDirNodeNotFound struct {
|
type renameNewDirNodeNotFound struct {
|
||||||
|
@ -694,7 +704,7 @@ type renameNewDirNodeNotFound struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *renameNewDirNodeNotFound) String() string {
|
func (m *renameNewDirNodeNotFound) String() string {
|
||||||
return fmt.Sprintf("In RenameRequest (request %#x), node %d not found", m.Request.Hdr().ID, m.In.NewDir)
|
return fmt.Sprintf("In RenameRequest (request %v), node %d not found", m.Request.Hdr().ID, m.In.NewDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
type handlerPanickedError struct {
|
type handlerPanickedError struct {
|
||||||
|
@ -717,13 +727,52 @@ func (h handlerPanickedError) Errno() fuse.Errno {
|
||||||
return fuse.DefaultErrno
|
return fuse.DefaultErrno
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handlerTerminatedError happens when a handler terminates itself
|
||||||
|
// with runtime.Goexit. This is most commonly because of incorrect use
|
||||||
|
// of testing.TB.FailNow, typically via t.Fatal.
|
||||||
|
type handlerTerminatedError struct {
|
||||||
|
Request interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = handlerTerminatedError{}
|
||||||
|
|
||||||
|
func (h handlerTerminatedError) Error() string {
|
||||||
|
return fmt.Sprintf("handler terminated (called runtime.Goexit)")
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fuse.ErrorNumber = handlerTerminatedError{}
|
||||||
|
|
||||||
|
func (h handlerTerminatedError) Errno() fuse.Errno {
|
||||||
|
return fuse.DefaultErrno
|
||||||
|
}
|
||||||
|
|
||||||
|
type handleNotReaderError struct {
|
||||||
|
handle Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = handleNotReaderError{}
|
||||||
|
|
||||||
|
func (e handleNotReaderError) Error() string {
|
||||||
|
return fmt.Sprintf("handle has no Read: %T", e.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fuse.ErrorNumber = handleNotReaderError{}
|
||||||
|
|
||||||
|
func (e handleNotReaderError) Errno() fuse.Errno {
|
||||||
|
return fuse.ENOTSUP
|
||||||
|
}
|
||||||
|
|
||||||
func initLookupResponse(s *fuse.LookupResponse) {
|
func initLookupResponse(s *fuse.LookupResponse) {
|
||||||
s.EntryValid = entryValidTime
|
s.EntryValid = entryValidTime
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Server) serve(r fuse.Request) {
|
func (c *Server) serve(r fuse.Request) {
|
||||||
ctx, cancel := context.WithCancel(c.context())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
parentCtx := ctx
|
||||||
|
if c.context != nil {
|
||||||
|
ctx = c.context(ctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
req := &serveRequest{Request: r, cancel: cancel}
|
req := &serveRequest{Request: r, cancel: cancel}
|
||||||
|
|
||||||
|
@ -800,6 +849,7 @@ func (c *Server) serve(r fuse.Request) {
|
||||||
c.meta.Unlock()
|
c.meta.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var responded bool
|
||||||
defer func() {
|
defer func() {
|
||||||
if rec := recover(); rec != nil {
|
if rec := recover(); rec != nil {
|
||||||
const size = 1 << 16
|
const size = 1 << 16
|
||||||
|
@ -813,114 +863,132 @@ func (c *Server) serve(r fuse.Request) {
|
||||||
}
|
}
|
||||||
done(err)
|
done(err)
|
||||||
r.RespondError(err)
|
r.RespondError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !responded {
|
||||||
|
err := handlerTerminatedError{
|
||||||
|
Request: r,
|
||||||
|
}
|
||||||
|
done(err)
|
||||||
|
r.RespondError(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if err := c.handleRequest(ctx, node, snode, r, done); err != nil {
|
||||||
|
if err == context.Canceled {
|
||||||
|
select {
|
||||||
|
case <-parentCtx.Done():
|
||||||
|
// We canceled the parent context because of an
|
||||||
|
// incoming interrupt request, so return EINTR
|
||||||
|
// to trigger the right behavior in the client app.
|
||||||
|
//
|
||||||
|
// Only do this when it's the parent context that was
|
||||||
|
// canceled, not a context controlled by the program
|
||||||
|
// using this library, so we don't return EINTR too
|
||||||
|
// eagerly -- it might cause busy loops.
|
||||||
|
//
|
||||||
|
// Decent write-up on role of EINTR:
|
||||||
|
// http://250bpm.com/blog:12
|
||||||
|
err = fuse.EINTR
|
||||||
|
default:
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done(err)
|
||||||
|
r.RespondError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// disarm runtime.Goexit protection
|
||||||
|
responded = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleRequest will either a) call done(s) and r.Respond(s) OR b) return an error.
|
||||||
|
func (c *Server) handleRequest(ctx context.Context, node Node, snode *serveNode, r fuse.Request, done func(resp interface{})) error {
|
||||||
switch r := r.(type) {
|
switch r := r.(type) {
|
||||||
default:
|
default:
|
||||||
// Note: To FUSE, ENOSYS means "this server never implements this request."
|
// Note: To FUSE, ENOSYS means "this server never implements this request."
|
||||||
// It would be inappropriate to return ENOSYS for other operations in this
|
// It would be inappropriate to return ENOSYS for other operations in this
|
||||||
// switch that might only be unavailable in some contexts, not all.
|
// switch that might only be unavailable in some contexts, not all.
|
||||||
done(fuse.ENOSYS)
|
return fuse.ENOSYS
|
||||||
r.RespondError(fuse.ENOSYS)
|
|
||||||
|
|
||||||
case *fuse.StatfsRequest:
|
case *fuse.StatfsRequest:
|
||||||
s := &fuse.StatfsResponse{}
|
s := &fuse.StatfsResponse{}
|
||||||
if fs, ok := c.fs.(FSStatfser); ok {
|
if fs, ok := c.fs.(FSStatfser); ok {
|
||||||
if err := fs.Statfs(ctx, r, s); err != nil {
|
if err := fs.Statfs(ctx, r, s); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
// Node operations.
|
// Node operations.
|
||||||
case *fuse.GetattrRequest:
|
case *fuse.GetattrRequest:
|
||||||
s := &fuse.GetattrResponse{}
|
s := &fuse.GetattrResponse{}
|
||||||
if n, ok := node.(NodeGetattrer); ok {
|
if n, ok := node.(NodeGetattrer); ok {
|
||||||
if err := n.Getattr(ctx, r, s); err != nil {
|
if err := n.Getattr(ctx, r, s); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := snode.attr(ctx, &s.Attr); err != nil {
|
if err := snode.attr(ctx, &s.Attr); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.SetattrRequest:
|
case *fuse.SetattrRequest:
|
||||||
s := &fuse.SetattrResponse{}
|
s := &fuse.SetattrResponse{}
|
||||||
if n, ok := node.(NodeSetattrer); ok {
|
if n, ok := node.(NodeSetattrer); ok {
|
||||||
if err := n.Setattr(ctx, r, s); err != nil {
|
if err := n.Setattr(ctx, r, s); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(s)
|
|
||||||
r.Respond(s)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := snode.attr(ctx, &s.Attr); err != nil {
|
if err := snode.attr(ctx, &s.Attr); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.SymlinkRequest:
|
case *fuse.SymlinkRequest:
|
||||||
s := &fuse.SymlinkResponse{}
|
s := &fuse.SymlinkResponse{}
|
||||||
initLookupResponse(&s.LookupResponse)
|
initLookupResponse(&s.LookupResponse)
|
||||||
n, ok := node.(NodeSymlinker)
|
n, ok := node.(NodeSymlinker)
|
||||||
if !ok {
|
if !ok {
|
||||||
done(fuse.EIO) // XXX or EPERM like Mkdir?
|
return fuse.EIO // XXX or EPERM like Mkdir?
|
||||||
r.RespondError(fuse.EIO)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
n2, err := n.Symlink(ctx, r)
|
n2, err := n.Symlink(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.NewName, n2); err != nil {
|
if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.NewName, n2); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.ReadlinkRequest:
|
case *fuse.ReadlinkRequest:
|
||||||
n, ok := node.(NodeReadlinker)
|
n, ok := node.(NodeReadlinker)
|
||||||
if !ok {
|
if !ok {
|
||||||
done(fuse.EIO) /// XXX or EPERM?
|
return fuse.EIO /// XXX or EPERM?
|
||||||
r.RespondError(fuse.EIO)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
target, err := n.Readlink(ctx, r)
|
target, err := n.Readlink(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(target)
|
done(target)
|
||||||
r.Respond(target)
|
r.Respond(target)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.LinkRequest:
|
case *fuse.LinkRequest:
|
||||||
n, ok := node.(NodeLinker)
|
n, ok := node.(NodeLinker)
|
||||||
if !ok {
|
if !ok {
|
||||||
done(fuse.EIO) /// XXX or EPERM?
|
return fuse.EIO /// XXX or EPERM?
|
||||||
r.RespondError(fuse.EIO)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
c.meta.Lock()
|
c.meta.Lock()
|
||||||
var oldNode *serveNode
|
var oldNode *serveNode
|
||||||
|
@ -933,52 +1001,43 @@ func (c *Server) serve(r fuse.Request) {
|
||||||
Request: r.Hdr(),
|
Request: r.Hdr(),
|
||||||
In: r,
|
In: r,
|
||||||
})
|
})
|
||||||
done(fuse.EIO)
|
return fuse.EIO
|
||||||
r.RespondError(fuse.EIO)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
n2, err := n.Link(ctx, r, oldNode.node)
|
n2, err := n.Link(ctx, r, oldNode.node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
s := &fuse.LookupResponse{}
|
s := &fuse.LookupResponse{}
|
||||||
initLookupResponse(s)
|
initLookupResponse(s)
|
||||||
if err := c.saveLookup(ctx, s, snode, r.NewName, n2); err != nil {
|
if err := c.saveLookup(ctx, s, snode, r.NewName, n2); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.RemoveRequest:
|
case *fuse.RemoveRequest:
|
||||||
n, ok := node.(NodeRemover)
|
n, ok := node.(NodeRemover)
|
||||||
if !ok {
|
if !ok {
|
||||||
done(fuse.EIO) /// XXX or EPERM?
|
return fuse.EIO /// XXX or EPERM?
|
||||||
r.RespondError(fuse.EIO)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
err := n.Remove(ctx, r)
|
err := n.Remove(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(nil)
|
done(nil)
|
||||||
r.Respond()
|
r.Respond()
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.AccessRequest:
|
case *fuse.AccessRequest:
|
||||||
if n, ok := node.(NodeAccesser); ok {
|
if n, ok := node.(NodeAccesser); ok {
|
||||||
if err := n.Access(ctx, r); err != nil {
|
if err := n.Access(ctx, r); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done(nil)
|
done(nil)
|
||||||
r.Respond()
|
r.Respond()
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.LookupRequest:
|
case *fuse.LookupRequest:
|
||||||
var n2 Node
|
var n2 Node
|
||||||
|
@ -990,45 +1049,35 @@ func (c *Server) serve(r fuse.Request) {
|
||||||
} else if n, ok := node.(NodeRequestLookuper); ok {
|
} else if n, ok := node.(NodeRequestLookuper); ok {
|
||||||
n2, err = n.Lookup(ctx, r, s)
|
n2, err = n.Lookup(ctx, r, s)
|
||||||
} else {
|
} else {
|
||||||
done(fuse.ENOENT)
|
return fuse.ENOENT
|
||||||
r.RespondError(fuse.ENOENT)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if err := c.saveLookup(ctx, s, snode, r.Name, n2); err != nil {
|
if err := c.saveLookup(ctx, s, snode, r.Name, n2); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.MkdirRequest:
|
case *fuse.MkdirRequest:
|
||||||
s := &fuse.MkdirResponse{}
|
s := &fuse.MkdirResponse{}
|
||||||
initLookupResponse(&s.LookupResponse)
|
initLookupResponse(&s.LookupResponse)
|
||||||
n, ok := node.(NodeMkdirer)
|
n, ok := node.(NodeMkdirer)
|
||||||
if !ok {
|
if !ok {
|
||||||
done(fuse.EPERM)
|
return fuse.EPERM
|
||||||
r.RespondError(fuse.EPERM)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
n2, err := n.Mkdir(ctx, r)
|
n2, err := n.Mkdir(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.Name, n2); err != nil {
|
if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.Name, n2); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.OpenRequest:
|
case *fuse.OpenRequest:
|
||||||
s := &fuse.OpenResponse{}
|
s := &fuse.OpenResponse{}
|
||||||
|
@ -1036,121 +1085,99 @@ func (c *Server) serve(r fuse.Request) {
|
||||||
if n, ok := node.(NodeOpener); ok {
|
if n, ok := node.(NodeOpener); ok {
|
||||||
hh, err := n.Open(ctx, r, s)
|
hh, err := n.Open(ctx, r, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
h2 = hh
|
h2 = hh
|
||||||
} else {
|
} else {
|
||||||
h2 = node
|
h2 = node
|
||||||
}
|
}
|
||||||
s.Handle = c.saveHandle(h2, hdr.Node)
|
s.Handle = c.saveHandle(h2, r.Hdr().Node)
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.CreateRequest:
|
case *fuse.CreateRequest:
|
||||||
n, ok := node.(NodeCreater)
|
n, ok := node.(NodeCreater)
|
||||||
if !ok {
|
if !ok {
|
||||||
// If we send back ENOSYS, FUSE will try mknod+open.
|
// If we send back ENOSYS, FUSE will try mknod+open.
|
||||||
done(fuse.EPERM)
|
return fuse.EPERM
|
||||||
r.RespondError(fuse.EPERM)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
s := &fuse.CreateResponse{OpenResponse: fuse.OpenResponse{}}
|
s := &fuse.CreateResponse{OpenResponse: fuse.OpenResponse{}}
|
||||||
initLookupResponse(&s.LookupResponse)
|
initLookupResponse(&s.LookupResponse)
|
||||||
n2, h2, err := n.Create(ctx, r, s)
|
n2, h2, err := n.Create(ctx, r, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.Name, n2); err != nil {
|
if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.Name, n2); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
s.Handle = c.saveHandle(h2, hdr.Node)
|
s.Handle = c.saveHandle(h2, r.Hdr().Node)
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.GetxattrRequest:
|
case *fuse.GetxattrRequest:
|
||||||
n, ok := node.(NodeGetxattrer)
|
n, ok := node.(NodeGetxattrer)
|
||||||
if !ok {
|
if !ok {
|
||||||
done(fuse.ENOTSUP)
|
return fuse.ENOTSUP
|
||||||
r.RespondError(fuse.ENOTSUP)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
s := &fuse.GetxattrResponse{}
|
s := &fuse.GetxattrResponse{}
|
||||||
err := n.Getxattr(ctx, r, s)
|
err := n.Getxattr(ctx, r, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if r.Size != 0 && uint64(len(s.Xattr)) > uint64(r.Size) {
|
if r.Size != 0 && uint64(len(s.Xattr)) > uint64(r.Size) {
|
||||||
done(fuse.ERANGE)
|
return fuse.ERANGE
|
||||||
r.RespondError(fuse.ERANGE)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.ListxattrRequest:
|
case *fuse.ListxattrRequest:
|
||||||
n, ok := node.(NodeListxattrer)
|
n, ok := node.(NodeListxattrer)
|
||||||
if !ok {
|
if !ok {
|
||||||
done(fuse.ENOTSUP)
|
return fuse.ENOTSUP
|
||||||
r.RespondError(fuse.ENOTSUP)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
s := &fuse.ListxattrResponse{}
|
s := &fuse.ListxattrResponse{}
|
||||||
err := n.Listxattr(ctx, r, s)
|
err := n.Listxattr(ctx, r, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if r.Size != 0 && uint64(len(s.Xattr)) > uint64(r.Size) {
|
if r.Size != 0 && uint64(len(s.Xattr)) > uint64(r.Size) {
|
||||||
done(fuse.ERANGE)
|
return fuse.ERANGE
|
||||||
r.RespondError(fuse.ERANGE)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.SetxattrRequest:
|
case *fuse.SetxattrRequest:
|
||||||
n, ok := node.(NodeSetxattrer)
|
n, ok := node.(NodeSetxattrer)
|
||||||
if !ok {
|
if !ok {
|
||||||
done(fuse.ENOTSUP)
|
return fuse.ENOTSUP
|
||||||
r.RespondError(fuse.ENOTSUP)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
err := n.Setxattr(ctx, r)
|
err := n.Setxattr(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(nil)
|
done(nil)
|
||||||
r.Respond()
|
r.Respond()
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.RemovexattrRequest:
|
case *fuse.RemovexattrRequest:
|
||||||
n, ok := node.(NodeRemovexattrer)
|
n, ok := node.(NodeRemovexattrer)
|
||||||
if !ok {
|
if !ok {
|
||||||
done(fuse.ENOTSUP)
|
return fuse.ENOTSUP
|
||||||
r.RespondError(fuse.ENOTSUP)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
err := n.Removexattr(ctx, r)
|
err := n.Removexattr(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(nil)
|
done(nil)
|
||||||
r.Respond()
|
r.Respond()
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.ForgetRequest:
|
case *fuse.ForgetRequest:
|
||||||
forget := c.dropNode(hdr.Node, r.N)
|
forget := c.dropNode(r.Hdr().Node, r.N)
|
||||||
if forget {
|
if forget {
|
||||||
n, ok := node.(NodeForgetter)
|
n, ok := node.(NodeForgetter)
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -1159,26 +1186,29 @@ func (c *Server) serve(r fuse.Request) {
|
||||||
}
|
}
|
||||||
done(nil)
|
done(nil)
|
||||||
r.Respond()
|
r.Respond()
|
||||||
|
return nil
|
||||||
|
|
||||||
// Handle operations.
|
// Handle operations.
|
||||||
case *fuse.ReadRequest:
|
case *fuse.ReadRequest:
|
||||||
shandle := c.getHandle(r.Handle)
|
shandle := c.getHandle(r.Handle)
|
||||||
if shandle == nil {
|
if shandle == nil {
|
||||||
done(fuse.ESTALE)
|
return fuse.ESTALE
|
||||||
r.RespondError(fuse.ESTALE)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
handle := shandle.handle
|
handle := shandle.handle
|
||||||
|
|
||||||
s := &fuse.ReadResponse{Data: make([]byte, 0, r.Size)}
|
s := &fuse.ReadResponse{Data: make([]byte, 0, r.Size)}
|
||||||
if r.Dir {
|
if r.Dir {
|
||||||
if h, ok := handle.(HandleReadDirAller); ok {
|
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 {
|
if shandle.readData == nil {
|
||||||
dirs, err := h.ReadDirAll(ctx)
|
dirs, err := h.ReadDirAll(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
var data []byte
|
var data []byte
|
||||||
for _, dir := range dirs {
|
for _, dir := range dirs {
|
||||||
|
@ -1192,16 +1222,14 @@ func (c *Server) serve(r fuse.Request) {
|
||||||
fuseutil.HandleRead(r, s, shandle.readData)
|
fuseutil.HandleRead(r, s, shandle.readData)
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
break
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if h, ok := handle.(HandleReadAller); ok {
|
if h, ok := handle.(HandleReadAller); ok {
|
||||||
if shandle.readData == nil {
|
if shandle.readData == nil {
|
||||||
data, err := h.ReadAll(ctx)
|
data, err := h.ReadAll(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if data == nil {
|
if data == nil {
|
||||||
data = []byte{}
|
data = []byte{}
|
||||||
|
@ -1211,71 +1239,58 @@ func (c *Server) serve(r fuse.Request) {
|
||||||
fuseutil.HandleRead(r, s, shandle.readData)
|
fuseutil.HandleRead(r, s, shandle.readData)
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
break
|
return nil
|
||||||
}
|
}
|
||||||
h, ok := handle.(HandleReader)
|
h, ok := handle.(HandleReader)
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Printf("NO READ FOR %T\n", handle)
|
err := handleNotReaderError{handle: handle}
|
||||||
done(fuse.EIO)
|
return err
|
||||||
r.RespondError(fuse.EIO)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if err := h.Read(ctx, r, s); err != nil {
|
if err := h.Read(ctx, r, s); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.WriteRequest:
|
case *fuse.WriteRequest:
|
||||||
shandle := c.getHandle(r.Handle)
|
shandle := c.getHandle(r.Handle)
|
||||||
if shandle == nil {
|
if shandle == nil {
|
||||||
done(fuse.ESTALE)
|
return fuse.ESTALE
|
||||||
r.RespondError(fuse.ESTALE)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s := &fuse.WriteResponse{}
|
s := &fuse.WriteResponse{}
|
||||||
if h, ok := shandle.handle.(HandleWriter); ok {
|
if h, ok := shandle.handle.(HandleWriter); ok {
|
||||||
if err := h.Write(ctx, r, s); err != nil {
|
if err := h.Write(ctx, r, s); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
break
|
return nil
|
||||||
}
|
}
|
||||||
done(fuse.EIO)
|
return fuse.EIO
|
||||||
r.RespondError(fuse.EIO)
|
|
||||||
|
|
||||||
case *fuse.FlushRequest:
|
case *fuse.FlushRequest:
|
||||||
shandle := c.getHandle(r.Handle)
|
shandle := c.getHandle(r.Handle)
|
||||||
if shandle == nil {
|
if shandle == nil {
|
||||||
done(fuse.ESTALE)
|
return fuse.ESTALE
|
||||||
r.RespondError(fuse.ESTALE)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
handle := shandle.handle
|
handle := shandle.handle
|
||||||
|
|
||||||
if h, ok := handle.(HandleFlusher); ok {
|
if h, ok := handle.(HandleFlusher); ok {
|
||||||
if err := h.Flush(ctx, r); err != nil {
|
if err := h.Flush(ctx, r); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done(nil)
|
done(nil)
|
||||||
r.Respond()
|
r.Respond()
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.ReleaseRequest:
|
case *fuse.ReleaseRequest:
|
||||||
shandle := c.getHandle(r.Handle)
|
shandle := c.getHandle(r.Handle)
|
||||||
if shandle == nil {
|
if shandle == nil {
|
||||||
done(fuse.ESTALE)
|
return fuse.ESTALE
|
||||||
r.RespondError(fuse.ESTALE)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
handle := shandle.handle
|
handle := shandle.handle
|
||||||
|
|
||||||
|
@ -1284,13 +1299,12 @@ func (c *Server) serve(r fuse.Request) {
|
||||||
|
|
||||||
if h, ok := handle.(HandleReleaser); ok {
|
if h, ok := handle.(HandleReleaser); ok {
|
||||||
if err := h.Release(ctx, r); err != nil {
|
if err := h.Release(ctx, r); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done(nil)
|
done(nil)
|
||||||
r.Respond()
|
r.Respond()
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.DestroyRequest:
|
case *fuse.DestroyRequest:
|
||||||
if fs, ok := c.fs.(FSDestroyer); ok {
|
if fs, ok := c.fs.(FSDestroyer); ok {
|
||||||
|
@ -1298,6 +1312,7 @@ func (c *Server) serve(r fuse.Request) {
|
||||||
}
|
}
|
||||||
done(nil)
|
done(nil)
|
||||||
r.Respond()
|
r.Respond()
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.RenameRequest:
|
case *fuse.RenameRequest:
|
||||||
c.meta.Lock()
|
c.meta.Lock()
|
||||||
|
@ -1311,63 +1326,50 @@ func (c *Server) serve(r fuse.Request) {
|
||||||
Request: r.Hdr(),
|
Request: r.Hdr(),
|
||||||
In: r,
|
In: r,
|
||||||
})
|
})
|
||||||
done(fuse.EIO)
|
return fuse.EIO
|
||||||
r.RespondError(fuse.EIO)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
n, ok := node.(NodeRenamer)
|
n, ok := node.(NodeRenamer)
|
||||||
if !ok {
|
if !ok {
|
||||||
done(fuse.EIO) // XXX or EPERM like Mkdir?
|
return fuse.EIO // XXX or EPERM like Mkdir?
|
||||||
r.RespondError(fuse.EIO)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
err := n.Rename(ctx, r, newDirNode.node)
|
err := n.Rename(ctx, r, newDirNode.node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(nil)
|
done(nil)
|
||||||
r.Respond()
|
r.Respond()
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.MknodRequest:
|
case *fuse.MknodRequest:
|
||||||
n, ok := node.(NodeMknoder)
|
n, ok := node.(NodeMknoder)
|
||||||
if !ok {
|
if !ok {
|
||||||
done(fuse.EIO)
|
return fuse.EIO
|
||||||
r.RespondError(fuse.EIO)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
n2, err := n.Mknod(ctx, r)
|
n2, err := n.Mknod(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
s := &fuse.LookupResponse{}
|
s := &fuse.LookupResponse{}
|
||||||
initLookupResponse(s)
|
initLookupResponse(s)
|
||||||
if err := c.saveLookup(ctx, s, snode, r.Name, n2); err != nil {
|
if err := c.saveLookup(ctx, s, snode, r.Name, n2); err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(s)
|
done(s)
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.FsyncRequest:
|
case *fuse.FsyncRequest:
|
||||||
n, ok := node.(NodeFsyncer)
|
n, ok := node.(NodeFsyncer)
|
||||||
if !ok {
|
if !ok {
|
||||||
done(fuse.EIO)
|
return fuse.EIO
|
||||||
r.RespondError(fuse.EIO)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
err := n.Fsync(ctx, r)
|
err := n.Fsync(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
done(err)
|
return err
|
||||||
r.RespondError(err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
done(nil)
|
done(nil)
|
||||||
r.Respond()
|
r.Respond()
|
||||||
|
return nil
|
||||||
|
|
||||||
case *fuse.InterruptRequest:
|
case *fuse.InterruptRequest:
|
||||||
c.meta.Lock()
|
c.meta.Lock()
|
||||||
|
@ -1379,24 +1381,23 @@ func (c *Server) serve(r fuse.Request) {
|
||||||
c.meta.Unlock()
|
c.meta.Unlock()
|
||||||
done(nil)
|
done(nil)
|
||||||
r.Respond()
|
r.Respond()
|
||||||
|
return nil
|
||||||
|
|
||||||
/* case *FsyncdirRequest:
|
/* case *FsyncdirRequest:
|
||||||
done(ENOSYS)
|
return ENOSYS
|
||||||
r.RespondError(ENOSYS)
|
|
||||||
|
|
||||||
case *GetlkRequest, *SetlkRequest, *SetlkwRequest:
|
case *GetlkRequest, *SetlkRequest, *SetlkwRequest:
|
||||||
done(ENOSYS)
|
return ENOSYS
|
||||||
r.RespondError(ENOSYS)
|
|
||||||
|
|
||||||
case *BmapRequest:
|
case *BmapRequest:
|
||||||
done(ENOSYS)
|
return ENOSYS
|
||||||
r.RespondError(ENOSYS)
|
|
||||||
|
|
||||||
case *SetvolnameRequest, *GetxtimesRequest, *ExchangeRequest:
|
case *SetvolnameRequest, *GetxtimesRequest, *ExchangeRequest:
|
||||||
done(ENOSYS)
|
return ENOSYS
|
||||||
r.RespondError(ENOSYS)
|
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
panic("not reached")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Server) saveLookup(ctx context.Context, s *fuse.LookupResponse, snode *serveNode, elem string, n2 Node) error {
|
func (c *Server) saveLookup(ctx context.Context, s *fuse.LookupResponse, snode *serveNode, elem string, n2 Node) error {
|
||||||
|
|
30
vendor/src/bazil.org/fuse/fs/serve_darwin_test.go
vendored
Normal file
30
vendor/src/bazil.org/fuse/fs/serve_darwin_test.go
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package fs_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"bazil.org/fuse/fs/fstestutil"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
type exchangeData struct {
|
||||||
|
fstestutil.File
|
||||||
|
// this struct cannot be zero size or multiple instances may look identical
|
||||||
|
_ int
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExchangeDataNotSupported(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{&fstestutil.ChildMap{
|
||||||
|
"one": &exchangeData{},
|
||||||
|
"two": &exchangeData{},
|
||||||
|
}}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer mnt.Close()
|
||||||
|
|
||||||
|
if err := unix.Exchangedata(mnt.Dir+"/one", mnt.Dir+"/two", 0); err != unix.ENOTSUP {
|
||||||
|
t.Fatalf("expected ENOTSUP from exchangedata: %v", err)
|
||||||
|
}
|
||||||
|
}
|
590
vendor/src/bazil.org/fuse/fs/serve_test.go
vendored
590
vendor/src/bazil.org/fuse/fs/serve_test.go
vendored
|
@ -7,10 +7,11 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -56,6 +57,25 @@ func (f fifo) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMountpointDoesNotExist(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
tmp, err := ioutil.TempDir("", "fusetest")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tmp)
|
||||||
|
|
||||||
|
mountpoint := path.Join(tmp, "does-not-exist")
|
||||||
|
conn, err := fuse.Mount(mountpoint)
|
||||||
|
if err == nil {
|
||||||
|
conn.Close()
|
||||||
|
t.Fatalf("expected error with non-existent mountpoint")
|
||||||
|
}
|
||||||
|
if _, ok := err.(*fuse.MountpointDoesNotExistError); !ok {
|
||||||
|
t.Fatalf("wrong error from mount: %T: %v", err, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type badRootFS struct{}
|
type badRootFS struct{}
|
||||||
|
|
||||||
func (badRootFS) Root() (fs.Node, error) {
|
func (badRootFS) Root() (fs.Node, error) {
|
||||||
|
@ -277,12 +297,18 @@ func (readAll) ReadAll(ctx context.Context) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testReadAll(t *testing.T, path string) {
|
func testReadAll(t *testing.T, path string) {
|
||||||
data, err := ioutil.ReadFile(path)
|
f, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("readAll: %v", err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if string(data) != hi {
|
defer f.Close()
|
||||||
t.Errorf("readAll = %q, want %q", data, hi)
|
data := make([]byte, 4096)
|
||||||
|
n, err := f.Read(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if g, e := string(data[:n]), hi; g != e {
|
||||||
|
t.Errorf("readAll = %q, want %q", g, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,6 +392,15 @@ func TestReadFileFlags(t *testing.T) {
|
||||||
_ = f.Close()
|
_ = f.Close()
|
||||||
|
|
||||||
want := fuse.OpenReadWrite | fuse.OpenAppend
|
want := fuse.OpenReadWrite | fuse.OpenAppend
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
// OSXFUSE shares one read and one write handle for all
|
||||||
|
// clients, so it uses a OpenReadOnly handle for performing
|
||||||
|
// our read.
|
||||||
|
//
|
||||||
|
// If this test starts failing in the future, that probably
|
||||||
|
// means they added the feature, and we want to notice that!
|
||||||
|
want = fuse.OpenReadOnly
|
||||||
|
}
|
||||||
if g, e := r.fileFlags.Recorded().(fuse.OpenFlags), want; g != e {
|
if g, e := r.fileFlags.Recorded().(fuse.OpenFlags), want; g != e {
|
||||||
t.Errorf("read saw file flags %+v, want %+v", g, e)
|
t.Errorf("read saw file flags %+v, want %+v", g, e)
|
||||||
}
|
}
|
||||||
|
@ -382,6 +417,13 @@ func (r *writeFlags) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *writeFlags) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
|
||||||
|
// OSXFUSE 3.0.4 does a read-modify-write cycle even when the
|
||||||
|
// write was for 4096 bytes.
|
||||||
|
fuseutil.HandleRead(req, resp, []byte(hi))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *writeFlags) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
|
func (r *writeFlags) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
|
||||||
r.fileFlags.Record(req.FileFlags)
|
r.fileFlags.Record(req.FileFlags)
|
||||||
resp.Size = len(req.Data)
|
resp.Size = len(req.Data)
|
||||||
|
@ -412,6 +454,15 @@ func TestWriteFileFlags(t *testing.T) {
|
||||||
_ = f.Close()
|
_ = f.Close()
|
||||||
|
|
||||||
want := fuse.OpenReadWrite | fuse.OpenAppend
|
want := fuse.OpenReadWrite | fuse.OpenAppend
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
// OSXFUSE shares one read and one write handle for all
|
||||||
|
// clients, so it uses a OpenWriteOnly handle for performing
|
||||||
|
// our read.
|
||||||
|
//
|
||||||
|
// If this test starts failing in the future, that probably
|
||||||
|
// means they added the feature, and we want to notice that!
|
||||||
|
want = fuse.OpenWriteOnly
|
||||||
|
}
|
||||||
if g, e := r.fileFlags.Recorded().(fuse.OpenFlags), want; g != e {
|
if g, e := r.fileFlags.Recorded().(fuse.OpenFlags), want; g != e {
|
||||||
t.Errorf("write saw file flags %+v, want %+v", g, e)
|
t.Errorf("write saw file flags %+v, want %+v", g, e)
|
||||||
}
|
}
|
||||||
|
@ -582,7 +633,6 @@ func (f *mkdir1) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, er
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMkdir(t *testing.T) {
|
func TestMkdir(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
f := &mkdir1{}
|
f := &mkdir1{}
|
||||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f}, nil)
|
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -601,6 +651,10 @@ func TestMkdir(t *testing.T) {
|
||||||
if mnt.Conn.Protocol().HasUmask() {
|
if mnt.Conn.Protocol().HasUmask() {
|
||||||
want.Umask = 0022
|
want.Umask = 0022
|
||||||
}
|
}
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
// https://github.com/osxfuse/osxfuse/issues/225
|
||||||
|
want.Umask = 0
|
||||||
|
}
|
||||||
if g, e := f.RecordedMkdir(), want; g != e {
|
if g, e := f.RecordedMkdir(), want; g != e {
|
||||||
t.Errorf("mkdir saw %+v, want %+v", g, e)
|
t.Errorf("mkdir saw %+v, want %+v", g, e)
|
||||||
}
|
}
|
||||||
|
@ -610,6 +664,7 @@ func TestMkdir(t *testing.T) {
|
||||||
|
|
||||||
type create1file struct {
|
type create1file struct {
|
||||||
fstestutil.File
|
fstestutil.File
|
||||||
|
record.Creates
|
||||||
record.Fsyncs
|
record.Fsyncs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,31 +678,12 @@ func (f *create1) Create(ctx context.Context, req *fuse.CreateRequest, resp *fus
|
||||||
log.Printf("ERROR create1.Create unexpected name: %q\n", req.Name)
|
log.Printf("ERROR create1.Create unexpected name: %q\n", req.Name)
|
||||||
return nil, nil, fuse.EPERM
|
return nil, nil, fuse.EPERM
|
||||||
}
|
}
|
||||||
flags := req.Flags
|
|
||||||
|
|
||||||
// OS X does not pass O_TRUNC here, Linux does; as this is a
|
_, _, _ = f.f.Creates.Create(ctx, req, resp)
|
||||||
// Create, that's acceptable
|
|
||||||
flags &^= fuse.OpenTruncate
|
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
|
||||||
// Linux <3.7 accidentally leaks O_CLOEXEC through to FUSE;
|
|
||||||
// avoid spurious test failures
|
|
||||||
flags &^= fuse.OpenFlags(syscall.O_CLOEXEC)
|
|
||||||
}
|
|
||||||
|
|
||||||
if g, e := flags, fuse.OpenReadWrite|fuse.OpenCreate; g != e {
|
|
||||||
log.Printf("ERROR create1.Create unexpected flags: %v != %v\n", g, e)
|
|
||||||
return nil, nil, fuse.EPERM
|
|
||||||
}
|
|
||||||
if g, e := req.Mode, os.FileMode(0644); g != e {
|
|
||||||
log.Printf("ERROR create1.Create unexpected mode: %v != %v\n", g, e)
|
|
||||||
return nil, nil, fuse.EPERM
|
|
||||||
}
|
|
||||||
return &f.f, &f.f, nil
|
return &f.f, &f.f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreate(t *testing.T) {
|
func TestCreate(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
f := &create1{}
|
f := &create1{}
|
||||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f}, nil)
|
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -658,12 +694,38 @@ func TestCreate(t *testing.T) {
|
||||||
// uniform umask needed to make os.Create's 0666 into something
|
// uniform umask needed to make os.Create's 0666 into something
|
||||||
// reproducible
|
// reproducible
|
||||||
defer syscall.Umask(syscall.Umask(0022))
|
defer syscall.Umask(syscall.Umask(0022))
|
||||||
ff, err := os.Create(mnt.Dir + "/foo")
|
ff, err := os.OpenFile(mnt.Dir+"/foo", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0640)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("create1 WriteFile: %v", err)
|
t.Fatalf("create1 WriteFile: %v", err)
|
||||||
}
|
}
|
||||||
defer ff.Close()
|
defer ff.Close()
|
||||||
|
|
||||||
|
want := fuse.CreateRequest{
|
||||||
|
Name: "foo",
|
||||||
|
Flags: fuse.OpenReadWrite | fuse.OpenCreate | fuse.OpenTruncate,
|
||||||
|
Mode: 0640,
|
||||||
|
}
|
||||||
|
if mnt.Conn.Protocol().HasUmask() {
|
||||||
|
want.Umask = 0022
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
// OS X does not pass O_TRUNC here, Linux does; as this is a
|
||||||
|
// Create, that's acceptable
|
||||||
|
want.Flags &^= fuse.OpenTruncate
|
||||||
|
|
||||||
|
// https://github.com/osxfuse/osxfuse/issues/225
|
||||||
|
want.Umask = 0
|
||||||
|
}
|
||||||
|
got := f.f.RecordedCreate()
|
||||||
|
if runtime.GOOS == "linux" {
|
||||||
|
// Linux <3.7 accidentally leaks O_CLOEXEC through to FUSE;
|
||||||
|
// avoid spurious test failures
|
||||||
|
got.Flags &^= fuse.OpenFlags(syscall.O_CLOEXEC)
|
||||||
|
}
|
||||||
|
if g, e := got, want; g != e {
|
||||||
|
t.Fatalf("create saw %+v, want %+v", g, e)
|
||||||
|
}
|
||||||
|
|
||||||
err = syscall.Fsync(int(ff.Fd()))
|
err = syscall.Fsync(int(ff.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Fsync = %v", err)
|
t.Fatalf("Fsync = %v", err)
|
||||||
|
@ -895,7 +957,6 @@ func (f *mknod1) Mknod(ctx context.Context, r *fuse.MknodRequest) (fs.Node, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMknod(t *testing.T) {
|
func TestMknod(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
if os.Getuid() != 0 {
|
if os.Getuid() != 0 {
|
||||||
t.Skip("skipping unless root")
|
t.Skip("skipping unless root")
|
||||||
}
|
}
|
||||||
|
@ -907,15 +968,15 @@ func TestMknod(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer mnt.Close()
|
defer mnt.Close()
|
||||||
|
|
||||||
defer syscall.Umask(syscall.Umask(0))
|
defer syscall.Umask(syscall.Umask(0022))
|
||||||
err = syscall.Mknod(mnt.Dir+"/node", syscall.S_IFIFO|0666, 123)
|
err = syscall.Mknod(mnt.Dir+"/node", syscall.S_IFIFO|0660, 123)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Mknod: %v", err)
|
t.Fatalf("mknod: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
want := fuse.MknodRequest{
|
want := fuse.MknodRequest{
|
||||||
Name: "node",
|
Name: "node",
|
||||||
Mode: os.FileMode(os.ModeNamedPipe | 0666),
|
Mode: os.FileMode(os.ModeNamedPipe | 0640),
|
||||||
Rdev: uint32(123),
|
Rdev: uint32(123),
|
||||||
}
|
}
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
|
@ -924,6 +985,13 @@ func TestMknod(t *testing.T) {
|
||||||
// bit is portable.)
|
// bit is portable.)
|
||||||
want.Rdev = 0
|
want.Rdev = 0
|
||||||
}
|
}
|
||||||
|
if mnt.Conn.Protocol().HasUmask() {
|
||||||
|
want.Umask = 0022
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
// https://github.com/osxfuse/osxfuse/issues/225
|
||||||
|
want.Umask = 0
|
||||||
|
}
|
||||||
if g, e := f.RecordedMknod(), want; g != e {
|
if g, e := f.RecordedMknod(), want; g != e {
|
||||||
t.Fatalf("mknod saw %+v, want %+v", g, e)
|
t.Fatalf("mknod saw %+v, want %+v", g, e)
|
||||||
}
|
}
|
||||||
|
@ -985,7 +1053,38 @@ func (it *interrupt) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
return fuse.EINTR
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func helperInterrupt() {
|
||||||
|
log.SetPrefix("interrupt child: ")
|
||||||
|
log.SetFlags(0)
|
||||||
|
|
||||||
|
log.Printf("starting...")
|
||||||
|
|
||||||
|
f, err := os.Open("child")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("cannot open file: %v", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
log.Printf("reading...")
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
n, err := syscall.Read(int(f.Fd()), buf)
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
log.Fatalf("read: expected error, got data: %q", buf[:n])
|
||||||
|
case syscall.EINTR:
|
||||||
|
log.Printf("read: saw EINTR, all good")
|
||||||
|
default:
|
||||||
|
log.Fatalf("read: wrong error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("exiting...")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
childHelpers["interrupt"] = helperInterrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInterrupt(t *testing.T) {
|
func TestInterrupt(t *testing.T) {
|
||||||
|
@ -999,46 +1098,71 @@ func TestInterrupt(t *testing.T) {
|
||||||
defer mnt.Close()
|
defer mnt.Close()
|
||||||
|
|
||||||
// start a subprocess that can hang until signaled
|
// start a subprocess that can hang until signaled
|
||||||
cmd := exec.Command("cat", mnt.Dir+"/child")
|
child, err := childCmd("interrupt")
|
||||||
|
|
||||||
err = cmd.Start()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("interrupt: cannot start cat: %v", err)
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
child.Dir = mnt.Dir
|
||||||
|
|
||||||
|
if err := child.Start(); err != nil {
|
||||||
|
t.Errorf("cannot start child: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to clean up if child is still alive when returning
|
// try to clean up if child is still alive when returning
|
||||||
defer cmd.Process.Kill()
|
defer child.Process.Kill()
|
||||||
|
|
||||||
// wait till we're sure it's hanging in read
|
// wait till we're sure it's hanging in read
|
||||||
<-f.hanging
|
<-f.hanging
|
||||||
|
|
||||||
err = cmd.Process.Signal(os.Interrupt)
|
// err = child.Process.Signal(os.Interrupt)
|
||||||
|
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 {
|
if err != nil {
|
||||||
t.Errorf("interrupt: cannot interrupt cat: %v", err)
|
t.Errorf("cannot interrupt child: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := cmd.Process.Wait()
|
p, err := child.Process.Wait()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("interrupt: cat bork: %v", err)
|
t.Errorf("child failed: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch ws := p.Sys().(type) {
|
switch ws := p.Sys().(type) {
|
||||||
case syscall.WaitStatus:
|
case syscall.WaitStatus:
|
||||||
if ws.CoreDump() {
|
if ws.CoreDump() {
|
||||||
t.Errorf("interrupt: didn't expect cat to dump core: %v", ws)
|
t.Fatalf("interrupt: didn't expect child to dump core: %v", ws)
|
||||||
}
|
}
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
// see comment above about EINTR on OS X
|
||||||
if ws.Exited() {
|
if ws.Exited() {
|
||||||
t.Errorf("interrupt: didn't expect cat to exit normally: %v", ws)
|
t.Fatalf("interrupt: expected child to die from signal, got exit status: %v", ws.ExitStatus())
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ws.Signaled() {
|
if !ws.Signaled() {
|
||||||
t.Errorf("interrupt: expected cat to get a signal: %v", ws)
|
t.Fatalf("interrupt: expected child to die from signal: %v", ws)
|
||||||
} else {
|
}
|
||||||
if ws.Signal() != os.Interrupt {
|
if got := ws.Signal(); got != sig {
|
||||||
t.Errorf("interrupt: cat got wrong signal: %v", ws)
|
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:
|
default:
|
||||||
|
@ -1046,6 +1170,51 @@ func TestInterrupt(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test deadline
|
||||||
|
|
||||||
|
type deadline struct {
|
||||||
|
fstestutil.File
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fs.NodeOpener = (*deadline)(nil)
|
||||||
|
|
||||||
|
func (it *deadline) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
|
||||||
|
<-ctx.Done()
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeadline(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
child := &deadline{}
|
||||||
|
config := &fs.Config{
|
||||||
|
WithContext: func(ctx context.Context, req fuse.Request) context.Context {
|
||||||
|
// return a context that has already deadlined
|
||||||
|
|
||||||
|
// Server.serve will cancel the parent context, which will
|
||||||
|
// cancel this one, so discarding cancel here should be
|
||||||
|
// safe.
|
||||||
|
ctx, _ = context.WithDeadline(ctx, time.Unix(0, 0))
|
||||||
|
return ctx
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{&fstestutil.ChildMap{"child": child}}, config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer mnt.Close()
|
||||||
|
|
||||||
|
f, err := os.Open(mnt.Dir + "/child")
|
||||||
|
if err == nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// not caused by signal -> should not get EINTR;
|
||||||
|
// context.DeadlineExceeded will be translated into EIO
|
||||||
|
if nerr, ok := err.(*os.PathError); !ok || nerr.Err != syscall.EIO {
|
||||||
|
t.Fatalf("wrong error from deadline open: %T: %v", err, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Test truncate
|
// Test truncate
|
||||||
|
|
||||||
type truncate struct {
|
type truncate struct {
|
||||||
|
@ -1224,6 +1393,58 @@ func TestReadDirAll(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type readDirAllBad struct {
|
||||||
|
fstestutil.Dir
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *readDirAllBad) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
||||||
|
r := []fuse.Dirent{
|
||||||
|
{Name: "one", Inode: 11, Type: fuse.DT_Dir},
|
||||||
|
{Name: "three", Inode: 13},
|
||||||
|
{Name: "two", Inode: 12, Type: fuse.DT_File},
|
||||||
|
}
|
||||||
|
// pick a really distinct error, to identify it later
|
||||||
|
return r, fuse.Errno(syscall.ENAMETOOLONG)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadDirAllBad(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
f := &readDirAllBad{}
|
||||||
|
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()
|
||||||
|
|
||||||
|
var names []string
|
||||||
|
for {
|
||||||
|
n, err := fil.Readdirnames(1)
|
||||||
|
if err != nil {
|
||||||
|
if nerr, ok := err.(*os.SyscallError); !ok || nerr.Err != syscall.ENAMETOOLONG {
|
||||||
|
t.Fatalf("wrong error: %v", err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
names = append(names, n...)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Got readdir: %q", names)
|
||||||
|
|
||||||
|
// TODO could serve partial results from ReadDirAll but the
|
||||||
|
// shandle.readData mechanism makes that awkward.
|
||||||
|
if len(names) != 0 {
|
||||||
|
t.Errorf(`expected 0 entries, got: %q`, names)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Test readdir without any ReadDir methods implemented.
|
// Test readdir without any ReadDir methods implemented.
|
||||||
|
|
||||||
type readDirNotImplemented struct {
|
type readDirNotImplemented struct {
|
||||||
|
@ -1255,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.
|
// Test Chmod.
|
||||||
|
|
||||||
type chmod struct {
|
type chmod struct {
|
||||||
|
@ -1364,6 +1652,10 @@ func (f *openNonSeekable) Open(ctx context.Context, req *fuse.OpenRequest, resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOpenNonSeekable(t *testing.T) {
|
func TestOpenNonSeekable(t *testing.T) {
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
t.Skip("OSXFUSE shares one read and one write handle for all clients, does not support open modes")
|
||||||
|
}
|
||||||
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
f := &openNonSeekable{}
|
f := &openNonSeekable{}
|
||||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{&fstestutil.ChildMap{"child": f}}, nil)
|
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{&fstestutil.ChildMap{"child": f}}, nil)
|
||||||
|
@ -2109,8 +2401,20 @@ func TestInvalidateNodeAttr(t *testing.T) {
|
||||||
t.Fatalf("stat error: %v", err)
|
t.Fatalf("stat error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if g, e := a.attr.Count(), uint32(1); g != e {
|
// With OSXFUSE 3.0.4, we seem to see typically two Attr calls by
|
||||||
t.Errorf("wrong Attr call count: %d != %d", g, e)
|
// this point; something not populating the in-kernel cache
|
||||||
|
// properly? Cope with it; we care more about seeing a new Attr
|
||||||
|
// call after the invalidation.
|
||||||
|
//
|
||||||
|
// We still enforce a max number here so that we know that the
|
||||||
|
// invalidate actually did something, and it's not just that every
|
||||||
|
// Stat results in an Attr.
|
||||||
|
before := a.attr.Count()
|
||||||
|
if before == 0 {
|
||||||
|
t.Error("no Attr call seen")
|
||||||
|
}
|
||||||
|
if g, e := before, uint32(3); g > e {
|
||||||
|
t.Errorf("too many Attr calls seen: %d > %d", g, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("invalidating...")
|
t.Logf("invalidating...")
|
||||||
|
@ -2123,7 +2427,7 @@ func TestInvalidateNodeAttr(t *testing.T) {
|
||||||
t.Fatalf("stat error: %v", err)
|
t.Fatalf("stat error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if g, e := a.attr.Count(), uint32(2); g != e {
|
if g, e := a.attr.Count(), before+1; g != e {
|
||||||
t.Errorf("wrong Attr call count: %d != %d", g, e)
|
t.Errorf("wrong Attr call count: %d != %d", g, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2133,9 +2437,13 @@ type invalidateData struct {
|
||||||
t testing.TB
|
t testing.TB
|
||||||
attr record.Counter
|
attr record.Counter
|
||||||
read record.Counter
|
read record.Counter
|
||||||
|
data atomic.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
const invalidateDataContent = "hello, world\n"
|
const (
|
||||||
|
invalidateDataContent1 = "hello, world\n"
|
||||||
|
invalidateDataContent2 = "so long!\n"
|
||||||
|
)
|
||||||
|
|
||||||
var _ fs.Node = (*invalidateData)(nil)
|
var _ fs.Node = (*invalidateData)(nil)
|
||||||
|
|
||||||
|
@ -2143,7 +2451,7 @@ func (i *invalidateData) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||||
i.attr.Inc()
|
i.attr.Inc()
|
||||||
i.t.Logf("Attr called, #%d", i.attr.Count())
|
i.t.Logf("Attr called, #%d", i.attr.Count())
|
||||||
a.Mode = 0600
|
a.Mode = 0600
|
||||||
a.Size = uint64(len(invalidateDataContent))
|
a.Size = uint64(len(i.data.Load().(string)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2152,17 +2460,18 @@ var _ fs.HandleReader = (*invalidateData)(nil)
|
||||||
func (i *invalidateData) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
|
func (i *invalidateData) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
|
||||||
i.read.Inc()
|
i.read.Inc()
|
||||||
i.t.Logf("Read called, #%d", i.read.Count())
|
i.t.Logf("Read called, #%d", i.read.Count())
|
||||||
fuseutil.HandleRead(req, resp, []byte(invalidateDataContent))
|
fuseutil.HandleRead(req, resp, []byte(i.data.Load().(string)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidateNodeData(t *testing.T) {
|
func TestInvalidateNodeDataInvalidatesAttr(t *testing.T) {
|
||||||
// This test may see false positive failures when run under
|
// This test may see false positive failures when run under
|
||||||
// extreme memory pressure.
|
// extreme memory pressure.
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
a := &invalidateData{
|
a := &invalidateData{
|
||||||
t: t,
|
t: t,
|
||||||
}
|
}
|
||||||
|
a.data.Store(invalidateDataContent1)
|
||||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{&fstestutil.ChildMap{"child": a}}, nil)
|
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{&fstestutil.ChildMap{"child": a}}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -2179,31 +2488,93 @@ func TestInvalidateNodeData(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
buf := make([]byte, 4)
|
attrBefore := a.attr.Count()
|
||||||
|
if g, min := attrBefore, uint32(1); g < min {
|
||||||
|
t.Errorf("wrong Attr call count: %d < %d", g, min)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("invalidating...")
|
||||||
|
a.data.Store(invalidateDataContent2)
|
||||||
|
if err := mnt.Server.InvalidateNodeData(a); err != nil {
|
||||||
|
t.Fatalf("invalidate error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// on OSXFUSE 3.0.6, the Attr has already triggered here, so don't
|
||||||
|
// check the count at this point
|
||||||
|
|
||||||
|
if _, err := f.Stat(); err != nil {
|
||||||
|
t.Errorf("stat error: %v", err)
|
||||||
|
}
|
||||||
|
if g, prev := a.attr.Count(), attrBefore; g <= prev {
|
||||||
|
t.Errorf("did not see Attr call after invalidate: %d <= %d", g, prev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidateNodeDataInvalidatesData(t *testing.T) {
|
||||||
|
// This test may see false positive failures when run under
|
||||||
|
// extreme memory pressure.
|
||||||
|
t.Parallel()
|
||||||
|
a := &invalidateData{
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
a.data.Store(invalidateDataContent1)
|
||||||
|
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{&fstestutil.ChildMap{"child": a}}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer mnt.Close()
|
||||||
|
|
||||||
|
if !mnt.Conn.Protocol().HasInvalidate() {
|
||||||
|
t.Skip("Old FUSE protocol")
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(mnt.Dir + "/child")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
{
|
||||||
|
buf := make([]byte, 100)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
if _, err := f.ReadAt(buf, 0); err != nil {
|
n, err := f.ReadAt(buf, 0)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
t.Fatalf("readat error: %v", err)
|
t.Fatalf("readat error: %v", err)
|
||||||
}
|
}
|
||||||
|
if g, e := string(buf[:n]), invalidateDataContent1; g != e {
|
||||||
|
t.Errorf("wrong content: %q != %q", g, e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if g, e := a.attr.Count(), uint32(1); g != e {
|
|
||||||
t.Errorf("wrong Attr call count: %d != %d", g, e)
|
|
||||||
}
|
}
|
||||||
if g, e := a.read.Count(), uint32(1); g != e {
|
if g, e := a.read.Count(), uint32(1); g != e {
|
||||||
t.Errorf("wrong Read call count: %d != %d", g, e)
|
t.Errorf("wrong Read call count: %d != %d", g, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("invalidating...")
|
t.Logf("invalidating...")
|
||||||
|
a.data.Store(invalidateDataContent2)
|
||||||
if err := mnt.Server.InvalidateNodeData(a); err != nil {
|
if err := mnt.Server.InvalidateNodeData(a); err != nil {
|
||||||
t.Fatalf("invalidate error: %v", err)
|
t.Fatalf("invalidate error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if g, e := a.read.Count(), uint32(1); g != e {
|
||||||
|
t.Errorf("wrong Read call count: %d != %d", g, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// explicitly don't cross the EOF, to trigger more edge cases
|
||||||
|
// (Linux will always do Getattr if you cross what it believes
|
||||||
|
// the EOF to be)
|
||||||
|
const bufSize = len(invalidateDataContent2) - 3
|
||||||
|
buf := make([]byte, bufSize)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
if _, err := f.ReadAt(buf, 0); err != nil {
|
n, err := f.ReadAt(buf, 0)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
t.Fatalf("readat error: %v", err)
|
t.Fatalf("readat error: %v", err)
|
||||||
}
|
}
|
||||||
|
if g, e := string(buf[:n]), invalidateDataContent2[:bufSize]; g != e {
|
||||||
|
t.Errorf("wrong content: %q != %q", g, e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if g, e := a.attr.Count(), uint32(1); g != e {
|
|
||||||
t.Errorf("wrong Attr call count: %d != %d", g, e)
|
|
||||||
}
|
}
|
||||||
if g, e := a.read.Count(), uint32(2); g != e {
|
if g, e := a.read.Count(), uint32(2); g != e {
|
||||||
t.Errorf("wrong Read call count: %d != %d", g, e)
|
t.Errorf("wrong Read call count: %d != %d", g, e)
|
||||||
|
@ -2238,7 +2609,7 @@ func (i *invalidateDataPartial) Read(ctx context.Context, req *fuse.ReadRequest,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidateNodeDataRange(t *testing.T) {
|
func TestInvalidateNodeDataRangeMiss(t *testing.T) {
|
||||||
// This test may see false positive failures when run under
|
// This test may see false positive failures when run under
|
||||||
// extreme memory pressure.
|
// extreme memory pressure.
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
@ -2267,14 +2638,11 @@ func TestInvalidateNodeDataRange(t *testing.T) {
|
||||||
t.Fatalf("readat error: %v", err)
|
t.Fatalf("readat error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if g, e := a.attr.Count(), uint32(1); g != e {
|
|
||||||
t.Errorf("wrong Attr call count: %d != %d", g, e)
|
|
||||||
}
|
|
||||||
if g, e := a.read.Count(), uint32(1); g != e {
|
if g, e := a.read.Count(), uint32(1); g != e {
|
||||||
t.Errorf("wrong Read call count: %d != %d", g, e)
|
t.Errorf("wrong Read call count: %d != %d", g, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("invalidating...")
|
t.Logf("invalidating an uninteresting block...")
|
||||||
if err := mnt.Server.InvalidateNodeDataRange(a, 4096, 4096); err != nil {
|
if err := mnt.Server.InvalidateNodeDataRange(a, 4096, 4096); err != nil {
|
||||||
t.Fatalf("invalidate error: %v", err)
|
t.Fatalf("invalidate error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2284,9 +2652,6 @@ func TestInvalidateNodeDataRange(t *testing.T) {
|
||||||
t.Fatalf("readat error: %v", err)
|
t.Fatalf("readat error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if g, e := a.attr.Count(), uint32(1); g != e {
|
|
||||||
t.Errorf("wrong Attr call count: %d != %d", g, e)
|
|
||||||
}
|
|
||||||
// The page invalidated is not the page we're reading, so it
|
// The page invalidated is not the page we're reading, so it
|
||||||
// should stay in cache.
|
// should stay in cache.
|
||||||
if g, e := a.read.Count(), uint32(1); g != e {
|
if g, e := a.read.Count(), uint32(1); g != e {
|
||||||
|
@ -2294,6 +2659,56 @@ func TestInvalidateNodeDataRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInvalidateNodeDataRangeHit(t *testing.T) {
|
||||||
|
// This test may see false positive failures when run under
|
||||||
|
// extreme memory pressure.
|
||||||
|
t.Parallel()
|
||||||
|
a := &invalidateDataPartial{
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{&fstestutil.ChildMap{"child": a}}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer mnt.Close()
|
||||||
|
|
||||||
|
if !mnt.Conn.Protocol().HasInvalidate() {
|
||||||
|
t.Skip("Old FUSE protocol")
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(mnt.Dir + "/child")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
const offset = 4096
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
if _, err := f.ReadAt(buf, offset); err != nil {
|
||||||
|
t.Fatalf("readat error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if g, e := a.read.Count(), uint32(1); g != e {
|
||||||
|
t.Errorf("wrong Read call count: %d != %d", g, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("invalidating where the reads are...")
|
||||||
|
if err := mnt.Server.InvalidateNodeDataRange(a, offset, 4096); err != nil {
|
||||||
|
t.Fatalf("invalidate error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
if _, err := f.ReadAt(buf, offset); err != nil {
|
||||||
|
t.Fatalf("readat error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// One new read
|
||||||
|
if g, e := a.read.Count(), uint32(2); g != e {
|
||||||
|
t.Errorf("wrong Read call count: %d != %d", g, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type invalidateEntryRoot struct {
|
type invalidateEntryRoot struct {
|
||||||
fs.NodeRef
|
fs.NodeRef
|
||||||
t testing.TB
|
t testing.TB
|
||||||
|
@ -2380,13 +2795,13 @@ func (contextFile) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.O
|
||||||
|
|
||||||
func TestContext(t *testing.T) {
|
func TestContext(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
ctx := context.Background()
|
|
||||||
const input = "kilroy was here"
|
const input = "kilroy was here"
|
||||||
ctx = context.WithValue(ctx, &contextFileSentinel, input)
|
|
||||||
mnt, err := fstestutil.MountedT(t,
|
mnt, err := fstestutil.MountedT(t,
|
||||||
fstestutil.SimpleFS{&fstestutil.ChildMap{"child": contextFile{}}},
|
fstestutil.SimpleFS{&fstestutil.ChildMap{"child": contextFile{}}},
|
||||||
&fs.Config{
|
&fs.Config{
|
||||||
GetContext: func() context.Context { return ctx },
|
WithContext: func(ctx context.Context, req fuse.Request) context.Context {
|
||||||
|
return context.WithValue(ctx, &contextFileSentinel, input)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -2401,3 +2816,28 @@ func TestContext(t *testing.T) {
|
||||||
t.Errorf("read wrong data: %q != %q", g, e)
|
t.Errorf("read wrong data: %q != %q", g, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type goexitFile struct {
|
||||||
|
fstestutil.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (goexitFile) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
|
||||||
|
log.Println("calling runtime.Goexit...")
|
||||||
|
runtime.Goexit()
|
||||||
|
panic("not reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGoexit(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
mnt, err := fstestutil.MountedT(t,
|
||||||
|
fstestutil.SimpleFS{&fstestutil.ChildMap{"child": goexitFile{}}}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer mnt.Close()
|
||||||
|
|
||||||
|
_, err = ioutil.ReadFile(mnt.Dir + "/child")
|
||||||
|
if nerr, ok := err.(*os.PathError); !ok || nerr.Err != syscall.EIO {
|
||||||
|
t.Fatalf("wrong error from exiting handler: %T: %v", err, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
164
vendor/src/bazil.org/fuse/fuse.go
vendored
164
vendor/src/bazil.org/fuse/fuse.go
vendored
|
@ -132,6 +132,18 @@ type Conn struct {
|
||||||
proto Protocol
|
proto Protocol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MountpointDoesNotExistError is an error returned when the
|
||||||
|
// mountpoint does not exist.
|
||||||
|
type MountpointDoesNotExistError struct {
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = (*MountpointDoesNotExistError)(nil)
|
||||||
|
|
||||||
|
func (e *MountpointDoesNotExistError) Error() string {
|
||||||
|
return fmt.Sprintf("mountpoint does not exist: %v", e.Path)
|
||||||
|
}
|
||||||
|
|
||||||
// Mount mounts a new FUSE connection on the named directory
|
// Mount mounts a new FUSE connection on the named directory
|
||||||
// and returns a connection for reading and writing FUSE messages.
|
// and returns a connection for reading and writing FUSE messages.
|
||||||
//
|
//
|
||||||
|
@ -164,6 +176,13 @@ func Mount(dir string, options ...MountOption) (*Conn, error) {
|
||||||
|
|
||||||
if err := initMount(c, &conf); err != nil {
|
if err := initMount(c, &conf); err != nil {
|
||||||
c.Close()
|
c.Close()
|
||||||
|
if err == ErrClosedWithoutInit {
|
||||||
|
// see if we can provide a better error
|
||||||
|
<-c.Ready
|
||||||
|
if err := c.MountError; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,11 +198,15 @@ func (e *OldVersionError) Error() string {
|
||||||
return fmt.Sprintf("kernel FUSE version is too old: %v < %v", e.Kernel, e.LibraryMin)
|
return fmt.Sprintf("kernel FUSE version is too old: %v < %v", e.Kernel, e.LibraryMin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrClosedWithoutInit = errors.New("fuse connection closed without init")
|
||||||
|
)
|
||||||
|
|
||||||
func initMount(c *Conn, conf *mountConfig) error {
|
func initMount(c *Conn, conf *mountConfig) error {
|
||||||
req, err := c.ReadRequest()
|
req, err := c.ReadRequest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return fmt.Errorf("missing init, got EOF")
|
return ErrClosedWithoutInit
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -212,7 +235,7 @@ func initMount(c *Conn, conf *mountConfig) error {
|
||||||
s := &InitResponse{
|
s := &InitResponse{
|
||||||
Library: proto,
|
Library: proto,
|
||||||
MaxReadahead: conf.maxReadahead,
|
MaxReadahead: conf.maxReadahead,
|
||||||
MaxWrite: 128 * 1024,
|
MaxWrite: maxWrite,
|
||||||
Flags: InitBigWrites | conf.initFlags,
|
Flags: InitBigWrites | conf.initFlags,
|
||||||
}
|
}
|
||||||
r.Respond(s)
|
r.Respond(s)
|
||||||
|
@ -235,15 +258,27 @@ type Request interface {
|
||||||
// A RequestID identifies an active FUSE request.
|
// A RequestID identifies an active FUSE request.
|
||||||
type RequestID uint64
|
type RequestID uint64
|
||||||
|
|
||||||
|
func (r RequestID) String() string {
|
||||||
|
return fmt.Sprintf("%#x", uint64(r))
|
||||||
|
}
|
||||||
|
|
||||||
// A NodeID is a number identifying a directory or file.
|
// A NodeID is a number identifying a directory or file.
|
||||||
// It must be unique among IDs returned in LookupResponses
|
// It must be unique among IDs returned in LookupResponses
|
||||||
// that have not yet been forgotten by ForgetRequests.
|
// that have not yet been forgotten by ForgetRequests.
|
||||||
type NodeID uint64
|
type NodeID uint64
|
||||||
|
|
||||||
|
func (n NodeID) String() string {
|
||||||
|
return fmt.Sprintf("%#x", uint64(n))
|
||||||
|
}
|
||||||
|
|
||||||
// A HandleID is a number identifying an open directory or file.
|
// A HandleID is a number identifying an open directory or file.
|
||||||
// It only needs to be unique while the directory or file is open.
|
// It only needs to be unique while the directory or file is open.
|
||||||
type HandleID uint64
|
type HandleID uint64
|
||||||
|
|
||||||
|
func (h HandleID) String() string {
|
||||||
|
return fmt.Sprintf("%#x", uint64(h))
|
||||||
|
}
|
||||||
|
|
||||||
// The RootID identifies the root directory of a FUSE file system.
|
// The RootID identifies the root directory of a FUSE file system.
|
||||||
const RootID NodeID = rootID
|
const RootID NodeID = rootID
|
||||||
|
|
||||||
|
@ -261,7 +296,7 @@ type Header struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Header) String() string {
|
func (h *Header) String() string {
|
||||||
return fmt.Sprintf("ID=%#x Node=%#x Uid=%d Gid=%d Pid=%d", h.ID, h.Node, h.Uid, h.Gid, h.Pid)
|
return fmt.Sprintf("ID=%v Node=%v Uid=%d Gid=%d Pid=%d", h.ID, h.Node, h.Uid, h.Gid, h.Pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Header) Hdr() *Header {
|
func (h *Header) Hdr() *Header {
|
||||||
|
@ -368,9 +403,6 @@ func (h *Header) RespondError(err error) {
|
||||||
h.respond(buf)
|
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
|
// All requests read from the kernel, without data, are shorter than
|
||||||
// this.
|
// this.
|
||||||
var maxRequestSize = syscall.Getpagesize()
|
var maxRequestSize = syscall.Getpagesize()
|
||||||
|
@ -985,7 +1017,33 @@ loop:
|
||||||
case opGetxtimes:
|
case opGetxtimes:
|
||||||
panic("opGetxtimes")
|
panic("opGetxtimes")
|
||||||
case opExchange:
|
case opExchange:
|
||||||
panic("opExchange")
|
in := (*exchangeIn)(m.data())
|
||||||
|
if m.len() < unsafe.Sizeof(*in) {
|
||||||
|
goto corrupt
|
||||||
|
}
|
||||||
|
oldDirNodeID := NodeID(in.Olddir)
|
||||||
|
newDirNodeID := NodeID(in.Newdir)
|
||||||
|
oldNew := m.bytes()[unsafe.Sizeof(*in):]
|
||||||
|
// oldNew should be "oldname\x00newname\x00"
|
||||||
|
if len(oldNew) < 4 {
|
||||||
|
goto corrupt
|
||||||
|
}
|
||||||
|
if oldNew[len(oldNew)-1] != '\x00' {
|
||||||
|
goto corrupt
|
||||||
|
}
|
||||||
|
i := bytes.IndexByte(oldNew, '\x00')
|
||||||
|
if i < 0 {
|
||||||
|
goto corrupt
|
||||||
|
}
|
||||||
|
oldName, newName := string(oldNew[:i]), string(oldNew[i+1:len(oldNew)-1])
|
||||||
|
req = &ExchangeDataRequest{
|
||||||
|
Header: m.Header(),
|
||||||
|
OldDir: oldDirNodeID,
|
||||||
|
NewDir: newDirNodeID,
|
||||||
|
OldName: oldName,
|
||||||
|
NewName: newName,
|
||||||
|
// TODO options
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return req, nil
|
return req, nil
|
||||||
|
@ -1147,7 +1205,7 @@ type InitRequest struct {
|
||||||
var _ = Request(&InitRequest{})
|
var _ = Request(&InitRequest{})
|
||||||
|
|
||||||
func (r *InitRequest) String() string {
|
func (r *InitRequest) String() string {
|
||||||
return fmt.Sprintf("Init [%s] %v ra=%d fl=%v", &r.Header, r.Kernel, r.MaxReadahead, r.Flags)
|
return fmt.Sprintf("Init [%v] %v ra=%d fl=%v", &r.Header, r.Kernel, r.MaxReadahead, r.Flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An InitResponse is the response to an InitRequest.
|
// An InitResponse is the response to an InitRequest.
|
||||||
|
@ -1163,7 +1221,7 @@ type InitResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *InitResponse) String() string {
|
func (r *InitResponse) String() string {
|
||||||
return fmt.Sprintf("Init %+v", *r)
|
return fmt.Sprintf("Init %v ra=%d fl=%v w=%d", r.Library, r.MaxReadahead, r.Flags, r.MaxWrite)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respond replies to the request with the given response.
|
// Respond replies to the request with the given response.
|
||||||
|
@ -1224,7 +1282,13 @@ type StatfsResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StatfsResponse) String() string {
|
func (r *StatfsResponse) String() string {
|
||||||
return fmt.Sprintf("Statfs %+v", *r)
|
return fmt.Sprintf("Statfs blocks=%d/%d/%d files=%d/%d bsize=%d frsize=%d namelen=%d",
|
||||||
|
r.Bavail, r.Bfree, r.Blocks,
|
||||||
|
r.Ffree, r.Files,
|
||||||
|
r.Bsize,
|
||||||
|
r.Frsize,
|
||||||
|
r.Namelen,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An AccessRequest asks whether the file can be accessed
|
// An AccessRequest asks whether the file can be accessed
|
||||||
|
@ -1259,7 +1323,7 @@ type Attr struct {
|
||||||
Ctime time.Time // time of last inode change
|
Ctime time.Time // time of last inode change
|
||||||
Crtime time.Time // time of creation (OS X only)
|
Crtime time.Time // time of creation (OS X only)
|
||||||
Mode os.FileMode // file mode
|
Mode os.FileMode // file mode
|
||||||
Nlink uint32 // number of links
|
Nlink uint32 // number of links (usually 1)
|
||||||
Uid uint32 // owner uid
|
Uid uint32 // owner uid
|
||||||
Gid uint32 // group gid
|
Gid uint32 // group gid
|
||||||
Rdev uint32 // device numbers
|
Rdev uint32 // device numbers
|
||||||
|
@ -1267,6 +1331,10 @@ type Attr struct {
|
||||||
BlockSize uint32 // preferred blocksize for filesystem I/O
|
BlockSize uint32 // preferred blocksize for filesystem I/O
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a Attr) String() string {
|
||||||
|
return fmt.Sprintf("valid=%v ino=%v size=%d mode=%v", a.Valid, a.Inode, a.Size, a.Mode)
|
||||||
|
}
|
||||||
|
|
||||||
func unix(t time.Time) (sec uint64, nsec uint32) {
|
func unix(t time.Time) (sec uint64, nsec uint32) {
|
||||||
nano := t.UnixNano()
|
nano := t.UnixNano()
|
||||||
sec = uint64(nano / 1e9)
|
sec = uint64(nano / 1e9)
|
||||||
|
@ -1329,7 +1397,7 @@ type GetattrRequest struct {
|
||||||
var _ = Request(&GetattrRequest{})
|
var _ = Request(&GetattrRequest{})
|
||||||
|
|
||||||
func (r *GetattrRequest) String() string {
|
func (r *GetattrRequest) String() string {
|
||||||
return fmt.Sprintf("Getattr [%s] %#x fl=%v", &r.Header, r.Handle, r.Flags)
|
return fmt.Sprintf("Getattr [%s] %v fl=%v", &r.Header, r.Handle, r.Flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respond replies to the request with the given response.
|
// Respond replies to the request with the given response.
|
||||||
|
@ -1349,7 +1417,7 @@ type GetattrResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *GetattrResponse) String() string {
|
func (r *GetattrResponse) String() string {
|
||||||
return fmt.Sprintf("Getattr %+v", *r)
|
return fmt.Sprintf("Getattr %v", r.Attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A GetxattrRequest asks for the extended attributes associated with r.Node.
|
// A GetxattrRequest asks for the extended attributes associated with r.Node.
|
||||||
|
@ -1540,8 +1608,12 @@ type LookupResponse struct {
|
||||||
Attr Attr
|
Attr Attr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *LookupResponse) string() string {
|
||||||
|
return fmt.Sprintf("%v gen=%d valid=%v attr={%v}", r.Node, r.Generation, r.EntryValid, r.Attr)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *LookupResponse) String() string {
|
func (r *LookupResponse) String() string {
|
||||||
return fmt.Sprintf("Lookup %+v", *r)
|
return fmt.Sprintf("Lookup %s", r.string())
|
||||||
}
|
}
|
||||||
|
|
||||||
// An OpenRequest asks to open a file or directory
|
// An OpenRequest asks to open a file or directory
|
||||||
|
@ -1572,8 +1644,12 @@ type OpenResponse struct {
|
||||||
Flags OpenResponseFlags
|
Flags OpenResponseFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *OpenResponse) string() string {
|
||||||
|
return fmt.Sprintf("%v fl=%v", r.Handle, r.Flags)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *OpenResponse) String() string {
|
func (r *OpenResponse) String() string {
|
||||||
return fmt.Sprintf("Open %+v", *r)
|
return fmt.Sprintf("Open %s", r.string())
|
||||||
}
|
}
|
||||||
|
|
||||||
// A CreateRequest asks to create and open a file (not a directory).
|
// A CreateRequest asks to create and open a file (not a directory).
|
||||||
|
@ -1582,6 +1658,7 @@ type CreateRequest struct {
|
||||||
Name string
|
Name string
|
||||||
Flags OpenFlags
|
Flags OpenFlags
|
||||||
Mode os.FileMode
|
Mode os.FileMode
|
||||||
|
// Umask of the request. Not supported on OS X.
|
||||||
Umask os.FileMode
|
Umask os.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1620,7 +1697,7 @@ type CreateResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CreateResponse) String() string {
|
func (r *CreateResponse) String() string {
|
||||||
return fmt.Sprintf("Create %+v", *r)
|
return fmt.Sprintf("Create {%s} {%s}", r.LookupResponse.string(), r.OpenResponse.string())
|
||||||
}
|
}
|
||||||
|
|
||||||
// A MkdirRequest asks to create (but not open) a directory.
|
// A MkdirRequest asks to create (but not open) a directory.
|
||||||
|
@ -1628,6 +1705,7 @@ type MkdirRequest struct {
|
||||||
Header `json:"-"`
|
Header `json:"-"`
|
||||||
Name string
|
Name string
|
||||||
Mode os.FileMode
|
Mode os.FileMode
|
||||||
|
// Umask of the request. Not supported on OS X.
|
||||||
Umask os.FileMode
|
Umask os.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1658,7 +1736,7 @@ type MkdirResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *MkdirResponse) String() string {
|
func (r *MkdirResponse) String() string {
|
||||||
return fmt.Sprintf("Mkdir %+v", *r)
|
return fmt.Sprintf("Mkdir %v", r.LookupResponse.string())
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ReadRequest asks to read from an open file.
|
// A ReadRequest asks to read from an open file.
|
||||||
|
@ -1676,7 +1754,7 @@ type ReadRequest struct {
|
||||||
var _ = Request(&ReadRequest{})
|
var _ = Request(&ReadRequest{})
|
||||||
|
|
||||||
func (r *ReadRequest) String() string {
|
func (r *ReadRequest) String() string {
|
||||||
return fmt.Sprintf("Read [%s] %#x %d @%#x dir=%v fl=%v lock=%d ffl=%v", &r.Header, r.Handle, r.Size, r.Offset, r.Dir, r.Flags, r.LockOwner, r.FileFlags)
|
return fmt.Sprintf("Read [%s] %v %d @%#x dir=%v fl=%v lock=%d ffl=%v", &r.Header, r.Handle, r.Size, r.Offset, r.Dir, r.Flags, r.LockOwner, r.FileFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respond replies to the request with the given response.
|
// Respond replies to the request with the given response.
|
||||||
|
@ -1719,7 +1797,7 @@ type ReleaseRequest struct {
|
||||||
var _ = Request(&ReleaseRequest{})
|
var _ = Request(&ReleaseRequest{})
|
||||||
|
|
||||||
func (r *ReleaseRequest) String() string {
|
func (r *ReleaseRequest) String() string {
|
||||||
return fmt.Sprintf("Release [%s] %#x fl=%v rfl=%v owner=%#x", &r.Header, r.Handle, r.Flags, r.ReleaseFlags, r.LockOwner)
|
return fmt.Sprintf("Release [%s] %v fl=%v rfl=%v owner=%#x", &r.Header, r.Handle, r.Flags, r.ReleaseFlags, r.LockOwner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respond replies to the request, indicating that the handle has been released.
|
// Respond replies to the request, indicating that the handle has been released.
|
||||||
|
@ -1861,7 +1939,7 @@ type WriteRequest struct {
|
||||||
var _ = Request(&WriteRequest{})
|
var _ = Request(&WriteRequest{})
|
||||||
|
|
||||||
func (r *WriteRequest) String() string {
|
func (r *WriteRequest) String() string {
|
||||||
return fmt.Sprintf("Write [%s] %#x %d @%d fl=%v lock=%d ffl=%v", &r.Header, r.Handle, len(r.Data), r.Offset, r.Flags, r.LockOwner, r.FileFlags)
|
return fmt.Sprintf("Write [%s] %v %d @%d fl=%v lock=%d ffl=%v", &r.Header, r.Handle, len(r.Data), r.Offset, r.Flags, r.LockOwner, r.FileFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
type jsonWriteRequest struct {
|
type jsonWriteRequest struct {
|
||||||
|
@ -1895,7 +1973,7 @@ type WriteResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *WriteResponse) String() string {
|
func (r *WriteResponse) String() string {
|
||||||
return fmt.Sprintf("Write %+v", *r)
|
return fmt.Sprintf("Write %d", r.Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A SetattrRequest asks to change one or more attributes associated with a file,
|
// A SetattrRequest asks to change one or more attributes associated with a file,
|
||||||
|
@ -1948,9 +2026,9 @@ func (r *SetattrRequest) String() string {
|
||||||
fmt.Fprintf(&buf, " mtime=now")
|
fmt.Fprintf(&buf, " mtime=now")
|
||||||
}
|
}
|
||||||
if r.Valid.Handle() {
|
if r.Valid.Handle() {
|
||||||
fmt.Fprintf(&buf, " handle=%#x", r.Handle)
|
fmt.Fprintf(&buf, " handle=%v", r.Handle)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(&buf, " handle=INVALID-%#x", r.Handle)
|
fmt.Fprintf(&buf, " handle=INVALID-%v", r.Handle)
|
||||||
}
|
}
|
||||||
if r.Valid.LockOwner() {
|
if r.Valid.LockOwner() {
|
||||||
fmt.Fprintf(&buf, " lockowner")
|
fmt.Fprintf(&buf, " lockowner")
|
||||||
|
@ -1965,7 +2043,7 @@ func (r *SetattrRequest) String() string {
|
||||||
fmt.Fprintf(&buf, " bkuptime=%v", r.Bkuptime)
|
fmt.Fprintf(&buf, " bkuptime=%v", r.Bkuptime)
|
||||||
}
|
}
|
||||||
if r.Valid.Flags() {
|
if r.Valid.Flags() {
|
||||||
fmt.Fprintf(&buf, " flags=%#x", r.Flags)
|
fmt.Fprintf(&buf, " flags=%v", r.Flags)
|
||||||
}
|
}
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
@ -1988,7 +2066,7 @@ type SetattrResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *SetattrResponse) String() string {
|
func (r *SetattrResponse) String() string {
|
||||||
return fmt.Sprintf("Setattr %+v", *r)
|
return fmt.Sprintf("Setattr %v", r.Attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A FlushRequest asks for the current state of an open file to be flushed
|
// A FlushRequest asks for the current state of an open file to be flushed
|
||||||
|
@ -2004,7 +2082,7 @@ type FlushRequest struct {
|
||||||
var _ = Request(&FlushRequest{})
|
var _ = Request(&FlushRequest{})
|
||||||
|
|
||||||
func (r *FlushRequest) String() string {
|
func (r *FlushRequest) String() string {
|
||||||
return fmt.Sprintf("Flush [%s] %#x fl=%#x lk=%#x", &r.Header, r.Handle, r.Flags, r.LockOwner)
|
return fmt.Sprintf("Flush [%s] %v fl=%#x lk=%#x", &r.Header, r.Handle, r.Flags, r.LockOwner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respond replies to the request, indicating that the flush succeeded.
|
// Respond replies to the request, indicating that the flush succeeded.
|
||||||
|
@ -2065,6 +2143,10 @@ type SymlinkResponse struct {
|
||||||
LookupResponse
|
LookupResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *SymlinkResponse) String() string {
|
||||||
|
return fmt.Sprintf("Symlink %v", r.LookupResponse.string())
|
||||||
|
}
|
||||||
|
|
||||||
// A ReadlinkRequest is a request to read a symlink's target.
|
// A ReadlinkRequest is a request to read a symlink's target.
|
||||||
type ReadlinkRequest struct {
|
type ReadlinkRequest struct {
|
||||||
Header `json:"-"`
|
Header `json:"-"`
|
||||||
|
@ -2119,7 +2201,7 @@ type RenameRequest struct {
|
||||||
var _ = Request(&RenameRequest{})
|
var _ = Request(&RenameRequest{})
|
||||||
|
|
||||||
func (r *RenameRequest) String() string {
|
func (r *RenameRequest) String() string {
|
||||||
return fmt.Sprintf("Rename [%s] from %q to dirnode %d %q", &r.Header, r.OldName, r.NewDir, r.NewName)
|
return fmt.Sprintf("Rename [%s] from %q to dirnode %v %q", &r.Header, r.OldName, r.NewDir, r.NewName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RenameRequest) Respond() {
|
func (r *RenameRequest) Respond() {
|
||||||
|
@ -2132,6 +2214,7 @@ type MknodRequest struct {
|
||||||
Name string
|
Name string
|
||||||
Mode os.FileMode
|
Mode os.FileMode
|
||||||
Rdev uint32
|
Rdev uint32
|
||||||
|
// Umask of the request. Not supported on OS X.
|
||||||
Umask os.FileMode
|
Umask os.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2191,3 +2274,30 @@ func (r *InterruptRequest) Respond() {
|
||||||
func (r *InterruptRequest) String() string {
|
func (r *InterruptRequest) String() string {
|
||||||
return fmt.Sprintf("Interrupt [%s] ID %v", &r.Header, r.IntrID)
|
return fmt.Sprintf("Interrupt [%s] ID %v", &r.Header, r.IntrID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An ExchangeDataRequest is a request to exchange the contents of two
|
||||||
|
// files, while leaving most metadata untouched.
|
||||||
|
//
|
||||||
|
// This request comes from OS X exchangedata(2) and represents its
|
||||||
|
// specific semantics. Crucially, it is very different from Linux
|
||||||
|
// renameat(2) RENAME_EXCHANGE.
|
||||||
|
//
|
||||||
|
// https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/exchangedata.2.html
|
||||||
|
type ExchangeDataRequest struct {
|
||||||
|
Header `json:"-"`
|
||||||
|
OldDir, NewDir NodeID
|
||||||
|
OldName, NewName string
|
||||||
|
// TODO options
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Request(&ExchangeDataRequest{})
|
||||||
|
|
||||||
|
func (r *ExchangeDataRequest) String() string {
|
||||||
|
// TODO options
|
||||||
|
return fmt.Sprintf("ExchangeData [%s] %v %q and %v %q", &r.Header, r.OldDir, r.OldName, r.NewDir, r.NewName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ExchangeDataRequest) Respond() {
|
||||||
|
buf := newBuffer(0)
|
||||||
|
r.respond(buf)
|
||||||
|
}
|
||||||
|
|
9
vendor/src/bazil.org/fuse/fuse_darwin.go
vendored
Normal file
9
vendor/src/bazil.org/fuse/fuse_darwin.go
vendored
Normal file
|
@ -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
|
6
vendor/src/bazil.org/fuse/fuse_freebsd.go
vendored
Normal file
6
vendor/src/bazil.org/fuse/fuse_freebsd.go
vendored
Normal file
|
@ -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
|
52
vendor/src/bazil.org/fuse/fuse_kernel.go
vendored
52
vendor/src/bazil.org/fuse/fuse_kernel.go
vendored
|
@ -62,7 +62,7 @@ type kstatfs struct {
|
||||||
Bsize uint32
|
Bsize uint32
|
||||||
Namelen uint32
|
Namelen uint32
|
||||||
Frsize uint32
|
Frsize uint32
|
||||||
Padding uint32
|
_ uint32
|
||||||
Spare [6]uint32
|
Spare [6]uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,9 +159,13 @@ const (
|
||||||
OpenWriteOnly OpenFlags = syscall.O_WRONLY
|
OpenWriteOnly OpenFlags = syscall.O_WRONLY
|
||||||
OpenReadWrite OpenFlags = syscall.O_RDWR
|
OpenReadWrite OpenFlags = syscall.O_RDWR
|
||||||
|
|
||||||
|
// File was opened in append-only mode, all writes will go to end
|
||||||
|
// of file. OS X does not provide this information.
|
||||||
OpenAppend OpenFlags = syscall.O_APPEND
|
OpenAppend OpenFlags = syscall.O_APPEND
|
||||||
OpenCreate OpenFlags = syscall.O_CREAT
|
OpenCreate OpenFlags = syscall.O_CREAT
|
||||||
|
OpenDirectory OpenFlags = syscall.O_DIRECTORY
|
||||||
OpenExclusive OpenFlags = syscall.O_EXCL
|
OpenExclusive OpenFlags = syscall.O_EXCL
|
||||||
|
OpenNonblock OpenFlags = syscall.O_NONBLOCK
|
||||||
OpenSync OpenFlags = syscall.O_SYNC
|
OpenSync OpenFlags = syscall.O_SYNC
|
||||||
OpenTruncate OpenFlags = syscall.O_TRUNC
|
OpenTruncate OpenFlags = syscall.O_TRUNC
|
||||||
)
|
)
|
||||||
|
@ -213,11 +217,13 @@ func accModeName(flags OpenFlags) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
var openFlagNames = []flagName{
|
var openFlagNames = []flagName{
|
||||||
{uint32(OpenCreate), "OpenCreate"},
|
|
||||||
{uint32(OpenExclusive), "OpenExclusive"},
|
|
||||||
{uint32(OpenTruncate), "OpenTruncate"},
|
|
||||||
{uint32(OpenAppend), "OpenAppend"},
|
{uint32(OpenAppend), "OpenAppend"},
|
||||||
|
{uint32(OpenCreate), "OpenCreate"},
|
||||||
|
{uint32(OpenDirectory), "OpenDirectory"},
|
||||||
|
{uint32(OpenExclusive), "OpenExclusive"},
|
||||||
|
{uint32(OpenNonblock), "OpenNonblock"},
|
||||||
{uint32(OpenSync), "OpenSync"},
|
{uint32(OpenSync), "OpenSync"},
|
||||||
|
{uint32(OpenTruncate), "OpenTruncate"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// The OpenResponseFlags are returned in the OpenResponse.
|
// The OpenResponseFlags are returned in the OpenResponse.
|
||||||
|
@ -254,6 +260,7 @@ const (
|
||||||
InitAtomicTrunc InitFlags = 1 << 3
|
InitAtomicTrunc InitFlags = 1 << 3
|
||||||
InitExportSupport InitFlags = 1 << 4
|
InitExportSupport InitFlags = 1 << 4
|
||||||
InitBigWrites InitFlags = 1 << 5
|
InitBigWrites InitFlags = 1 << 5
|
||||||
|
// Do not mask file access modes with umask. Not supported on OS X.
|
||||||
InitDontMask InitFlags = 1 << 6
|
InitDontMask InitFlags = 1 << 6
|
||||||
InitSpliceWrite InitFlags = 1 << 7
|
InitSpliceWrite InitFlags = 1 << 7
|
||||||
InitSpliceMove InitFlags = 1 << 8
|
InitSpliceMove InitFlags = 1 << 8
|
||||||
|
@ -412,14 +419,14 @@ type forgetIn struct {
|
||||||
|
|
||||||
type getattrIn struct {
|
type getattrIn struct {
|
||||||
GetattrFlags uint32
|
GetattrFlags uint32
|
||||||
dummy uint32
|
_ uint32
|
||||||
Fh uint64
|
Fh uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type attrOut struct {
|
type attrOut struct {
|
||||||
AttrValid uint64 // Cache timeout for the attributes
|
AttrValid uint64 // Cache timeout for the attributes
|
||||||
AttrValidNsec uint32
|
AttrValidNsec uint32
|
||||||
Dummy uint32
|
_ uint32
|
||||||
Attr attr
|
Attr attr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +451,7 @@ type mknodIn struct {
|
||||||
Mode uint32
|
Mode uint32
|
||||||
Rdev uint32
|
Rdev uint32
|
||||||
Umask uint32
|
Umask uint32
|
||||||
padding uint32
|
_ uint32
|
||||||
// "filename\x00" follows.
|
// "filename\x00" follows.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,6 +489,7 @@ type exchangeIn struct {
|
||||||
Olddir uint64
|
Olddir uint64
|
||||||
Newdir uint64
|
Newdir uint64
|
||||||
Options uint64
|
Options uint64
|
||||||
|
// "oldname\x00newname\x00" follows
|
||||||
}
|
}
|
||||||
|
|
||||||
type linkIn struct {
|
type linkIn struct {
|
||||||
|
@ -490,7 +498,7 @@ type linkIn struct {
|
||||||
|
|
||||||
type setattrInCommon struct {
|
type setattrInCommon struct {
|
||||||
Valid uint32
|
Valid uint32
|
||||||
Padding uint32
|
_ uint32
|
||||||
Fh uint64
|
Fh uint64
|
||||||
Size uint64
|
Size uint64
|
||||||
LockOwner uint64 // unused on OS X?
|
LockOwner uint64 // unused on OS X?
|
||||||
|
@ -515,14 +523,14 @@ type openIn struct {
|
||||||
type openOut struct {
|
type openOut struct {
|
||||||
Fh uint64
|
Fh uint64
|
||||||
OpenFlags uint32
|
OpenFlags uint32
|
||||||
Padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type createIn struct {
|
type createIn struct {
|
||||||
Flags uint32
|
Flags uint32
|
||||||
Mode uint32
|
Mode uint32
|
||||||
Umask uint32
|
Umask uint32
|
||||||
padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func createInSize(p Protocol) uintptr {
|
func createInSize(p Protocol) uintptr {
|
||||||
|
@ -544,7 +552,7 @@ type releaseIn struct {
|
||||||
type flushIn struct {
|
type flushIn struct {
|
||||||
Fh uint64
|
Fh uint64
|
||||||
FlushFlags uint32
|
FlushFlags uint32
|
||||||
Padding uint32
|
_ uint32
|
||||||
LockOwner uint64
|
LockOwner uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,7 +563,7 @@ type readIn struct {
|
||||||
ReadFlags uint32
|
ReadFlags uint32
|
||||||
LockOwner uint64
|
LockOwner uint64
|
||||||
Flags uint32
|
Flags uint32
|
||||||
padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func readInSize(p Protocol) uintptr {
|
func readInSize(p Protocol) uintptr {
|
||||||
|
@ -590,7 +598,7 @@ type writeIn struct {
|
||||||
WriteFlags uint32
|
WriteFlags uint32
|
||||||
LockOwner uint64
|
LockOwner uint64
|
||||||
Flags uint32
|
Flags uint32
|
||||||
padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeInSize(p Protocol) uintptr {
|
func writeInSize(p Protocol) uintptr {
|
||||||
|
@ -604,7 +612,7 @@ func writeInSize(p Protocol) uintptr {
|
||||||
|
|
||||||
type writeOut struct {
|
type writeOut struct {
|
||||||
Size uint32
|
Size uint32
|
||||||
Padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// The WriteFlags are passed in WriteRequest.
|
// The WriteFlags are passed in WriteRequest.
|
||||||
|
@ -634,7 +642,7 @@ type statfsOut struct {
|
||||||
type fsyncIn struct {
|
type fsyncIn struct {
|
||||||
Fh uint64
|
Fh uint64
|
||||||
FsyncFlags uint32
|
FsyncFlags uint32
|
||||||
Padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type setxattrInCommon struct {
|
type setxattrInCommon struct {
|
||||||
|
@ -648,7 +656,7 @@ func (setxattrInCommon) position() uint32 {
|
||||||
|
|
||||||
type getxattrInCommon struct {
|
type getxattrInCommon struct {
|
||||||
Size uint32
|
Size uint32
|
||||||
Padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (getxattrInCommon) position() uint32 {
|
func (getxattrInCommon) position() uint32 {
|
||||||
|
@ -657,7 +665,7 @@ func (getxattrInCommon) position() uint32 {
|
||||||
|
|
||||||
type getxattrOut struct {
|
type getxattrOut struct {
|
||||||
Size uint32
|
Size uint32
|
||||||
Padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type lkIn struct {
|
type lkIn struct {
|
||||||
|
@ -665,7 +673,7 @@ type lkIn struct {
|
||||||
Owner uint64
|
Owner uint64
|
||||||
Lk fileLock
|
Lk fileLock
|
||||||
LkFlags uint32
|
LkFlags uint32
|
||||||
padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func lkInSize(p Protocol) uintptr {
|
func lkInSize(p Protocol) uintptr {
|
||||||
|
@ -683,7 +691,7 @@ type lkOut struct {
|
||||||
|
|
||||||
type accessIn struct {
|
type accessIn struct {
|
||||||
Mask uint32
|
Mask uint32
|
||||||
Padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type initIn struct {
|
type initIn struct {
|
||||||
|
@ -711,7 +719,7 @@ type interruptIn struct {
|
||||||
type bmapIn struct {
|
type bmapIn struct {
|
||||||
Block uint64
|
Block uint64
|
||||||
BlockSize uint32
|
BlockSize uint32
|
||||||
Padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type bmapOut struct {
|
type bmapOut struct {
|
||||||
|
@ -726,7 +734,7 @@ type inHeader struct {
|
||||||
Uid uint32
|
Uid uint32
|
||||||
Gid uint32
|
Gid uint32
|
||||||
Pid uint32
|
Pid uint32
|
||||||
Padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
const inHeaderSize = int(unsafe.Sizeof(inHeader{}))
|
const inHeaderSize = int(unsafe.Sizeof(inHeader{}))
|
||||||
|
@ -762,5 +770,5 @@ type notifyInvalInodeOut struct {
|
||||||
type notifyInvalEntryOut struct {
|
type notifyInvalEntryOut struct {
|
||||||
Parent uint64
|
Parent uint64
|
||||||
Namelen uint32
|
Namelen uint32
|
||||||
padding uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|
34
vendor/src/bazil.org/fuse/fuse_kernel_test.go
vendored
34
vendor/src/bazil.org/fuse/fuse_kernel_test.go
vendored
|
@ -7,7 +7,7 @@ import (
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOpenFlagsAccmodeMask(t *testing.T) {
|
func TestOpenFlagsAccmodeMaskReadWrite(t *testing.T) {
|
||||||
var f = fuse.OpenFlags(os.O_RDWR | os.O_SYNC)
|
var f = fuse.OpenFlags(os.O_RDWR | os.O_SYNC)
|
||||||
if g, e := f&fuse.OpenAccessModeMask, fuse.OpenReadWrite; g != e {
|
if g, e := f&fuse.OpenAccessModeMask, fuse.OpenReadWrite; g != e {
|
||||||
t.Fatalf("OpenAccessModeMask behaves wrong: %v: %o != %o", f, g, e)
|
t.Fatalf("OpenAccessModeMask behaves wrong: %v: %o != %o", f, g, e)
|
||||||
|
@ -23,6 +23,38 @@ func TestOpenFlagsAccmodeMask(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOpenFlagsAccmodeMaskReadOnly(t *testing.T) {
|
||||||
|
var f = fuse.OpenFlags(os.O_RDONLY | os.O_SYNC)
|
||||||
|
if g, e := f&fuse.OpenAccessModeMask, fuse.OpenReadOnly; g != e {
|
||||||
|
t.Fatalf("OpenAccessModeMask behaves wrong: %v: %o != %o", f, g, e)
|
||||||
|
}
|
||||||
|
if !f.IsReadOnly() {
|
||||||
|
t.Fatalf("IsReadOnly is wrong: %v", f)
|
||||||
|
}
|
||||||
|
if f.IsWriteOnly() {
|
||||||
|
t.Fatalf("IsWriteOnly is wrong: %v", f)
|
||||||
|
}
|
||||||
|
if f.IsReadWrite() {
|
||||||
|
t.Fatalf("IsReadWrite is wrong: %v", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpenFlagsAccmodeMaskWriteOnly(t *testing.T) {
|
||||||
|
var f = fuse.OpenFlags(os.O_WRONLY | os.O_SYNC)
|
||||||
|
if g, e := f&fuse.OpenAccessModeMask, fuse.OpenWriteOnly; g != e {
|
||||||
|
t.Fatalf("OpenAccessModeMask behaves wrong: %v: %o != %o", f, g, e)
|
||||||
|
}
|
||||||
|
if f.IsReadOnly() {
|
||||||
|
t.Fatalf("IsReadOnly is wrong: %v", f)
|
||||||
|
}
|
||||||
|
if !f.IsWriteOnly() {
|
||||||
|
t.Fatalf("IsWriteOnly is wrong: %v", f)
|
||||||
|
}
|
||||||
|
if f.IsReadWrite() {
|
||||||
|
t.Fatalf("IsReadWrite is wrong: %v", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestOpenFlagsString(t *testing.T) {
|
func TestOpenFlagsString(t *testing.T) {
|
||||||
var f = fuse.OpenFlags(os.O_RDWR | os.O_SYNC | os.O_APPEND)
|
var f = fuse.OpenFlags(os.O_RDWR | os.O_SYNC | os.O_APPEND)
|
||||||
if g, e := f.String(), "OpenReadWrite+OpenAppend+OpenSync"; g != e {
|
if g, e := f.String(), "OpenReadWrite+OpenAppend+OpenSync"; g != e {
|
||||||
|
|
7
vendor/src/bazil.org/fuse/fuse_linux.go
vendored
Normal file
7
vendor/src/bazil.org/fuse/fuse_linux.go
vendored
Normal file
|
@ -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
|
38
vendor/src/bazil.org/fuse/mount.go
vendored
Normal file
38
vendor/src/bazil.org/fuse/mount.go
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package fuse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrOSXFUSENotFound is returned from Mount when the OSXFUSE
|
||||||
|
// installation is not detected.
|
||||||
|
//
|
||||||
|
// Only happens on OS X. Make sure OSXFUSE is installed, or see
|
||||||
|
// OSXFUSELocations for customization.
|
||||||
|
ErrOSXFUSENotFound = errors.New("cannot locate OSXFUSE")
|
||||||
|
)
|
||||||
|
|
||||||
|
func neverIgnoreLine(line string) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func lineLogger(wg *sync.WaitGroup, prefix string, ignore func(line string) bool, r io.ReadCloser) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if ignore(line) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Printf("%s: %s", prefix, line)
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
log.Printf("%s, error reading: %v", prefix, err)
|
||||||
|
}
|
||||||
|
}
|
144
vendor/src/bazil.org/fuse/mount_darwin.go
vendored
144
vendor/src/bazil.org/fuse/mount_darwin.go
vendored
|
@ -1,22 +1,25 @@
|
||||||
package fuse
|
package fuse
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errNoAvail = errors.New("no available fuse devices")
|
var (
|
||||||
|
errNoAvail = errors.New("no available fuse devices")
|
||||||
|
errNotLoaded = errors.New("osxfuse is not loaded")
|
||||||
|
)
|
||||||
|
|
||||||
var errNotLoaded = errors.New("osxfusefs is not loaded")
|
func loadOSXFUSE(bin string) error {
|
||||||
|
cmd := exec.Command(bin)
|
||||||
func loadOSXFUSE() error {
|
|
||||||
cmd := exec.Command("/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs")
|
|
||||||
cmd.Dir = "/"
|
cmd.Dir = "/"
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
@ -24,11 +27,11 @@ func loadOSXFUSE() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func openOSXFUSEDev() (*os.File, error) {
|
func openOSXFUSEDev(devPrefix string) (*os.File, error) {
|
||||||
var f *os.File
|
var f *os.File
|
||||||
var err error
|
var err error
|
||||||
for i := uint64(0); ; i++ {
|
for i := uint64(0); ; i++ {
|
||||||
path := "/dev/osxfuse" + strconv.FormatUint(i, 10)
|
path := devPrefix + strconv.FormatUint(i, 10)
|
||||||
f, err = os.OpenFile(path, os.O_RDWR, 0000)
|
f, err = os.OpenFile(path, os.O_RDWR, 0000)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
|
@ -52,9 +55,42 @@ func openOSXFUSEDev() (*os.File, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func callMount(dir string, conf *mountConfig, f *os.File, ready chan<- struct{}, errp *error) error {
|
func handleMountOSXFUSE(helperName string, errCh chan<- error) func(line string) (ignore bool) {
|
||||||
bin := "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs"
|
var noMountpointPrefix = helperName + `: `
|
||||||
|
const noMountpointSuffix = `: No such file or directory`
|
||||||
|
return func(line string) (ignore bool) {
|
||||||
|
if strings.HasPrefix(line, noMountpointPrefix) && strings.HasSuffix(line, noMountpointSuffix) {
|
||||||
|
// re-extract it from the error message in case some layer
|
||||||
|
// changed the path
|
||||||
|
mountpoint := line[len(noMountpointPrefix) : len(line)-len(noMountpointSuffix)]
|
||||||
|
err := &MountpointDoesNotExistError{
|
||||||
|
Path: mountpoint,
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case errCh <- err:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
// not the first error; fall back to logging it
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isBoringMountOSXFUSEError returns whether the Wait error is
|
||||||
|
// uninteresting; exit status 64 is.
|
||||||
|
func isBoringMountOSXFUSEError(err error) bool {
|
||||||
|
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
|
||||||
|
if status, ok := err.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 64 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func callMount(bin string, daemonVar string, dir string, conf *mountConfig, f *os.File, ready chan<- struct{}, errp *error) error {
|
||||||
for k, v := range conf.options {
|
for k, v := range conf.options {
|
||||||
if strings.Contains(k, ",") || strings.Contains(v, ",") {
|
if strings.Contains(k, ",") || strings.Contains(v, ",") {
|
||||||
// Silly limitation but the mount helper does not
|
// Silly limitation but the mount helper does not
|
||||||
|
@ -77,50 +113,96 @@ func callMount(dir string, conf *mountConfig, f *os.File, ready chan<- struct{},
|
||||||
)
|
)
|
||||||
cmd.ExtraFiles = []*os.File{f}
|
cmd.ExtraFiles = []*os.File{f}
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = os.Environ()
|
||||||
|
// OSXFUSE <3.3.0
|
||||||
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=")
|
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=")
|
||||||
// TODO this is used for fs typenames etc, let app influence it
|
// OSXFUSE >=3.3.0
|
||||||
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_DAEMON_PATH="+bin)
|
cmd.Env = append(cmd.Env, "MOUNT_OSXFUSE_CALL_BY_LIB=")
|
||||||
var buf bytes.Buffer
|
|
||||||
cmd.Stdout = &buf
|
|
||||||
cmd.Stderr = &buf
|
|
||||||
|
|
||||||
err := cmd.Start()
|
daemon := os.Args[0]
|
||||||
if err != nil {
|
if daemonVar != "" {
|
||||||
return err
|
cmd.Env = append(cmd.Env, daemonVar+"="+daemon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("setting up mount_osxfusefs stderr: %v", err)
|
||||||
|
}
|
||||||
|
stderr, err := cmd.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("setting up mount_osxfusefs stderr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
return fmt.Errorf("mount_osxfusefs: %v", err)
|
||||||
|
}
|
||||||
|
helperErrCh := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
err := cmd.Wait()
|
var wg sync.WaitGroup
|
||||||
if err != nil {
|
wg.Add(2)
|
||||||
if buf.Len() > 0 {
|
go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout)
|
||||||
output := buf.Bytes()
|
helperName := path.Base(bin)
|
||||||
output = bytes.TrimRight(output, "\n")
|
go lineLogger(&wg, "mount helper error", handleMountOSXFUSE(helperName, helperErrCh), stderr)
|
||||||
msg := err.Error() + ": " + string(output)
|
wg.Wait()
|
||||||
err = errors.New(msg)
|
if err := cmd.Wait(); err != nil {
|
||||||
|
// see if we have a better error to report
|
||||||
|
select {
|
||||||
|
case helperErr := <-helperErrCh:
|
||||||
|
// log the Wait error if it's not what we expected
|
||||||
|
if !isBoringMountOSXFUSEError(err) {
|
||||||
|
log.Printf("mount helper failed: %v", err)
|
||||||
}
|
}
|
||||||
|
// and now return what we grabbed from stderr as the real
|
||||||
|
// error
|
||||||
|
*errp = helperErr
|
||||||
|
close(ready)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
// nope, fall back to generic message
|
||||||
}
|
}
|
||||||
*errp = err
|
|
||||||
|
*errp = fmt.Errorf("mount_osxfusefs: %v", err)
|
||||||
|
close(ready)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
*errp = nil
|
||||||
close(ready)
|
close(ready)
|
||||||
}()
|
}()
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
|
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
|
||||||
f, err := openOSXFUSEDev()
|
locations := conf.osxfuseLocations
|
||||||
|
if locations == nil {
|
||||||
|
locations = []OSXFUSEPaths{
|
||||||
|
OSXFUSELocationV3,
|
||||||
|
OSXFUSELocationV2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, loc := range locations {
|
||||||
|
if _, err := os.Stat(loc.Mount); os.IsNotExist(err) {
|
||||||
|
// try the other locations
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := openOSXFUSEDev(loc.DevicePrefix)
|
||||||
if err == errNotLoaded {
|
if err == errNotLoaded {
|
||||||
err = loadOSXFUSE()
|
err = loadOSXFUSE(loc.Load)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// try again
|
// try again
|
||||||
f, err = openOSXFUSEDev()
|
f, err = openOSXFUSEDev(loc.DevicePrefix)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = callMount(dir, conf, f, ready, errp)
|
err = callMount(loc.Mount, loc.DaemonVar, dir, conf, f, ready, errp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
return nil, ErrOSXFUSENotFound
|
||||||
|
}
|
||||||
|
|
74
vendor/src/bazil.org/fuse/mount_freebsd.go
vendored
74
vendor/src/bazil.org/fuse/mount_freebsd.go
vendored
|
@ -2,11 +2,51 @@ package fuse
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func handleMountFusefsStderr(errCh chan<- error) func(line string) (ignore bool) {
|
||||||
|
return func(line string) (ignore bool) {
|
||||||
|
const (
|
||||||
|
noMountpointPrefix = `mount_fusefs: `
|
||||||
|
noMountpointSuffix = `: No such file or directory`
|
||||||
|
)
|
||||||
|
if strings.HasPrefix(line, noMountpointPrefix) && strings.HasSuffix(line, noMountpointSuffix) {
|
||||||
|
// re-extract it from the error message in case some layer
|
||||||
|
// changed the path
|
||||||
|
mountpoint := line[len(noMountpointPrefix) : len(line)-len(noMountpointSuffix)]
|
||||||
|
err := &MountpointDoesNotExistError{
|
||||||
|
Path: mountpoint,
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case errCh <- err:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
// not the first error; fall back to logging it
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isBoringMountFusefsError returns whether the Wait error is
|
||||||
|
// uninteresting; exit status 1 is.
|
||||||
|
func isBoringMountFusefsError(err error) bool {
|
||||||
|
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
|
||||||
|
if status, ok := err.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
|
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
|
||||||
for k, v := range conf.options {
|
for k, v := range conf.options {
|
||||||
if strings.Contains(k, ",") || strings.Contains(v, ",") {
|
if strings.Contains(k, ",") || strings.Contains(v, ",") {
|
||||||
|
@ -31,9 +71,39 @@ func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*
|
||||||
)
|
)
|
||||||
cmd.ExtraFiles = []*os.File{f}
|
cmd.ExtraFiles = []*os.File{f}
|
||||||
|
|
||||||
out, err := cmd.CombinedOutput()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("mount_fusefs: %q, %v", out, err)
|
return nil, fmt.Errorf("setting up mount_fusefs stderr: %v", err)
|
||||||
|
}
|
||||||
|
stderr, err := cmd.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("setting up mount_fusefs stderr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
return nil, fmt.Errorf("mount_fusefs: %v", err)
|
||||||
|
}
|
||||||
|
helperErrCh := make(chan error, 1)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout)
|
||||||
|
go lineLogger(&wg, "mount helper error", handleMountFusefsStderr(helperErrCh), stderr)
|
||||||
|
wg.Wait()
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
// see if we have a better error to report
|
||||||
|
select {
|
||||||
|
case helperErr := <-helperErrCh:
|
||||||
|
// log the Wait error if it's not what we expected
|
||||||
|
if !isBoringMountFusefsError(err) {
|
||||||
|
log.Printf("mount helper failed: %v", err)
|
||||||
|
}
|
||||||
|
// and now return what we grabbed from stderr as the real
|
||||||
|
// error
|
||||||
|
return nil, helperErr
|
||||||
|
default:
|
||||||
|
// nope, fall back to generic message
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("mount_fusefs: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
close(ready)
|
close(ready)
|
||||||
|
|
68
vendor/src/bazil.org/fuse/mount_linux.go
vendored
68
vendor/src/bazil.org/fuse/mount_linux.go
vendored
|
@ -1,37 +1,60 @@
|
||||||
package fuse
|
package fuse
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func lineLogger(wg *sync.WaitGroup, prefix string, r io.ReadCloser) {
|
func handleFusermountStderr(errCh chan<- error) func(line string) (ignore bool) {
|
||||||
defer wg.Done()
|
return func(line string) (ignore bool) {
|
||||||
|
if line == `fusermount: failed to open /etc/fuse.conf: Permission denied` {
|
||||||
scanner := bufio.NewScanner(r)
|
|
||||||
for scanner.Scan() {
|
|
||||||
switch line := scanner.Text(); line {
|
|
||||||
case `fusermount: failed to open /etc/fuse.conf: Permission denied`:
|
|
||||||
// Silence this particular message, it occurs way too
|
// Silence this particular message, it occurs way too
|
||||||
// commonly and isn't very relevant to whether the mount
|
// commonly and isn't very relevant to whether the mount
|
||||||
// succeeds or not.
|
// succeeds or not.
|
||||||
continue
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
noMountpointPrefix = `fusermount: failed to access mountpoint `
|
||||||
|
noMountpointSuffix = `: No such file or directory`
|
||||||
|
)
|
||||||
|
if strings.HasPrefix(line, noMountpointPrefix) && strings.HasSuffix(line, noMountpointSuffix) {
|
||||||
|
// re-extract it from the error message in case some layer
|
||||||
|
// changed the path
|
||||||
|
mountpoint := line[len(noMountpointPrefix) : len(line)-len(noMountpointSuffix)]
|
||||||
|
err := &MountpointDoesNotExistError{
|
||||||
|
Path: mountpoint,
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case errCh <- err:
|
||||||
|
return true
|
||||||
default:
|
default:
|
||||||
log.Printf("%s: %s", prefix, line)
|
// not the first error; fall back to logging it
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
log.Printf("%s, error reading: %v", prefix, err)
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isBoringFusermountError returns whether the Wait error is
|
||||||
|
// uninteresting; exit status 1 is.
|
||||||
|
func isBoringFusermountError(err error) bool {
|
||||||
|
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
|
||||||
|
if status, ok := err.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) {
|
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) {
|
||||||
// linux mount is never delayed
|
// linux mount is never delayed
|
||||||
close(ready)
|
close(ready)
|
||||||
|
@ -70,11 +93,26 @@ func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (f
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
return nil, fmt.Errorf("fusermount: %v", err)
|
return nil, fmt.Errorf("fusermount: %v", err)
|
||||||
}
|
}
|
||||||
|
helperErrCh := make(chan error, 1)
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
go lineLogger(&wg, "mount helper output", stdout)
|
go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout)
|
||||||
go lineLogger(&wg, "mount helper error", stderr)
|
go lineLogger(&wg, "mount helper error", handleFusermountStderr(helperErrCh), stderr)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
if err := cmd.Wait(); err != nil {
|
if err := cmd.Wait(); err != nil {
|
||||||
|
// see if we have a better error to report
|
||||||
|
select {
|
||||||
|
case helperErr := <-helperErrCh:
|
||||||
|
// log the Wait error if it's not what we expected
|
||||||
|
if !isBoringFusermountError(err) {
|
||||||
|
log.Printf("mount helper failed: %v", err)
|
||||||
|
}
|
||||||
|
// and now return what we grabbed from stderr as the real
|
||||||
|
// error
|
||||||
|
return nil, helperErr
|
||||||
|
default:
|
||||||
|
// nope, fall back to generic message
|
||||||
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("fusermount: %v", err)
|
return nil, fmt.Errorf("fusermount: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
140
vendor/src/bazil.org/fuse/options.go
vendored
140
vendor/src/bazil.org/fuse/options.go
vendored
|
@ -15,6 +15,7 @@ type mountConfig struct {
|
||||||
options map[string]string
|
options map[string]string
|
||||||
maxReadahead uint32
|
maxReadahead uint32
|
||||||
initFlags InitFlags
|
initFlags InitFlags
|
||||||
|
osxfuseLocations []OSXFUSEPaths
|
||||||
}
|
}
|
||||||
|
|
||||||
func escapeComma(s string) string {
|
func escapeComma(s string) string {
|
||||||
|
@ -82,6 +83,63 @@ func VolumeName(name string) MountOption {
|
||||||
return volumeName(name)
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExclCreate causes O_EXCL flag to be set for only "truly" exclusive creates,
|
||||||
|
// i.e. create calls for which the initiator explicitly set the O_EXCL flag.
|
||||||
|
//
|
||||||
|
// OSXFUSE expects all create calls to return EEXIST in case the file
|
||||||
|
// already exists, regardless of whether O_EXCL was specified or not.
|
||||||
|
// To ensure this behavior, it normally sets OpenExclusive for all
|
||||||
|
// Create calls, regardless of whether the original call had it set.
|
||||||
|
// For distributed filesystems, that may force every file create to be
|
||||||
|
// a distributed consensus action, causing undesirable delays.
|
||||||
|
//
|
||||||
|
// This option makes the FUSE filesystem see the original flag value,
|
||||||
|
// and better decide when to ensure global consensus.
|
||||||
|
//
|
||||||
|
// Note that returning EEXIST on existing file create is still
|
||||||
|
// expected with OSXFUSE, regardless of the presence of the
|
||||||
|
// OpenExclusive flag.
|
||||||
|
//
|
||||||
|
// For more information, see
|
||||||
|
// https://github.com/osxfuse/osxfuse/issues/209
|
||||||
|
//
|
||||||
|
// OS X only. Others ignore this options.
|
||||||
|
// Requires OSXFUSE 3.4.1 or newer.
|
||||||
|
func ExclCreate() MountOption {
|
||||||
|
return exclCreate
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
var ErrCannotCombineAllowOtherAndAllowRoot = errors.New("cannot combine AllowOther and AllowRoot")
|
||||||
|
|
||||||
// AllowOther allows other users to access the file system.
|
// AllowOther allows other users to access the file system.
|
||||||
|
@ -112,6 +170,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
|
// DefaultPermissions makes the kernel enforce access control based on
|
||||||
// the file mode (as in chmod).
|
// the file mode (as in chmod).
|
||||||
//
|
//
|
||||||
|
@ -168,3 +244,67 @@ func WritebackCache() MountOption {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OSXFUSEPaths describes the paths used by an installed OSXFUSE
|
||||||
|
// version. See OSXFUSELocationV3 for typical values.
|
||||||
|
type OSXFUSEPaths struct {
|
||||||
|
// Prefix for the device file. At mount time, an incrementing
|
||||||
|
// number is suffixed until a free FUSE device is found.
|
||||||
|
DevicePrefix string
|
||||||
|
// Path of the load helper, used to load the kernel extension if
|
||||||
|
// no device files are found.
|
||||||
|
Load string
|
||||||
|
// Path of the mount helper, used for the actual mount operation.
|
||||||
|
Mount string
|
||||||
|
// Environment variable used to pass the path to the executable
|
||||||
|
// calling the mount helper.
|
||||||
|
DaemonVar string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default paths for OSXFUSE. See OSXFUSELocations.
|
||||||
|
var (
|
||||||
|
OSXFUSELocationV3 = OSXFUSEPaths{
|
||||||
|
DevicePrefix: "/dev/osxfuse",
|
||||||
|
Load: "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse",
|
||||||
|
Mount: "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse",
|
||||||
|
DaemonVar: "MOUNT_OSXFUSE_DAEMON_PATH",
|
||||||
|
}
|
||||||
|
OSXFUSELocationV2 = OSXFUSEPaths{
|
||||||
|
DevicePrefix: "/dev/osxfuse",
|
||||||
|
Load: "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs",
|
||||||
|
Mount: "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs",
|
||||||
|
DaemonVar: "MOUNT_FUSEFS_DAEMON_PATH",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// OSXFUSELocations sets where to look for OSXFUSE files. The
|
||||||
|
// arguments are all the possible locations. The previous locations
|
||||||
|
// are replaced.
|
||||||
|
//
|
||||||
|
// Without this option, OSXFUSELocationV3 and OSXFUSELocationV2 are
|
||||||
|
// used.
|
||||||
|
//
|
||||||
|
// OS X only. Others ignore this option.
|
||||||
|
func OSXFUSELocations(paths ...OSXFUSEPaths) MountOption {
|
||||||
|
return func(conf *mountConfig) error {
|
||||||
|
if len(paths) == 0 {
|
||||||
|
return errors.New("must specify at least one location for OSXFUSELocations")
|
||||||
|
}
|
||||||
|
// replace previous values, but make a copy so there's no
|
||||||
|
// worries about caller mutating their slice
|
||||||
|
conf.osxfuseLocations = append(conf.osxfuseLocations[:0], paths...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowNonEmptyMount allows the mounting over a non-empty directory.
|
||||||
|
//
|
||||||
|
// The files in it will be shadowed by the freshly created mount. By
|
||||||
|
// default these mounts are rejected to prevent accidental covering up
|
||||||
|
// of data, which could for example prevent automatic backup.
|
||||||
|
func AllowNonEmptyMount() MountOption {
|
||||||
|
return func(conf *mountConfig) error {
|
||||||
|
conf.options["nonempty"] = ""
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
64
vendor/src/bazil.org/fuse/options_daemon_timeout_test.go
vendored
Normal file
64
vendor/src/bazil.org/fuse/options_daemon_timeout_test.go
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
22
vendor/src/bazil.org/fuse/options_darwin.go
vendored
22
vendor/src/bazil.org/fuse/options_darwin.go
vendored
|
@ -11,3 +11,25 @@ func volumeName(name string) MountOption {
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func exclCreate(conf *mountConfig) error {
|
||||||
|
conf.options["excl_create"] = ""
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
19
vendor/src/bazil.org/fuse/options_freebsd.go
vendored
19
vendor/src/bazil.org/fuse/options_freebsd.go
vendored
|
@ -7,3 +7,22 @@ func localVolume(conf *mountConfig) error {
|
||||||
func volumeName(name string) MountOption {
|
func volumeName(name string) MountOption {
|
||||||
return dummyOption
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func exclCreate(conf *mountConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
16
vendor/src/bazil.org/fuse/options_linux.go
vendored
16
vendor/src/bazil.org/fuse/options_linux.go
vendored
|
@ -7,3 +7,19 @@ func localVolume(conf *mountConfig) error {
|
||||||
func volumeName(name string) MountOption {
|
func volumeName(name string) MountOption {
|
||||||
return dummyOption
|
return dummyOption
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func daemonTimeout(name string) MountOption {
|
||||||
|
return dummyOption
|
||||||
|
}
|
||||||
|
|
||||||
|
func noAppleXattr(conf *mountConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func noAppleDouble(conf *mountConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func exclCreate(conf *mountConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
4
vendor/src/bazil.org/fuse/options_test.go
vendored
4
vendor/src/bazil.org/fuse/options_test.go
vendored
|
@ -165,6 +165,7 @@ func TestMountOptionDefaultPermissions(t *testing.T) {
|
||||||
t.Skip("FreeBSD does not support DefaultPermissions")
|
t.Skip("FreeBSD does not support DefaultPermissions")
|
||||||
}
|
}
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
mnt, err := fstestutil.MountedT(t,
|
mnt, err := fstestutil.MountedT(t,
|
||||||
fstestutil.SimpleFS{
|
fstestutil.SimpleFS{
|
||||||
&fstestutil.ChildMap{"child": unwritableFile{}},
|
&fstestutil.ChildMap{"child": unwritableFile{}},
|
||||||
|
@ -172,7 +173,6 @@ func TestMountOptionDefaultPermissions(t *testing.T) {
|
||||||
nil,
|
nil,
|
||||||
fuse.DefaultPermissions(),
|
fuse.DefaultPermissions(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -203,12 +203,12 @@ func (createrDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fus
|
||||||
|
|
||||||
func TestMountOptionReadOnly(t *testing.T) {
|
func TestMountOptionReadOnly(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
mnt, err := fstestutil.MountedT(t,
|
mnt, err := fstestutil.MountedT(t,
|
||||||
fstestutil.SimpleFS{createrDir{}},
|
fstestutil.SimpleFS{createrDir{}},
|
||||||
nil,
|
nil,
|
||||||
fuse.ReadOnly(),
|
fuse.ReadOnly(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue