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",
|
||||
"repository": "https://github.com/bazil/fuse",
|
||||
"revision": "18419ee53958df28fcfc9490fe6123bd59e237bb",
|
||||
"branch": "HEAD"
|
||||
"revision": "371fbbdaa8987b715bdd21d6adc4c9b20155f748",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
"importpath": "github.com/elithrar/simple-scrypt",
|
||||
|
|
|
@ -24,16 +24,7 @@ func usage() {
|
|||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
mountpoint := flag.Arg(0)
|
||||
|
||||
func run(mountpoint string) error {
|
||||
c, err := fuse.Mount(
|
||||
mountpoint,
|
||||
fuse.FSName("clock"),
|
||||
|
@ -42,10 +33,14 @@ func main() {
|
|||
fuse.VolumeName("Clock filesystem"),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
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)
|
||||
filesys := &FS{
|
||||
// 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.
|
||||
go filesys.clockFile.update()
|
||||
if err := srv.Serve(filesys); err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the mount process has an error to report.
|
||||
<-c.Ready
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,18 +13,18 @@ import (
|
|||
"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, " %s MOUNTPOINT\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = Usage
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
Usage()
|
||||
usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
// workaround).
|
||||
//
|
||||
// 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")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -75,6 +77,7 @@ func Mounted(filesys fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Moun
|
|||
Error: serveErr,
|
||||
done: done,
|
||||
}
|
||||
filesys := fn(mnt)
|
||||
go func() {
|
||||
defer close(done)
|
||||
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.
|
||||
//
|
||||
// See Mounted for usage.
|
||||
// See MountedFunc 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) {
|
||||
func MountedFuncT(t testing.TB, fn func(*Mount) fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Mount, error) {
|
||||
if conf == nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
// 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 (
|
||||
"bytes"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fuseutil"
|
||||
)
|
||||
|
@ -89,6 +91,13 @@ type FSInodeGenerator interface {
|
|||
// simple, read-only filesystem.
|
||||
type Node interface {
|
||||
// Attr fills attr with the standard metadata for the node.
|
||||
//
|
||||
// Fields with reasonable defaults are prepopulated. For example,
|
||||
// all times are set to a fixed moment when the program started.
|
||||
//
|
||||
// If Inode is left as 0, a dynamic inode number is chosen.
|
||||
//
|
||||
// The result may be cached for the duration set in Valid.
|
||||
Attr(ctx context.Context, attr *fuse.Attr) error
|
||||
}
|
||||
|
||||
|
@ -105,9 +114,7 @@ type NodeSetattrer interface {
|
|||
// Setattr sets the standard metadata for the receiver.
|
||||
//
|
||||
// Note, this is also used to communicate changes in the size of
|
||||
// the file. Not implementing Setattr causes writes to be unable
|
||||
// to grow the file (except with OpenDirectIO, which bypasses that
|
||||
// mechanism).
|
||||
// the file, outside of Writes.
|
||||
//
|
||||
// req.Valid is a bitmask of what fields are actually being set.
|
||||
// For example, the method should not change the mode of the file
|
||||
|
@ -297,16 +304,17 @@ type HandleReader 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
|
||||
// only page-aligned writes spanning one or more pages. However,
|
||||
// you should not rely on this. To see individual requests as
|
||||
// submitted by the file system clients, set OpenDirectIO.
|
||||
//
|
||||
// Note that file size changes are communicated through Setattr.
|
||||
// Writes beyond the size of the file as reported by Attr are not
|
||||
// even attempted (except in OpenDirectIO mode).
|
||||
// Writes that grow the file are expected to update the file size
|
||||
// (as seen through Attr). Note that file size changes are
|
||||
// communicated also through Setattr.
|
||||
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.
|
||||
Debug func(msg interface{})
|
||||
|
||||
// Function to create new contexts. If nil, use
|
||||
// context.Background.
|
||||
// Function to put things into context for processing the request.
|
||||
// The returned context must have ctx as its parent.
|
||||
//
|
||||
// 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
|
||||
|
@ -342,14 +352,11 @@ func New(conn *fuse.Conn, config *Config) *Server {
|
|||
}
|
||||
if config != nil {
|
||||
s.debug = config.Debug
|
||||
s.context = config.GetContext
|
||||
s.context = config.WithContext
|
||||
}
|
||||
if s.debug == nil {
|
||||
s.debug = fuse.Debug
|
||||
}
|
||||
if s.context == nil {
|
||||
s.context = context.Background
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -357,7 +364,7 @@ type Server struct {
|
|||
// set in New
|
||||
conn *fuse.Conn
|
||||
debug func(msg interface{})
|
||||
context func() context.Context
|
||||
context func(ctx context.Context, req fuse.Request) context.Context
|
||||
|
||||
// set once at Serve time
|
||||
fs FS
|
||||
|
@ -494,7 +501,7 @@ func (c *Server) saveNode(inode uint64, node Node) (id fuse.NodeID, gen uint64)
|
|||
}
|
||||
sn.generation = c.nodeGen
|
||||
c.nodeRef[node] = id
|
||||
return
|
||||
return id, sn.generation
|
||||
}
|
||||
|
||||
func (c *Server) saveHandle(handle Handle, nodeID fuse.NodeID) (id fuse.HandleID) {
|
||||
|
@ -601,7 +608,7 @@ type logResponseHeader struct {
|
|||
}
|
||||
|
||||
func (m logResponseHeader) String() string {
|
||||
return fmt.Sprintf("ID=%#x", m.ID)
|
||||
return fmt.Sprintf("ID=%v", m.ID)
|
||||
}
|
||||
|
||||
type response struct {
|
||||
|
@ -626,21 +633,21 @@ func (r response) errstr() string {
|
|||
func (r response) String() string {
|
||||
switch {
|
||||
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 != "":
|
||||
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:
|
||||
// make sure (seemingly) empty values are readable
|
||||
switch r.Out.(type) {
|
||||
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:
|
||||
return fmt.Sprintf("-> %s [% x]", r.Request, r.Out)
|
||||
return fmt.Sprintf("-> [%v] %s [% x]", r.Request, r.Op, r.Out)
|
||||
default:
|
||||
return fmt.Sprintf("-> %s %s", r.Request, r.Out)
|
||||
return fmt.Sprintf("-> [%v] %v", r.Request, r.Out)
|
||||
}
|
||||
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 {
|
||||
switch {
|
||||
case n.Out != nil:
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "=> %s %v", n.Op, n.Node)
|
||||
if n.Out != nil {
|
||||
// make sure (seemingly) empty values are readable
|
||||
switch n.Out.(type) {
|
||||
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:
|
||||
return fmt.Sprintf("=> %s %d [% x] Err:%v", n.Op, n.Node, n.Out, n.Err)
|
||||
fmt.Fprintf(&buf, " [% x]", n.Out)
|
||||
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 {
|
||||
|
@ -685,7 +695,7 @@ type logLinkRequestOldNodeNotFound struct {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -694,7 +704,7 @@ type renameNewDirNodeNotFound struct {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -717,13 +727,52 @@ func (h handlerPanickedError) Errno() fuse.Errno {
|
|||
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) {
|
||||
s.EntryValid = entryValidTime
|
||||
}
|
||||
|
||||
func (c *Server) serve(r fuse.Request) {
|
||||
ctx, cancel := context.WithCancel(c.context())
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
parentCtx := ctx
|
||||
if c.context != nil {
|
||||
ctx = c.context(ctx, r)
|
||||
}
|
||||
|
||||
req := &serveRequest{Request: r, cancel: cancel}
|
||||
|
||||
|
@ -800,6 +849,7 @@ func (c *Server) serve(r fuse.Request) {
|
|||
c.meta.Unlock()
|
||||
}
|
||||
|
||||
var responded bool
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
const size = 1 << 16
|
||||
|
@ -813,114 +863,132 @@ func (c *Server) serve(r fuse.Request) {
|
|||
}
|
||||
done(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) {
|
||||
default:
|
||||
// Note: To FUSE, ENOSYS means "this server never implements this request."
|
||||
// It would be inappropriate to return ENOSYS for other operations in this
|
||||
// switch that might only be unavailable in some contexts, not all.
|
||||
done(fuse.ENOSYS)
|
||||
r.RespondError(fuse.ENOSYS)
|
||||
return fuse.ENOSYS
|
||||
|
||||
case *fuse.StatfsRequest:
|
||||
s := &fuse.StatfsResponse{}
|
||||
if fs, ok := c.fs.(FSStatfser); ok {
|
||||
if err := fs.Statfs(ctx, r, s); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
// Node operations.
|
||||
case *fuse.GetattrRequest:
|
||||
s := &fuse.GetattrResponse{}
|
||||
if n, ok := node.(NodeGetattrer); ok {
|
||||
if err := n.Getattr(ctx, r, s); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := snode.attr(ctx, &s.Attr); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
case *fuse.SetattrRequest:
|
||||
s := &fuse.SetattrResponse{}
|
||||
if n, ok := node.(NodeSetattrer); ok {
|
||||
if err := n.Setattr(ctx, r, s); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
break
|
||||
}
|
||||
|
||||
if err := snode.attr(ctx, &s.Attr); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
case *fuse.SymlinkRequest:
|
||||
s := &fuse.SymlinkResponse{}
|
||||
initLookupResponse(&s.LookupResponse)
|
||||
n, ok := node.(NodeSymlinker)
|
||||
if !ok {
|
||||
done(fuse.EIO) // XXX or EPERM like Mkdir?
|
||||
r.RespondError(fuse.EIO)
|
||||
break
|
||||
return fuse.EIO // XXX or EPERM like Mkdir?
|
||||
}
|
||||
n2, err := n.Symlink(ctx, r)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.NewName, n2); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
case *fuse.ReadlinkRequest:
|
||||
n, ok := node.(NodeReadlinker)
|
||||
if !ok {
|
||||
done(fuse.EIO) /// XXX or EPERM?
|
||||
r.RespondError(fuse.EIO)
|
||||
break
|
||||
return fuse.EIO /// XXX or EPERM?
|
||||
}
|
||||
target, err := n.Readlink(ctx, r)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(target)
|
||||
r.Respond(target)
|
||||
return nil
|
||||
|
||||
case *fuse.LinkRequest:
|
||||
n, ok := node.(NodeLinker)
|
||||
if !ok {
|
||||
done(fuse.EIO) /// XXX or EPERM?
|
||||
r.RespondError(fuse.EIO)
|
||||
break
|
||||
return fuse.EIO /// XXX or EPERM?
|
||||
}
|
||||
c.meta.Lock()
|
||||
var oldNode *serveNode
|
||||
|
@ -933,52 +1001,43 @@ func (c *Server) serve(r fuse.Request) {
|
|||
Request: r.Hdr(),
|
||||
In: r,
|
||||
})
|
||||
done(fuse.EIO)
|
||||
r.RespondError(fuse.EIO)
|
||||
break
|
||||
return fuse.EIO
|
||||
}
|
||||
n2, err := n.Link(ctx, r, oldNode.node)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
s := &fuse.LookupResponse{}
|
||||
initLookupResponse(s)
|
||||
if err := c.saveLookup(ctx, s, snode, r.NewName, n2); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
case *fuse.RemoveRequest:
|
||||
n, ok := node.(NodeRemover)
|
||||
if !ok {
|
||||
done(fuse.EIO) /// XXX or EPERM?
|
||||
r.RespondError(fuse.EIO)
|
||||
break
|
||||
return fuse.EIO /// XXX or EPERM?
|
||||
}
|
||||
err := n.Remove(ctx, r)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(nil)
|
||||
r.Respond()
|
||||
return nil
|
||||
|
||||
case *fuse.AccessRequest:
|
||||
if n, ok := node.(NodeAccesser); ok {
|
||||
if err := n.Access(ctx, r); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
}
|
||||
done(nil)
|
||||
r.Respond()
|
||||
return nil
|
||||
|
||||
case *fuse.LookupRequest:
|
||||
var n2 Node
|
||||
|
@ -990,45 +1049,35 @@ func (c *Server) serve(r fuse.Request) {
|
|||
} else if n, ok := node.(NodeRequestLookuper); ok {
|
||||
n2, err = n.Lookup(ctx, r, s)
|
||||
} else {
|
||||
done(fuse.ENOENT)
|
||||
r.RespondError(fuse.ENOENT)
|
||||
break
|
||||
return fuse.ENOENT
|
||||
}
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
if err := c.saveLookup(ctx, s, snode, r.Name, n2); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
case *fuse.MkdirRequest:
|
||||
s := &fuse.MkdirResponse{}
|
||||
initLookupResponse(&s.LookupResponse)
|
||||
n, ok := node.(NodeMkdirer)
|
||||
if !ok {
|
||||
done(fuse.EPERM)
|
||||
r.RespondError(fuse.EPERM)
|
||||
break
|
||||
return fuse.EPERM
|
||||
}
|
||||
n2, err := n.Mkdir(ctx, r)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.Name, n2); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
case *fuse.OpenRequest:
|
||||
s := &fuse.OpenResponse{}
|
||||
|
@ -1036,121 +1085,99 @@ func (c *Server) serve(r fuse.Request) {
|
|||
if n, ok := node.(NodeOpener); ok {
|
||||
hh, err := n.Open(ctx, r, s)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
h2 = hh
|
||||
} else {
|
||||
h2 = node
|
||||
}
|
||||
s.Handle = c.saveHandle(h2, hdr.Node)
|
||||
s.Handle = c.saveHandle(h2, r.Hdr().Node)
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
case *fuse.CreateRequest:
|
||||
n, ok := node.(NodeCreater)
|
||||
if !ok {
|
||||
// If we send back ENOSYS, FUSE will try mknod+open.
|
||||
done(fuse.EPERM)
|
||||
r.RespondError(fuse.EPERM)
|
||||
break
|
||||
return fuse.EPERM
|
||||
}
|
||||
s := &fuse.CreateResponse{OpenResponse: fuse.OpenResponse{}}
|
||||
initLookupResponse(&s.LookupResponse)
|
||||
n2, h2, err := n.Create(ctx, r, s)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.Name, n2); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
s.Handle = c.saveHandle(h2, hdr.Node)
|
||||
s.Handle = c.saveHandle(h2, r.Hdr().Node)
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
case *fuse.GetxattrRequest:
|
||||
n, ok := node.(NodeGetxattrer)
|
||||
if !ok {
|
||||
done(fuse.ENOTSUP)
|
||||
r.RespondError(fuse.ENOTSUP)
|
||||
break
|
||||
return fuse.ENOTSUP
|
||||
}
|
||||
s := &fuse.GetxattrResponse{}
|
||||
err := n.Getxattr(ctx, r, s)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
if r.Size != 0 && uint64(len(s.Xattr)) > uint64(r.Size) {
|
||||
done(fuse.ERANGE)
|
||||
r.RespondError(fuse.ERANGE)
|
||||
break
|
||||
return fuse.ERANGE
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
case *fuse.ListxattrRequest:
|
||||
n, ok := node.(NodeListxattrer)
|
||||
if !ok {
|
||||
done(fuse.ENOTSUP)
|
||||
r.RespondError(fuse.ENOTSUP)
|
||||
break
|
||||
return fuse.ENOTSUP
|
||||
}
|
||||
s := &fuse.ListxattrResponse{}
|
||||
err := n.Listxattr(ctx, r, s)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
if r.Size != 0 && uint64(len(s.Xattr)) > uint64(r.Size) {
|
||||
done(fuse.ERANGE)
|
||||
r.RespondError(fuse.ERANGE)
|
||||
break
|
||||
return fuse.ERANGE
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
case *fuse.SetxattrRequest:
|
||||
n, ok := node.(NodeSetxattrer)
|
||||
if !ok {
|
||||
done(fuse.ENOTSUP)
|
||||
r.RespondError(fuse.ENOTSUP)
|
||||
break
|
||||
return fuse.ENOTSUP
|
||||
}
|
||||
err := n.Setxattr(ctx, r)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(nil)
|
||||
r.Respond()
|
||||
return nil
|
||||
|
||||
case *fuse.RemovexattrRequest:
|
||||
n, ok := node.(NodeRemovexattrer)
|
||||
if !ok {
|
||||
done(fuse.ENOTSUP)
|
||||
r.RespondError(fuse.ENOTSUP)
|
||||
break
|
||||
return fuse.ENOTSUP
|
||||
}
|
||||
err := n.Removexattr(ctx, r)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(nil)
|
||||
r.Respond()
|
||||
return nil
|
||||
|
||||
case *fuse.ForgetRequest:
|
||||
forget := c.dropNode(hdr.Node, r.N)
|
||||
forget := c.dropNode(r.Hdr().Node, r.N)
|
||||
if forget {
|
||||
n, ok := node.(NodeForgetter)
|
||||
if ok {
|
||||
|
@ -1159,26 +1186,29 @@ func (c *Server) serve(r fuse.Request) {
|
|||
}
|
||||
done(nil)
|
||||
r.Respond()
|
||||
return nil
|
||||
|
||||
// Handle operations.
|
||||
case *fuse.ReadRequest:
|
||||
shandle := c.getHandle(r.Handle)
|
||||
if shandle == nil {
|
||||
done(fuse.ESTALE)
|
||||
r.RespondError(fuse.ESTALE)
|
||||
return
|
||||
return fuse.ESTALE
|
||||
}
|
||||
handle := shandle.handle
|
||||
|
||||
s := &fuse.ReadResponse{Data: make([]byte, 0, r.Size)}
|
||||
if r.Dir {
|
||||
if h, ok := handle.(HandleReadDirAller); ok {
|
||||
// detect rewinddir(3) or similar seek and refresh
|
||||
// contents
|
||||
if r.Offset == 0 {
|
||||
shandle.readData = nil
|
||||
}
|
||||
|
||||
if shandle.readData == nil {
|
||||
dirs, err := h.ReadDirAll(ctx)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
var data []byte
|
||||
for _, dir := range dirs {
|
||||
|
@ -1192,16 +1222,14 @@ func (c *Server) serve(r fuse.Request) {
|
|||
fuseutil.HandleRead(r, s, shandle.readData)
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
break
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
if h, ok := handle.(HandleReadAller); ok {
|
||||
if shandle.readData == nil {
|
||||
data, err := h.ReadAll(ctx)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
if data == nil {
|
||||
data = []byte{}
|
||||
|
@ -1211,71 +1239,58 @@ func (c *Server) serve(r fuse.Request) {
|
|||
fuseutil.HandleRead(r, s, shandle.readData)
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
break
|
||||
return nil
|
||||
}
|
||||
h, ok := handle.(HandleReader)
|
||||
if !ok {
|
||||
fmt.Printf("NO READ FOR %T\n", handle)
|
||||
done(fuse.EIO)
|
||||
r.RespondError(fuse.EIO)
|
||||
break
|
||||
err := handleNotReaderError{handle: handle}
|
||||
return err
|
||||
}
|
||||
if err := h.Read(ctx, r, s); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
case *fuse.WriteRequest:
|
||||
shandle := c.getHandle(r.Handle)
|
||||
if shandle == nil {
|
||||
done(fuse.ESTALE)
|
||||
r.RespondError(fuse.ESTALE)
|
||||
return
|
||||
return fuse.ESTALE
|
||||
}
|
||||
|
||||
s := &fuse.WriteResponse{}
|
||||
if h, ok := shandle.handle.(HandleWriter); ok {
|
||||
if err := h.Write(ctx, r, s); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
break
|
||||
return nil
|
||||
}
|
||||
done(fuse.EIO)
|
||||
r.RespondError(fuse.EIO)
|
||||
return fuse.EIO
|
||||
|
||||
case *fuse.FlushRequest:
|
||||
shandle := c.getHandle(r.Handle)
|
||||
if shandle == nil {
|
||||
done(fuse.ESTALE)
|
||||
r.RespondError(fuse.ESTALE)
|
||||
return
|
||||
return fuse.ESTALE
|
||||
}
|
||||
handle := shandle.handle
|
||||
|
||||
if h, ok := handle.(HandleFlusher); ok {
|
||||
if err := h.Flush(ctx, r); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
}
|
||||
done(nil)
|
||||
r.Respond()
|
||||
return nil
|
||||
|
||||
case *fuse.ReleaseRequest:
|
||||
shandle := c.getHandle(r.Handle)
|
||||
if shandle == nil {
|
||||
done(fuse.ESTALE)
|
||||
r.RespondError(fuse.ESTALE)
|
||||
return
|
||||
return fuse.ESTALE
|
||||
}
|
||||
handle := shandle.handle
|
||||
|
||||
|
@ -1284,13 +1299,12 @@ func (c *Server) serve(r fuse.Request) {
|
|||
|
||||
if h, ok := handle.(HandleReleaser); ok {
|
||||
if err := h.Release(ctx, r); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
}
|
||||
done(nil)
|
||||
r.Respond()
|
||||
return nil
|
||||
|
||||
case *fuse.DestroyRequest:
|
||||
if fs, ok := c.fs.(FSDestroyer); ok {
|
||||
|
@ -1298,6 +1312,7 @@ func (c *Server) serve(r fuse.Request) {
|
|||
}
|
||||
done(nil)
|
||||
r.Respond()
|
||||
return nil
|
||||
|
||||
case *fuse.RenameRequest:
|
||||
c.meta.Lock()
|
||||
|
@ -1311,63 +1326,50 @@ func (c *Server) serve(r fuse.Request) {
|
|||
Request: r.Hdr(),
|
||||
In: r,
|
||||
})
|
||||
done(fuse.EIO)
|
||||
r.RespondError(fuse.EIO)
|
||||
break
|
||||
return fuse.EIO
|
||||
}
|
||||
n, ok := node.(NodeRenamer)
|
||||
if !ok {
|
||||
done(fuse.EIO) // XXX or EPERM like Mkdir?
|
||||
r.RespondError(fuse.EIO)
|
||||
break
|
||||
return fuse.EIO // XXX or EPERM like Mkdir?
|
||||
}
|
||||
err := n.Rename(ctx, r, newDirNode.node)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(nil)
|
||||
r.Respond()
|
||||
return nil
|
||||
|
||||
case *fuse.MknodRequest:
|
||||
n, ok := node.(NodeMknoder)
|
||||
if !ok {
|
||||
done(fuse.EIO)
|
||||
r.RespondError(fuse.EIO)
|
||||
break
|
||||
return fuse.EIO
|
||||
}
|
||||
n2, err := n.Mknod(ctx, r)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
s := &fuse.LookupResponse{}
|
||||
initLookupResponse(s)
|
||||
if err := c.saveLookup(ctx, s, snode, r.Name, n2); err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(s)
|
||||
r.Respond(s)
|
||||
return nil
|
||||
|
||||
case *fuse.FsyncRequest:
|
||||
n, ok := node.(NodeFsyncer)
|
||||
if !ok {
|
||||
done(fuse.EIO)
|
||||
r.RespondError(fuse.EIO)
|
||||
break
|
||||
return fuse.EIO
|
||||
}
|
||||
err := n.Fsync(ctx, r)
|
||||
if err != nil {
|
||||
done(err)
|
||||
r.RespondError(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
done(nil)
|
||||
r.Respond()
|
||||
return nil
|
||||
|
||||
case *fuse.InterruptRequest:
|
||||
c.meta.Lock()
|
||||
|
@ -1379,24 +1381,23 @@ func (c *Server) serve(r fuse.Request) {
|
|||
c.meta.Unlock()
|
||||
done(nil)
|
||||
r.Respond()
|
||||
return nil
|
||||
|
||||
/* case *FsyncdirRequest:
|
||||
done(ENOSYS)
|
||||
r.RespondError(ENOSYS)
|
||||
return ENOSYS
|
||||
|
||||
case *GetlkRequest, *SetlkRequest, *SetlkwRequest:
|
||||
done(ENOSYS)
|
||||
r.RespondError(ENOSYS)
|
||||
return ENOSYS
|
||||
|
||||
case *BmapRequest:
|
||||
done(ENOSYS)
|
||||
r.RespondError(ENOSYS)
|
||||
return ENOSYS
|
||||
|
||||
case *SetvolnameRequest, *GetxtimesRequest, *ExchangeRequest:
|
||||
done(ENOSYS)
|
||||
r.RespondError(ENOSYS)
|
||||
return ENOSYS
|
||||
*/
|
||||
}
|
||||
|
||||
panic("not reached")
|
||||
}
|
||||
|
||||
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"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -56,6 +57,25 @@ func (f fifo) Attr(ctx context.Context, a *fuse.Attr) error {
|
|||
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{}
|
||||
|
||||
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) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
t.Fatalf("readAll: %v", err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(data) != hi {
|
||||
t.Errorf("readAll = %q, want %q", data, hi)
|
||||
defer f.Close()
|
||||
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()
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
r.fileFlags.Record(req.FileFlags)
|
||||
resp.Size = len(req.Data)
|
||||
|
@ -412,6 +454,15 @@ func TestWriteFileFlags(t *testing.T) {
|
|||
_ = f.Close()
|
||||
|
||||
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 {
|
||||
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) {
|
||||
t.Parallel()
|
||||
f := &mkdir1{}
|
||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f}, nil)
|
||||
if err != nil {
|
||||
|
@ -601,6 +651,10 @@ func TestMkdir(t *testing.T) {
|
|||
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.RecordedMkdir(), want; g != e {
|
||||
t.Errorf("mkdir saw %+v, want %+v", g, e)
|
||||
}
|
||||
|
@ -610,6 +664,7 @@ func TestMkdir(t *testing.T) {
|
|||
|
||||
type create1file struct {
|
||||
fstestutil.File
|
||||
record.Creates
|
||||
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)
|
||||
return nil, nil, fuse.EPERM
|
||||
}
|
||||
flags := req.Flags
|
||||
|
||||
// OS X does not pass O_TRUNC here, Linux does; as this is a
|
||||
// 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
|
||||
}
|
||||
_, _, _ = f.f.Creates.Create(ctx, req, resp)
|
||||
return &f.f, &f.f, nil
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
t.Parallel()
|
||||
f := &create1{}
|
||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f}, nil)
|
||||
if err != nil {
|
||||
|
@ -658,12 +694,38 @@ func TestCreate(t *testing.T) {
|
|||
// uniform umask needed to make os.Create's 0666 into something
|
||||
// reproducible
|
||||
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 {
|
||||
t.Fatalf("create1 WriteFile: %v", err)
|
||||
}
|
||||
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()))
|
||||
if err != nil {
|
||||
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) {
|
||||
t.Parallel()
|
||||
if os.Getuid() != 0 {
|
||||
t.Skip("skipping unless root")
|
||||
}
|
||||
|
@ -907,15 +968,15 @@ func TestMknod(t *testing.T) {
|
|||
}
|
||||
defer mnt.Close()
|
||||
|
||||
defer syscall.Umask(syscall.Umask(0))
|
||||
err = syscall.Mknod(mnt.Dir+"/node", syscall.S_IFIFO|0666, 123)
|
||||
defer syscall.Umask(syscall.Umask(0022))
|
||||
err = syscall.Mknod(mnt.Dir+"/node", syscall.S_IFIFO|0660, 123)
|
||||
if err != nil {
|
||||
t.Fatalf("Mknod: %v", err)
|
||||
t.Fatalf("mknod: %v", err)
|
||||
}
|
||||
|
||||
want := fuse.MknodRequest{
|
||||
Name: "node",
|
||||
Mode: os.FileMode(os.ModeNamedPipe | 0666),
|
||||
Mode: os.FileMode(os.ModeNamedPipe | 0640),
|
||||
Rdev: uint32(123),
|
||||
}
|
||||
if runtime.GOOS == "linux" {
|
||||
|
@ -924,6 +985,13 @@ func TestMknod(t *testing.T) {
|
|||
// bit is portable.)
|
||||
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 {
|
||||
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:
|
||||
}
|
||||
<-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) {
|
||||
|
@ -999,46 +1098,71 @@ func TestInterrupt(t *testing.T) {
|
|||
defer mnt.Close()
|
||||
|
||||
// start a subprocess that can hang until signaled
|
||||
cmd := exec.Command("cat", mnt.Dir+"/child")
|
||||
|
||||
err = cmd.Start()
|
||||
child, err := childCmd("interrupt")
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
<-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 {
|
||||
t.Errorf("interrupt: cannot interrupt cat: %v", err)
|
||||
t.Errorf("cannot interrupt child: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
p, err := cmd.Process.Wait()
|
||||
p, err := child.Process.Wait()
|
||||
if err != nil {
|
||||
t.Errorf("interrupt: cat bork: %v", err)
|
||||
t.Errorf("child failed: %v", err)
|
||||
return
|
||||
}
|
||||
switch ws := p.Sys().(type) {
|
||||
case syscall.WaitStatus:
|
||||
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() {
|
||||
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() {
|
||||
t.Errorf("interrupt: expected cat to get a signal: %v", ws)
|
||||
} else {
|
||||
if ws.Signal() != os.Interrupt {
|
||||
t.Errorf("interrupt: cat got wrong signal: %v", ws)
|
||||
t.Fatalf("interrupt: expected child to die from signal: %v", ws)
|
||||
}
|
||||
if got := ws.Signal(); got != sig {
|
||||
t.Errorf("interrupt: child failed: signal %d", got)
|
||||
}
|
||||
default:
|
||||
if ws.Signaled() {
|
||||
t.Fatalf("interrupt: didn't expect child to exit with a signal: %v", ws)
|
||||
}
|
||||
if !ws.Exited() {
|
||||
t.Fatalf("interrupt: expected child to exit normally: %v", ws)
|
||||
}
|
||||
if status := ws.ExitStatus(); status != 0 {
|
||||
t.Errorf("interrupt: child failed: exit status %d", status)
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
@ -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
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
type chmod struct {
|
||||
|
@ -1364,6 +1652,10 @@ func (f *openNonSeekable) Open(ctx context.Context, req *fuse.OpenRequest, resp
|
|||
}
|
||||
|
||||
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()
|
||||
f := &openNonSeekable{}
|
||||
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)
|
||||
}
|
||||
}
|
||||
if g, e := a.attr.Count(), uint32(1); g != e {
|
||||
t.Errorf("wrong Attr call count: %d != %d", g, e)
|
||||
// With OSXFUSE 3.0.4, we seem to see typically two Attr calls by
|
||||
// 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...")
|
||||
|
@ -2123,7 +2427,7 @@ func TestInvalidateNodeAttr(t *testing.T) {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
@ -2133,9 +2437,13 @@ type invalidateData struct {
|
|||
t testing.TB
|
||||
attr 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)
|
||||
|
||||
|
@ -2143,7 +2451,7 @@ func (i *invalidateData) Attr(ctx context.Context, a *fuse.Attr) error {
|
|||
i.attr.Inc()
|
||||
i.t.Logf("Attr called, #%d", i.attr.Count())
|
||||
a.Mode = 0600
|
||||
a.Size = uint64(len(invalidateDataContent))
|
||||
a.Size = uint64(len(i.data.Load().(string)))
|
||||
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 {
|
||||
i.read.Inc()
|
||||
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
|
||||
}
|
||||
|
||||
func TestInvalidateNodeData(t *testing.T) {
|
||||
func TestInvalidateNodeDataInvalidatesAttr(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)
|
||||
|
@ -2179,31 +2488,93 @@ func TestInvalidateNodeData(t *testing.T) {
|
|||
}
|
||||
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++ {
|
||||
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)
|
||||
}
|
||||
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 {
|
||||
t.Errorf("wrong Read call count: %d != %d", g, e)
|
||||
}
|
||||
|
||||
t.Logf("invalidating...")
|
||||
a.data.Store(invalidateDataContent2)
|
||||
if err := mnt.Server.InvalidateNodeData(a); err != nil {
|
||||
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++ {
|
||||
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)
|
||||
}
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
func TestInvalidateNodeDataRange(t *testing.T) {
|
||||
func TestInvalidateNodeDataRangeMiss(t *testing.T) {
|
||||
// This test may see false positive failures when run under
|
||||
// extreme memory pressure.
|
||||
t.Parallel()
|
||||
|
@ -2267,14 +2638,11 @@ func TestInvalidateNodeDataRange(t *testing.T) {
|
|||
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 {
|
||||
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 {
|
||||
t.Fatalf("invalidate error: %v", err)
|
||||
}
|
||||
|
@ -2284,9 +2652,6 @@ func TestInvalidateNodeDataRange(t *testing.T) {
|
|||
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
|
||||
// should stay in cache.
|
||||
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 {
|
||||
fs.NodeRef
|
||||
t testing.TB
|
||||
|
@ -2380,13 +2795,13 @@ func (contextFile) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.O
|
|||
|
||||
func TestContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
const input = "kilroy was here"
|
||||
ctx = context.WithValue(ctx, &contextFileSentinel, input)
|
||||
mnt, err := fstestutil.MountedT(t,
|
||||
fstestutil.SimpleFS{&fstestutil.ChildMap{"child": contextFile{}}},
|
||||
&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 {
|
||||
t.Fatal(err)
|
||||
|
@ -2401,3 +2816,28 @@ func TestContext(t *testing.T) {
|
|||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -179,11 +198,15 @@ func (e *OldVersionError) Error() string {
|
|||
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 {
|
||||
req, err := c.ReadRequest()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return fmt.Errorf("missing init, got EOF")
|
||||
return ErrClosedWithoutInit
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -212,7 +235,7 @@ func initMount(c *Conn, conf *mountConfig) error {
|
|||
s := &InitResponse{
|
||||
Library: proto,
|
||||
MaxReadahead: conf.maxReadahead,
|
||||
MaxWrite: 128 * 1024,
|
||||
MaxWrite: maxWrite,
|
||||
Flags: InitBigWrites | conf.initFlags,
|
||||
}
|
||||
r.Respond(s)
|
||||
|
@ -235,15 +258,27 @@ type Request interface {
|
|||
// A RequestID identifies an active FUSE request.
|
||||
type RequestID uint64
|
||||
|
||||
func (r RequestID) String() string {
|
||||
return fmt.Sprintf("%#x", uint64(r))
|
||||
}
|
||||
|
||||
// A NodeID is a number identifying a directory or file.
|
||||
// It must be unique among IDs returned in LookupResponses
|
||||
// that have not yet been forgotten by ForgetRequests.
|
||||
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.
|
||||
// It only needs to be unique while the directory or file is open.
|
||||
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.
|
||||
const RootID NodeID = rootID
|
||||
|
||||
|
@ -261,7 +296,7 @@ type Header struct {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -368,9 +403,6 @@ func (h *Header) RespondError(err error) {
|
|||
h.respond(buf)
|
||||
}
|
||||
|
||||
// Maximum file write size we are prepared to receive from the kernel.
|
||||
const maxWrite = 16 * 1024 * 1024
|
||||
|
||||
// All requests read from the kernel, without data, are shorter than
|
||||
// this.
|
||||
var maxRequestSize = syscall.Getpagesize()
|
||||
|
@ -985,7 +1017,33 @@ loop:
|
|||
case opGetxtimes:
|
||||
panic("opGetxtimes")
|
||||
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
|
||||
|
@ -1147,7 +1205,7 @@ type InitRequest struct {
|
|||
var _ = Request(&InitRequest{})
|
||||
|
||||
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.
|
||||
|
@ -1163,7 +1221,7 @@ type InitResponse struct {
|
|||
}
|
||||
|
||||
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.
|
||||
|
@ -1224,7 +1282,13 @@ type StatfsResponse struct {
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -1259,7 +1323,7 @@ type Attr struct {
|
|||
Ctime time.Time // time of last inode change
|
||||
Crtime time.Time // time of creation (OS X only)
|
||||
Mode os.FileMode // file mode
|
||||
Nlink uint32 // number of links
|
||||
Nlink uint32 // number of links (usually 1)
|
||||
Uid uint32 // owner uid
|
||||
Gid uint32 // group gid
|
||||
Rdev uint32 // device numbers
|
||||
|
@ -1267,6 +1331,10 @@ type Attr struct {
|
|||
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) {
|
||||
nano := t.UnixNano()
|
||||
sec = uint64(nano / 1e9)
|
||||
|
@ -1329,7 +1397,7 @@ type GetattrRequest struct {
|
|||
var _ = Request(&GetattrRequest{})
|
||||
|
||||
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.
|
||||
|
@ -1349,7 +1417,7 @@ type GetattrResponse struct {
|
|||
}
|
||||
|
||||
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.
|
||||
|
@ -1540,8 +1608,12 @@ type LookupResponse struct {
|
|||
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 {
|
||||
return fmt.Sprintf("Lookup %+v", *r)
|
||||
return fmt.Sprintf("Lookup %s", r.string())
|
||||
}
|
||||
|
||||
// An OpenRequest asks to open a file or directory
|
||||
|
@ -1572,8 +1644,12 @@ type OpenResponse struct {
|
|||
Flags OpenResponseFlags
|
||||
}
|
||||
|
||||
func (r *OpenResponse) string() string {
|
||||
return fmt.Sprintf("%v fl=%v", r.Handle, r.Flags)
|
||||
}
|
||||
|
||||
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).
|
||||
|
@ -1582,6 +1658,7 @@ type CreateRequest struct {
|
|||
Name string
|
||||
Flags OpenFlags
|
||||
Mode os.FileMode
|
||||
// Umask of the request. Not supported on OS X.
|
||||
Umask os.FileMode
|
||||
}
|
||||
|
||||
|
@ -1620,7 +1697,7 @@ type CreateResponse struct {
|
|||
}
|
||||
|
||||
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.
|
||||
|
@ -1628,6 +1705,7 @@ type MkdirRequest struct {
|
|||
Header `json:"-"`
|
||||
Name string
|
||||
Mode os.FileMode
|
||||
// Umask of the request. Not supported on OS X.
|
||||
Umask os.FileMode
|
||||
}
|
||||
|
||||
|
@ -1658,7 +1736,7 @@ type MkdirResponse struct {
|
|||
}
|
||||
|
||||
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.
|
||||
|
@ -1676,7 +1754,7 @@ type ReadRequest struct {
|
|||
var _ = Request(&ReadRequest{})
|
||||
|
||||
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.
|
||||
|
@ -1719,7 +1797,7 @@ type ReleaseRequest struct {
|
|||
var _ = Request(&ReleaseRequest{})
|
||||
|
||||
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.
|
||||
|
@ -1861,7 +1939,7 @@ type WriteRequest struct {
|
|||
var _ = Request(&WriteRequest{})
|
||||
|
||||
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 {
|
||||
|
@ -1895,7 +1973,7 @@ type WriteResponse struct {
|
|||
}
|
||||
|
||||
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,
|
||||
|
@ -1948,9 +2026,9 @@ func (r *SetattrRequest) String() string {
|
|||
fmt.Fprintf(&buf, " mtime=now")
|
||||
}
|
||||
if r.Valid.Handle() {
|
||||
fmt.Fprintf(&buf, " handle=%#x", r.Handle)
|
||||
fmt.Fprintf(&buf, " handle=%v", r.Handle)
|
||||
} else {
|
||||
fmt.Fprintf(&buf, " handle=INVALID-%#x", r.Handle)
|
||||
fmt.Fprintf(&buf, " handle=INVALID-%v", r.Handle)
|
||||
}
|
||||
if r.Valid.LockOwner() {
|
||||
fmt.Fprintf(&buf, " lockowner")
|
||||
|
@ -1965,7 +2043,7 @@ func (r *SetattrRequest) String() string {
|
|||
fmt.Fprintf(&buf, " bkuptime=%v", r.Bkuptime)
|
||||
}
|
||||
if r.Valid.Flags() {
|
||||
fmt.Fprintf(&buf, " flags=%#x", r.Flags)
|
||||
fmt.Fprintf(&buf, " flags=%v", r.Flags)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
@ -1988,7 +2066,7 @@ type SetattrResponse struct {
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -2004,7 +2082,7 @@ type FlushRequest struct {
|
|||
var _ = Request(&FlushRequest{})
|
||||
|
||||
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.
|
||||
|
@ -2065,6 +2143,10 @@ type SymlinkResponse struct {
|
|||
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.
|
||||
type ReadlinkRequest struct {
|
||||
Header `json:"-"`
|
||||
|
@ -2119,7 +2201,7 @@ type RenameRequest struct {
|
|||
var _ = Request(&RenameRequest{})
|
||||
|
||||
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() {
|
||||
|
@ -2132,6 +2214,7 @@ type MknodRequest struct {
|
|||
Name string
|
||||
Mode os.FileMode
|
||||
Rdev uint32
|
||||
// Umask of the request. Not supported on OS X.
|
||||
Umask os.FileMode
|
||||
}
|
||||
|
||||
|
@ -2191,3 +2274,30 @@ func (r *InterruptRequest) Respond() {
|
|||
func (r *InterruptRequest) String() string {
|
||||
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
|
||||
Namelen uint32
|
||||
Frsize uint32
|
||||
Padding uint32
|
||||
_ uint32
|
||||
Spare [6]uint32
|
||||
}
|
||||
|
||||
|
@ -159,9 +159,13 @@ const (
|
|||
OpenWriteOnly OpenFlags = syscall.O_WRONLY
|
||||
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
|
||||
OpenCreate OpenFlags = syscall.O_CREAT
|
||||
OpenDirectory OpenFlags = syscall.O_DIRECTORY
|
||||
OpenExclusive OpenFlags = syscall.O_EXCL
|
||||
OpenNonblock OpenFlags = syscall.O_NONBLOCK
|
||||
OpenSync OpenFlags = syscall.O_SYNC
|
||||
OpenTruncate OpenFlags = syscall.O_TRUNC
|
||||
)
|
||||
|
@ -213,11 +217,13 @@ func accModeName(flags OpenFlags) string {
|
|||
}
|
||||
|
||||
var openFlagNames = []flagName{
|
||||
{uint32(OpenCreate), "OpenCreate"},
|
||||
{uint32(OpenExclusive), "OpenExclusive"},
|
||||
{uint32(OpenTruncate), "OpenTruncate"},
|
||||
{uint32(OpenAppend), "OpenAppend"},
|
||||
{uint32(OpenCreate), "OpenCreate"},
|
||||
{uint32(OpenDirectory), "OpenDirectory"},
|
||||
{uint32(OpenExclusive), "OpenExclusive"},
|
||||
{uint32(OpenNonblock), "OpenNonblock"},
|
||||
{uint32(OpenSync), "OpenSync"},
|
||||
{uint32(OpenTruncate), "OpenTruncate"},
|
||||
}
|
||||
|
||||
// The OpenResponseFlags are returned in the OpenResponse.
|
||||
|
@ -254,6 +260,7 @@ const (
|
|||
InitAtomicTrunc InitFlags = 1 << 3
|
||||
InitExportSupport InitFlags = 1 << 4
|
||||
InitBigWrites InitFlags = 1 << 5
|
||||
// Do not mask file access modes with umask. Not supported on OS X.
|
||||
InitDontMask InitFlags = 1 << 6
|
||||
InitSpliceWrite InitFlags = 1 << 7
|
||||
InitSpliceMove InitFlags = 1 << 8
|
||||
|
@ -412,14 +419,14 @@ type forgetIn struct {
|
|||
|
||||
type getattrIn struct {
|
||||
GetattrFlags uint32
|
||||
dummy uint32
|
||||
_ uint32
|
||||
Fh uint64
|
||||
}
|
||||
|
||||
type attrOut struct {
|
||||
AttrValid uint64 // Cache timeout for the attributes
|
||||
AttrValidNsec uint32
|
||||
Dummy uint32
|
||||
_ uint32
|
||||
Attr attr
|
||||
}
|
||||
|
||||
|
@ -444,7 +451,7 @@ type mknodIn struct {
|
|||
Mode uint32
|
||||
Rdev uint32
|
||||
Umask uint32
|
||||
padding uint32
|
||||
_ uint32
|
||||
// "filename\x00" follows.
|
||||
}
|
||||
|
||||
|
@ -482,6 +489,7 @@ type exchangeIn struct {
|
|||
Olddir uint64
|
||||
Newdir uint64
|
||||
Options uint64
|
||||
// "oldname\x00newname\x00" follows
|
||||
}
|
||||
|
||||
type linkIn struct {
|
||||
|
@ -490,7 +498,7 @@ type linkIn struct {
|
|||
|
||||
type setattrInCommon struct {
|
||||
Valid uint32
|
||||
Padding uint32
|
||||
_ uint32
|
||||
Fh uint64
|
||||
Size uint64
|
||||
LockOwner uint64 // unused on OS X?
|
||||
|
@ -515,14 +523,14 @@ type openIn struct {
|
|||
type openOut struct {
|
||||
Fh uint64
|
||||
OpenFlags uint32
|
||||
Padding uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type createIn struct {
|
||||
Flags uint32
|
||||
Mode uint32
|
||||
Umask uint32
|
||||
padding uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
func createInSize(p Protocol) uintptr {
|
||||
|
@ -544,7 +552,7 @@ type releaseIn struct {
|
|||
type flushIn struct {
|
||||
Fh uint64
|
||||
FlushFlags uint32
|
||||
Padding uint32
|
||||
_ uint32
|
||||
LockOwner uint64
|
||||
}
|
||||
|
||||
|
@ -555,7 +563,7 @@ type readIn struct {
|
|||
ReadFlags uint32
|
||||
LockOwner uint64
|
||||
Flags uint32
|
||||
padding uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
func readInSize(p Protocol) uintptr {
|
||||
|
@ -590,7 +598,7 @@ type writeIn struct {
|
|||
WriteFlags uint32
|
||||
LockOwner uint64
|
||||
Flags uint32
|
||||
padding uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
func writeInSize(p Protocol) uintptr {
|
||||
|
@ -604,7 +612,7 @@ func writeInSize(p Protocol) uintptr {
|
|||
|
||||
type writeOut struct {
|
||||
Size uint32
|
||||
Padding uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
// The WriteFlags are passed in WriteRequest.
|
||||
|
@ -634,7 +642,7 @@ type statfsOut struct {
|
|||
type fsyncIn struct {
|
||||
Fh uint64
|
||||
FsyncFlags uint32
|
||||
Padding uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type setxattrInCommon struct {
|
||||
|
@ -648,7 +656,7 @@ func (setxattrInCommon) position() uint32 {
|
|||
|
||||
type getxattrInCommon struct {
|
||||
Size uint32
|
||||
Padding uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
func (getxattrInCommon) position() uint32 {
|
||||
|
@ -657,7 +665,7 @@ func (getxattrInCommon) position() uint32 {
|
|||
|
||||
type getxattrOut struct {
|
||||
Size uint32
|
||||
Padding uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type lkIn struct {
|
||||
|
@ -665,7 +673,7 @@ type lkIn struct {
|
|||
Owner uint64
|
||||
Lk fileLock
|
||||
LkFlags uint32
|
||||
padding uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
func lkInSize(p Protocol) uintptr {
|
||||
|
@ -683,7 +691,7 @@ type lkOut struct {
|
|||
|
||||
type accessIn struct {
|
||||
Mask uint32
|
||||
Padding uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type initIn struct {
|
||||
|
@ -711,7 +719,7 @@ type interruptIn struct {
|
|||
type bmapIn struct {
|
||||
Block uint64
|
||||
BlockSize uint32
|
||||
Padding uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type bmapOut struct {
|
||||
|
@ -726,7 +734,7 @@ type inHeader struct {
|
|||
Uid uint32
|
||||
Gid uint32
|
||||
Pid uint32
|
||||
Padding uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
const inHeaderSize = int(unsafe.Sizeof(inHeader{}))
|
||||
|
@ -762,5 +770,5 @@ type notifyInvalInodeOut struct {
|
|||
type notifyInvalEntryOut struct {
|
||||
Parent uint64
|
||||
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"
|
||||
)
|
||||
|
||||
func TestOpenFlagsAccmodeMask(t *testing.T) {
|
||||
func TestOpenFlagsAccmodeMaskReadWrite(t *testing.T) {
|
||||
var f = fuse.OpenFlags(os.O_RDWR | os.O_SYNC)
|
||||
if g, e := f&fuse.OpenAccessModeMask, fuse.OpenReadWrite; 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) {
|
||||
var f = fuse.OpenFlags(os.O_RDWR | os.O_SYNC | os.O_APPEND)
|
||||
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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"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() error {
|
||||
cmd := exec.Command("/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs")
|
||||
func loadOSXFUSE(bin string) error {
|
||||
cmd := exec.Command(bin)
|
||||
cmd.Dir = "/"
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
@ -24,11 +27,11 @@ func loadOSXFUSE() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func openOSXFUSEDev() (*os.File, error) {
|
||||
func openOSXFUSEDev(devPrefix string) (*os.File, error) {
|
||||
var f *os.File
|
||||
var err error
|
||||
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)
|
||||
if os.IsNotExist(err) {
|
||||
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 {
|
||||
bin := "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs"
|
||||
func handleMountOSXFUSE(helperName string, errCh chan<- error) func(line string) (ignore bool) {
|
||||
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 {
|
||||
if strings.Contains(k, ",") || strings.Contains(v, ",") {
|
||||
// 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.Env = os.Environ()
|
||||
// OSXFUSE <3.3.0
|
||||
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=")
|
||||
// TODO this is used for fs typenames etc, let app influence it
|
||||
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_DAEMON_PATH="+bin)
|
||||
var buf bytes.Buffer
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = &buf
|
||||
// OSXFUSE >=3.3.0
|
||||
cmd.Env = append(cmd.Env, "MOUNT_OSXFUSE_CALL_BY_LIB=")
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
daemon := os.Args[0]
|
||||
if daemonVar != "" {
|
||||
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() {
|
||||
err := cmd.Wait()
|
||||
if err != nil {
|
||||
if buf.Len() > 0 {
|
||||
output := buf.Bytes()
|
||||
output = bytes.TrimRight(output, "\n")
|
||||
msg := err.Error() + ": " + string(output)
|
||||
err = errors.New(msg)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout)
|
||||
helperName := path.Base(bin)
|
||||
go lineLogger(&wg, "mount helper error", handleMountOSXFUSE(helperName, 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 !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)
|
||||
}()
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
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 {
|
||||
err = loadOSXFUSE()
|
||||
err = loadOSXFUSE(loc.Load)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// try again
|
||||
f, err = openOSXFUSEDev()
|
||||
f, err = openOSXFUSEDev(loc.DevicePrefix)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = callMount(dir, conf, f, ready, errp)
|
||||
err = callMount(loc.Mount, loc.DaemonVar, dir, conf, f, ready, errp)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
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 (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"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) {
|
||||
for k, v := range conf.options {
|
||||
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}
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
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)
|
||||
|
|
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
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func lineLogger(wg *sync.WaitGroup, prefix string, r io.ReadCloser) {
|
||||
defer wg.Done()
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
switch line := scanner.Text(); line {
|
||||
case `fusermount: failed to open /etc/fuse.conf: Permission denied`:
|
||||
func handleFusermountStderr(errCh chan<- error) func(line string) (ignore bool) {
|
||||
return func(line string) (ignore bool) {
|
||||
if line == `fusermount: failed to open /etc/fuse.conf: Permission denied` {
|
||||
// Silence this particular message, it occurs way too
|
||||
// commonly and isn't very relevant to whether the mount
|
||||
// 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:
|
||||
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) {
|
||||
// linux mount is never delayed
|
||||
close(ready)
|
||||
|
@ -70,11 +93,26 @@ func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (f
|
|||
if err := cmd.Start(); err != nil {
|
||||
return nil, fmt.Errorf("fusermount: %v", err)
|
||||
}
|
||||
helperErrCh := make(chan error, 1)
|
||||
wg.Add(2)
|
||||
go lineLogger(&wg, "mount helper output", stdout)
|
||||
go lineLogger(&wg, "mount helper error", stderr)
|
||||
go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout)
|
||||
go lineLogger(&wg, "mount helper error", handleFusermountStderr(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 !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)
|
||||
}
|
||||
|
||||
|
|
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
|
||||
maxReadahead uint32
|
||||
initFlags InitFlags
|
||||
osxfuseLocations []OSXFUSEPaths
|
||||
}
|
||||
|
||||
func escapeComma(s string) string {
|
||||
|
@ -82,6 +83,63 @@ func VolumeName(name string) MountOption {
|
|||
return volumeName(name)
|
||||
}
|
||||
|
||||
// NoAppleDouble makes OSXFUSE disallow files with names used by OS X
|
||||
// to store extended attributes on file systems that do not support
|
||||
// them natively.
|
||||
//
|
||||
// Such file names are:
|
||||
//
|
||||
// ._*
|
||||
// .DS_Store
|
||||
//
|
||||
// OS X only. Others ignore this option.
|
||||
func NoAppleDouble() MountOption {
|
||||
return noAppleDouble
|
||||
}
|
||||
|
||||
// NoAppleXattr makes OSXFUSE disallow extended attributes with the
|
||||
// prefix "com.apple.". This disables persistent Finder state and
|
||||
// other such information.
|
||||
//
|
||||
// OS X only. Others ignore this option.
|
||||
func NoAppleXattr() MountOption {
|
||||
return noAppleXattr
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
||||
// 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
|
||||
// the file mode (as in chmod).
|
||||
//
|
||||
|
@ -168,3 +244,67 @@ func WritebackCache() MountOption {
|
|||
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
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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.Parallel()
|
||||
|
||||
mnt, err := fstestutil.MountedT(t,
|
||||
fstestutil.SimpleFS{
|
||||
&fstestutil.ChildMap{"child": unwritableFile{}},
|
||||
|
@ -172,7 +173,6 @@ func TestMountOptionDefaultPermissions(t *testing.T) {
|
|||
nil,
|
||||
fuse.DefaultPermissions(),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -203,12 +203,12 @@ func (createrDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fus
|
|||
|
||||
func TestMountOptionReadOnly(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mnt, err := fstestutil.MountedT(t,
|
||||
fstestutil.SimpleFS{createrDir{}},
|
||||
nil,
|
||||
fuse.ReadOnly(),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue