diff --git a/go.mod b/go.mod index 29f1b8eaf..dc8f62147 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/rclone/rclone go 1.14 require ( - bazil.org/fuse v0.0.0-20200524192727-fb710f7dfd05 + bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512 cloud.google.com/go v0.59.0 // indirect github.com/Azure/azure-pipeline-go v0.2.2 github.com/Azure/azure-storage-blob-go v0.10.0 @@ -20,6 +20,8 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/djherbis/times v1.2.0 github.com/dropbox/dropbox-sdk-go-unofficial v5.6.0+incompatible + github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813 // indirect + github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect github.com/gogo/protobuf v1.3.1 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/hanwen/go-fuse/v2 v2.0.3 @@ -49,6 +51,7 @@ require ( github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 + github.com/stephens2424/writerset v1.0.2 // indirect github.com/stretchr/testify v1.6.1 github.com/t3rm1n4l/go-mega v0.0.0-20200416171014-ffad7fcb44b8 github.com/xanzy/ssh-agent v0.2.1 diff --git a/go.sum b/go.sum index 3bfda6b22..ca3d921b9 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc h1:utDghgcjE8u+EBjHOgYT+dJPcnDF05KqWMBcjuJy510= +bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= +bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512 h1:SRsZGA7aFnCZETmov57jwPrWuTmaZK6+4R4v5FUe1/c= +bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= bazil.org/fuse v0.0.0-20200524192727-fb710f7dfd05 h1:UrYe9YkT4Wpm6D+zByEyCJQzDqTPXqTDUI7bZ41i9VE= bazil.org/fuse v0.0.0-20200524192727-fb710f7dfd05/go.mod h1:h0h5FBYpXThbvSfTqthw+0I4nmHnhTHkO5BoOHsBWqg= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= diff --git a/vendor/bazil.org/fuse/error_darwin.go b/vendor/bazil.org/fuse/error_darwin.go new file mode 100644 index 000000000..a3fb89ca2 --- /dev/null +++ b/vendor/bazil.org/fuse/error_darwin.go @@ -0,0 +1,17 @@ +package fuse + +import ( + "syscall" +) + +const ( + ENOATTR = Errno(syscall.ENOATTR) +) + +const ( + errNoXattr = ENOATTR +) + +func init() { + errnoNames[errNoXattr] = "ENOATTR" +} diff --git a/vendor/bazil.org/fuse/error_std.go b/vendor/bazil.org/fuse/error_std.go index af4946cde..398f43fbf 100644 --- a/vendor/bazil.org/fuse/error_std.go +++ b/vendor/bazil.org/fuse/error_std.go @@ -4,16 +4,17 @@ package fuse // across platforms. // // getxattr return value for "extended attribute does not exist" is -// ENODATA on Linux and apparently at least NetBSD. There may be a -// #define ENOATTR on Linux too, but the value is ENODATA in the -// actual syscalls. FreeBSD and OpenBSD have no ENODATA, only ENOATTR. -// ENOATTR is not in any of the standards, ENODATA exists but is only -// used for STREAMs. +// ENOATTR on OS X, and ENODATA on Linux and apparently at least +// NetBSD. There may be a #define ENOATTR on Linux too, but the value +// is ENODATA in the actual syscalls. FreeBSD and OpenBSD have no +// ENODATA, only ENOATTR. ENOATTR is not in any of the standards, +// ENODATA exists but is only used for STREAMs. // // Each platform will define it a errNoXattr constant, and this file // will enforce that it implements the right interfaces and hide the // implementation. // +// https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/getxattr.2.html // http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013090.html // http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013097.html // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html diff --git a/vendor/bazil.org/fuse/fs/serve.go b/vendor/bazil.org/fuse/fs/serve.go index 32ad8ee65..b754e3f92 100644 --- a/vendor/bazil.org/fuse/fs/serve.go +++ b/vendor/bazil.org/fuse/fs/serve.go @@ -6,7 +6,6 @@ import ( "bytes" "context" "encoding/binary" - "errors" "fmt" "hash/fnv" "io" @@ -20,7 +19,6 @@ import ( "bazil.org/fuse" "bazil.org/fuse/fuseutil" - "golang.org/x/sys/unix" ) const ( @@ -258,6 +256,7 @@ func nodeAttr(ctx context.Context, n Node, attr *fuse.Attr) error { attr.Atime = startTime attr.Mtime = startTime attr.Ctime = startTime + attr.Crtime = startTime if err := n.Attr(ctx, attr); err != nil { return err } @@ -323,86 +322,6 @@ type HandleReleaser interface { Release(ctx context.Context, req *fuse.ReleaseRequest) error } -type HandlePoller interface { - // Poll checks whether the handle is currently ready for I/O, and - // may request a wakeup when it is. - // - // Poll should always return quickly. Clients waiting for - // readiness can be woken up by passing the return value of - // PollRequest.Wakeup to fs.Server.NotifyPollWakeup or - // fuse.Conn.NotifyPollWakeup. - // - // To allow supporting poll for only some of your Nodes/Handles, - // the default behavior is to report immediate readiness. If your - // FS does not support polling and you want to minimize needless - // requests and log noise, implement NodePoller and return - // syscall.ENOSYS. - // - // The Go runtime uses epoll-based I/O whenever possible, even for - // regular files. - Poll(ctx context.Context, req *fuse.PollRequest, resp *fuse.PollResponse) error -} - -type NodePoller interface { - // Poll checks whether the node is currently ready for I/O, and - // may request a wakeup when it is. See HandlePoller. - Poll(ctx context.Context, req *fuse.PollRequest, resp *fuse.PollResponse) error -} - -// HandleLocker contains the common operations for all kinds of file -// locks. See also lock family specific interfaces: HandleFlockLocker, -// HandlePOSIXLocker. -type HandleLocker interface { - // Lock tries to acquire a lock on a byte range of the node. If a - // conflicting lock is already held, returns syscall.EAGAIN. - // - // LockRequest.LockOwner is a file-unique identifier for this - // lock, and will be seen in calls releasing this lock - // (UnlockRequest, ReleaseRequest, FlushRequest) and also - // in e.g. ReadRequest, WriteRequest. - Lock(ctx context.Context, req *fuse.LockRequest) error - - // LockWait acquires a lock on a byte range of the node, waiting - // until the lock can be obtained (or context is canceled). - LockWait(ctx context.Context, req *fuse.LockWaitRequest) error - - // Unlock releases the lock on a byte range of the node. Locks can - // be released also implicitly, see HandleFlockLocker and - // HandlePOSIXLocker. - Unlock(ctx context.Context, req *fuse.UnlockRequest) error - - // QueryLock returns the current state of locks held for the byte - // range of the node. - // - // See QueryLockRequest for details on how to respond. - // - // To simplify implementing this method, resp.Lock is prefilled to - // have Lock.Type F_UNLCK, and the whole struct should be - // overwritten for in case of conflicting locks. - QueryLock(ctx context.Context, req *fuse.QueryLockRequest, resp *fuse.QueryLockResponse) error -} - -// HandleFlockLocker describes locking behavior unique to flock (BSD) -// locks. See HandleLocker. -type HandleFlockLocker interface { - HandleLocker - - // Flock unlocking can also happen implicitly as part of Release, - // in which case Unlock is not called, and Release will have - // ReleaseFlags bit ReleaseFlockUnlock set. - HandleReleaser -} - -// HandlePOSIXLocker describes locking behavior unique to POSIX (fcntl -// F_SETLK) locks. See HandleLocker. -type HandlePOSIXLocker interface { - HandleLocker - - // POSIX unlocking can also happen implicitly as part of Flush, - // in which case Unlock is not called. - HandleFlusher -} - type Config struct { // Function to send debug log messages to. If nil, use fuse.Debug. // Note that changing this or fuse.Debug may not affect existing @@ -429,7 +348,6 @@ func New(conn *fuse.Conn, config *Config) *Server { conn: conn, req: map[fuse.RequestID]*serveRequest{}, nodeRef: map[Node]fuse.NodeID{}, - notifyWait: map[fuse.RequestID]chan<- *fuse.NotifyReply{}, dynamicInode: GenerateDynamicInode, } if config != nil { @@ -462,11 +380,6 @@ type Server struct { freeHandle []fuse.HandleID nodeGen uint64 - // pending notify upcalls to kernel - notifyMu sync.Mutex - notifySeq fuse.RequestID - notifyWait map[fuse.RequestID]chan<- *fuse.NotifyReply - // Used to ensure worker goroutines finish before Serve returns wg sync.WaitGroup } @@ -557,6 +470,12 @@ type serveHandle struct { readData []byte } +// NodeRef is deprecated. It remains here to decrease code churn on +// FUSE library users. You may remove it from your program now; +// returning the same Node values are now recognized automatically, +// without needing NodeRef. +type NodeRef struct{} + func (c *Server) saveNode(inode uint64, node Node) (id fuse.NodeID, gen uint64) { c.meta.Lock() defer c.meta.Unlock() @@ -603,14 +522,11 @@ type nodeRefcountDropBug struct { Node fuse.NodeID } -func (n nodeRefcountDropBug) String() string { +func (n *nodeRefcountDropBug) String() string { return fmt.Sprintf("bug: trying to drop %d of %d references to %v", n.N, n.Refs, n.Node) } -// dropNode decreases reference count for node with id by n. -// If reference count dropped to zero, returns true. -// Note that node is not guaranteed to be non-nil. -func (c *Server) dropNode(id fuse.NodeID, n uint64) (node Node, forget bool) { +func (c *Server) dropNode(id fuse.NodeID, n uint64) (forget bool) { c.meta.Lock() defer c.meta.Unlock() snode := c.node[id] @@ -623,7 +539,7 @@ func (c *Server) dropNode(id fuse.NodeID, n uint64) (node Node, forget bool) { // we may end up triggering Forget twice, but that's better // than not even once, and that's the best we can do - return nil, true + return true } if n > snode.refs { @@ -637,9 +553,9 @@ func (c *Server) dropNode(id fuse.NodeID, n uint64) (node Node, forget bool) { c.node[id] = nil delete(c.nodeRef, snode.node) c.freeNode = append(c.freeNode, id) - return snode.node, true + return true } - return nil, false + return false } func (c *Server) dropHandle(id fuse.HandleID) { @@ -675,7 +591,9 @@ func (c *Server) getHandle(id fuse.HandleID) (shandle *serveHandle) { } type request struct { - In interface{} `json:",omitempty"` + Op string + Request *fuse.Header + In interface{} `json:",omitempty"` } func (r request) String() string { @@ -757,57 +675,6 @@ func (n notification) String() string { return buf.String() } -type notificationRequest struct { - ID fuse.RequestID - Op string - Node fuse.NodeID - Out interface{} `json:",omitempty"` -} - -func (n notificationRequest) String() string { - var buf bytes.Buffer - fmt.Fprintf(&buf, ">> %s [ID=%d] %v", n.Op, n.ID, n.Node) - if n.Out != nil { - // make sure (seemingly) empty values are readable - switch n.Out.(type) { - case string: - fmt.Fprintf(&buf, " %q", n.Out) - case []byte: - fmt.Fprintf(&buf, " [% x]", n.Out) - default: - fmt.Fprintf(&buf, " %s", n.Out) - } - } - return buf.String() -} - -type notificationResponse struct { - ID fuse.RequestID - Op string - In interface{} `json:",omitempty"` - Err string `json:",omitempty"` -} - -func (n notificationResponse) String() string { - var buf bytes.Buffer - fmt.Fprintf(&buf, "<< [ID=%d] %s", n.ID, n.Op) - if n.In != nil { - // make sure (seemingly) empty values are readable - switch n.In.(type) { - case string: - fmt.Fprintf(&buf, " %q", n.In) - case []byte: - fmt.Fprintf(&buf, " [% x]", n.In) - default: - fmt.Fprintf(&buf, " %s", n.In) - } - } - if n.Err != "" { - fmt.Fprintf(&buf, " Err:%v", n.Err) - } - return buf.String() -} - type logMissingNode struct { MaxNode fuse.NodeID } @@ -896,15 +763,6 @@ func initLookupResponse(s *fuse.LookupResponse) { s.EntryValid = entryValidTime } -type logDuplicateRequestID struct { - New fuse.Request - Old fuse.Request -} - -func (m *logDuplicateRequestID) String() string { - return fmt.Sprintf("Duplicate request: new %v, old %v", m.New, m.Old) -} - func (c *Server) serve(r fuse.Request) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -915,15 +773,11 @@ func (c *Server) serve(r fuse.Request) { req := &serveRequest{Request: r, cancel: cancel} - switch r.(type) { - case *fuse.NotifyReply: - // don't log NotifyReply here, they're logged by the recipient - // as soon as we have decoded them to the right types - default: - c.debug(request{ - In: r, - }) - } + c.debug(request{ + Op: opName(r), + Request: r.Hdr(), + In: r, + }) var node Node var snode *serveNode c.meta.Lock() @@ -951,13 +805,15 @@ func (c *Server) serve(r fuse.Request) { } node = snode.node } - if old, found := c.req[hdr.ID]; found { - c.debug(logDuplicateRequestID{ - New: req.Request, - Old: old.Request, - }) + if c.req[hdr.ID] != nil { + // This happens with OSXFUSE. Assume it's okay and + // that we'll never see an interrupt for this one. + // Otherwise everything wedges. TODO: Report to OSXFUSE? + // + // TODO this might have been because of missing done() calls + } else { + c.req[hdr.ID] = req } - c.req[hdr.ID] = req c.meta.Unlock() // Call this before responding. @@ -1314,7 +1170,7 @@ func (c *Server) handleRequest(ctx context.Context, node Node, snode *serveNode, return nil case *fuse.ForgetRequest: - _, forget := c.dropNode(r.Hdr().Node, r.N) + forget := c.dropNode(r.Hdr().Node, r.N) if forget { n, ok := node.(NodeForgetter) if ok { @@ -1325,38 +1181,6 @@ func (c *Server) handleRequest(ctx context.Context, node Node, snode *serveNode, r.Respond() return nil - case *fuse.BatchForgetRequest: - // BatchForgetRequest is hard to unit test, as it - // fundamentally relies on something unprivileged userspace - // has little control over. A root-only, Linux-only test could - // be written with `echo 2 >/proc/sys/vm/drop_caches`, but - // that would still rely on timing, the number of batches and - // operation spread over them could vary, it wouldn't run in a - // typical container regardless of privileges, and it would - // degrade performance for the rest of the machine. It would - // still probably be worth doing, just not the most fun. - - // node is nil here because BatchForget as a message is not - // aimed at a any one node - for _, item := range r.Forget { - node, forget := c.dropNode(item.NodeID, item.N) - // node can be nil here if kernel vs our refcount were out - // of sync and multiple Forgets raced each other - if node == nil { - // nothing we can do about that - continue - } - if forget { - n, ok := node.(NodeForgetter) - if ok { - n.Forget() - } - } - } - done(nil) - r.Respond() - return nil - // Handle operations. case *fuse.ReadRequest: shandle := c.getHandle(r.Handle) @@ -1552,129 +1376,17 @@ func (c *Server) handleRequest(ctx context.Context, node Node, snode *serveNode, r.Respond() return nil - case *fuse.PollRequest: - shandle := c.getHandle(r.Handle) - if shandle == nil { - return syscall.ESTALE - } - s := &fuse.PollResponse{} - - if h, ok := shandle.handle.(HandlePoller); ok { - if err := h.Poll(ctx, r, s); err != nil { - return err - } - done(s) - r.Respond(s) - return nil - } - - if n, ok := node.(NodePoller); ok { - if err := n.Poll(ctx, r, s); err != nil { - return err - } - done(s) - r.Respond(s) - return nil - } - - // fallback to always claim ready - s.REvents = fuse.DefaultPollMask - done(s) - r.Respond(s) - return nil - - case *fuse.NotifyReply: - c.notifyMu.Lock() - w, ok := c.notifyWait[r.Hdr().ID] - if ok { - delete(c.notifyWait, r.Hdr().ID) - } - c.notifyMu.Unlock() - if !ok { - c.debug(notificationResponse{ - ID: r.Hdr().ID, - Op: "NotifyReply", - Err: "unknown ID", - }) - return nil - } - w <- r - return nil - - case *fuse.LockRequest: - shandle := c.getHandle(r.Handle) - if shandle == nil { - return syscall.ESTALE - } - h, ok := shandle.handle.(HandleLocker) - if !ok { - return syscall.ENOTSUP - } - if err := h.Lock(ctx, r); err != nil { - return err - } - done(nil) - r.Respond() - return nil - - case *fuse.LockWaitRequest: - shandle := c.getHandle(r.Handle) - if shandle == nil { - return syscall.ESTALE - } - h, ok := shandle.handle.(HandleLocker) - if !ok { - return syscall.ENOTSUP - } - if err := h.LockWait(ctx, r); err != nil { - return err - } - done(nil) - r.Respond() - return nil - - case *fuse.UnlockRequest: - shandle := c.getHandle(r.Handle) - if shandle == nil { - return syscall.ESTALE - } - h, ok := shandle.handle.(HandleLocker) - if !ok { - return syscall.ENOTSUP - } - if err := h.Unlock(ctx, r); err != nil { - return err - } - done(nil) - r.Respond() - return nil - - case *fuse.QueryLockRequest: - shandle := c.getHandle(r.Handle) - if shandle == nil { - return syscall.ESTALE - } - h, ok := shandle.handle.(HandleLocker) - if !ok { - return syscall.ENOTSUP - } - s := &fuse.QueryLockResponse{ - Lock: fuse.FileLock{ - Type: unix.F_UNLCK, - }, - } - if err := h.QueryLock(ctx, r, s); err != nil { - return err - } - done(s) - r.Respond(s) - return nil - /* case *FsyncdirRequest: return ENOSYS + case *GetlkRequest, *SetlkRequest, *SetlkwRequest: + return ENOSYS + case *BmapRequest: return ENOSYS + + case *SetvolnameRequest, *GetxtimesRequest, *ExchangeRequest: + return ENOSYS */ } @@ -1810,132 +1522,6 @@ func (s *Server) InvalidateEntry(parent Node, name string) error { return err } -type notifyStoreRetrieveDetail struct { - Off uint64 - Size uint64 -} - -func (i notifyStoreRetrieveDetail) String() string { - return fmt.Sprintf("Off:%d Size:%d", i.Off, i.Size) -} - -type notifyRetrieveReplyDetail struct { - Size uint64 -} - -func (i notifyRetrieveReplyDetail) String() string { - return fmt.Sprintf("Size:%d", i.Size) -} - -// NotifyStore puts data into the kernel page cache. -// -// Returns fuse.ErrNotCached if the kernel is not currently caching -// the node. -func (s *Server) NotifyStore(node Node, offset uint64, data []byte) error { - s.meta.Lock() - id, ok := s.nodeRef[node] - if ok { - snode := s.node[id] - snode.wg.Add(1) - defer snode.wg.Done() - } - s.meta.Unlock() - if !ok { - // This is what the kernel would have said, if we had been - // able to send this message; it's not cached. - return fuse.ErrNotCached - } - // Delay logging until after we can record the error too. We - // consider a /dev/fuse write to be instantaneous enough to not - // need separate before and after messages. - err := s.conn.NotifyStore(id, offset, data) - s.debug(notification{ - Op: "NotifyStore", - Node: id, - Out: notifyStoreRetrieveDetail{ - Off: offset, - Size: uint64(len(data)), - }, - Err: errstr(err), - }) - return err -} - -// NotifyRetrieve gets data from the kernel page cache. -// -// Returns fuse.ErrNotCached if the kernel is not currently caching -// the node. -func (s *Server) NotifyRetrieve(node Node, offset uint64, size uint32) ([]byte, error) { - s.meta.Lock() - id, ok := s.nodeRef[node] - if ok { - snode := s.node[id] - snode.wg.Add(1) - defer snode.wg.Done() - } - s.meta.Unlock() - if !ok { - // This is what the kernel would have said, if we had been - // able to send this message; it's not cached. - return nil, fuse.ErrNotCached - } - - ch := make(chan *fuse.NotifyReply, 1) - s.notifyMu.Lock() - const wraparoundThreshold = 1 << 63 - if s.notifySeq > wraparoundThreshold { - s.notifyMu.Unlock() - return nil, errors.New("running out of notify sequence numbers") - } - s.notifySeq++ - seq := s.notifySeq - s.notifyWait[seq] = ch - s.notifyMu.Unlock() - - s.debug(notificationRequest{ - ID: seq, - Op: "NotifyRetrieve", - Node: id, - Out: notifyStoreRetrieveDetail{ - Off: offset, - Size: uint64(size), - }, - }) - retrieval, err := s.conn.NotifyRetrieve(seq, id, offset, size) - if err != nil { - s.debug(notificationResponse{ - ID: seq, - Op: "NotifyRetrieve", - Err: errstr(err), - }) - return nil, err - } - - reply := <-ch - data := retrieval.Finish(reply) - s.debug(notificationResponse{ - ID: seq, - Op: "NotifyRetrieve", - In: notifyRetrieveReplyDetail{ - Size: uint64(len(data)), - }, - }) - return data, nil -} - -func (s *Server) NotifyPollWakeup(wakeup fuse.PollWakeup) error { - // Delay logging until after we can record the error too. We - // consider a /dev/fuse write to be instantaneous enough to not - // need separate before and after messages. - err := s.conn.NotifyPollWakeup(wakeup) - s.debug(notification{ - Op: "NotifyPollWakeup", - Out: wakeup, - Err: errstr(err), - }) - return err -} - // DataHandle returns a read-only Handle that satisfies reads // using the given data. func DataHandle(data []byte) Handle { diff --git a/vendor/bazil.org/fuse/fs/tree.go b/vendor/bazil.org/fuse/fs/tree.go index 9dc99a4f2..329be1389 100644 --- a/vendor/bazil.org/fuse/fs/tree.go +++ b/vendor/bazil.org/fuse/fs/tree.go @@ -76,7 +76,7 @@ func (t *tree) add(name string, n Node) { } func (t *tree) Attr(ctx context.Context, a *fuse.Attr) error { - a.Mode = os.ModeDir | 0o555 + a.Mode = os.ModeDir | 0555 return nil } diff --git a/vendor/bazil.org/fuse/fuse.go b/vendor/bazil.org/fuse/fuse.go index c301b4210..b774a25c5 100644 --- a/vendor/bazil.org/fuse/fuse.go +++ b/vendor/bazil.org/fuse/fuse.go @@ -20,7 +20,9 @@ // OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS // FITNESS FOR ANY PARTICULAR PURPOSE. -// Package fuse enables writing FUSE file systems on Linux and FreeBSD. +// Package fuse enables writing FUSE file systems on Linux, OS X, and FreeBSD. +// +// On OS X, it requires OSXFUSE (http://osxfuse.github.com/). // // There are two approaches to writing a FUSE file system. The first is to speak // the low-level message protocol, reading from a Conn using ReadRequest and @@ -105,7 +107,6 @@ import ( "fmt" "io" "os" - "strings" "sync" "syscall" "time" @@ -114,14 +115,11 @@ import ( // A Conn represents a connection to a mounted FUSE file system. type Conn struct { - // Always closed, mount is ready when Mount returns. - // - // Deprecated: Not used, OS X remnant. + // Ready is closed when the mount is complete or has failed. Ready <-chan struct{} - // Use error returned from Mount. - // - // Deprecated: Not used, OS X remnant. + // MountError stores any error from the mount process. Only valid + // after Ready is closed. MountError error // File handle for kernel communication. Only safe to access if @@ -130,10 +128,8 @@ type Conn struct { wio sync.RWMutex rio sync.RWMutex - // Protocol version negotiated with initRequest/initResponse. + // Protocol version negotiated with InitRequest/InitResponse. proto Protocol - // Feature flags negotiated with initRequest/initResponse. - flags InitFlags } // MountpointDoesNotExistError is an error returned when the @@ -153,6 +149,11 @@ func (e *MountpointDoesNotExistError) Error() string { // // After a successful return, caller must call Close to free // resources. +// +// Even on successful return, the new mount is not guaranteed to be +// visible until after Conn.Ready is closed. See Conn.MountError for +// possible errors. Incoming requests on Conn must be served to make +// progress. func Mount(dir string, options ...MountOption) (*Conn, error) { conf := mountConfig{ options: make(map[string]string), @@ -163,12 +164,11 @@ func Mount(dir string, options ...MountOption) (*Conn, error) { } } - ready := make(chan struct{}) - close(ready) + ready := make(chan struct{}, 1) c := &Conn{ Ready: ready, } - f, err := mount(dir, &conf) + f, err := mount(dir, &conf, ready, &c.MountError) if err != nil { return nil, err } @@ -176,7 +176,13 @@ func Mount(dir string, options ...MountOption) (*Conn, error) { if err := initMount(c, &conf); err != nil { c.Close() - _ = Unmount(dir) + 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 } @@ -204,7 +210,7 @@ func initMount(c *Conn, conf *mountConfig) error { } return err } - r, ok := req.(*initRequest) + r, ok := req.(*InitRequest) if !ok { return fmt.Errorf("missing init, got: %T", req) } @@ -226,14 +232,11 @@ func initMount(c *Conn, conf *mountConfig) error { } c.proto = proto - c.flags = r.Flags & (InitBigWrites | conf.initFlags) - s := &initResponse{ - Library: proto, - MaxReadahead: conf.maxReadahead, - Flags: c.flags, - MaxBackground: conf.maxBackground, - CongestionThreshold: conf.congestionThreshold, - MaxWrite: maxWrite, + s := &InitResponse{ + Library: proto, + MaxReadahead: conf.maxReadahead, + MaxWrite: maxWrite, + Flags: InitBigWrites | conf.initFlags, } r.Respond(s) return nil @@ -360,8 +363,8 @@ var errnoNames = map[Errno]string{ // Errno implements Error and ErrorNumber using a syscall.Errno. type Errno syscall.Errno -var _ ErrorNumber = Errno(0) -var _ error = Errno(0) +var _ = ErrorNumber(Errno(0)) +var _ = error(Errno(0)) func (e Errno) Errno() Errno { return e @@ -413,6 +416,7 @@ func ToErrno(err error) Errno { func (h *Header) RespondError(err error) { errno := ToErrno(err) // FUSE uses negative errors! + // TODO: File bug report against OSXFUSE: positive error causes kernel panic. buf := newBuffer(0) hOut := (*outHeader)(unsafe.Pointer(&buf[0])) hOut.Error = -int32(errno) @@ -495,7 +499,7 @@ func (m *message) Header() Header { // fileMode returns a Go os.FileMode from a Unix mode. func fileMode(unixMode uint32) os.FileMode { - mode := os.FileMode(unixMode & 0o777) + mode := os.FileMode(unixMode & 0777) switch unixMode & syscall.S_IFMT { case syscall.S_IFREG: // nothing @@ -563,22 +567,21 @@ func (c *Conn) Protocol() Protocol { return c.proto } -// Features reports the feature flags negotiated between the kernel and -// the FUSE library. See MountOption for how to influence features -// activated. -func (c *Conn) Features() InitFlags { - return c.flags -} - // ReadRequest returns the next FUSE request from the kernel. // // Caller must call either Request.Respond or Request.RespondError in // a reasonable time. Caller must not retain Request after that call. func (c *Conn) ReadRequest() (Request, error) { m := getMessage(c) +loop: c.rio.RLock() n, err := syscall.Read(c.fd(), m.buf) c.rio.RUnlock() + if err == syscall.EINTR { + // OSXFUSE sends EINTR to userspace when a request interrupt + // completed before it got sent to userspace? + goto loop + } if err != nil && err != syscall.ENODEV { putMessage(m) return nil, err @@ -600,6 +603,11 @@ func (c *Conn) ReadRequest() (Request, error) { m.hdr.Len = uint32(n) } + // OSXFUSE sometimes sends the wrong m.hdr.Len in a FUSE_WRITE message. + if m.hdr.Len < uint32(n) && m.hdr.Len >= uint32(unsafe.Sizeof(writeIn{})) && m.hdr.Opcode == opWrite { + m.hdr.Len = uint32(n) + } + if m.hdr.Len != uint32(n) { // prepare error message before returning m to pool err := fmt.Errorf("fuse: read %d opcode %d but expected %d", n, m.hdr.Opcode, m.hdr.Len) @@ -663,15 +671,18 @@ func (c *Conn) ReadRequest() (Request, error) { goto corrupt } req = &SetattrRequest{ - Header: m.Header(), - Valid: SetattrValid(in.Valid), - Handle: HandleID(in.Fh), - Size: in.Size, - Atime: time.Unix(int64(in.Atime), int64(in.AtimeNsec)), - Mtime: time.Unix(int64(in.Mtime), int64(in.MtimeNsec)), - Mode: fileMode(in.Mode), - Uid: in.Uid, - Gid: in.Gid, + Header: m.Header(), + Valid: SetattrValid(in.Valid), + Handle: HandleID(in.Fh), + Size: in.Size, + Atime: time.Unix(int64(in.Atime), int64(in.AtimeNsec)), + Mtime: time.Unix(int64(in.Mtime), int64(in.MtimeNsec)), + Mode: fileMode(in.Mode), + Uid: in.Uid, + Gid: in.Gid, + Bkuptime: in.BkupTime(), + Chgtime: in.Chgtime(), + Flags: in.Flags(), } case opReadlink: @@ -824,7 +835,7 @@ func (c *Conn) ReadRequest() (Request, error) { } if c.proto.GE(Protocol{7, 9}) { r.Flags = ReadFlags(in.ReadFlags) - r.LockOwner = LockOwner(in.LockOwner) + r.LockOwner = in.LockOwner r.FileFlags = openFlags(in.Flags) } req = r @@ -841,7 +852,7 @@ func (c *Conn) ReadRequest() (Request, error) { Flags: WriteFlags(in.WriteFlags), } if c.proto.GE(Protocol{7, 9}) { - r.LockOwner = LockOwner(in.LockOwner) + r.LockOwner = in.LockOwner r.FileFlags = openFlags(in.Flags) } buf := m.bytes()[writeInSize(c.proto):] @@ -867,7 +878,7 @@ func (c *Conn) ReadRequest() (Request, error) { Handle: HandleID(in.Fh), Flags: openFlags(in.Flags), ReleaseFlags: ReleaseFlags(in.ReleaseFlags), - LockOwner: LockOwner(in.LockOwner), + LockOwner: in.LockOwner, } case opFsync, opFsyncdir: @@ -899,10 +910,11 @@ func (c *Conn) ReadRequest() (Request, error) { } xattr = xattr[:in.Size] req = &SetxattrRequest{ - Header: m.Header(), - Flags: in.Flags, - Name: string(name[:i]), - Xattr: xattr, + Header: m.Header(), + Flags: in.Flags, + Position: in.position(), + Name: string(name[:i]), + Xattr: xattr, } case opGetxattr: @@ -916,9 +928,10 @@ func (c *Conn) ReadRequest() (Request, error) { goto corrupt } req = &GetxattrRequest{ - Header: m.Header(), - Name: string(name[:i]), - Size: in.Size, + Header: m.Header(), + Name: string(name[:i]), + Size: in.Size, + Position: in.position(), } case opListxattr: @@ -927,8 +940,9 @@ func (c *Conn) ReadRequest() (Request, error) { goto corrupt } req = &ListxattrRequest{ - Header: m.Header(), - Size: in.Size, + Header: m.Header(), + Size: in.Size, + Position: in.position(), } case opRemovexattr: @@ -950,7 +964,8 @@ func (c *Conn) ReadRequest() (Request, error) { req = &FlushRequest{ Header: m.Header(), Handle: HandleID(in.Fh), - LockOwner: LockOwner(in.LockOwner), + Flags: in.FlushFlags, + LockOwner: in.LockOwner, } case opInit: @@ -958,13 +973,20 @@ func (c *Conn) ReadRequest() (Request, error) { if m.len() < unsafe.Sizeof(*in) { goto corrupt } - req = &initRequest{ + req = &InitRequest{ Header: m.Header(), Kernel: Protocol{in.Major, in.Minor}, MaxReadahead: in.MaxReadahead, Flags: InitFlags(in.Flags), } + case opGetlk: + panic("opGetlk") + case opSetlk: + panic("opSetlk") + case opSetlkw: + panic("opSetlkw") + case opAccess: in := (*accessIn)(m.data()) if m.len() < unsafe.Sizeof(*in) { @@ -1020,94 +1042,38 @@ func (c *Conn) ReadRequest() (Request, error) { Header: m.Header(), } - case opNotifyReply: - req = &NotifyReply{ - Header: m.Header(), - msg: m, - } - - case opPoll: - in := (*pollIn)(m.data()) + // OS X + case opSetvolname: + panic("opSetvolname") + case opGetxtimes: + panic("opGetxtimes") + case opExchange: + in := (*exchangeIn)(m.data()) if m.len() < unsafe.Sizeof(*in) { goto corrupt } - req = &PollRequest{ - Header: m.Header(), - Handle: HandleID(in.Fh), - kh: in.Kh, - Flags: PollFlags(in.Flags), - Events: PollEvents(in.Events), - } - - case opBatchForget: - in := (*batchForgetIn)(m.data()) - if m.len() < unsafe.Sizeof(*in) { + 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 } - m.off += int(unsafe.Sizeof(*in)) - items := make([]BatchForgetItem, 0, in.Count) - for count := in.Count; count > 0; count-- { - one := (*forgetOne)(m.data()) - if m.len() < unsafe.Sizeof(*one) { - goto corrupt - } - m.off += int(unsafe.Sizeof(*one)) - items = append(items, BatchForgetItem{ - NodeID: NodeID(one.NodeID), - N: one.Nlookup, - }) - } - req = &BatchForgetRequest{ - Header: m.Header(), - Forget: items, - } - - case opSetlk, opSetlkw: - in := (*lkIn)(m.data()) - if m.len() < unsafe.Sizeof(*in) { + if oldNew[len(oldNew)-1] != '\x00' { goto corrupt } - tmp := &LockRequest{ - Header: m.Header(), - Handle: HandleID(in.Fh), - LockOwner: LockOwner(in.Owner), - Lock: FileLock{ - Start: in.Lk.Start, - End: in.Lk.End, - Type: LockType(in.Lk.Type), - PID: int32(in.Lk.PID), - }, - LockFlags: LockFlags(in.LkFlags), - } - switch { - case tmp.Lock.Type == LockUnlock: - req = (*UnlockRequest)(tmp) - case m.hdr.Opcode == opSetlkw: - req = (*LockWaitRequest)(tmp) - default: - req = tmp - } - - case opGetlk: - in := (*lkIn)(m.data()) - if m.len() < unsafe.Sizeof(*in) { + i := bytes.IndexByte(oldNew, '\x00') + if i < 0 { goto corrupt } - req = &QueryLockRequest{ - Header: m.Header(), - Handle: HandleID(in.Fh), - LockOwner: LockOwner(in.Owner), - Lock: FileLock{ - Start: in.Lk.Start, - End: in.Lk.End, - Type: LockType(in.Lk.Type), - // fuse.h claims this field is a uint32, but then the - // spec talks about -1 as a value, and using int as - // the C definition is pretty common. Make our API use - // a signed integer. - PID: int32(in.Lk.PID), - }, - LockFlags: LockFlags(in.LkFlags), + 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 } } @@ -1121,11 +1087,8 @@ corrupt: unrecognized: // Unrecognized message. // Assume higher-level code will send a "no idea what you mean" error. - req = &UnrecognizedRequest{ - Header: m.Header(), - Opcode: m.hdr.Opcode, - } - return req, nil + h := m.Header() + return &h, nil } type bugShortKernelWrite struct { @@ -1201,10 +1164,10 @@ var ( ErrNotCached = notCachedError{} ) -// sendNotify sends a notification to kernel. +// sendInvalidate sends an invalidate notification to kernel. // // A returned ENOENT is translated to a friendlier error. -func (c *Conn) sendNotify(msg []byte) error { +func (c *Conn) sendInvalidate(msg []byte) error { switch err := c.writeToKernel(msg); err { case syscall.ENOENT: return ErrNotCached @@ -1230,7 +1193,7 @@ func (c *Conn) InvalidateNode(nodeID NodeID, off int64, size int64) error { out.Ino = uint64(nodeID) out.Off = off out.Len = size - return c.sendNotify(buf) + return c.sendInvalidate(buf) } // InvalidateEntry invalidates the kernel cache of the directory entry @@ -1258,100 +1221,11 @@ func (c *Conn) InvalidateEntry(parent NodeID, name string) error { out.Namelen = uint32(len(name)) buf = append(buf, name...) buf = append(buf, '\x00') - return c.sendNotify(buf) + return c.sendInvalidate(buf) } -func (c *Conn) NotifyStore(nodeID NodeID, offset uint64, data []byte) error { - buf := newBuffer(unsafe.Sizeof(notifyStoreOut{}) + uintptr(len(data))) - h := (*outHeader)(unsafe.Pointer(&buf[0])) - // h.Unique is 0 - h.Error = notifyCodeStore - out := (*notifyStoreOut)(buf.alloc(unsafe.Sizeof(notifyStoreOut{}))) - out.Nodeid = uint64(nodeID) - out.Offset = offset - out.Size = uint32(len(data)) - buf = append(buf, data...) - return c.sendNotify(buf) -} - -type NotifyRetrieval struct { - // we may want fields later, so don't let callers know it's the - // empty struct - _ struct{} -} - -func (n *NotifyRetrieval) Finish(r *NotifyReply) []byte { - m := r.msg - defer putMessage(m) - in := (*notifyRetrieveIn)(m.data()) - if m.len() < unsafe.Sizeof(*in) { - Debug(malformedMessage{}) - return nil - } - m.off += int(unsafe.Sizeof(*in)) - buf := m.bytes() - if uint32(len(buf)) < in.Size { - Debug(malformedMessage{}) - return nil - } - - data := make([]byte, in.Size) - copy(data, buf) - return data -} - -func (c *Conn) NotifyRetrieve(notificationID RequestID, nodeID NodeID, offset uint64, size uint32) (*NotifyRetrieval, error) { - // notificationID may collide with kernel-chosen requestIDs, it's - // up to the caller to branch based on the opCode. - - buf := newBuffer(unsafe.Sizeof(notifyRetrieveOut{})) - h := (*outHeader)(unsafe.Pointer(&buf[0])) - // h.Unique is 0 - h.Error = notifyCodeRetrieve - out := (*notifyRetrieveOut)(buf.alloc(unsafe.Sizeof(notifyRetrieveOut{}))) - out.NotifyUnique = uint64(notificationID) - out.Nodeid = uint64(nodeID) - out.Offset = offset - // kernel constrains size to maxWrite for us - out.Size = size - if err := c.sendNotify(buf); err != nil { - return nil, err - } - r := &NotifyRetrieval{} - return r, nil -} - -// NotifyPollWakeup sends a notification to the kernel to wake up all -// clients waiting on this node. Wakeup is a value from a PollRequest -// for a Handle or a Node currently alive (Forget has not been called -// on it). -func (c *Conn) NotifyPollWakeup(wakeup PollWakeup) error { - if wakeup.kh == 0 { - // likely somebody ignored the comma-ok return - return nil - } - buf := newBuffer(unsafe.Sizeof(notifyPollWakeupOut{})) - h := (*outHeader)(unsafe.Pointer(&buf[0])) - // h.Unique is 0 - h.Error = notifyCodePoll - out := (*notifyPollWakeupOut)(buf.alloc(unsafe.Sizeof(notifyPollWakeupOut{}))) - out.Kh = wakeup.kh - return c.sendNotify(buf) -} - -// LockOwner is a file-local opaque identifier assigned by the kernel -// to identify the owner of a particular lock. -type LockOwner uint64 - -func (o LockOwner) String() string { - if o == 0 { - return "0" - } - return fmt.Sprintf("%016x", uint64(o)) -} - -// An initRequest is the first request sent on a FUSE file system. -type initRequest struct { +// An InitRequest is the first request sent on a FUSE file system. +type InitRequest struct { Header `json:"-"` Kernel Protocol // Maximum readahead in bytes that the kernel plans to use. @@ -1359,53 +1233,36 @@ type initRequest struct { Flags InitFlags } -var _ Request = (*initRequest)(nil) +var _ = Request(&InitRequest{}) -func (r *initRequest) String() string { +func (r *InitRequest) String() string { return fmt.Sprintf("Init [%v] %v ra=%d fl=%v", &r.Header, r.Kernel, r.MaxReadahead, r.Flags) } -type UnrecognizedRequest struct { - Header `json:"-"` - Opcode uint32 -} - -var _ Request = (*UnrecognizedRequest)(nil) - -func (r *UnrecognizedRequest) String() string { - return fmt.Sprintf("Unrecognized [%v] opcode=%d", &r.Header, r.Opcode) -} - -// An initResponse is the response to an initRequest. -type initResponse struct { +// An InitResponse is the response to an InitRequest. +type InitResponse struct { Library Protocol // Maximum readahead in bytes that the kernel can use. Ignored if - // greater than initRequest.MaxReadahead. + // greater than InitRequest.MaxReadahead. MaxReadahead uint32 Flags InitFlags - // Maximum number of outstanding background requests - MaxBackground uint16 - // Number of background requests at which congestion starts - CongestionThreshold uint16 // Maximum size of a single write operation. // Linux enforces a minimum of 4 KiB. MaxWrite uint32 } -func (r *initResponse) String() string { - return fmt.Sprintf("Init %v ra=%d fl=%v maxbg=%d cg=%d w=%d", r.Library, r.MaxReadahead, r.Flags, r.MaxBackground, r.CongestionThreshold, r.MaxWrite) +func (r *InitResponse) String() string { + 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. -func (r *initRequest) Respond(resp *initResponse) { +func (r *InitRequest) Respond(resp *InitResponse) { buf := newBuffer(unsafe.Sizeof(initOut{})) out := (*initOut)(buf.alloc(unsafe.Sizeof(initOut{}))) out.Major = resp.Library.Major out.Minor = resp.Library.Minor out.MaxReadahead = resp.MaxReadahead out.Flags = uint32(resp.Flags) - out.MaxBackground = resp.MaxBackground - out.CongestionThreshold = resp.CongestionThreshold out.MaxWrite = resp.MaxWrite // MaxWrite larger than our receive buffer would just lead to @@ -1421,7 +1278,7 @@ type StatfsRequest struct { Header `json:"-"` } -var _ Request = (*StatfsRequest)(nil) +var _ = Request(&StatfsRequest{}) func (r *StatfsRequest) String() string { return fmt.Sprintf("Statfs [%s]", &r.Header) @@ -1473,7 +1330,7 @@ type AccessRequest struct { Mask uint32 } -var _ Request = (*AccessRequest)(nil) +var _ = Request(&AccessRequest{}) func (r *AccessRequest) String() string { return fmt.Sprintf("Access [%s] mask=%#x", &r.Header, r.Mask) @@ -1496,24 +1353,21 @@ type Attr struct { Atime time.Time // time of last access Mtime time.Time // time of last modification 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 (usually 1) Uid uint32 // owner uid Gid uint32 // group gid Rdev uint32 // device numbers + Flags uint32 // chflags(2) flags (OS X only) BlockSize uint32 // preferred blocksize for filesystem I/O - - // Deprecated: Not used, OS X remnant. - Crtime time.Time - // Deprecated: Not used, OS X remnant. - Flags uint32 } 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 unixTime(t time.Time) (sec uint64, nsec uint32) { +func unix(t time.Time) (sec uint64, nsec uint32) { nano := t.UnixNano() sec = uint64(nano / 1e9) nsec = uint32(nano % 1e9) @@ -1524,10 +1378,11 @@ func (a *Attr) attr(out *attr, proto Protocol) { out.Ino = a.Inode out.Size = a.Size out.Blocks = a.Blocks - out.Atime, out.AtimeNsec = unixTime(a.Atime) - out.Mtime, out.MtimeNsec = unixTime(a.Mtime) - out.Ctime, out.CtimeNsec = unixTime(a.Ctime) - out.Mode = uint32(a.Mode) & 0o777 + out.Atime, out.AtimeNsec = unix(a.Atime) + out.Mtime, out.MtimeNsec = unix(a.Mtime) + out.Ctime, out.CtimeNsec = unix(a.Ctime) + out.SetCrtime(unix(a.Crtime)) + out.Mode = uint32(a.Mode) & 0777 switch { default: out.Mode |= syscall.S_IFREG @@ -1556,6 +1411,7 @@ func (a *Attr) attr(out *attr, proto Protocol) { out.Uid = a.Uid out.Gid = a.Gid out.Rdev = a.Rdev + out.SetFlags(a.Flags) if proto.GE(Protocol{7, 9}) { out.Blksize = a.BlockSize } @@ -1568,7 +1424,7 @@ type GetattrRequest struct { Handle HandleID } -var _ Request = (*GetattrRequest)(nil) +var _ = Request(&GetattrRequest{}) func (r *GetattrRequest) String() string { return fmt.Sprintf("Getattr [%s] %v fl=%v", &r.Header, r.Handle, r.Flags) @@ -1604,14 +1460,17 @@ type GetxattrRequest struct { // Name of the attribute requested. Name string - // Deprecated: Not used, OS X remnant. + // Offset within extended attributes. + // + // Only valid for OS X, and then only with the resource fork + // attribute. Position uint32 } -var _ Request = (*GetxattrRequest)(nil) +var _ = Request(&GetxattrRequest{}) func (r *GetxattrRequest) String() string { - return fmt.Sprintf("Getxattr [%s] %q %d", &r.Header, r.Name, r.Size) + return fmt.Sprintf("Getxattr [%s] %q %d @%d", &r.Header, r.Name, r.Size, r.Position) } // Respond replies to the request with the given response. @@ -1639,17 +1498,15 @@ func (r *GetxattrResponse) String() string { // A ListxattrRequest asks to list the extended attributes associated with r.Node. type ListxattrRequest struct { - Header `json:"-"` - Size uint32 // maximum size to return - - // Deprecated: Not used, OS X remnant. - Position uint32 + Header `json:"-"` + Size uint32 // maximum size to return + Position uint32 // offset within attribute list } -var _ Request = (*ListxattrRequest)(nil) +var _ = Request(&ListxattrRequest{}) func (r *ListxattrRequest) String() string { - return fmt.Sprintf("Listxattr [%s] %d", &r.Header, r.Size) + return fmt.Sprintf("Listxattr [%s] %d @%d", &r.Header, r.Size, r.Position) } // Respond replies to the request with the given response. @@ -1689,7 +1546,7 @@ type RemovexattrRequest struct { Name string // name of extended attribute } -var _ Request = (*RemovexattrRequest)(nil) +var _ = Request(&RemovexattrRequest{}) func (r *RemovexattrRequest) String() string { return fmt.Sprintf("Removexattr [%s] %q", &r.Header, r.Name) @@ -1716,14 +1573,17 @@ type SetxattrRequest struct { // TODO XATTR_REPLACE and not exist -> ENODATA Flags uint32 - // Deprecated: Not used, OS X remnant. + // Offset within extended attributes. + // + // Only valid for OS X, and then only with the resource fork + // attribute. Position uint32 Name string Xattr []byte } -var _ Request = (*SetxattrRequest)(nil) +var _ = Request(&SetxattrRequest{}) func trunc(b []byte, max int) ([]byte, string) { if len(b) > max { @@ -1734,7 +1594,7 @@ func trunc(b []byte, max int) ([]byte, string) { func (r *SetxattrRequest) String() string { xattr, tail := trunc(r.Xattr, 16) - return fmt.Sprintf("Setxattr [%s] %q %q%s fl=%v", &r.Header, r.Name, xattr, tail, r.Flags) + return fmt.Sprintf("Setxattr [%s] %q %q%s fl=%v @%#x", &r.Header, r.Name, xattr, tail, r.Flags, r.Position) } // Respond replies to the request, indicating that the extended attribute was set. @@ -1749,7 +1609,7 @@ type LookupRequest struct { Name string } -var _ Request = (*LookupRequest)(nil) +var _ = Request(&LookupRequest{}) func (r *LookupRequest) String() string { return fmt.Sprintf("Lookup [%s] %q", &r.Header, r.Name) @@ -1793,7 +1653,7 @@ type OpenRequest struct { Flags OpenFlags } -var _ Request = (*OpenRequest)(nil) +var _ = Request(&OpenRequest{}) func (r *OpenRequest) String() string { return fmt.Sprintf("Open [%s] dir=%v fl=%v", &r.Header, r.Dir, r.Flags) @@ -1828,11 +1688,11 @@ type CreateRequest struct { Name string Flags OpenFlags Mode os.FileMode - // Umask of the request. + // Umask of the request. Not supported on OS X. Umask os.FileMode } -var _ Request = (*CreateRequest)(nil) +var _ = Request(&CreateRequest{}) func (r *CreateRequest) String() string { return fmt.Sprintf("Create [%s] %q fl=%v mode=%v umask=%v", &r.Header, r.Name, r.Flags, r.Mode, r.Umask) @@ -1875,11 +1735,11 @@ type MkdirRequest struct { Header `json:"-"` Name string Mode os.FileMode - // Umask of the request. + // Umask of the request. Not supported on OS X. Umask os.FileMode } -var _ Request = (*MkdirRequest)(nil) +var _ = Request(&MkdirRequest{}) func (r *MkdirRequest) String() string { return fmt.Sprintf("Mkdir [%s] %q mode=%v umask=%v", &r.Header, r.Name, r.Mode, r.Umask) @@ -1917,14 +1777,14 @@ type ReadRequest struct { Offset int64 Size int Flags ReadFlags - LockOwner LockOwner + LockOwner uint64 FileFlags OpenFlags } -var _ Request = (*ReadRequest)(nil) +var _ = Request(&ReadRequest{}) func (r *ReadRequest) String() string { - return fmt.Sprintf("Read [%s] %v %d @%#x dir=%v fl=%v owner=%v 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. @@ -1961,13 +1821,13 @@ type ReleaseRequest struct { Handle HandleID Flags OpenFlags // flags from OpenRequest ReleaseFlags ReleaseFlags - LockOwner LockOwner + LockOwner uint32 } -var _ Request = (*ReleaseRequest)(nil) +var _ = Request(&ReleaseRequest{}) func (r *ReleaseRequest) String() string { - return fmt.Sprintf("Release [%s] %v fl=%v rfl=%v owner=%v", &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. @@ -1983,7 +1843,7 @@ type DestroyRequest struct { Header `json:"-"` } -var _ Request = (*DestroyRequest)(nil) +var _ = Request(&DestroyRequest{}) func (r *DestroyRequest) String() string { return fmt.Sprintf("Destroy [%s]", &r.Header) @@ -2002,7 +1862,7 @@ type ForgetRequest struct { N uint64 } -var _ Request = (*ForgetRequest)(nil) +var _ = Request(&ForgetRequest{}) func (r *ForgetRequest) String() string { return fmt.Sprintf("Forget [%s] %d", &r.Header, r.N) @@ -2014,37 +1874,6 @@ func (r *ForgetRequest) Respond() { r.noResponse() } -type BatchForgetItem struct { - NodeID NodeID - N uint64 -} - -type BatchForgetRequest struct { - Header `json:"-"` - Forget []BatchForgetItem -} - -var _ Request = (*BatchForgetRequest)(nil) - -func (r *BatchForgetRequest) String() string { - b := new(strings.Builder) - fmt.Fprintf(b, "BatchForget [%s]", &r.Header) - if len(r.Forget) == 0 { - b.WriteString(" empty") - } else { - for _, item := range r.Forget { - fmt.Fprintf(b, " %dx%d", item.NodeID, item.N) - } - } - return b.String() -} - -// Respond replies to the request, indicating that the forgetfulness has been recorded. -func (r *BatchForgetRequest) Respond() { - // Don't reply to forget messages. - r.noResponse() -} - // A Dirent represents a single directory entry. type Dirent struct { // Inode this entry names. @@ -2133,14 +1962,14 @@ type WriteRequest struct { Offset int64 Data []byte Flags WriteFlags - LockOwner LockOwner + LockOwner uint64 FileFlags OpenFlags } -var _ Request = (*WriteRequest)(nil) +var _ = Request(&WriteRequest{}) func (r *WriteRequest) String() string { - return fmt.Sprintf("Write [%s] %v %d @%d fl=%v owner=%v 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 { @@ -2195,17 +2024,14 @@ type SetattrRequest struct { Uid uint32 Gid uint32 - // Deprecated: Not used, OS X remnant. + // OS X only Bkuptime time.Time - // Deprecated: Not used, OS X remnant. - Chgtime time.Time - // Deprecated: Not used, OS X remnant. - Crtime time.Time - // Deprecated: Not used, OS X remnant. - Flags uint32 + Chgtime time.Time + Crtime time.Time + Flags uint32 // see chflags(2) } -var _ Request = (*SetattrRequest)(nil) +var _ = Request(&SetattrRequest{}) func (r *SetattrRequest) String() string { var buf bytes.Buffer @@ -2242,6 +2068,18 @@ func (r *SetattrRequest) String() string { if r.Valid.LockOwner() { fmt.Fprintf(&buf, " lockowner") } + if r.Valid.Crtime() { + fmt.Fprintf(&buf, " crtime=%v", r.Crtime) + } + if r.Valid.Chgtime() { + fmt.Fprintf(&buf, " chgtime=%v", r.Chgtime) + } + if r.Valid.Bkuptime() { + fmt.Fprintf(&buf, " bkuptime=%v", r.Bkuptime) + } + if r.Valid.Flags() { + fmt.Fprintf(&buf, " flags=%v", r.Flags) + } return buf.String() } @@ -2270,17 +2108,16 @@ func (r *SetattrResponse) String() string { // to storage, as when a file descriptor is being closed. A single opened Handle // may receive multiple FlushRequests over its lifetime. type FlushRequest struct { - Header `json:"-"` - Handle HandleID - // Deprecated: Unused since 2006. + Header `json:"-"` + Handle HandleID Flags uint32 - LockOwner LockOwner + LockOwner uint64 } -var _ Request = (*FlushRequest)(nil) +var _ = Request(&FlushRequest{}) func (r *FlushRequest) String() string { - return fmt.Sprintf("Flush [%s] %v fl=%#x owner=%v", &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. @@ -2297,7 +2134,7 @@ type RemoveRequest struct { Dir bool // is this rmdir? } -var _ Request = (*RemoveRequest)(nil) +var _ = Request(&RemoveRequest{}) func (r *RemoveRequest) String() string { return fmt.Sprintf("Remove [%s] %q dir=%v", &r.Header, r.Name, r.Dir) @@ -2315,7 +2152,7 @@ type SymlinkRequest struct { NewName, Target string } -var _ Request = (*SymlinkRequest)(nil) +var _ = Request(&SymlinkRequest{}) func (r *SymlinkRequest) String() string { return fmt.Sprintf("Symlink [%s] from %q to target %q", &r.Header, r.NewName, r.Target) @@ -2350,7 +2187,7 @@ type ReadlinkRequest struct { Header `json:"-"` } -var _ Request = (*ReadlinkRequest)(nil) +var _ = Request(&ReadlinkRequest{}) func (r *ReadlinkRequest) String() string { return fmt.Sprintf("Readlink [%s]", &r.Header) @@ -2369,7 +2206,7 @@ type LinkRequest struct { NewName string } -var _ Request = (*LinkRequest)(nil) +var _ = Request(&LinkRequest{}) func (r *LinkRequest) String() string { return fmt.Sprintf("Link [%s] node %d to %q", &r.Header, r.OldNode, r.NewName) @@ -2396,7 +2233,7 @@ type RenameRequest struct { OldName, NewName string } -var _ Request = (*RenameRequest)(nil) +var _ = Request(&RenameRequest{}) func (r *RenameRequest) String() string { return fmt.Sprintf("Rename [%s] from %q to dirnode %v %q", &r.Header, r.OldName, r.NewDir, r.NewName) @@ -2412,11 +2249,11 @@ type MknodRequest struct { Name string Mode os.FileMode Rdev uint32 - // Umask of the request. + // Umask of the request. Not supported on OS X. Umask os.FileMode } -var _ Request = (*MknodRequest)(nil) +var _ = Request(&MknodRequest{}) func (r *MknodRequest) String() string { return fmt.Sprintf("Mknod [%s] Name %q mode=%v umask=%v rdev=%d", &r.Header, r.Name, r.Mode, r.Umask, r.Rdev) @@ -2444,7 +2281,7 @@ type FsyncRequest struct { Dir bool } -var _ Request = (*FsyncRequest)(nil) +var _ = Request(&FsyncRequest{}) func (r *FsyncRequest) String() string { return fmt.Sprintf("Fsync [%s] Handle %v Flags %v", &r.Header, r.Handle, r.Flags) @@ -2462,7 +2299,7 @@ type InterruptRequest struct { IntrID RequestID // ID of the request to be interrupt. } -var _ Request = (*InterruptRequest)(nil) +var _ = Request(&InterruptRequest{}) func (r *InterruptRequest) Respond() { // nothing to do here @@ -2473,14 +2310,22 @@ func (r *InterruptRequest) String() string { return fmt.Sprintf("Interrupt [%s] ID %v", &r.Header, r.IntrID) } -// Deprecated: Not used, OS X remnant. +// 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)(nil) +var _ = Request(&ExchangeDataRequest{}) func (r *ExchangeDataRequest) String() string { // TODO options @@ -2491,213 +2336,3 @@ func (r *ExchangeDataRequest) Respond() { buf := newBuffer(0) r.respond(buf) } - -// NotifyReply is a response to an earlier notification. It behaves -// like a Request, but is not really a request expecting a response. -type NotifyReply struct { - Header `json:"-"` - msg *message -} - -var _ Request = (*NotifyReply)(nil) - -func (r *NotifyReply) String() string { - return fmt.Sprintf("NotifyReply [%s]", &r.Header) -} - -type PollRequest struct { - Header `json:"-"` - Handle HandleID - kh uint64 - Flags PollFlags - // Events is a bitmap of events of interest. - // - // This field is only set for FUSE protocol 7.21 and later. - Events PollEvents -} - -var _ Request = (*PollRequest)(nil) - -func (r *PollRequest) String() string { - return fmt.Sprintf("Poll [%s] %v kh=%v fl=%v ev=%v", &r.Header, r.Handle, r.kh, r.Flags, r.Events) -} - -type PollWakeup struct { - kh uint64 -} - -func (p PollWakeup) String() string { - return fmt.Sprintf("PollWakeup{kh=%d}", p.kh) -} - -// Wakeup returns information that can be used later to wake up file -// system clients polling a Handle or a Node. -// -// ok is false if wakeups are not requested for this poll. -// -// Do not retain PollWakeup past the lifetime of the Handle or Node. -func (r *PollRequest) Wakeup() (_ PollWakeup, ok bool) { - if r.Flags&PollScheduleNotify == 0 { - return PollWakeup{}, false - } - p := PollWakeup{ - kh: r.kh, - } - return p, true -} - -func (r *PollRequest) Respond(resp *PollResponse) { - buf := newBuffer(unsafe.Sizeof(pollOut{})) - out := (*pollOut)(buf.alloc(unsafe.Sizeof(pollOut{}))) - out.REvents = uint32(resp.REvents) - r.respond(buf) -} - -type PollResponse struct { - REvents PollEvents -} - -func (r *PollResponse) String() string { - return fmt.Sprintf("Poll revents=%v", r.REvents) -} - -type FileLock struct { - Start uint64 - End uint64 - Type LockType - PID int32 -} - -// LockRequest asks to try acquire a byte range lock on a node. The -// response should be immediate, do not wait to obtain lock. -// -// Unlocking can be -// -// - explicit with UnlockRequest -// - for flock: implicit on final close (ReleaseRequest.ReleaseFlags -// has ReleaseFlockUnlock set) -// - for POSIX locks: implicit on any close (FlushRequest) -// - for Open File Description locks: implicit on final close -// (no LockOwner observed as of 2020-04) -// -// See LockFlags to know which kind of a lock is being requested. (As -// of 2020-04, Open File Descriptor locks are indistinguishable from -// POSIX. This means programs using those locks will likely misbehave -// when closing FDs on FUSE-based distributed filesystems, as the -// filesystem has no better knowledge than to follow POSIX locking -// rules and release the global lock too early.) -// -// Most of the other differences between flock (BSD) and POSIX (fcntl -// F_SETLK) locks are relevant only to the caller, not the filesystem. -// FUSE always sees ranges, and treats flock whole-file locks as -// requests for the maximum byte range. Filesystems should do the -// same, as this provides a forwards compatibility path to -// Linux-native Open file description locks. -// -// To enable locking events in FUSE, pass LockingFlock() and/or -// LockingPOSIX() to Mount. -// -// See also LockWaitRequest. -type LockRequest struct { - Header - Handle HandleID - // LockOwner is a unique identifier for the originating client, to - // identify locks. - LockOwner LockOwner - Lock FileLock - LockFlags LockFlags -} - -var _ Request = (*LockRequest)(nil) - -func (r *LockRequest) String() string { - return fmt.Sprintf("Lock [%s] %v owner=%v range=%d..%d type=%v pid=%v fl=%v", &r.Header, r.Handle, r.LockOwner, r.Lock.Start, r.Lock.End, r.Lock.Type, r.Lock.PID, r.LockFlags) -} - -func (r *LockRequest) Respond() { - buf := newBuffer(0) - r.respond(buf) -} - -// LockWaitRequest asks to acquire a byte range lock on a node, -// delaying response until lock can be obtained (or the request is -// interrupted). -// -// See LockRequest. LockWaitRequest can be converted to a LockRequest. -type LockWaitRequest LockRequest - -var _ LockRequest = LockRequest(LockWaitRequest{}) - -var _ Request = (*LockWaitRequest)(nil) - -func (r *LockWaitRequest) String() string { - return fmt.Sprintf("LockWait [%s] %v owner=%v range=%d..%d type=%v pid=%v fl=%v", &r.Header, r.Handle, r.LockOwner, r.Lock.Start, r.Lock.End, r.Lock.Type, r.Lock.PID, r.LockFlags) -} - -func (r *LockWaitRequest) Respond() { - buf := newBuffer(0) - r.respond(buf) -} - -// UnlockRequest asks to release a lock on a byte range on a node. -// -// UnlockRequests always have Lock.Type == LockUnlock. -// -// See LockRequest. UnlockRequest can be converted to a LockRequest. -type UnlockRequest LockRequest - -var _ LockRequest = LockRequest(UnlockRequest{}) - -var _ Request = (*UnlockRequest)(nil) - -func (r *UnlockRequest) String() string { - return fmt.Sprintf("Unlock [%s] %v owner=%v range=%d..%d type=%v pid=%v fl=%v", &r.Header, r.Handle, r.LockOwner, r.Lock.Start, r.Lock.End, r.Lock.Type, r.Lock.PID, r.LockFlags) -} - -func (r *UnlockRequest) Respond() { - buf := newBuffer(0) - r.respond(buf) -} - -// QueryLockRequest queries the lock status. -// -// If the lock could be placed, set response Lock.Type to -// unix.F_UNLCK. -// -// If there are conflicting locks, the response should describe one of -// them. For Open File Description locks, set PID to -1. (This is -// probably also the sane behavior for locks held by remote parties.) -type QueryLockRequest struct { - Header - Handle HandleID - LockOwner LockOwner - Lock FileLock - LockFlags LockFlags -} - -var _ Request = (*QueryLockRequest)(nil) - -func (r *QueryLockRequest) String() string { - return fmt.Sprintf("QueryLock [%s] %v owner=%v range=%d..%d type=%v pid=%v fl=%v", &r.Header, r.Handle, r.LockOwner, r.Lock.Start, r.Lock.End, r.Lock.Type, r.Lock.PID, r.LockFlags) -} - -// Respond replies to the request with the given response. -func (r *QueryLockRequest) Respond(resp *QueryLockResponse) { - buf := newBuffer(unsafe.Sizeof(lkOut{})) - out := (*lkOut)(buf.alloc(unsafe.Sizeof(lkOut{}))) - out.Lk = fileLock{ - Start: resp.Lock.Start, - End: resp.Lock.End, - Type: uint32(resp.Lock.Type), - PID: uint32(resp.Lock.PID), - } - r.respond(buf) -} - -type QueryLockResponse struct { - Lock FileLock -} - -func (r *QueryLockResponse) String() string { - return fmt.Sprintf("QueryLock range=%d..%d type=%v pid=%v", r.Lock.Start, r.Lock.End, r.Lock.Type, r.Lock.PID) -} diff --git a/vendor/bazil.org/fuse/fuse_darwin.go b/vendor/bazil.org/fuse/fuse_darwin.go new file mode 100644 index 000000000..b58dca97d --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_darwin.go @@ -0,0 +1,9 @@ +package fuse + +// Maximum file write size we are prepared to receive from the kernel. +// +// This value has to be >=16MB or OSXFUSE (3.4.0 observed) will +// forcibly close the /dev/fuse file descriptor on a Setxattr with a +// 16MB value. See TestSetxattr16MB and +// https://github.com/bazil/fuse/issues/42 +const maxWrite = 16 * 1024 * 1024 diff --git a/vendor/bazil.org/fuse/fuse_kernel.go b/vendor/bazil.org/fuse/fuse_kernel.go index 18e511267..416d32aa1 100644 --- a/vendor/bazil.org/fuse/fuse_kernel.go +++ b/vendor/bazil.org/fuse/fuse_kernel.go @@ -39,41 +39,20 @@ import ( "fmt" "syscall" "unsafe" - - "golang.org/x/sys/unix" ) // The FUSE version implemented by the package. const ( protoVersionMinMajor = 7 - protoVersionMinMinor = 17 + protoVersionMinMinor = 8 protoVersionMaxMajor = 7 - protoVersionMaxMinor = 17 + protoVersionMaxMinor = 12 ) const ( rootID = 1 ) -type attr struct { - Ino uint64 - Size uint64 - Blocks uint64 - Atime uint64 - Mtime uint64 - Ctime uint64 - AtimeNsec uint32 - MtimeNsec uint32 - CtimeNsec uint32 - Mode uint32 - Nlink uint32 - Uid uint32 - Gid uint32 - Rdev uint32 - Blksize uint32 - _ uint32 -} - type kstatfs struct { Blocks uint64 Bfree uint64 @@ -87,6 +66,13 @@ type kstatfs struct { Spare [6]uint32 } +type fileLock struct { + Start uint64 + End uint64 + Type uint32 + Pid uint32 +} + // GetattrFlags are bit flags that can be seen in GetattrRequest. type GetattrFlags uint32 @@ -108,25 +94,24 @@ func (fl GetattrFlags) String() string { type SetattrValid uint32 const ( - SetattrMode SetattrValid = 1 << 0 - SetattrUid SetattrValid = 1 << 1 - SetattrGid SetattrValid = 1 << 2 - SetattrSize SetattrValid = 1 << 3 - SetattrAtime SetattrValid = 1 << 4 - SetattrMtime SetattrValid = 1 << 5 - SetattrHandle SetattrValid = 1 << 6 + SetattrMode SetattrValid = 1 << 0 + SetattrUid SetattrValid = 1 << 1 + SetattrGid SetattrValid = 1 << 2 + SetattrSize SetattrValid = 1 << 3 + SetattrAtime SetattrValid = 1 << 4 + SetattrMtime SetattrValid = 1 << 5 + SetattrHandle SetattrValid = 1 << 6 + + // Linux only(?) SetattrAtimeNow SetattrValid = 1 << 7 SetattrMtimeNow SetattrValid = 1 << 8 SetattrLockOwner SetattrValid = 1 << 9 // http://www.mail-archive.com/git-commits-head@vger.kernel.org/msg27852.html - // Deprecated: Not used, OS X remnant. - SetattrCrtime SetattrValid = 1 << 28 - // Deprecated: Not used, OS X remnant. - SetattrChgtime SetattrValid = 1 << 29 - // Deprecated: Not used, OS X remnant. + // OS X only + SetattrCrtime SetattrValid = 1 << 28 + SetattrChgtime SetattrValid = 1 << 29 SetattrBkuptime SetattrValid = 1 << 30 - // Deprecated: Not used, OS X remnant. - SetattrFlags SetattrValid = 1 << 31 + SetattrFlags SetattrValid = 1 << 31 ) func (fl SetattrValid) Mode() bool { return fl&SetattrMode != 0 } @@ -139,18 +124,10 @@ func (fl SetattrValid) Handle() bool { return fl&SetattrHandle != 0 } func (fl SetattrValid) AtimeNow() bool { return fl&SetattrAtimeNow != 0 } func (fl SetattrValid) MtimeNow() bool { return fl&SetattrMtimeNow != 0 } func (fl SetattrValid) LockOwner() bool { return fl&SetattrLockOwner != 0 } - -// Deprecated: Not used, OS X remnant. -func (fl SetattrValid) Crtime() bool { return false } - -// Deprecated: Not used, OS X remnant. -func (fl SetattrValid) Chgtime() bool { return false } - -// Deprecated: Not used, OS X remnant. -func (fl SetattrValid) Bkuptime() bool { return false } - -// Deprecated: Not used, OS X remnant. -func (fl SetattrValid) Flags() bool { return false } +func (fl SetattrValid) Crtime() bool { return fl&SetattrCrtime != 0 } +func (fl SetattrValid) Chgtime() bool { return fl&SetattrChgtime != 0 } +func (fl SetattrValid) Bkuptime() bool { return fl&SetattrBkuptime != 0 } +func (fl SetattrValid) Flags() bool { return fl&SetattrFlags != 0 } func (fl SetattrValid) String() string { return flagString(uint32(fl), setattrValidNames) @@ -167,6 +144,10 @@ var setattrValidNames = []flagName{ {uint32(SetattrAtimeNow), "SetattrAtimeNow"}, {uint32(SetattrMtimeNow), "SetattrMtimeNow"}, {uint32(SetattrLockOwner), "SetattrLockOwner"}, + {uint32(SetattrCrtime), "SetattrCrtime"}, + {uint32(SetattrChgtime), "SetattrChgtime"}, + {uint32(SetattrBkuptime), "SetattrBkuptime"}, + {uint32(SetattrFlags), "SetattrFlags"}, } // Flags that can be seen in OpenRequest.Flags. @@ -179,7 +160,7 @@ const ( OpenReadWrite OpenFlags = syscall.O_RDWR // File was opened in append-only mode, all writes will go to end - // of file. FreeBSD does not provide this information. + // of file. OS X does not provide this information. OpenAppend OpenFlags = syscall.O_APPEND OpenCreate OpenFlags = syscall.O_CREAT OpenDirectory OpenFlags = syscall.O_DIRECTORY @@ -251,12 +232,10 @@ type OpenResponseFlags uint32 const ( OpenDirectIO OpenResponseFlags = 1 << 0 // bypass page cache for this open file OpenKeepCache OpenResponseFlags = 1 << 1 // don't invalidate the data cache on open - OpenNonSeekable OpenResponseFlags = 1 << 2 // mark the file as non-seekable (not supported on FreeBSD) + OpenNonSeekable OpenResponseFlags = 1 << 2 // mark the file as non-seekable (not supported on OS X or FreeBSD) - // Deprecated: Not used, OS X remnant. - OpenPurgeAttr OpenResponseFlags = 1 << 30 - // Deprecated: Not used, OS X remnant. - OpenPurgeUBC OpenResponseFlags = 1 << 31 + OpenPurgeAttr OpenResponseFlags = 1 << 30 // OS X + OpenPurgeUBC OpenResponseFlags = 1 << 31 // OS X ) func (fl OpenResponseFlags) String() string { @@ -267,6 +246,8 @@ var openResponseFlagNames = []flagName{ {uint32(OpenDirectIO), "OpenDirectIO"}, {uint32(OpenKeepCache), "OpenKeepCache"}, {uint32(OpenNonSeekable), "OpenNonSeekable"}, + {uint32(OpenPurgeAttr), "OpenPurgeAttr"}, + {uint32(OpenPurgeUBC), "OpenPurgeUBC"}, } // The InitFlags are used in the Init exchange. @@ -274,12 +255,12 @@ type InitFlags uint32 const ( InitAsyncRead InitFlags = 1 << 0 - InitPOSIXLocks InitFlags = 1 << 1 + InitPosixLocks InitFlags = 1 << 1 InitFileOps InitFlags = 1 << 2 InitAtomicTrunc InitFlags = 1 << 3 InitExportSupport InitFlags = 1 << 4 InitBigWrites InitFlags = 1 << 5 - // Do not mask file access modes with umask. + // 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 @@ -293,12 +274,9 @@ const ( InitWritebackCache InitFlags = 1 << 16 InitNoOpenSupport InitFlags = 1 << 17 - // Deprecated: Not used, OS X remnant. - InitCaseSensitive InitFlags = 1 << 29 - // Deprecated: Not used, OS X remnant. - InitVolRename InitFlags = 1 << 30 - // Deprecated: Not used, OS X remnant. - InitXtimes InitFlags = 1 << 31 + InitCaseSensitive InitFlags = 1 << 29 // OS X only + InitVolRename InitFlags = 1 << 30 // OS X only + InitXtimes InitFlags = 1 << 31 // OS X only ) type flagName struct { @@ -308,7 +286,7 @@ type flagName struct { var initFlagNames = []flagName{ {uint32(InitAsyncRead), "InitAsyncRead"}, - {uint32(InitPOSIXLocks), "InitPOSIXLocks"}, + {uint32(InitPosixLocks), "InitPosixLocks"}, {uint32(InitFileOps), "InitFileOps"}, {uint32(InitAtomicTrunc), "InitAtomicTrunc"}, {uint32(InitExportSupport), "InitExportSupport"}, @@ -325,6 +303,10 @@ var initFlagNames = []flagName{ {uint32(InitAsyncDIO), "InitAsyncDIO"}, {uint32(InitWritebackCache), "InitWritebackCache"}, {uint32(InitNoOpenSupport), "InitNoOpenSupport"}, + + {uint32(InitCaseSensitive), "InitCaseSensitive"}, + {uint32(InitVolRename), "InitVolRename"}, + {uint32(InitXtimes), "InitXtimes"}, } func (fl InitFlags) String() string { @@ -354,8 +336,7 @@ func flagString(f uint32, names []flagName) string { type ReleaseFlags uint32 const ( - ReleaseFlush ReleaseFlags = 1 << 0 - ReleaseFlockUnlock ReleaseFlags = 1 << 1 + ReleaseFlush ReleaseFlags = 1 << 0 ) func (fl ReleaseFlags) String() string { @@ -364,7 +345,6 @@ func (fl ReleaseFlags) String() string { var releaseFlagNames = []flagName{ {uint32(ReleaseFlush), "ReleaseFlush"}, - {uint32(ReleaseFlockUnlock), "ReleaseFlockUnlock"}, } // Opcodes @@ -405,10 +385,13 @@ const ( opInterrupt = 36 opBmap = 37 opDestroy = 38 - opIoctl = 39 - opPoll = 40 - opNotifyReply = 41 - opBatchForget = 42 + opIoctl = 39 // Linux? + opPoll = 40 // Linux? + + // OS X + opSetvolname = 61 + opGetxtimes = 62 + opExchange = 63 ) type entryOut struct { @@ -434,16 +417,6 @@ type forgetIn struct { Nlookup uint64 } -type forgetOne struct { - NodeID uint64 - Nlookup uint64 -} - -type batchForgetIn struct { - Count uint32 - _ uint32 -} - type getattrIn struct { GetattrFlags uint32 _ uint32 @@ -466,6 +439,14 @@ func attrOutSize(p Protocol) uintptr { } } +// OS X +type getxtimesOut struct { + Bkuptime uint64 + Crtime uint64 + BkuptimeNsec uint32 + CrtimeNsec uint32 +} + type mknodIn struct { Mode uint32 Rdev uint32 @@ -503,16 +484,24 @@ type renameIn struct { // "oldname\x00newname\x00" follows } +// OS X +type exchangeIn struct { + Olddir uint64 + Newdir uint64 + Options uint64 + // "oldname\x00newname\x00" follows +} + type linkIn struct { Oldnodeid uint64 } -type setattrIn struct { +type setattrInCommon struct { Valid uint32 _ uint32 Fh uint64 Size uint64 - LockOwner uint64 + LockOwner uint64 // unused on OS X? Atime uint64 Mtime uint64 Unused2 uint64 @@ -557,14 +546,14 @@ type releaseIn struct { Fh uint64 Flags uint32 ReleaseFlags uint32 - LockOwner uint64 + LockOwner uint32 } type flushIn struct { - Fh uint64 - _ uint32 - _ uint32 - LockOwner uint64 + Fh uint64 + FlushFlags uint32 + _ uint32 + LockOwner uint64 } type readIn struct { @@ -654,70 +643,29 @@ type fsyncIn struct { _ uint32 } -type setxattrIn struct { +type setxattrInCommon struct { Size uint32 Flags uint32 } -type getxattrIn struct { +func (setxattrInCommon) position() uint32 { + return 0 +} + +type getxattrInCommon struct { Size uint32 _ uint32 } +func (getxattrInCommon) position() uint32 { + return 0 +} + type getxattrOut struct { Size uint32 _ uint32 } -// The LockFlags are passed in LockRequest or LockWaitRequest. -type LockFlags uint32 - -const ( - // BSD-style flock lock (not POSIX lock) - LockFlock LockFlags = 1 << 0 -) - -var lockFlagNames = []flagName{ - {uint32(LockFlock), "LockFlock"}, -} - -func (fl LockFlags) String() string { - return flagString(uint32(fl), lockFlagNames) -} - -type LockType uint32 - -const ( - // It seems FreeBSD FUSE passes these through using its local - // values, not whatever Linux enshrined into the protocol. It's - // unclear what the intended behavior is. - - LockRead LockType = unix.F_RDLCK - LockWrite LockType = unix.F_WRLCK - LockUnlock LockType = unix.F_UNLCK -) - -var lockTypeNames = map[LockType]string{ - LockRead: "LockRead", - LockWrite: "LockWrite", - LockUnlock: "LockUnlock", -} - -func (l LockType) String() string { - s, ok := lockTypeNames[l] - if ok { - return s - } - return fmt.Sprintf("LockType(%d)", l) -} - -type fileLock struct { - Start uint64 - End uint64 - Type uint32 - PID uint32 -} - type lkIn struct { Fh uint64 Owner uint64 @@ -726,6 +674,15 @@ type lkIn struct { _ uint32 } +func lkInSize(p Protocol) uintptr { + switch { + case p.LT(Protocol{7, 9}): + return unsafe.Offsetof(lkIn{}.LkFlags) + default: + return unsafe.Sizeof(lkIn{}) + } +} + type lkOut struct { Lk fileLock } @@ -745,13 +702,12 @@ type initIn struct { const initInSize = int(unsafe.Sizeof(initIn{})) type initOut struct { - Major uint32 - Minor uint32 - MaxReadahead uint32 - Flags uint32 - MaxBackground uint16 - CongestionThreshold uint16 - MaxWrite uint32 + Major uint32 + Minor uint32 + MaxReadahead uint32 + Flags uint32 + Unused uint32 + MaxWrite uint32 } type interruptIn struct { @@ -792,6 +748,7 @@ type dirent struct { Off uint64 Namelen uint32 Type uint32 + Name [0]byte } const direntSize = 8 + 8 + 4 + 4 @@ -800,8 +757,6 @@ const ( notifyCodePoll int32 = 1 notifyCodeInvalInode int32 = 2 notifyCodeInvalEntry int32 = 3 - notifyCodeStore int32 = 4 - notifyCodeRetrieve int32 = 5 ) type notifyInvalInodeOut struct { @@ -815,101 +770,3 @@ type notifyInvalEntryOut struct { Namelen uint32 _ uint32 } - -type notifyStoreOut struct { - Nodeid uint64 - Offset uint64 - Size uint32 - _ uint32 -} - -type notifyRetrieveOut struct { - NotifyUnique uint64 - Nodeid uint64 - Offset uint64 - Size uint32 - _ uint32 -} - -type notifyRetrieveIn struct { - // matches writeIn - - _ uint64 - Offset uint64 - Size uint32 - _ uint32 - _ uint64 - _ uint64 -} - -// PollFlags are passed in PollRequest.Flags -type PollFlags uint32 - -const ( - // PollScheduleNotify requests that a poll notification is done - // once the node is ready. - PollScheduleNotify PollFlags = 1 << 0 -) - -var pollFlagNames = []flagName{ - {uint32(PollScheduleNotify), "PollScheduleNotify"}, -} - -func (fl PollFlags) String() string { - return flagString(uint32(fl), pollFlagNames) -} - -type PollEvents uint32 - -const ( - PollIn PollEvents = 0x0000_0001 - PollPriority PollEvents = 0x0000_0002 - PollOut PollEvents = 0x0000_0004 - PollError PollEvents = 0x0000_0008 - PollHangup PollEvents = 0x0000_0010 - // PollInvalid doesn't seem to be used in the FUSE protocol. - PollInvalid PollEvents = 0x0000_0020 - PollReadNormal PollEvents = 0x0000_0040 - PollReadOutOfBand PollEvents = 0x0000_0080 - PollWriteNormal PollEvents = 0x0000_0100 - PollWriteOutOfBand PollEvents = 0x0000_0200 - PollMessage PollEvents = 0x0000_0400 - PollReadHangup PollEvents = 0x0000_2000 - - DefaultPollMask = PollIn | PollOut | PollReadNormal | PollWriteNormal -) - -var pollEventNames = []flagName{ - {uint32(PollIn), "PollIn"}, - {uint32(PollPriority), "PollPriority"}, - {uint32(PollOut), "PollOut"}, - {uint32(PollError), "PollError"}, - {uint32(PollHangup), "PollHangup"}, - {uint32(PollInvalid), "PollInvalid"}, - {uint32(PollReadNormal), "PollReadNormal"}, - {uint32(PollReadOutOfBand), "PollReadOutOfBand"}, - {uint32(PollWriteNormal), "PollWriteNormal"}, - {uint32(PollWriteOutOfBand), "PollWriteOutOfBand"}, - {uint32(PollMessage), "PollMessage"}, - {uint32(PollReadHangup), "PollReadHangup"}, -} - -func (fl PollEvents) String() string { - return flagString(uint32(fl), pollEventNames) -} - -type pollIn struct { - Fh uint64 - Kh uint64 - Flags uint32 - Events uint32 -} - -type pollOut struct { - REvents uint32 - _ uint32 -} - -type notifyPollWakeupOut struct { - Kh uint64 -} diff --git a/vendor/bazil.org/fuse/fuse_kernel_darwin.go b/vendor/bazil.org/fuse/fuse_kernel_darwin.go new file mode 100644 index 000000000..b9873fdf3 --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_kernel_darwin.go @@ -0,0 +1,88 @@ +package fuse + +import ( + "time" +) + +type attr struct { + Ino uint64 + Size uint64 + Blocks uint64 + Atime uint64 + Mtime uint64 + Ctime uint64 + Crtime_ uint64 // OS X only + AtimeNsec uint32 + MtimeNsec uint32 + CtimeNsec uint32 + CrtimeNsec uint32 // OS X only + Mode uint32 + Nlink uint32 + Uid uint32 + Gid uint32 + Rdev uint32 + Flags_ uint32 // OS X only; see chflags(2) + Blksize uint32 + padding uint32 +} + +func (a *attr) SetCrtime(s uint64, ns uint32) { + a.Crtime_, a.CrtimeNsec = s, ns +} + +func (a *attr) SetFlags(f uint32) { + a.Flags_ = f +} + +type setattrIn struct { + setattrInCommon + + // OS X only + Bkuptime_ uint64 + Chgtime_ uint64 + Crtime uint64 + BkuptimeNsec uint32 + ChgtimeNsec uint32 + CrtimeNsec uint32 + Flags_ uint32 // see chflags(2) +} + +func (in *setattrIn) BkupTime() time.Time { + return time.Unix(int64(in.Bkuptime_), int64(in.BkuptimeNsec)) +} + +func (in *setattrIn) Chgtime() time.Time { + return time.Unix(int64(in.Chgtime_), int64(in.ChgtimeNsec)) +} + +func (in *setattrIn) Flags() uint32 { + return in.Flags_ +} + +func openFlags(flags uint32) OpenFlags { + return OpenFlags(flags) +} + +type getxattrIn struct { + getxattrInCommon + + // OS X only + Position uint32 + Padding uint32 +} + +func (g *getxattrIn) position() uint32 { + return g.Position +} + +type setxattrIn struct { + setxattrInCommon + + // OS X only + Position uint32 + Padding uint32 +} + +func (s *setxattrIn) position() uint32 { + return s.Position +} diff --git a/vendor/bazil.org/fuse/fuse_kernel_freebsd.go b/vendor/bazil.org/fuse/fuse_kernel_freebsd.go index 3b3eff18f..b1141e41d 100644 --- a/vendor/bazil.org/fuse/fuse_kernel_freebsd.go +++ b/vendor/bazil.org/fuse/fuse_kernel_freebsd.go @@ -1,5 +1,62 @@ package fuse +import "time" + +type attr struct { + Ino uint64 + Size uint64 + Blocks uint64 + Atime uint64 + Mtime uint64 + Ctime uint64 + AtimeNsec uint32 + MtimeNsec uint32 + CtimeNsec uint32 + Mode uint32 + Nlink uint32 + Uid uint32 + Gid uint32 + Rdev uint32 + Blksize uint32 + padding uint32 +} + +func (a *attr) Crtime() time.Time { + return time.Time{} +} + +func (a *attr) SetCrtime(s uint64, ns uint32) { + // ignored on freebsd +} + +func (a *attr) SetFlags(f uint32) { + // ignored on freebsd +} + +type setattrIn struct { + setattrInCommon +} + +func (in *setattrIn) BkupTime() time.Time { + return time.Time{} +} + +func (in *setattrIn) Chgtime() time.Time { + return time.Time{} +} + +func (in *setattrIn) Flags() uint32 { + return 0 +} + func openFlags(flags uint32) OpenFlags { return OpenFlags(flags) } + +type getxattrIn struct { + getxattrInCommon +} + +type setxattrIn struct { + setxattrInCommon +} diff --git a/vendor/bazil.org/fuse/fuse_kernel_linux.go b/vendor/bazil.org/fuse/fuse_kernel_linux.go index 9f8a30f3b..fe603de92 100644 --- a/vendor/bazil.org/fuse/fuse_kernel_linux.go +++ b/vendor/bazil.org/fuse/fuse_kernel_linux.go @@ -1,5 +1,54 @@ package fuse +import "time" + +type attr struct { + Ino uint64 + Size uint64 + Blocks uint64 + Atime uint64 + Mtime uint64 + Ctime uint64 + AtimeNsec uint32 + MtimeNsec uint32 + CtimeNsec uint32 + Mode uint32 + Nlink uint32 + Uid uint32 + Gid uint32 + Rdev uint32 + Blksize uint32 + _ uint32 +} + +func (a *attr) Crtime() time.Time { + return time.Time{} +} + +func (a *attr) SetCrtime(s uint64, ns uint32) { + // Ignored on Linux. +} + +func (a *attr) SetFlags(f uint32) { + // Ignored on Linux. +} + +type setattrIn struct { + setattrInCommon +} + +func (in *setattrIn) BkupTime() time.Time { + return time.Time{} +} + +func (in *setattrIn) Chgtime() time.Time { + return time.Time{} +} + +func (in *setattrIn) Flags() uint32 { + return 0 +} + func openFlags(flags uint32) OpenFlags { // on amd64, the 32-bit O_LARGEFILE flag is always seen; // on i386, the flag probably depends on the app @@ -11,3 +60,11 @@ func openFlags(flags uint32) OpenFlags { return OpenFlags(flags) } + +type getxattrIn struct { + getxattrInCommon +} + +type setxattrIn struct { + setxattrInCommon +} diff --git a/vendor/bazil.org/fuse/fuse_kernel_std.go b/vendor/bazil.org/fuse/fuse_kernel_std.go new file mode 100644 index 000000000..074cfd322 --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_kernel_std.go @@ -0,0 +1 @@ +package fuse diff --git a/vendor/bazil.org/fuse/go.mod b/vendor/bazil.org/fuse/go.mod index c12bb7054..a80fda910 100644 --- a/vendor/bazil.org/fuse/go.mod +++ b/vendor/bazil.org/fuse/go.mod @@ -3,10 +3,6 @@ module bazil.org/fuse go 1.13 require ( - github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813 - github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect - github.com/stephens2424/writerset v1.0.2 // indirect github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 - golang.org/x/tools v0.0.0-20200423201157-2723c5de0d66 // indirect ) diff --git a/vendor/bazil.org/fuse/go.sum b/vendor/bazil.org/fuse/go.sum index 663af8076..ef81dc7c2 100644 --- a/vendor/bazil.org/fuse/go.sum +++ b/vendor/bazil.org/fuse/go.sum @@ -1,39 +1,4 @@ -github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813 h1:NgO45/5mBLRVfiXerEFzH6ikcZ7DNRPS639xFg3ENzU= -github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= -github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= -github.com/stephens2424/writerset v1.0.2 h1:znRLgU6g8RS5euYRcy004XeE4W+Tu44kALzy7ghPif8= -github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200423201157-2723c5de0d66 h1:EqVh9e7SxFnv93tWN3j33DpFAMKlFhaqI736Yp4kynk= -golang.org/x/tools v0.0.0-20200423201157-2723c5de0d66/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/vendor/bazil.org/fuse/mount.go b/vendor/bazil.org/fuse/mount.go index a143d527d..8054e9021 100644 --- a/vendor/bazil.org/fuse/mount.go +++ b/vendor/bazil.org/fuse/mount.go @@ -9,7 +9,11 @@ import ( ) var ( - // Deprecated: Never used, OS X remnant. + // 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") ) diff --git a/vendor/bazil.org/fuse/mount_darwin.go b/vendor/bazil.org/fuse/mount_darwin.go new file mode 100644 index 000000000..c1c36e62b --- /dev/null +++ b/vendor/bazil.org/fuse/mount_darwin.go @@ -0,0 +1,208 @@ +package fuse + +import ( + "errors" + "fmt" + "log" + "os" + "os/exec" + "path" + "strconv" + "strings" + "sync" + "syscall" +) + +var ( + errNoAvail = errors.New("no available fuse devices") + errNotLoaded = errors.New("osxfuse is not loaded") +) + +func loadOSXFUSE(bin string) error { + cmd := exec.Command(bin) + cmd.Dir = "/" + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + return err +} + +func openOSXFUSEDev(devPrefix string) (*os.File, error) { + var f *os.File + var err error + for i := uint64(0); ; i++ { + path := devPrefix + strconv.FormatUint(i, 10) + f, err = os.OpenFile(path, os.O_RDWR, 0000) + if os.IsNotExist(err) { + if i == 0 { + // not even the first device was found -> fuse is not loaded + return nil, errNotLoaded + } + + // we've run out of kernel-provided devices + return nil, errNoAvail + } + + if err2, ok := err.(*os.PathError); ok && err2.Err == syscall.EBUSY { + // try the next one + continue + } + + if err != nil { + return nil, err + } + return f, nil + } +} + +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 + // understand any escaping. See TestMountOptionCommaError. + return fmt.Errorf("mount options cannot contain commas on darwin: %q=%q", k, v) + } + } + cmd := exec.Command( + bin, + "-o", conf.getOptions(), + // Tell osxfuse-kext how large our buffer is. It must split + // writes larger than this into multiple writes. + // + // OSXFUSE seems to ignore InitResponse.MaxWrite, and uses + // this instead. + "-o", "iosize="+strconv.FormatUint(maxWrite, 10), + // refers to fd passed in cmd.ExtraFiles + "3", + dir, + ) + cmd.ExtraFiles = []*os.File{f} + cmd.Env = os.Environ() + // OSXFUSE <3.3.0 + cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=") + // OSXFUSE >=3.3.0 + cmd.Env = append(cmd.Env, "MOUNT_OSXFUSE_CALL_BY_LIB=") + + daemon := os.Args[0] + if daemonVar != "" { + 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() { + 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 = fmt.Errorf("mount_osxfusefs: %v", err) + close(ready) + return + } + + *errp = nil + close(ready) + }() + return nil +} + +func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) { + 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(loc.Load) + if err != nil { + return nil, err + } + // try again + f, err = openOSXFUSEDev(loc.DevicePrefix) + } + if err != nil { + return nil, err + } + 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 +} diff --git a/vendor/bazil.org/fuse/mount_freebsd.go b/vendor/bazil.org/fuse/mount_freebsd.go index 9106a18dd..70bb41024 100644 --- a/vendor/bazil.org/fuse/mount_freebsd.go +++ b/vendor/bazil.org/fuse/mount_freebsd.go @@ -47,7 +47,7 @@ func isBoringMountFusefsError(err error) bool { return false } -func mount(dir string, conf *mountConfig) (*os.File, error) { +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, ",") { // Silly limitation but the mount helper does not @@ -56,8 +56,9 @@ func mount(dir string, conf *mountConfig) (*os.File, error) { } } - f, err := os.OpenFile("/dev/fuse", os.O_RDWR, 0o000) + f, err := os.OpenFile("/dev/fuse", os.O_RDWR, 0000) if err != nil { + *errp = err return nil, err } @@ -105,5 +106,6 @@ func mount(dir string, conf *mountConfig) (*os.File, error) { return nil, fmt.Errorf("mount_fusefs: %v", err) } + close(ready) return f, nil } diff --git a/vendor/bazil.org/fuse/mount_linux.go b/vendor/bazil.org/fuse/mount_linux.go index 0d242c173..197d1044e 100644 --- a/vendor/bazil.org/fuse/mount_linux.go +++ b/vendor/bazil.org/fuse/mount_linux.go @@ -55,7 +55,10 @@ func isBoringFusermountError(err error) bool { return false } -func mount(dir string, conf *mountConfig) (fusefd *os.File, err error) { +func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) { + // linux mount is never delayed + close(ready) + fds, err := syscall.Socketpair(syscall.AF_FILE, syscall.SOCK_STREAM, 0) if err != nil { return nil, fmt.Errorf("socketpair error: %v", err) diff --git a/vendor/bazil.org/fuse/options.go b/vendor/bazil.org/fuse/options.go index ea973068d..f09ffd4ed 100644 --- a/vendor/bazil.org/fuse/options.go +++ b/vendor/bazil.org/fuse/options.go @@ -1,6 +1,7 @@ package fuse import ( + "errors" "strings" ) @@ -11,11 +12,10 @@ func dummyOption(conf *mountConfig) error { // mountConfig holds the configuration for a mount operation. // Use it by passing MountOption values to Mount. type mountConfig struct { - options map[string]string - maxReadahead uint32 - initFlags InitFlags - maxBackground uint16 - congestionThreshold uint16 + options map[string]string + maxReadahead uint32 + initFlags InitFlags + osxfuseLocations []OSXFUSEPaths } func escapeComma(s string) string { @@ -59,6 +59,7 @@ func FSName(name string) MountOption { // `fuse`. The type in a list of mounted file systems will look like // `fuse.foo`. // +// OS X ignores this option. // FreeBSD ignores this option. func Subtype(fstype string) MountOption { return func(conf *mountConfig) error { @@ -67,40 +68,82 @@ func Subtype(fstype string) MountOption { } } -// Deprecated: Ignored, OS X remnant. +// LocalVolume sets the volume to be local (instead of network), +// changing the behavior of Finder, Spotlight, and such. +// +// OS X only. Others ignore this option. func LocalVolume() MountOption { - return dummyOption + return localVolume } -// Deprecated: Ignored, OS X remnant. +// VolumeName sets the volume name shown in Finder. +// +// OS X only. Others ignore this option. func VolumeName(name string) MountOption { - return dummyOption + return volumeName(name) } -// Deprecated: Ignored, OS X remnant. +// 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 dummyOption + return noAppleDouble } -// Deprecated: Ignored, OS X remnant. +// 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 dummyOption + return noAppleXattr } -// Deprecated: Ignored, OS X remnant. +// NoBrowse makes OSXFUSE mark the volume as non-browsable, so that +// Finder won't automatically browse it. +// +// OS X only. Others ignore this option. func NoBrowse() MountOption { - return dummyOption + return noBrowse } -// Deprecated: Ignored, OS X remnant. +// 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 dummyOption + return exclCreate } // DaemonTimeout sets the time in seconds between a request and a reply before // the FUSE mount is declared dead. // -// FreeBSD only. Others ignore this option. +// OS X and FreeBSD only. Others ignore this option. func DaemonTimeout(name string) MountOption { return daemonTimeout(name) } @@ -188,7 +231,8 @@ func WritebackCache() MountOption { } } -// Deprecated: Not used, OS X remnant. +// 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. @@ -203,7 +247,7 @@ type OSXFUSEPaths struct { DaemonVar string } -// Deprecated: Not used, OS X remnant. +// Default paths for OSXFUSE. See OSXFUSELocations. var ( OSXFUSELocationV3 = OSXFUSEPaths{ DevicePrefix: "/dev/osxfuse", @@ -219,9 +263,24 @@ var ( } ) -// Deprecated: Ignored, OS X remnant. +// 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 dummyOption + 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. @@ -235,54 +294,3 @@ func AllowNonEmptyMount() MountOption { return nil } } - -// MaxBackground sets the maximum number of FUSE requests the kernel -// will submit in the background. Background requests are used when an -// immediate answer is not needed. This may help with request latency. -// -// On Linux, this can be adjusted on the fly with -// /sys/fs/fuse/connections/CONN/max_background -func MaxBackground(n uint16) MountOption { - return func(conf *mountConfig) error { - conf.maxBackground = n - return nil - } -} - -// CongestionThreshold sets the number of outstanding background FUSE -// requests beyond which the kernel considers the filesystem -// congested. This may help with request latency. -// -// On Linux, this can be adjusted on the fly with -// /sys/fs/fuse/connections/CONN/congestion_threshold -func CongestionThreshold(n uint16) MountOption { - // TODO to test this, we'd have to figure out our connection id - // and read /sys - return func(conf *mountConfig) error { - conf.congestionThreshold = n - return nil - } -} - -// LockingFlock enables flock-based (BSD) locking. This is mostly -// useful for distributed filesystems with global locking. Without -// this, kernel manages local locking automatically. -func LockingFlock() MountOption { - return func(conf *mountConfig) error { - conf.initFlags |= InitFlockLocks - return nil - } -} - -// LockingPOSIX enables flock-based (BSD) locking. This is mostly -// useful for distributed filesystems with global locking. Without -// this, kernel manages local locking automatically. -// -// Beware POSIX locks are a broken API with unintuitive behavior for -// callers. -func LockingPOSIX() MountOption { - return func(conf *mountConfig) error { - conf.initFlags |= InitPOSIXLocks - return nil - } -} diff --git a/vendor/bazil.org/fuse/options_darwin.go b/vendor/bazil.org/fuse/options_darwin.go new file mode 100644 index 000000000..a85e64cbf --- /dev/null +++ b/vendor/bazil.org/fuse/options_darwin.go @@ -0,0 +1,40 @@ +package fuse + +func localVolume(conf *mountConfig) error { + conf.options["local"] = "" + return nil +} + +func volumeName(name string) MountOption { + return func(conf *mountConfig) error { + conf.options["volname"] = name + 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 +} + +func noBrowse(conf *mountConfig) error { + conf.options["nobrowse"] = "" + return nil +} diff --git a/vendor/bazil.org/fuse/options_freebsd.go b/vendor/bazil.org/fuse/options_freebsd.go index d812d8838..2c956e7fb 100644 --- a/vendor/bazil.org/fuse/options_freebsd.go +++ b/vendor/bazil.org/fuse/options_freebsd.go @@ -1,8 +1,32 @@ package fuse +func localVolume(conf *mountConfig) error { + return nil +} + +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 +} + +func noBrowse(conf *mountConfig) error { + return nil +} diff --git a/vendor/bazil.org/fuse/options_linux.go b/vendor/bazil.org/fuse/options_linux.go index 4eb365944..2c925b18d 100644 --- a/vendor/bazil.org/fuse/options_linux.go +++ b/vendor/bazil.org/fuse/options_linux.go @@ -1,5 +1,29 @@ package fuse +func localVolume(conf *mountConfig) error { + return nil +} + +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 +} + +func noBrowse(conf *mountConfig) error { + return nil +} diff --git a/vendor/bazil.org/fuse/protocol.go b/vendor/bazil.org/fuse/protocol.go index 0e93063bf..a77bbf72f 100644 --- a/vendor/bazil.org/fuse/protocol.go +++ b/vendor/bazil.org/fuse/protocol.go @@ -26,56 +26,50 @@ func (a Protocol) GE(b Protocol) bool { (a.Major == b.Major && a.Minor >= b.Minor) } +func (a Protocol) is79() bool { + return a.GE(Protocol{7, 9}) +} + // HasAttrBlockSize returns whether Attr.BlockSize is respected by the // kernel. -// -// Deprecated: Guaranteed to be true with our minimum supported -// protocol version. func (a Protocol) HasAttrBlockSize() bool { - return true + return a.is79() } // HasReadWriteFlags returns whether ReadRequest/WriteRequest // fields Flags and FileFlags are valid. -// -// Deprecated: Guaranteed to be true with our minimum supported -// protocol version. func (a Protocol) HasReadWriteFlags() bool { - return true + return a.is79() } // HasGetattrFlags returns whether GetattrRequest field Flags is // valid. -// -// Deprecated: Guaranteed to be true with our minimum supported -// protocol version. func (a Protocol) HasGetattrFlags() bool { - return true + return a.is79() +} + +func (a Protocol) is710() bool { + return a.GE(Protocol{7, 10}) } // HasOpenNonSeekable returns whether OpenResponse field Flags flag // OpenNonSeekable is supported. -// -// Deprecated: Guaranteed to be true with our minimum supported -// protocol version. func (a Protocol) HasOpenNonSeekable() bool { - return true + return a.is710() +} + +func (a Protocol) is712() bool { + return a.GE(Protocol{7, 12}) } // HasUmask returns whether CreateRequest/MkdirRequest/MknodRequest // field Umask is valid. -// -// Deprecated: Guaranteed to be true with our minimum supported -// protocol version. func (a Protocol) HasUmask() bool { - return true + return a.is712() } // HasInvalidate returns whether InvalidateNode/InvalidateEntry are // supported. -// -// Deprecated: Guaranteed to be true with our minimum supported -// protocol version. func (a Protocol) HasInvalidate() bool { - return true + return a.is712() } diff --git a/vendor/modules.txt b/vendor/modules.txt index c5e8ff14d..d4a0081a0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# bazil.org/fuse v0.0.0-20200524192727-fb710f7dfd05 +# bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512 ## explicit bazil.org/fuse bazil.org/fuse/fs @@ -116,6 +116,10 @@ github.com/dropbox/dropbox-sdk-go-unofficial/dropbox/team_common github.com/dropbox/dropbox-sdk-go-unofficial/dropbox/team_policies github.com/dropbox/dropbox-sdk-go-unofficial/dropbox/users github.com/dropbox/dropbox-sdk-go-unofficial/dropbox/users_common +# github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813 +## explicit +# github.com/elazarl/go-bindata-assetfs v1.0.0 +## explicit # github.com/gogo/protobuf v1.3.1 ## explicit github.com/gogo/protobuf/proto @@ -275,6 +279,8 @@ github.com/spf13/cobra/doc # github.com/spf13/pflag v1.0.5 ## explicit github.com/spf13/pflag +# github.com/stephens2424/writerset v1.0.2 +## explicit # github.com/stretchr/testify v1.6.1 ## explicit github.com/stretchr/testify/assert