summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mattermost/rsc/fuse
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mattermost/rsc/fuse')
-rw-r--r--vendor/github.com/mattermost/rsc/fuse/debug.go20
-rw-r--r--vendor/github.com/mattermost/rsc/fuse/fuse.go1650
-rw-r--r--vendor/github.com/mattermost/rsc/fuse/fuse_kernel.go539
-rw-r--r--vendor/github.com/mattermost/rsc/fuse/fuse_kernel_darwin.go58
-rw-r--r--vendor/github.com/mattermost/rsc/fuse/fuse_kernel_linux.go50
-rw-r--r--vendor/github.com/mattermost/rsc/fuse/fuse_kernel_std.go1
-rw-r--r--vendor/github.com/mattermost/rsc/fuse/fuse_test.go594
-rw-r--r--vendor/github.com/mattermost/rsc/fuse/hellofs/hello.go62
-rw-r--r--vendor/github.com/mattermost/rsc/fuse/mount_darwin.go122
-rw-r--r--vendor/github.com/mattermost/rsc/fuse/mount_linux.go67
-rw-r--r--vendor/github.com/mattermost/rsc/fuse/serve.go1022
-rw-r--r--vendor/github.com/mattermost/rsc/fuse/tree.go93
12 files changed, 4278 insertions, 0 deletions
diff --git a/vendor/github.com/mattermost/rsc/fuse/debug.go b/vendor/github.com/mattermost/rsc/fuse/debug.go
new file mode 100644
index 000000000..2dc5d4312
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/debug.go
@@ -0,0 +1,20 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// FUSE service loop, for servers that wish to use it.
+
+package fuse
+
+import (
+ "runtime"
+)
+
+func stack() string {
+ buf := make([]byte, 1024)
+ return string(buf[:runtime.Stack(buf, false)])
+}
+
+var Debugf = nop
+
+func nop(string, ...interface{}) {}
diff --git a/vendor/github.com/mattermost/rsc/fuse/fuse.go b/vendor/github.com/mattermost/rsc/fuse/fuse.go
new file mode 100644
index 000000000..2ad10c93e
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/fuse.go
@@ -0,0 +1,1650 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c,
+// which carries this notice:
+//
+// The files in this directory are subject to the following license.
+//
+// The author of this software is Russ Cox.
+//
+// Copyright (c) 2006 Russ Cox
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose without fee is hereby granted, provided that this entire notice
+// is included in all copies of any software which is or includes a copy
+// or modification of this software and in all copies of the supporting
+// documentation for such software.
+//
+// THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+// WARRANTY. IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR WARRANTY
+// OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS
+// FITNESS FOR ANY PARTICULAR PURPOSE.
+
+// Package fuse enables writing FUSE file systems on FreeBSD, Linux, and OS X.
+//
+// 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
+// writing using the various Respond methods. This approach is closest to
+// the actual interaction with the kernel and can be the simplest one in contexts
+// such as protocol translators.
+//
+// Servers of synthesized file systems tend to share common bookkeeping
+// abstracted away by the second approach, which is to call the Conn's
+// Serve method to serve the FUSE protocol using
+// an implementation of the service methods in the interfaces
+// FS (file system), Node (file or directory), and Handle (opened file or directory).
+// There are a daunting number of such methods that can be written,
+// but few are required.
+// The specific methods are described in the documentation for those interfaces.
+//
+// The hellofs subdirectory contains a simple illustration of the ServeFS approach.
+//
+// Service Methods
+//
+// The required and optional methods for the FS, Node, and Handle interfaces
+// have the general form
+//
+// Op(req *OpRequest, resp *OpResponse, intr Intr) Error
+//
+// where Op is the name of a FUSE operation. Op reads request parameters
+// from req and writes results to resp. An operation whose only result is
+// the error result omits the resp parameter. Multiple goroutines may call
+// service methods simultaneously; the methods being called are responsible
+// for appropriate synchronization.
+//
+// Interrupted Operations
+//
+// In some file systems, some operations
+// may take an undetermined amount of time. For example, a Read waiting for
+// a network message or a matching Write might wait indefinitely. If the request
+// is cancelled and no longer needed, the package will close intr, a chan struct{}.
+// Blocking operations should select on a receive from intr and attempt to
+// abort the operation early if the receive succeeds (meaning the channel is closed).
+// To indicate that the operation failed because it was aborted, return fuse.EINTR.
+//
+// If an operation does not block for an indefinite amount of time, the intr parameter
+// can be ignored.
+//
+// Authentication
+//
+// All requests types embed a Header, meaning that the method can inspect
+// req.Pid, req.Uid, and req.Gid as necessary to implement permission checking.
+// Alternately, XXX.
+//
+// Mount Options
+//
+// XXX
+//
+package fuse
+
+// BUG(rsc): The mount code for FreeBSD has not been written yet.
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "runtime"
+ "sync"
+ "syscall"
+ "time"
+ "unsafe"
+)
+
+// A Conn represents a connection to a mounted FUSE file system.
+type Conn struct {
+ fd int
+ buf []byte
+ wio sync.Mutex
+
+ serveConn
+}
+
+// Mount mounts a new FUSE connection on the named directory
+// and returns a connection for reading and writing FUSE messages.
+func Mount(dir string) (*Conn, error) {
+ // TODO(rsc): mount options (...string?)
+ fd, errstr := mount(dir)
+ if errstr != "" {
+ return nil, errors.New(errstr)
+ }
+
+ return &Conn{fd: fd}, nil
+}
+
+// A Request represents a single FUSE request received from the kernel.
+// Use a type switch to determine the specific kind.
+// A request of unrecognized type will have concrete type *Header.
+type Request interface {
+ // Hdr returns the Header associated with this request.
+ Hdr() *Header
+
+ // RespondError responds to the request with the given error.
+ RespondError(Error)
+
+ String() string
+
+ // handle returns the HandleID for the request, or 0.
+ handle() HandleID
+}
+
+// A RequestID identifies an active FUSE request.
+type RequestID uint64
+
+// A NodeID is a number identifying a directory or file.
+// It must be unique among IDs returned in LookupResponses
+// that have not yet been forgotten by ForgetRequests.
+type NodeID uint64
+
+// A HandleID is a number identifying an open directory or file.
+// It only needs to be unique while the directory or file is open.
+type HandleID uint64
+
+// The RootID identifies the root directory of a FUSE file system.
+const RootID NodeID = rootID
+
+// A Header describes the basic information sent in every request.
+type Header struct {
+ Conn *Conn // connection this request was received on
+ ID RequestID // unique ID for request
+ Node NodeID // file or directory the request is about
+ Uid uint32 // user ID of process making request
+ Gid uint32 // group ID of process making request
+ Pid uint32 // process ID of process making request
+}
+
+func (h *Header) String() string {
+ return fmt.Sprintf("ID=%#x Node=%#x Uid=%d Gid=%d Pid=%d", h.ID, h.Node, h.Uid, h.Gid, h.Pid)
+}
+
+func (h *Header) Hdr() *Header {
+ return h
+}
+
+func (h *Header) handle() HandleID {
+ return 0
+}
+
+// An Error is a FUSE error.
+type Error interface {
+ errno() int32
+}
+
+const (
+ // ENOSYS indicates that the call is not supported.
+ ENOSYS = Errno(syscall.ENOSYS)
+
+ // ESTALE is used by Serve to respond to violations of the FUSE protocol.
+ ESTALE = Errno(syscall.ESTALE)
+
+ ENOENT = Errno(syscall.ENOENT)
+ EIO = Errno(syscall.EIO)
+ EPERM = Errno(syscall.EPERM)
+)
+
+type errno int
+
+func (e errno) errno() int32 {
+ return int32(e)
+}
+
+// Errno implements Error using a syscall.Errno.
+type Errno syscall.Errno
+
+func (e Errno) errno() int32 {
+ return int32(e)
+}
+
+func (e Errno) String() string {
+ return syscall.Errno(e).Error()
+}
+
+func (h *Header) RespondError(err Error) {
+ // FUSE uses negative errors!
+ // TODO: File bug report against OSXFUSE: positive error causes kernel panic.
+ out := &outHeader{Error: -err.errno(), Unique: uint64(h.ID)}
+ h.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+var maxWrite = syscall.Getpagesize()
+var bufSize = 4096 + maxWrite
+
+// a message represents the bytes of a single FUSE message
+type message struct {
+ conn *Conn
+ buf []byte // all bytes
+ hdr *inHeader // header
+ off int // offset for reading additional fields
+}
+
+func newMessage(c *Conn) *message {
+ m := &message{conn: c, buf: make([]byte, bufSize)}
+ m.hdr = (*inHeader)(unsafe.Pointer(&m.buf[0]))
+ return m
+}
+
+func (m *message) len() uintptr {
+ return uintptr(len(m.buf) - m.off)
+}
+
+func (m *message) data() unsafe.Pointer {
+ var p unsafe.Pointer
+ if m.off < len(m.buf) {
+ p = unsafe.Pointer(&m.buf[m.off])
+ }
+ return p
+}
+
+func (m *message) bytes() []byte {
+ return m.buf[m.off:]
+}
+
+func (m *message) Header() Header {
+ h := m.hdr
+ return Header{Conn: m.conn, ID: RequestID(h.Unique), Node: NodeID(h.Nodeid), Uid: h.Uid, Gid: h.Gid, Pid: h.Pid}
+}
+
+// fileMode returns a Go os.FileMode from a Unix mode.
+func fileMode(unixMode uint32) os.FileMode {
+ mode := os.FileMode(unixMode & 0777)
+ switch unixMode & syscall.S_IFMT {
+ case syscall.S_IFREG:
+ // nothing
+ case syscall.S_IFDIR:
+ mode |= os.ModeDir
+ case syscall.S_IFCHR:
+ mode |= os.ModeCharDevice | os.ModeDevice
+ case syscall.S_IFBLK:
+ mode |= os.ModeDevice
+ case syscall.S_IFIFO:
+ mode |= os.ModeNamedPipe
+ case syscall.S_IFLNK:
+ mode |= os.ModeSymlink
+ case syscall.S_IFSOCK:
+ mode |= os.ModeSocket
+ default:
+ // no idea
+ mode |= os.ModeDevice
+ }
+ if unixMode&syscall.S_ISUID != 0 {
+ mode |= os.ModeSetuid
+ }
+ if unixMode&syscall.S_ISGID != 0 {
+ mode |= os.ModeSetgid
+ }
+ return mode
+}
+
+func (c *Conn) ReadRequest() (Request, error) {
+ // TODO: Some kind of buffer reuse.
+ m := newMessage(c)
+ n, err := syscall.Read(c.fd, m.buf)
+ if err != nil && err != syscall.ENODEV {
+ return nil, err
+ }
+ if n <= 0 {
+ return nil, io.EOF
+ }
+ m.buf = m.buf[:n]
+
+ if n < inHeaderSize {
+ return nil, errors.New("fuse: message too short")
+ }
+
+ // FreeBSD FUSE sends a short length in the header
+ // for FUSE_INIT even though the actual read length is correct.
+ if n == inHeaderSize+initInSize && m.hdr.Opcode == opInit && m.hdr.Len < uint32(n) {
+ 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) {
+ return nil, fmt.Errorf("fuse: read %d opcode %d but expected %d", n, m.hdr.Opcode, m.hdr.Len)
+ }
+
+ m.off = inHeaderSize
+
+ // Convert to data structures.
+ // Do not trust kernel to hand us well-formed data.
+ var req Request
+ switch m.hdr.Opcode {
+ default:
+ println("No opcode", m.hdr.Opcode)
+ goto unrecognized
+
+ case opLookup:
+ buf := m.bytes()
+ n := len(buf)
+ if n == 0 || buf[n-1] != '\x00' {
+ goto corrupt
+ }
+ req = &LookupRequest{
+ Header: m.Header(),
+ Name: string(buf[:n-1]),
+ }
+
+ case opForget:
+ in := (*forgetIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ req = &ForgetRequest{
+ Header: m.Header(),
+ N: in.Nlookup,
+ }
+
+ case opGetattr:
+ req = &GetattrRequest{
+ Header: m.Header(),
+ }
+
+ case opSetattr:
+ in := (*setattrIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ 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,
+ Bkuptime: in.BkupTime(),
+ Chgtime: in.Chgtime(),
+ Flags: in.Flags(),
+ }
+
+ case opReadlink:
+ if len(m.bytes()) > 0 {
+ goto corrupt
+ }
+ req = &ReadlinkRequest{
+ Header: m.Header(),
+ }
+
+ case opSymlink:
+ // m.bytes() is "newName\0target\0"
+ names := m.bytes()
+ if len(names) == 0 || names[len(names)-1] != 0 {
+ goto corrupt
+ }
+ i := bytes.IndexByte(names, '\x00')
+ if i < 0 {
+ goto corrupt
+ }
+ newName, target := names[0:i], names[i+1:len(names)-1]
+ req = &SymlinkRequest{
+ Header: m.Header(),
+ NewName: string(newName),
+ Target: string(target),
+ }
+
+ case opLink:
+ in := (*linkIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ newName := m.bytes()[unsafe.Sizeof(*in):]
+ if len(newName) < 2 || newName[len(newName)-1] != 0 {
+ goto corrupt
+ }
+ newName = newName[:len(newName)-1]
+ req = &LinkRequest{
+ Header: m.Header(),
+ OldNode: NodeID(in.Oldnodeid),
+ NewName: string(newName),
+ }
+
+ case opMknod:
+ in := (*mknodIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ name := m.bytes()[unsafe.Sizeof(*in):]
+ if len(name) < 2 || name[len(name)-1] != '\x00' {
+ goto corrupt
+ }
+ name = name[:len(name)-1]
+ req = &MknodRequest{
+ Header: m.Header(),
+ Mode: fileMode(in.Mode),
+ Rdev: in.Rdev,
+ Name: string(name),
+ }
+
+ case opMkdir:
+ in := (*mkdirIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ name := m.bytes()[unsafe.Sizeof(*in):]
+ i := bytes.IndexByte(name, '\x00')
+ if i < 0 {
+ goto corrupt
+ }
+ req = &MkdirRequest{
+ Header: m.Header(),
+ Name: string(name[:i]),
+ Mode: fileMode(in.Mode) | os.ModeDir,
+ }
+
+ case opUnlink, opRmdir:
+ buf := m.bytes()
+ n := len(buf)
+ if n == 0 || buf[n-1] != '\x00' {
+ goto corrupt
+ }
+ req = &RemoveRequest{
+ Header: m.Header(),
+ Name: string(buf[:n-1]),
+ Dir: m.hdr.Opcode == opRmdir,
+ }
+
+ case opRename:
+ in := (*renameIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ newDirNodeID := NodeID(in.Newdir)
+ oldNew := m.bytes()[unsafe.Sizeof(*in):]
+ // oldNew should be "old\x00new\x00"
+ if len(oldNew) < 4 {
+ goto corrupt
+ }
+ if oldNew[len(oldNew)-1] != '\x00' {
+ goto corrupt
+ }
+ i := bytes.IndexByte(oldNew, '\x00')
+ if i < 0 {
+ goto corrupt
+ }
+ oldName, newName := string(oldNew[:i]), string(oldNew[i+1:len(oldNew)-1])
+ // log.Printf("RENAME: newDirNode = %d; old = %q, new = %q", newDirNodeID, oldName, newName)
+ req = &RenameRequest{
+ Header: m.Header(),
+ NewDir: newDirNodeID,
+ OldName: oldName,
+ NewName: newName,
+ }
+
+ case opOpendir, opOpen:
+ in := (*openIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ req = &OpenRequest{
+ Header: m.Header(),
+ Dir: m.hdr.Opcode == opOpendir,
+ Flags: in.Flags,
+ Mode: fileMode(in.Mode),
+ }
+
+ case opRead, opReaddir:
+ in := (*readIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ req = &ReadRequest{
+ Header: m.Header(),
+ Dir: m.hdr.Opcode == opReaddir,
+ Handle: HandleID(in.Fh),
+ Offset: int64(in.Offset),
+ Size: int(in.Size),
+ }
+
+ case opWrite:
+ in := (*writeIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ r := &WriteRequest{
+ Header: m.Header(),
+ Handle: HandleID(in.Fh),
+ Offset: int64(in.Offset),
+ Flags: WriteFlags(in.WriteFlags),
+ }
+ buf := m.bytes()[unsafe.Sizeof(*in):]
+ if uint32(len(buf)) < in.Size {
+ goto corrupt
+ }
+ r.Data = buf
+ req = r
+
+ case opStatfs:
+ req = &StatfsRequest{
+ Header: m.Header(),
+ }
+
+ case opRelease, opReleasedir:
+ in := (*releaseIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ req = &ReleaseRequest{
+ Header: m.Header(),
+ Dir: m.hdr.Opcode == opReleasedir,
+ Handle: HandleID(in.Fh),
+ Flags: in.Flags,
+ ReleaseFlags: ReleaseFlags(in.ReleaseFlags),
+ LockOwner: in.LockOwner,
+ }
+
+ case opFsync:
+ in := (*fsyncIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ req = &FsyncRequest{
+ Header: m.Header(),
+ Handle: HandleID(in.Fh),
+ Flags: in.FsyncFlags,
+ }
+
+ case opSetxattr:
+ var size uint32
+ var r *SetxattrRequest
+ if runtime.GOOS == "darwin" {
+ in := (*setxattrInOSX)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ r = &SetxattrRequest{
+ Flags: in.Flags,
+ Position: in.Position,
+ }
+ size = in.Size
+ m.off += int(unsafe.Sizeof(*in))
+ } else {
+ in := (*setxattrIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ r = &SetxattrRequest{}
+ size = in.Size
+ m.off += int(unsafe.Sizeof(*in))
+ }
+ r.Header = m.Header()
+ name := m.bytes()
+ i := bytes.IndexByte(name, '\x00')
+ if i < 0 {
+ goto corrupt
+ }
+ r.Name = string(name[:i])
+ r.Xattr = name[i+1:]
+ if uint32(len(r.Xattr)) < size {
+ goto corrupt
+ }
+ r.Xattr = r.Xattr[:size]
+ req = r
+
+ case opGetxattr:
+ if runtime.GOOS == "darwin" {
+ in := (*getxattrInOSX)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ req = &GetxattrRequest{
+ Header: m.Header(),
+ Size: in.Size,
+ Position: in.Position,
+ }
+ } else {
+ in := (*getxattrIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ req = &GetxattrRequest{
+ Header: m.Header(),
+ Size: in.Size,
+ }
+ }
+
+ case opListxattr:
+ if runtime.GOOS == "darwin" {
+ in := (*getxattrInOSX)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ req = &ListxattrRequest{
+ Header: m.Header(),
+ Size: in.Size,
+ Position: in.Position,
+ }
+ } else {
+ in := (*getxattrIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ req = &ListxattrRequest{
+ Header: m.Header(),
+ Size: in.Size,
+ }
+ }
+
+ case opRemovexattr:
+ buf := m.bytes()
+ n := len(buf)
+ if n == 0 || buf[n-1] != '\x00' {
+ goto corrupt
+ }
+ req = &RemovexattrRequest{
+ Header: m.Header(),
+ Name: string(buf[:n-1]),
+ }
+
+ case opFlush:
+ in := (*flushIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ req = &FlushRequest{
+ Header: m.Header(),
+ Handle: HandleID(in.Fh),
+ Flags: in.FlushFlags,
+ LockOwner: in.LockOwner,
+ }
+
+ case opInit:
+ in := (*initIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ req = &InitRequest{
+ Header: m.Header(),
+ Major: in.Major,
+ Minor: in.Minor,
+ MaxReadahead: in.MaxReadahead,
+ Flags: InitFlags(in.Flags),
+ }
+
+ case opFsyncdir:
+ panic("opFsyncdir")
+ case opGetlk:
+ panic("opGetlk")
+ case opSetlk:
+ panic("opSetlk")
+ case opSetlkw:
+ panic("opSetlkw")
+
+ case opAccess:
+ in := (*accessIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ req = &AccessRequest{
+ Header: m.Header(),
+ Mask: in.Mask,
+ }
+
+ case opCreate:
+ in := (*openIn)(m.data())
+ if m.len() < unsafe.Sizeof(*in) {
+ goto corrupt
+ }
+ name := m.bytes()[unsafe.Sizeof(*in):]
+ i := bytes.IndexByte(name, '\x00')
+ if i < 0 {
+ goto corrupt
+ }
+ req = &CreateRequest{
+ Header: m.Header(),
+ Flags: in.Flags,
+ Mode: fileMode(in.Mode),
+ Name: string(name[:i]),
+ }
+
+ case opInterrupt:
+ panic("opInterrupt")
+ case opBmap:
+ panic("opBmap")
+
+ case opDestroy:
+ req = &DestroyRequest{
+ Header: m.Header(),
+ }
+
+ // OS X
+ case opSetvolname:
+ panic("opSetvolname")
+ case opGetxtimes:
+ panic("opGetxtimes")
+ case opExchange:
+ panic("opExchange")
+ }
+
+ return req, nil
+
+corrupt:
+ println("malformed message")
+ return nil, fmt.Errorf("fuse: malformed message")
+
+unrecognized:
+ // Unrecognized message.
+ // Assume higher-level code will send a "no idea what you mean" error.
+ h := m.Header()
+ return &h, nil
+}
+
+func (c *Conn) respond(out *outHeader, n uintptr) {
+ c.wio.Lock()
+ defer c.wio.Unlock()
+ out.Len = uint32(n)
+ msg := (*[1 << 30]byte)(unsafe.Pointer(out))[:n]
+ nn, err := syscall.Write(c.fd, msg)
+ if nn != len(msg) || err != nil {
+ log.Printf("RESPOND WRITE: %d %v", nn, err)
+ log.Printf("with stack: %s", stack())
+ }
+}
+
+func (c *Conn) respondData(out *outHeader, n uintptr, data []byte) {
+ c.wio.Lock()
+ defer c.wio.Unlock()
+ // TODO: use writev
+ out.Len = uint32(n + uintptr(len(data)))
+ msg := make([]byte, out.Len)
+ copy(msg, (*[1 << 30]byte)(unsafe.Pointer(out))[:n])
+ copy(msg[n:], data)
+ syscall.Write(c.fd, msg)
+}
+
+// An InitRequest is the first request sent on a FUSE file system.
+type InitRequest struct {
+ Header
+ Major uint32
+ Minor uint32
+ MaxReadahead uint32
+ Flags InitFlags
+}
+
+func (r *InitRequest) String() string {
+ return fmt.Sprintf("Init [%s] %d.%d ra=%d fl=%v", &r.Header, r.Major, r.Minor, r.MaxReadahead, r.Flags)
+}
+
+// An InitResponse is the response to an InitRequest.
+type InitResponse struct {
+ MaxReadahead uint32
+ Flags InitFlags
+ MaxWrite uint32
+}
+
+func (r *InitResponse) String() string {
+ return fmt.Sprintf("Init %+v", *r)
+}
+
+// Respond replies to the request with the given response.
+func (r *InitRequest) Respond(resp *InitResponse) {
+ out := &initOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ Major: kernelVersion,
+ Minor: kernelMinorVersion,
+ MaxReadahead: resp.MaxReadahead,
+ Flags: uint32(resp.Flags),
+ MaxWrite: resp.MaxWrite,
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A StatfsRequest requests information about the mounted file system.
+type StatfsRequest struct {
+ Header
+}
+
+func (r *StatfsRequest) String() string {
+ return fmt.Sprintf("Statfs [%s]\n", &r.Header)
+}
+
+// Respond replies to the request with the given response.
+func (r *StatfsRequest) Respond(resp *StatfsResponse) {
+ out := &statfsOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ St: kstatfs{
+ Blocks: resp.Blocks,
+ Bfree: resp.Bfree,
+ Bavail: resp.Bavail,
+ Files: resp.Files,
+ Bsize: resp.Bsize,
+ Namelen: resp.Namelen,
+ Frsize: resp.Frsize,
+ },
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A StatfsResponse is the response to a StatfsRequest.
+type StatfsResponse struct {
+ Blocks uint64 // Total data blocks in file system.
+ Bfree uint64 // Free blocks in file system.
+ Bavail uint64 // Free blocks in file system if you're not root.
+ Files uint64 // Total files in file system.
+ Ffree uint64 // Free files in file system.
+ Bsize uint32 // Block size
+ Namelen uint32 // Maximum file name length?
+ Frsize uint32 // ?
+}
+
+func (r *StatfsResponse) String() string {
+ return fmt.Sprintf("Statfs %+v", *r)
+}
+
+// An AccessRequest asks whether the file can be accessed
+// for the purpose specified by the mask.
+type AccessRequest struct {
+ Header
+ Mask uint32
+}
+
+func (r *AccessRequest) String() string {
+ return fmt.Sprintf("Access [%s] mask=%#x", &r.Header, r.Mask)
+}
+
+// Respond replies to the request indicating that access is allowed.
+// To deny access, use RespondError.
+func (r *AccessRequest) Respond() {
+ out := &outHeader{Unique: uint64(r.ID)}
+ r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// An Attr is the metadata for a single file or directory.
+type Attr struct {
+ Inode uint64 // inode number
+ Size uint64 // size in bytes
+ Blocks uint64 // size in blocks
+ 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
+ Uid uint32 // owner uid
+ Gid uint32 // group gid
+ Rdev uint32 // device numbers
+ Flags uint32 // chflags(2) flags (OS X only)
+}
+
+func unix(t time.Time) (sec uint64, nsec uint32) {
+ nano := t.UnixNano()
+ sec = uint64(nano / 1e9)
+ nsec = uint32(nano % 1e9)
+ return
+}
+
+func (a *Attr) attr() (out attr) {
+ out.Ino = a.Inode
+ out.Size = a.Size
+ out.Blocks = a.Blocks
+ 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
+ case a.Mode&os.ModeDir != 0:
+ out.Mode |= syscall.S_IFDIR
+ case a.Mode&os.ModeDevice != 0:
+ if a.Mode&os.ModeCharDevice != 0 {
+ out.Mode |= syscall.S_IFCHR
+ } else {
+ out.Mode |= syscall.S_IFBLK
+ }
+ case a.Mode&os.ModeNamedPipe != 0:
+ out.Mode |= syscall.S_IFIFO
+ case a.Mode&os.ModeSymlink != 0:
+ out.Mode |= syscall.S_IFLNK
+ case a.Mode&os.ModeSocket != 0:
+ out.Mode |= syscall.S_IFSOCK
+ }
+ if a.Mode&os.ModeSetuid != 0 {
+ out.Mode |= syscall.S_ISUID
+ }
+ if a.Mode&os.ModeSetgid != 0 {
+ out.Mode |= syscall.S_ISGID
+ }
+ out.Nlink = a.Nlink
+ if out.Nlink < 1 {
+ out.Nlink = 1
+ }
+ out.Uid = a.Uid
+ out.Gid = a.Gid
+ out.Rdev = a.Rdev
+ out.SetFlags(a.Flags)
+
+ return
+}
+
+// A GetattrRequest asks for the metadata for the file denoted by r.Node.
+type GetattrRequest struct {
+ Header
+}
+
+func (r *GetattrRequest) String() string {
+ return fmt.Sprintf("Getattr [%s]", &r.Header)
+}
+
+// Respond replies to the request with the given response.
+func (r *GetattrRequest) Respond(resp *GetattrResponse) {
+ out := &attrOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ AttrValid: uint64(resp.AttrValid / time.Second),
+ AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
+ Attr: resp.Attr.attr(),
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A GetattrResponse is the response to a GetattrRequest.
+type GetattrResponse struct {
+ AttrValid time.Duration // how long Attr can be cached
+ Attr Attr // file attributes
+}
+
+func (r *GetattrResponse) String() string {
+ return fmt.Sprintf("Getattr %+v", *r)
+}
+
+// A GetxattrRequest asks for the extended attributes associated with r.Node.
+type GetxattrRequest struct {
+ Header
+ Size uint32 // maximum size to return
+ Position uint32 // offset within extended attributes
+}
+
+func (r *GetxattrRequest) String() string {
+ return fmt.Sprintf("Getxattr [%s] %d @%d", &r.Header, r.Size, r.Position)
+}
+
+// Respond replies to the request with the given response.
+func (r *GetxattrRequest) Respond(resp *GetxattrResponse) {
+ out := &getxattrOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ Size: uint32(len(resp.Xattr)),
+ }
+ r.Conn.respondData(&out.outHeader, unsafe.Sizeof(*out), resp.Xattr)
+}
+
+// A GetxattrResponse is the response to a GetxattrRequest.
+type GetxattrResponse struct {
+ Xattr []byte
+}
+
+func (r *GetxattrResponse) String() string {
+ return fmt.Sprintf("Getxattr %x", r.Xattr)
+}
+
+// A ListxattrRequest asks to list the extended attributes associated with r.Node.
+type ListxattrRequest struct {
+ Header
+ Size uint32 // maximum size to return
+ Position uint32 // offset within attribute list
+}
+
+func (r *ListxattrRequest) String() string {
+ return fmt.Sprintf("Listxattr [%s] %d @%d", &r.Header, r.Size, r.Position)
+}
+
+// Respond replies to the request with the given response.
+func (r *ListxattrRequest) Respond(resp *ListxattrResponse) {
+ out := &getxattrOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ Size: uint32(len(resp.Xattr)),
+ }
+ r.Conn.respondData(&out.outHeader, unsafe.Sizeof(*out), resp.Xattr)
+}
+
+// A ListxattrResponse is the response to a ListxattrRequest.
+type ListxattrResponse struct {
+ Xattr []byte
+}
+
+func (r *ListxattrResponse) String() string {
+ return fmt.Sprintf("Listxattr %x", r.Xattr)
+}
+
+// A RemovexattrRequest asks to remove an extended attribute associated with r.Node.
+type RemovexattrRequest struct {
+ Header
+ Name string // name of extended attribute
+}
+
+func (r *RemovexattrRequest) String() string {
+ return fmt.Sprintf("Removexattr [%s] %q", &r.Header, r.Name)
+}
+
+// Respond replies to the request, indicating that the attribute was removed.
+func (r *RemovexattrRequest) Respond() {
+ out := &outHeader{Unique: uint64(r.ID)}
+ r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// A SetxattrRequest asks to set an extended attribute associated with a file.
+type SetxattrRequest struct {
+ Header
+ Flags uint32
+ Position uint32 // OS X only
+ Name string
+ Xattr []byte
+}
+
+func (r *SetxattrRequest) String() string {
+ return fmt.Sprintf("Setxattr [%s] %q %x fl=%v @%#x", &r.Header, r.Name, r.Xattr, r.Flags, r.Position)
+}
+
+// Respond replies to the request, indicating that the extended attribute was set.
+func (r *SetxattrRequest) Respond() {
+ out := &outHeader{Unique: uint64(r.ID)}
+ r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// A LookupRequest asks to look up the given name in the directory named by r.Node.
+type LookupRequest struct {
+ Header
+ Name string
+}
+
+func (r *LookupRequest) String() string {
+ return fmt.Sprintf("Lookup [%s] %q", &r.Header, r.Name)
+}
+
+// Respond replies to the request with the given response.
+func (r *LookupRequest) Respond(resp *LookupResponse) {
+ out := &entryOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ Nodeid: uint64(resp.Node),
+ Generation: resp.Generation,
+ EntryValid: uint64(resp.EntryValid / time.Second),
+ EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
+ AttrValid: uint64(resp.AttrValid / time.Second),
+ AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
+ Attr: resp.Attr.attr(),
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A LookupResponse is the response to a LookupRequest.
+type LookupResponse struct {
+ Node NodeID
+ Generation uint64
+ EntryValid time.Duration
+ AttrValid time.Duration
+ Attr Attr
+}
+
+func (r *LookupResponse) String() string {
+ return fmt.Sprintf("Lookup %+v", *r)
+}
+
+// An OpenRequest asks to open a file or directory
+type OpenRequest struct {
+ Header
+ Dir bool // is this Opendir?
+ Flags uint32
+ Mode os.FileMode
+}
+
+func (r *OpenRequest) String() string {
+ return fmt.Sprintf("Open [%s] dir=%v fl=%v mode=%v", &r.Header, r.Dir, r.Flags, r.Mode)
+}
+
+// Respond replies to the request with the given response.
+func (r *OpenRequest) Respond(resp *OpenResponse) {
+ out := &openOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ Fh: uint64(resp.Handle),
+ OpenFlags: uint32(resp.Flags),
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A OpenResponse is the response to a OpenRequest.
+type OpenResponse struct {
+ Handle HandleID
+ Flags OpenFlags
+}
+
+func (r *OpenResponse) String() string {
+ return fmt.Sprintf("Open %+v", *r)
+}
+
+// A CreateRequest asks to create and open a file (not a directory).
+type CreateRequest struct {
+ Header
+ Name string
+ Flags uint32
+ Mode os.FileMode
+}
+
+func (r *CreateRequest) String() string {
+ return fmt.Sprintf("Create [%s] %q fl=%v mode=%v", &r.Header, r.Name, r.Flags, r.Mode)
+}
+
+// Respond replies to the request with the given response.
+func (r *CreateRequest) Respond(resp *CreateResponse) {
+ out := &createOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+
+ Nodeid: uint64(resp.Node),
+ Generation: resp.Generation,
+ EntryValid: uint64(resp.EntryValid / time.Second),
+ EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
+ AttrValid: uint64(resp.AttrValid / time.Second),
+ AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
+ Attr: resp.Attr.attr(),
+
+ Fh: uint64(resp.Handle),
+ OpenFlags: uint32(resp.Flags),
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A CreateResponse is the response to a CreateRequest.
+// It describes the created node and opened handle.
+type CreateResponse struct {
+ LookupResponse
+ OpenResponse
+}
+
+func (r *CreateResponse) String() string {
+ return fmt.Sprintf("Create %+v", *r)
+}
+
+// A MkdirRequest asks to create (but not open) a directory.
+type MkdirRequest struct {
+ Header
+ Name string
+ Mode os.FileMode
+}
+
+func (r *MkdirRequest) String() string {
+ return fmt.Sprintf("Mkdir [%s] %q mode=%v", &r.Header, r.Name, r.Mode)
+}
+
+// Respond replies to the request with the given response.
+func (r *MkdirRequest) Respond(resp *MkdirResponse) {
+ out := &entryOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ Nodeid: uint64(resp.Node),
+ Generation: resp.Generation,
+ EntryValid: uint64(resp.EntryValid / time.Second),
+ EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
+ AttrValid: uint64(resp.AttrValid / time.Second),
+ AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
+ Attr: resp.Attr.attr(),
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A MkdirResponse is the response to a MkdirRequest.
+type MkdirResponse struct {
+ LookupResponse
+}
+
+func (r *MkdirResponse) String() string {
+ return fmt.Sprintf("Mkdir %+v", *r)
+}
+
+// A ReadRequest asks to read from an open file.
+type ReadRequest struct {
+ Header
+ Dir bool // is this Readdir?
+ Handle HandleID
+ Offset int64
+ Size int
+}
+
+func (r *ReadRequest) handle() HandleID {
+ return r.Handle
+}
+
+func (r *ReadRequest) String() string {
+ return fmt.Sprintf("Read [%s] %#x %d @%#x dir=%v", &r.Header, r.Handle, r.Size, r.Offset, r.Dir)
+}
+
+// Respond replies to the request with the given response.
+func (r *ReadRequest) Respond(resp *ReadResponse) {
+ out := &outHeader{Unique: uint64(r.ID)}
+ r.Conn.respondData(out, unsafe.Sizeof(*out), resp.Data)
+}
+
+// A ReadResponse is the response to a ReadRequest.
+type ReadResponse struct {
+ Data []byte
+}
+
+func (r *ReadResponse) String() string {
+ return fmt.Sprintf("Read %x", r.Data)
+}
+
+// A ReleaseRequest asks to release (close) an open file handle.
+type ReleaseRequest struct {
+ Header
+ Dir bool // is this Releasedir?
+ Handle HandleID
+ Flags uint32 // flags from OpenRequest
+ ReleaseFlags ReleaseFlags
+ LockOwner uint32
+}
+
+func (r *ReleaseRequest) handle() HandleID {
+ return r.Handle
+}
+
+func (r *ReleaseRequest) String() string {
+ return fmt.Sprintf("Release [%s] %#x fl=%v rfl=%v owner=%#x", &r.Header, r.Handle, r.Flags, r.ReleaseFlags, r.LockOwner)
+}
+
+// Respond replies to the request, indicating that the handle has been released.
+func (r *ReleaseRequest) Respond() {
+ out := &outHeader{Unique: uint64(r.ID)}
+ r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// A DestroyRequest is sent by the kernel when unmounting the file system.
+// No more requests will be received after this one, but it should still be
+// responded to.
+type DestroyRequest struct {
+ Header
+}
+
+func (r *DestroyRequest) String() string {
+ return fmt.Sprintf("Destroy [%s]", &r.Header)
+}
+
+// Respond replies to the request.
+func (r *DestroyRequest) Respond() {
+ out := &outHeader{Unique: uint64(r.ID)}
+ r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// A ForgetRequest is sent by the kernel when forgetting about r.Node
+// as returned by r.N lookup requests.
+type ForgetRequest struct {
+ Header
+ N uint64
+}
+
+func (r *ForgetRequest) String() string {
+ return fmt.Sprintf("Forget [%s] %d", &r.Header, r.N)
+}
+
+// Respond replies to the request, indicating that the forgetfulness has been recorded.
+func (r *ForgetRequest) Respond() {
+ // Don't reply to forget messages.
+}
+
+// A Dirent represents a single directory entry.
+type Dirent struct {
+ Inode uint64 // inode this entry names
+ Type uint32 // ?
+ Name string // name of entry
+}
+
+// AppendDirent appends the encoded form of a directory entry to data
+// and returns the resulting slice.
+func AppendDirent(data []byte, dir Dirent) []byte {
+ de := dirent{
+ Ino: dir.Inode,
+ Namelen: uint32(len(dir.Name)),
+ Type: dir.Type,
+ }
+ de.Off = uint64(len(data) + direntSize + (len(dir.Name)+7)&^7)
+ data = append(data, (*[direntSize]byte)(unsafe.Pointer(&de))[:]...)
+ data = append(data, dir.Name...)
+ n := direntSize + uintptr(len(dir.Name))
+ if n%8 != 0 {
+ var pad [8]byte
+ data = append(data, pad[:8-n%8]...)
+ }
+ return data
+}
+
+// A WriteRequest asks to write to an open file.
+type WriteRequest struct {
+ Header
+ Handle HandleID
+ Offset int64
+ Data []byte
+ Flags WriteFlags
+}
+
+func (r *WriteRequest) String() string {
+ return fmt.Sprintf("Write [%s] %#x %d @%d fl=%v", &r.Header, r.Handle, len(r.Data), r.Offset, r.Flags)
+}
+
+// Respond replies to the request with the given response.
+func (r *WriteRequest) Respond(resp *WriteResponse) {
+ out := &writeOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ Size: uint32(resp.Size),
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+func (r *WriteRequest) handle() HandleID {
+ return r.Handle
+}
+
+// A WriteResponse replies to a write indicating how many bytes were written.
+type WriteResponse struct {
+ Size int
+}
+
+func (r *WriteResponse) String() string {
+ return fmt.Sprintf("Write %+v", *r)
+}
+
+// A SetattrRequest asks to change one or more attributes associated with a file,
+// as indicated by Valid.
+type SetattrRequest struct {
+ Header
+ Valid SetattrValid
+ Handle HandleID
+ Size uint64
+ Atime time.Time
+ Mtime time.Time
+ Mode os.FileMode
+ Uid uint32
+ Gid uint32
+
+ // OS X only
+ Bkuptime time.Time
+ Chgtime time.Time
+ Crtime time.Time
+ Flags uint32 // see chflags(2)
+}
+
+func (r *SetattrRequest) String() string {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "Setattr [%s]", &r.Header)
+ if r.Valid.Mode() {
+ fmt.Fprintf(&buf, " mode=%v", r.Mode)
+ }
+ if r.Valid.Uid() {
+ fmt.Fprintf(&buf, " uid=%d", r.Uid)
+ }
+ if r.Valid.Gid() {
+ fmt.Fprintf(&buf, " gid=%d", r.Gid)
+ }
+ if r.Valid.Size() {
+ fmt.Fprintf(&buf, " size=%d", r.Size)
+ }
+ if r.Valid.Atime() {
+ fmt.Fprintf(&buf, " atime=%v", r.Atime)
+ }
+ if r.Valid.Mtime() {
+ fmt.Fprintf(&buf, " mtime=%v", r.Mtime)
+ }
+ if r.Valid.Handle() {
+ fmt.Fprintf(&buf, " handle=%#x", r.Handle)
+ } else {
+ fmt.Fprintf(&buf, " handle=INVALID-%#x", r.Handle)
+ }
+ 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=%#x", r.Flags)
+ }
+ return buf.String()
+}
+
+func (r *SetattrRequest) handle() HandleID {
+ if r.Valid.Handle() {
+ return r.Handle
+ }
+ return 0
+}
+
+// Respond replies to the request with the given response,
+// giving the updated attributes.
+func (r *SetattrRequest) Respond(resp *SetattrResponse) {
+ out := &attrOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ AttrValid: uint64(resp.AttrValid / time.Second),
+ AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
+ Attr: resp.Attr.attr(),
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A SetattrResponse is the response to a SetattrRequest.
+type SetattrResponse struct {
+ AttrValid time.Duration // how long Attr can be cached
+ Attr Attr // file attributes
+}
+
+func (r *SetattrResponse) String() string {
+ return fmt.Sprintf("Setattr %+v", *r)
+}
+
+// A FlushRequest asks for the current state of an open file to be flushed
+// 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
+ Handle HandleID
+ Flags uint32
+ LockOwner uint64
+}
+
+func (r *FlushRequest) String() string {
+ return fmt.Sprintf("Flush [%s] %#x fl=%#x lk=%#x", &r.Header, r.Handle, r.Flags, r.LockOwner)
+}
+
+func (r *FlushRequest) handle() HandleID {
+ return r.Handle
+}
+
+// Respond replies to the request, indicating that the flush succeeded.
+func (r *FlushRequest) Respond() {
+ out := &outHeader{Unique: uint64(r.ID)}
+ r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// A RemoveRequest asks to remove a file or directory.
+type RemoveRequest struct {
+ Header
+ Name string // name of extended attribute
+ Dir bool // is this rmdir?
+}
+
+func (r *RemoveRequest) String() string {
+ return fmt.Sprintf("Remove [%s] %q dir=%v", &r.Header, r.Name, r.Dir)
+}
+
+// Respond replies to the request, indicating that the file was removed.
+func (r *RemoveRequest) Respond() {
+ out := &outHeader{Unique: uint64(r.ID)}
+ r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// A SymlinkRequest is a request to create a symlink making NewName point to Target.
+type SymlinkRequest struct {
+ Header
+ NewName, Target string
+}
+
+func (r *SymlinkRequest) String() string {
+ return fmt.Sprintf("Symlink [%s] from %q to target %q", &r.Header, r.NewName, r.Target)
+}
+
+func (r *SymlinkRequest) handle() HandleID {
+ return 0
+}
+
+// Respond replies to the request, indicating that the symlink was created.
+func (r *SymlinkRequest) Respond(resp *SymlinkResponse) {
+ out := &entryOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ Nodeid: uint64(resp.Node),
+ Generation: resp.Generation,
+ EntryValid: uint64(resp.EntryValid / time.Second),
+ EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
+ AttrValid: uint64(resp.AttrValid / time.Second),
+ AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
+ Attr: resp.Attr.attr(),
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A SymlinkResponse is the response to a SymlinkRequest.
+type SymlinkResponse struct {
+ LookupResponse
+}
+
+// A ReadlinkRequest is a request to read a symlink's target.
+type ReadlinkRequest struct {
+ Header
+}
+
+func (r *ReadlinkRequest) String() string {
+ return fmt.Sprintf("Readlink [%s]", &r.Header)
+}
+
+func (r *ReadlinkRequest) handle() HandleID {
+ return 0
+}
+
+func (r *ReadlinkRequest) Respond(target string) {
+ out := &outHeader{Unique: uint64(r.ID)}
+ r.Conn.respondData(out, unsafe.Sizeof(*out), []byte(target))
+}
+
+// A LinkRequest is a request to create a hard link.
+type LinkRequest struct {
+ Header
+ OldNode NodeID
+ NewName string
+}
+
+func (r *LinkRequest) Respond(resp *LookupResponse) {
+ out := &entryOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ Nodeid: uint64(resp.Node),
+ Generation: resp.Generation,
+ EntryValid: uint64(resp.EntryValid / time.Second),
+ EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
+ AttrValid: uint64(resp.AttrValid / time.Second),
+ AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
+ Attr: resp.Attr.attr(),
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A RenameRequest is a request to rename a file.
+type RenameRequest struct {
+ Header
+ NewDir NodeID
+ OldName, NewName string
+}
+
+func (r *RenameRequest) handle() HandleID {
+ return 0
+}
+
+func (r *RenameRequest) String() string {
+ return fmt.Sprintf("Rename [%s] from %q to dirnode %d %q", &r.Header, r.OldName, r.NewDir, r.NewName)
+}
+
+func (r *RenameRequest) Respond() {
+ out := &outHeader{Unique: uint64(r.ID)}
+ r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+type MknodRequest struct {
+ Header
+ Name string
+ Mode os.FileMode
+ Rdev uint32
+}
+
+func (r *MknodRequest) String() string {
+ return fmt.Sprintf("Mknod [%s] Name %q mode %v rdev %d", &r.Header, r.Name, r.Mode, r.Rdev)
+}
+
+func (r *MknodRequest) Respond(resp *LookupResponse) {
+ out := &entryOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ Nodeid: uint64(resp.Node),
+ Generation: resp.Generation,
+ EntryValid: uint64(resp.EntryValid / time.Second),
+ EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
+ AttrValid: uint64(resp.AttrValid / time.Second),
+ AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
+ Attr: resp.Attr.attr(),
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+type FsyncRequest struct {
+ Header
+ Handle HandleID
+ Flags uint32
+}
+
+func (r *FsyncRequest) String() string {
+ return fmt.Sprintf("Fsync [%s] Handle %v Flags %v", &r.Header, r.Handle, r.Flags)
+}
+
+func (r *FsyncRequest) Respond() {
+ out := &outHeader{Unique: uint64(r.ID)}
+ r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+/*{
+
+// A XXXRequest xxx.
+type XXXRequest struct {
+ Header
+ xxx
+}
+
+func (r *XXXRequest) String() string {
+ return fmt.Sprintf("XXX [%s] xxx", &r.Header)
+}
+
+func (r *XXXRequest) handle() HandleID {
+ return r.Handle
+}
+
+// Respond replies to the request with the given response.
+func (r *XXXRequest) Respond(resp *XXXResponse) {
+ out := &xxxOut{
+ outHeader: outHeader{Unique: uint64(r.ID)},
+ xxx,
+ }
+ r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A XXXResponse is the response to a XXXRequest.
+type XXXResponse struct {
+ xxx
+}
+
+func (r *XXXResponse) String() string {
+ return fmt.Sprintf("XXX %+v", *r)
+}
+
+ }
+*/
diff --git a/vendor/github.com/mattermost/rsc/fuse/fuse_kernel.go b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel.go
new file mode 100644
index 000000000..a57360e63
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel.go
@@ -0,0 +1,539 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Derived from FUSE's fuse_kernel.h
+/*
+ This file defines the kernel interface of FUSE
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+
+ This -- and only this -- header file may also be distributed under
+ the terms of the BSD Licence as follows:
+
+ Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+*/
+
+package fuse
+
+import (
+ "fmt"
+ "unsafe"
+)
+
+// Version is the FUSE version implemented by the package.
+const Version = "7.8"
+
+const (
+ kernelVersion = 7
+ kernelMinorVersion = 8
+ rootID = 1
+)
+
+type kstatfs struct {
+ Blocks uint64
+ Bfree uint64
+ Bavail uint64
+ Files uint64
+ Ffree uint64
+ Bsize uint32
+ Namelen uint32
+ Frsize uint32
+ Padding uint32
+ Spare [6]uint32
+}
+
+type fileLock struct {
+ Start uint64
+ End uint64
+ Type uint32
+ Pid uint32
+}
+
+// The SetattrValid are bit flags describing which fields in the SetattrRequest
+// are included in the change.
+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 // TODO: What does this mean?
+
+ // 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
+
+ // OS X only
+ SetattrCrtime SetattrValid = 1 << 28
+ SetattrChgtime SetattrValid = 1 << 29
+ SetattrBkuptime SetattrValid = 1 << 30
+ SetattrFlags SetattrValid = 1 << 31
+)
+
+func (fl SetattrValid) Mode() bool { return fl&SetattrMode != 0 }
+func (fl SetattrValid) Uid() bool { return fl&SetattrUid != 0 }
+func (fl SetattrValid) Gid() bool { return fl&SetattrGid != 0 }
+func (fl SetattrValid) Size() bool { return fl&SetattrSize != 0 }
+func (fl SetattrValid) Atime() bool { return fl&SetattrAtime != 0 }
+func (fl SetattrValid) Mtime() bool { return fl&SetattrMtime != 0 }
+func (fl SetattrValid) Handle() bool { return fl&SetattrHandle != 0 }
+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)
+}
+
+var setattrValidNames = []flagName{
+ {uint32(SetattrMode), "SetattrMode"},
+ {uint32(SetattrUid), "SetattrUid"},
+ {uint32(SetattrGid), "SetattrGid"},
+ {uint32(SetattrSize), "SetattrSize"},
+ {uint32(SetattrAtime), "SetattrAtime"},
+ {uint32(SetattrMtime), "SetattrMtime"},
+ {uint32(SetattrHandle), "SetattrHandle"},
+ {uint32(SetattrCrtime), "SetattrCrtime"},
+ {uint32(SetattrChgtime), "SetattrChgtime"},
+ {uint32(SetattrBkuptime), "SetattrBkuptime"},
+ {uint32(SetattrFlags), "SetattrFlags"},
+}
+
+// The OpenFlags are returned in the OpenResponse.
+type OpenFlags uint32
+
+const (
+ OpenDirectIO OpenFlags = 1 << 0 // bypass page cache for this open file
+ OpenKeepCache OpenFlags = 1 << 1 // don't invalidate the data cache on open
+ OpenNonSeekable OpenFlags = 1 << 2 // (Linux?)
+
+ OpenPurgeAttr OpenFlags = 1 << 30 // OS X
+ OpenPurgeUBC OpenFlags = 1 << 31 // OS X
+)
+
+func (fl OpenFlags) String() string {
+ return flagString(uint32(fl), openFlagNames)
+}
+
+var openFlagNames = []flagName{
+ {uint32(OpenDirectIO), "OpenDirectIO"},
+ {uint32(OpenKeepCache), "OpenKeepCache"},
+ {uint32(OpenPurgeAttr), "OpenPurgeAttr"},
+ {uint32(OpenPurgeUBC), "OpenPurgeUBC"},
+}
+
+// The InitFlags are used in the Init exchange.
+type InitFlags uint32
+
+const (
+ InitAsyncRead InitFlags = 1 << 0
+ InitPosixLocks InitFlags = 1 << 1
+
+ InitCaseSensitive InitFlags = 1 << 29 // OS X only
+ InitVolRename InitFlags = 1 << 30 // OS X only
+ InitXtimes InitFlags = 1 << 31 // OS X only
+)
+
+type flagName struct {
+ bit uint32
+ name string
+}
+
+var initFlagNames = []flagName{
+ {uint32(InitAsyncRead), "InitAsyncRead"},
+ {uint32(InitPosixLocks), "InitPosixLocks"},
+ {uint32(InitCaseSensitive), "InitCaseSensitive"},
+ {uint32(InitVolRename), "InitVolRename"},
+ {uint32(InitXtimes), "InitXtimes"},
+}
+
+func (fl InitFlags) String() string {
+ return flagString(uint32(fl), initFlagNames)
+}
+
+func flagString(f uint32, names []flagName) string {
+ var s string
+
+ if f == 0 {
+ return "0"
+ }
+
+ for _, n := range names {
+ if f&n.bit != 0 {
+ s += "+" + n.name
+ f &^= n.bit
+ }
+ }
+ if f != 0 {
+ s += fmt.Sprintf("%+#x", f)
+ }
+ return s[1:]
+}
+
+// The ReleaseFlags are used in the Release exchange.
+type ReleaseFlags uint32
+
+const (
+ ReleaseFlush ReleaseFlags = 1 << 0
+)
+
+func (fl ReleaseFlags) String() string {
+ return flagString(uint32(fl), releaseFlagNames)
+}
+
+var releaseFlagNames = []flagName{
+ {uint32(ReleaseFlush), "ReleaseFlush"},
+}
+
+// Opcodes
+const (
+ opLookup = 1
+ opForget = 2 // no reply
+ opGetattr = 3
+ opSetattr = 4
+ opReadlink = 5
+ opSymlink = 6
+ opMknod = 8
+ opMkdir = 9
+ opUnlink = 10
+ opRmdir = 11
+ opRename = 12
+ opLink = 13
+ opOpen = 14
+ opRead = 15
+ opWrite = 16
+ opStatfs = 17
+ opRelease = 18
+ opFsync = 20
+ opSetxattr = 21
+ opGetxattr = 22
+ opListxattr = 23
+ opRemovexattr = 24
+ opFlush = 25
+ opInit = 26
+ opOpendir = 27
+ opReaddir = 28
+ opReleasedir = 29
+ opFsyncdir = 30
+ opGetlk = 31
+ opSetlk = 32
+ opSetlkw = 33
+ opAccess = 34
+ opCreate = 35
+ opInterrupt = 36
+ opBmap = 37
+ opDestroy = 38
+ opIoctl = 39 // Linux?
+ opPoll = 40 // Linux?
+
+ // OS X
+ opSetvolname = 61
+ opGetxtimes = 62
+ opExchange = 63
+)
+
+// The read buffer is required to be at least 8k but may be much larger
+const minReadBuffer = 8192
+
+type entryOut struct {
+ outHeader
+ Nodeid uint64 // Inode ID
+ Generation uint64 // Inode generation
+ EntryValid uint64 // Cache timeout for the name
+ AttrValid uint64 // Cache timeout for the attributes
+ EntryValidNsec uint32
+ AttrValidNsec uint32
+ Attr attr
+}
+
+type forgetIn struct {
+ Nlookup uint64
+}
+
+type attrOut struct {
+ outHeader
+ AttrValid uint64 // Cache timeout for the attributes
+ AttrValidNsec uint32
+ Dummy uint32
+ Attr attr
+}
+
+// OS X
+type getxtimesOut struct {
+ outHeader
+ Bkuptime uint64
+ Crtime uint64
+ BkuptimeNsec uint32
+ CrtimeNsec uint32
+}
+
+type mknodIn struct {
+ Mode uint32
+ Rdev uint32
+ // "filename\x00" follows.
+}
+
+type mkdirIn struct {
+ Mode uint32
+ Padding uint32
+ // filename follows
+}
+
+type renameIn struct {
+ Newdir uint64
+ // "oldname\x00newname\x00" follows
+}
+
+// OS X
+type exchangeIn struct {
+ Olddir uint64
+ Newdir uint64
+ Options uint64
+}
+
+type linkIn struct {
+ Oldnodeid uint64
+}
+
+type setattrInCommon struct {
+ Valid uint32
+ Padding uint32
+ Fh uint64
+ Size uint64
+ LockOwner uint64 // unused on OS X?
+ Atime uint64
+ Mtime uint64
+ Unused2 uint64
+ AtimeNsec uint32
+ MtimeNsec uint32
+ Unused3 uint32
+ Mode uint32
+ Unused4 uint32
+ Uid uint32
+ Gid uint32
+ Unused5 uint32
+}
+
+type openIn struct {
+ Flags uint32
+ Mode uint32
+}
+
+type openOut struct {
+ outHeader
+ Fh uint64
+ OpenFlags uint32
+ Padding uint32
+}
+
+type createOut struct {
+ outHeader
+
+ Nodeid uint64 // Inode ID
+ Generation uint64 // Inode generation
+ EntryValid uint64 // Cache timeout for the name
+ AttrValid uint64 // Cache timeout for the attributes
+ EntryValidNsec uint32
+ AttrValidNsec uint32
+ Attr attr
+
+ Fh uint64
+ OpenFlags uint32
+ Padding uint32
+}
+
+type releaseIn struct {
+ Fh uint64
+ Flags uint32
+ ReleaseFlags uint32
+ LockOwner uint32
+}
+
+type flushIn struct {
+ Fh uint64
+ FlushFlags uint32
+ Padding uint32
+ LockOwner uint64
+}
+
+type readIn struct {
+ Fh uint64
+ Offset uint64
+ Size uint32
+ Padding uint32
+}
+
+type writeIn struct {
+ Fh uint64
+ Offset uint64
+ Size uint32
+ WriteFlags uint32
+}
+
+type writeOut struct {
+ outHeader
+ Size uint32
+ Padding uint32
+}
+
+// The WriteFlags are returned in the WriteResponse.
+type WriteFlags uint32
+
+func (fl WriteFlags) String() string {
+ return flagString(uint32(fl), writeFlagNames)
+}
+
+var writeFlagNames = []flagName{}
+
+const compatStatfsSize = 48
+
+type statfsOut struct {
+ outHeader
+ St kstatfs
+}
+
+type fsyncIn struct {
+ Fh uint64
+ FsyncFlags uint32
+ Padding uint32
+}
+
+type setxattrIn struct {
+ Size uint32
+ Flags uint32
+}
+
+type setxattrInOSX struct {
+ Size uint32
+ Flags uint32
+
+ // OS X only
+ Position uint32
+ Padding uint32
+}
+
+type getxattrIn struct {
+ Size uint32
+ Padding uint32
+}
+
+type getxattrInOSX struct {
+ Size uint32
+ Padding uint32
+
+ // OS X only
+ Position uint32
+ Padding2 uint32
+}
+
+type getxattrOut struct {
+ outHeader
+ Size uint32
+ Padding uint32
+}
+
+type lkIn struct {
+ Fh uint64
+ Owner uint64
+ Lk fileLock
+}
+
+type lkOut struct {
+ outHeader
+ Lk fileLock
+}
+
+type accessIn struct {
+ Mask uint32
+ Padding uint32
+}
+
+type initIn struct {
+ Major uint32
+ Minor uint32
+ MaxReadahead uint32
+ Flags uint32
+}
+
+const initInSize = int(unsafe.Sizeof(initIn{}))
+
+type initOut struct {
+ outHeader
+ Major uint32
+ Minor uint32
+ MaxReadahead uint32
+ Flags uint32
+ Unused uint32
+ MaxWrite uint32
+}
+
+type interruptIn struct {
+ Unique uint64
+}
+
+type bmapIn struct {
+ Block uint64
+ BlockSize uint32
+ Padding uint32
+}
+
+type bmapOut struct {
+ outHeader
+ Block uint64
+}
+
+type inHeader struct {
+ Len uint32
+ Opcode uint32
+ Unique uint64
+ Nodeid uint64
+ Uid uint32
+ Gid uint32
+ Pid uint32
+ Padding uint32
+}
+
+const inHeaderSize = int(unsafe.Sizeof(inHeader{}))
+
+type outHeader struct {
+ Len uint32
+ Error int32
+ Unique uint64
+}
+
+type dirent struct {
+ Ino uint64
+ Off uint64
+ Namelen uint32
+ Type uint32
+ Name [0]byte
+}
+
+const direntSize = 8 + 8 + 4 + 4
diff --git a/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_darwin.go b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_darwin.go
new file mode 100644
index 000000000..f7bc37766
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_darwin.go
@@ -0,0 +1,58 @@
+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)
+}
+
+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_
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_linux.go b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_linux.go
new file mode 100644
index 000000000..914bc3063
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_linux.go
@@ -0,0 +1,50 @@
+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 // Only in protocol 7.9
+ // padding_ uint32 // Only in protocol 7.9
+}
+
+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
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_std.go b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_std.go
new file mode 100644
index 000000000..074cfd322
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_std.go
@@ -0,0 +1 @@
+package fuse
diff --git a/vendor/github.com/mattermost/rsc/fuse/fuse_test.go b/vendor/github.com/mattermost/rsc/fuse/fuse_test.go
new file mode 100644
index 000000000..61533b8c5
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/fuse_test.go
@@ -0,0 +1,594 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fuse
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "runtime"
+ "syscall"
+ "testing"
+ "time"
+)
+
+var fuseRun = flag.String("fuserun", "", "which fuse test to run. runs all if empty.")
+
+// umount tries its best to unmount dir.
+func umount(dir string) {
+ err := exec.Command("umount", dir).Run()
+ if err != nil && runtime.GOOS == "linux" {
+ exec.Command("/bin/fusermount", "-u", dir).Run()
+ }
+}
+
+func TestFuse(t *testing.T) {
+ Debugf = log.Printf
+ dir, err := ioutil.TempDir("", "fusetest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ os.MkdirAll(dir, 0777)
+
+ c, err := Mount(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer umount(dir)
+
+ go func() {
+ err := c.Serve(testFS{})
+ if err != nil {
+ fmt.Printf("SERVE ERROR: %v\n", err)
+ }
+ }()
+
+ waitForMount(t, dir)
+
+ for _, tt := range fuseTests {
+ if *fuseRun == "" || *fuseRun == tt.name {
+ t.Logf("running %T", tt.node)
+ tt.node.test(dir+"/"+tt.name, t)
+ }
+ }
+}
+
+func waitForMount(t *testing.T, dir string) {
+ // Filename to wait for in dir:
+ probeEntry := *fuseRun
+ if probeEntry == "" {
+ probeEntry = fuseTests[0].name
+ }
+ for tries := 0; tries < 100; tries++ {
+ _, err := os.Stat(dir + "/" + probeEntry)
+ if err == nil {
+ return
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+ t.Fatalf("mount did not work")
+}
+
+var fuseTests = []struct {
+ name string
+ node interface {
+ Node
+ test(string, *testing.T)
+ }
+}{
+ {"readAll", readAll{}},
+ {"readAll1", &readAll1{}},
+ {"write", &write{}},
+ {"writeAll", &writeAll{}},
+ {"writeAll2", &writeAll2{}},
+ {"release", &release{}},
+ {"mkdir1", &mkdir1{}},
+ {"create1", &create1{}},
+ {"create2", &create2{}},
+ {"symlink1", &symlink1{}},
+ {"link1", &link1{}},
+ {"rename1", &rename1{}},
+ {"mknod1", &mknod1{}},
+}
+
+// TO TEST:
+// Statfs
+// Lookup(*LookupRequest, *LookupResponse)
+// Getattr(*GetattrRequest, *GetattrResponse)
+// Attr with explicit inode
+// Setattr(*SetattrRequest, *SetattrResponse)
+// Access(*AccessRequest)
+// Open(*OpenRequest, *OpenResponse)
+// Getxattr, Setxattr, Listxattr, Removexattr
+// Write(*WriteRequest, *WriteResponse)
+// Flush(*FlushRequest, *FlushResponse)
+
+// Test Read calling ReadAll.
+
+type readAll struct{ file }
+
+const hi = "hello, world"
+
+func (readAll) ReadAll(intr Intr) ([]byte, Error) {
+ return []byte(hi), nil
+}
+
+func (readAll) test(path string, t *testing.T) {
+ data, err := ioutil.ReadFile(path)
+ if err != nil {
+ t.Errorf("readAll: %v", err)
+ return
+ }
+ if string(data) != hi {
+ t.Errorf("readAll = %q, want %q", data, hi)
+ }
+}
+
+// Test Read.
+
+type readAll1 struct{ file }
+
+func (readAll1) Read(req *ReadRequest, resp *ReadResponse, intr Intr) Error {
+ HandleRead(req, resp, []byte(hi))
+ return nil
+}
+
+func (readAll1) test(path string, t *testing.T) {
+ readAll{}.test(path, t)
+}
+
+// Test Write calling basic Write, with an fsync thrown in too.
+
+type write struct {
+ file
+ data []byte
+ gotfsync bool
+}
+
+func (w *write) Write(req *WriteRequest, resp *WriteResponse, intr Intr) Error {
+ w.data = append(w.data, req.Data...)
+ resp.Size = len(req.Data)
+ return nil
+}
+
+func (w *write) Fsync(r *FsyncRequest, intr Intr) Error {
+ w.gotfsync = true
+ return nil
+}
+
+func (w *write) test(path string, t *testing.T) {
+ log.Printf("pre-write Create")
+ f, err := os.Create(path)
+ if err != nil {
+ t.Fatalf("Create: %v", err)
+ }
+ log.Printf("pre-write Write")
+ n, err := f.Write([]byte(hi))
+ if err != nil {
+ t.Fatalf("Write: %v", err)
+ }
+ if n != len(hi) {
+ t.Fatalf("short write; n=%d; hi=%d", n, len(hi))
+ }
+
+ err = syscall.Fsync(int(f.Fd()))
+ if err != nil {
+ t.Fatalf("Fsync = %v", err)
+ }
+ if !w.gotfsync {
+ t.Errorf("never received expected fsync call")
+ }
+
+ log.Printf("pre-write Close")
+ err = f.Close()
+ if err != nil {
+ t.Fatalf("Close: %v", err)
+ }
+ log.Printf("post-write Close")
+ if string(w.data) != hi {
+ t.Errorf("writeAll = %q, want %q", w.data, hi)
+ }
+}
+
+// Test Write calling WriteAll.
+
+type writeAll struct {
+ file
+ data []byte
+ gotfsync bool
+}
+
+func (w *writeAll) Fsync(r *FsyncRequest, intr Intr) Error {
+ w.gotfsync = true
+ return nil
+}
+
+func (w *writeAll) WriteAll(data []byte, intr Intr) Error {
+ w.data = data
+ return nil
+}
+
+func (w *writeAll) test(path string, t *testing.T) {
+ err := ioutil.WriteFile(path, []byte(hi), 0666)
+ if err != nil {
+ t.Fatalf("WriteFile: %v", err)
+ return
+ }
+ if string(w.data) != hi {
+ t.Errorf("writeAll = %q, want %q", w.data, hi)
+ }
+}
+
+// Test Write calling Setattr+Write+Flush.
+
+type writeAll2 struct {
+ file
+ data []byte
+ setattr bool
+ flush bool
+}
+
+func (w *writeAll2) Setattr(req *SetattrRequest, resp *SetattrResponse, intr Intr) Error {
+ w.setattr = true
+ return nil
+}
+
+func (w *writeAll2) Flush(req *FlushRequest, intr Intr) Error {
+ w.flush = true
+ return nil
+}
+
+func (w *writeAll2) Write(req *WriteRequest, resp *WriteResponse, intr Intr) Error {
+ w.data = append(w.data, req.Data...)
+ resp.Size = len(req.Data)
+ return nil
+}
+
+func (w *writeAll2) test(path string, t *testing.T) {
+ err := ioutil.WriteFile(path, []byte(hi), 0666)
+ if err != nil {
+ t.Errorf("WriteFile: %v", err)
+ return
+ }
+ if !w.setattr || string(w.data) != hi || !w.flush {
+ t.Errorf("writeAll = %v, %q, %v, want %v, %q, %v", w.setattr, string(w.data), w.flush, true, hi, true)
+ }
+}
+
+// Test Mkdir.
+
+type mkdir1 struct {
+ dir
+ name string
+}
+
+func (f *mkdir1) Mkdir(req *MkdirRequest, intr Intr) (Node, Error) {
+ f.name = req.Name
+ return &mkdir1{}, nil
+}
+
+func (f *mkdir1) test(path string, t *testing.T) {
+ f.name = ""
+ err := os.Mkdir(path+"/foo", 0777)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if f.name != "foo" {
+ t.Error(err)
+ return
+ }
+}
+
+// Test Create (and fsync)
+
+type create1 struct {
+ dir
+ name string
+ f *writeAll
+}
+
+func (f *create1) Create(req *CreateRequest, resp *CreateResponse, intr Intr) (Node, Handle, Error) {
+ f.name = req.Name
+ f.f = &writeAll{}
+ return f.f, f.f, nil
+}
+
+func (f *create1) test(path string, t *testing.T) {
+ f.name = ""
+ ff, err := os.Create(path + "/foo")
+ if err != nil {
+ t.Errorf("create1 WriteFile: %v", err)
+ return
+ }
+
+ err = syscall.Fsync(int(ff.Fd()))
+ if err != nil {
+ t.Fatalf("Fsync = %v", err)
+ }
+
+ if !f.f.gotfsync {
+ t.Errorf("never received expected fsync call")
+ }
+
+ ff.Close()
+ if f.name != "foo" {
+ t.Errorf("create1 name=%q want foo", f.name)
+ }
+}
+
+// Test Create + WriteAll + Remove
+
+type create2 struct {
+ dir
+ name string
+ f *writeAll
+ fooExists bool
+}
+
+func (f *create2) Create(req *CreateRequest, resp *CreateResponse, intr Intr) (Node, Handle, Error) {
+ f.name = req.Name
+ f.f = &writeAll{}
+ return f.f, f.f, nil
+}
+
+func (f *create2) Lookup(name string, intr Intr) (Node, Error) {
+ if f.fooExists && name == "foo" {
+ return file{}, nil
+ }
+ return nil, ENOENT
+}
+
+func (f *create2) Remove(r *RemoveRequest, intr Intr) Error {
+ if f.fooExists && r.Name == "foo" && !r.Dir {
+ f.fooExists = false
+ return nil
+ }
+ return ENOENT
+}
+
+func (f *create2) test(path string, t *testing.T) {
+ f.name = ""
+ err := ioutil.WriteFile(path+"/foo", []byte(hi), 0666)
+ if err != nil {
+ t.Fatalf("create2 WriteFile: %v", err)
+ }
+ if string(f.f.data) != hi {
+ t.Fatalf("create2 writeAll = %q, want %q", f.f.data, hi)
+ }
+
+ f.fooExists = true
+ log.Printf("pre-Remove")
+ err = os.Remove(path + "/foo")
+ if err != nil {
+ t.Fatalf("Remove: %v", err)
+ }
+ err = os.Remove(path + "/foo")
+ if err == nil {
+ t.Fatalf("second Remove = nil; want some error")
+ }
+}
+
+// Test symlink + readlink
+
+type symlink1 struct {
+ dir
+ newName, target string
+}
+
+func (f *symlink1) Symlink(req *SymlinkRequest, intr Intr) (Node, Error) {
+ f.newName = req.NewName
+ f.target = req.Target
+ return symlink{target: req.Target}, nil
+}
+
+func (f *symlink1) test(path string, t *testing.T) {
+ const target = "/some-target"
+
+ err := os.Symlink(target, path+"/symlink.file")
+ if err != nil {
+ t.Errorf("os.Symlink: %v", err)
+ return
+ }
+
+ if f.newName != "symlink.file" {
+ t.Errorf("symlink newName = %q; want %q", f.newName, "symlink.file")
+ }
+ if f.target != target {
+ t.Errorf("symlink target = %q; want %q", f.target, target)
+ }
+
+ gotName, err := os.Readlink(path + "/symlink.file")
+ if err != nil {
+ t.Errorf("os.Readlink: %v", err)
+ return
+ }
+ if gotName != target {
+ t.Errorf("os.Readlink = %q; want %q", gotName, target)
+ }
+}
+
+// Test link
+
+type link1 struct {
+ dir
+ newName string
+}
+
+func (f *link1) Lookup(name string, intr Intr) (Node, Error) {
+ if name == "old" {
+ return file{}, nil
+ }
+ return nil, ENOENT
+}
+
+func (f *link1) Link(r *LinkRequest, old Node, intr Intr) (Node, Error) {
+ f.newName = r.NewName
+ return file{}, nil
+}
+
+func (f *link1) test(path string, t *testing.T) {
+ err := os.Link(path+"/old", path+"/new")
+ if err != nil {
+ t.Fatalf("Link: %v", err)
+ }
+ if f.newName != "new" {
+ t.Fatalf("saw Link for newName %q; want %q", f.newName, "new")
+ }
+}
+
+// Test Rename
+
+type rename1 struct {
+ dir
+ renames int
+}
+
+func (f *rename1) Lookup(name string, intr Intr) (Node, Error) {
+ if name == "old" {
+ return file{}, nil
+ }
+ return nil, ENOENT
+}
+
+func (f *rename1) Rename(r *RenameRequest, newDir Node, intr Intr) Error {
+ if r.OldName == "old" && r.NewName == "new" && newDir == f {
+ f.renames++
+ return nil
+ }
+ return EIO
+}
+
+func (f *rename1) test(path string, t *testing.T) {
+ err := os.Rename(path+"/old", path+"/new")
+ if err != nil {
+ t.Fatalf("Rename: %v", err)
+ }
+ if f.renames != 1 {
+ t.Fatalf("expected rename didn't happen")
+ }
+ err = os.Rename(path+"/old2", path+"/new2")
+ if err == nil {
+ t.Fatal("expected error on second Rename; got nil")
+ }
+}
+
+// Test Release.
+
+type release struct {
+ file
+ did bool
+}
+
+func (r *release) Release(*ReleaseRequest, Intr) Error {
+ r.did = true
+ return nil
+}
+
+func (r *release) test(path string, t *testing.T) {
+ r.did = false
+ f, err := os.Open(path)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ f.Close()
+ time.Sleep(1 * time.Second)
+ if !r.did {
+ t.Error("Close did not Release")
+ }
+}
+
+// Test mknod
+
+type mknod1 struct {
+ dir
+ gotr *MknodRequest
+}
+
+func (f *mknod1) Mknod(r *MknodRequest, intr Intr) (Node, Error) {
+ f.gotr = r
+ return fifo{}, nil
+}
+
+func (f *mknod1) test(path string, t *testing.T) {
+ if os.Getuid() != 0 {
+ t.Logf("skipping unless root")
+ return
+ }
+ defer syscall.Umask(syscall.Umask(0))
+ err := syscall.Mknod(path+"/node", syscall.S_IFIFO|0666, 123)
+ if err != nil {
+ t.Fatalf("Mknod: %v", err)
+ }
+ if f.gotr == nil {
+ t.Fatalf("no recorded MknodRequest")
+ }
+ if g, e := f.gotr.Name, "node"; g != e {
+ t.Errorf("got Name = %q; want %q", g, e)
+ }
+ if g, e := f.gotr.Rdev, uint32(123); g != e {
+ if runtime.GOOS == "linux" {
+ // Linux fuse doesn't echo back the rdev if the node
+ // isn't a device (we're using a FIFO here, as that
+ // bit is portable.)
+ } else {
+ t.Errorf("got Rdev = %v; want %v", g, e)
+ }
+ }
+ if g, e := f.gotr.Mode, os.FileMode(os.ModeNamedPipe|0666); g != e {
+ t.Errorf("got Mode = %v; want %v", g, e)
+ }
+ t.Logf("Got request: %#v", f.gotr)
+}
+
+type file struct{}
+type dir struct{}
+type fifo struct{}
+type symlink struct {
+ target string
+}
+
+func (f file) Attr() Attr { return Attr{Mode: 0666} }
+func (f dir) Attr() Attr { return Attr{Mode: os.ModeDir | 0777} }
+func (f fifo) Attr() Attr { return Attr{Mode: os.ModeNamedPipe | 0666} }
+func (f symlink) Attr() Attr { return Attr{Mode: os.ModeSymlink | 0666} }
+
+func (f symlink) Readlink(*ReadlinkRequest, Intr) (string, Error) {
+ return f.target, nil
+}
+
+type testFS struct{}
+
+func (testFS) Root() (Node, Error) {
+ return testFS{}, nil
+}
+
+func (testFS) Attr() Attr {
+ return Attr{Mode: os.ModeDir | 0555}
+}
+
+func (testFS) Lookup(name string, intr Intr) (Node, Error) {
+ for _, tt := range fuseTests {
+ if tt.name == name {
+ return tt.node, nil
+ }
+ }
+ return nil, ENOENT
+}
+
+func (testFS) ReadDir(intr Intr) ([]Dirent, Error) {
+ var dirs []Dirent
+ for _, tt := range fuseTests {
+ if *fuseRun == "" || *fuseRun == tt.name {
+ log.Printf("Readdir; adding %q", tt.name)
+ dirs = append(dirs, Dirent{Name: tt.name})
+ }
+ }
+ return dirs, nil
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/hellofs/hello.go b/vendor/github.com/mattermost/rsc/fuse/hellofs/hello.go
new file mode 100644
index 000000000..d915473f1
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/hellofs/hello.go
@@ -0,0 +1,62 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Hellofs implements a simple "hello world" file system.
+package main
+
+import (
+ "log"
+ "os"
+
+ "github.com/mattermost/rsc/fuse"
+)
+
+func main() {
+ c, err := fuse.Mount("/mnt/hellofs")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ c.Serve(FS{})
+}
+
+// FS implements the hello world file system.
+type FS struct{}
+
+func (FS) Root() (fuse.Node, fuse.Error) {
+ return Dir{}, nil
+}
+
+// Dir implements both Node and Handle for the root directory.
+type Dir struct{}
+
+func (Dir) Attr() fuse.Attr {
+ return fuse.Attr{Mode: os.ModeDir | 0555}
+}
+
+func (Dir) Lookup(name string, intr fuse.Intr) (fuse.Node, fuse.Error) {
+ if name == "hello" {
+ return File{}, nil
+ }
+ return nil, fuse.ENOENT
+}
+
+var dirDirs = []fuse.Dirent{
+ {Inode: 2, Name: "hello", Type: 0},
+}
+
+func (Dir) ReadDir(intr fuse.Intr) ([]fuse.Dirent, fuse.Error) {
+ return dirDirs, nil
+}
+
+// File implements both Node and Handle for the hello file.
+type File struct{}
+
+func (File) Attr() fuse.Attr {
+ return fuse.Attr{Mode: 0444}
+}
+
+func (File) ReadAll(intr fuse.Intr) ([]byte, fuse.Error) {
+ return []byte("hello, world\n"), nil
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/mount_darwin.go b/vendor/github.com/mattermost/rsc/fuse/mount_darwin.go
new file mode 100644
index 000000000..5e2caaa76
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/mount_darwin.go
@@ -0,0 +1,122 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TODO: Rewrite using package syscall not cgo
+
+package fuse
+
+/*
+
+// Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c,
+// which carries this notice:
+//
+// The files in this directory are subject to the following license.
+//
+// The author of this software is Russ Cox.
+//
+// Copyright (c) 2006 Russ Cox
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose without fee is hereby granted, provided that this entire notice
+// is included in all copies of any software which is or includes a copy
+// or modification of this software and in all copies of the supporting
+// documentation for such software.
+//
+// THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+// WARRANTY. IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR WARRANTY
+// OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS
+// FITNESS FOR ANY PARTICULAR PURPOSE.
+
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#define nil ((void*)0)
+
+static int
+mountfuse(char *mtpt, char **err)
+{
+ int i, pid, fd, r;
+ char buf[200];
+ struct vfsconf vfs;
+ char *f;
+
+ if(getvfsbyname("fusefs", &vfs) < 0){
+ if(access(f="/Library/Filesystems/osxfusefs.fs"
+ "/Support/load_osxfusefs", 0) < 0){
+ *err = strdup("cannot find load_fusefs");
+ return -1;
+ }
+ if((r=system(f)) < 0){
+ snprintf(buf, sizeof buf, "%s: %s", f, strerror(errno));
+ *err = strdup(buf);
+ return -1;
+ }
+ if(r != 0){
+ snprintf(buf, sizeof buf, "load_fusefs failed: exit %d", r);
+ *err = strdup(buf);
+ return -1;
+ }
+ if(getvfsbyname("osxfusefs", &vfs) < 0){
+ snprintf(buf, sizeof buf, "getvfsbyname osxfusefs: %s", strerror(errno));
+ *err = strdup(buf);
+ return -1;
+ }
+ }
+
+ // Look for available FUSE device.
+ for(i=0;; i++){
+ snprintf(buf, sizeof buf, "/dev/osxfuse%d", i);
+ if(access(buf, 0) < 0){
+ *err = strdup("no available fuse devices");
+ return -1;
+ }
+ if((fd = open(buf, O_RDWR)) >= 0)
+ break;
+ }
+
+ pid = fork();
+ if(pid < 0)
+ return -1;
+ if(pid == 0){
+ snprintf(buf, sizeof buf, "%d", fd);
+ setenv("MOUNT_FUSEFS_CALL_BY_LIB", "", 1);
+ // Different versions of MacFUSE put the
+ // mount_fusefs binary in different places.
+ // Try all.
+ // Leopard location
+ setenv("MOUNT_FUSEFS_DAEMON_PATH",
+ "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs", 1);
+ execl("/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs",
+ "mount_osxfusefs",
+ "-o", "iosize=4096", buf, mtpt, nil);
+ fprintf(stderr, "exec mount_osxfusefs: %s\n", strerror(errno));
+ _exit(1);
+ }
+ return fd;
+}
+
+*/
+import "C"
+
+import "unsafe"
+
+func mount(dir string) (int, string) {
+ errp := (**C.char)(C.malloc(16))
+ *errp = nil
+ defer C.free(unsafe.Pointer(errp))
+ cdir := C.CString(dir)
+ defer C.free(unsafe.Pointer(cdir))
+ fd := C.mountfuse(cdir, errp)
+ var err string
+ if *errp != nil {
+ err = C.GoString(*errp)
+ }
+ return int(fd), err
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/mount_linux.go b/vendor/github.com/mattermost/rsc/fuse/mount_linux.go
new file mode 100644
index 000000000..e5bc58b8a
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/mount_linux.go
@@ -0,0 +1,67 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fuse
+
+import (
+ "fmt"
+ "net"
+ "os"
+ "os/exec"
+ "syscall"
+)
+
+func mount(dir string) (fusefd int, errmsg string) {
+ fds, err := syscall.Socketpair(syscall.AF_FILE, syscall.SOCK_STREAM, 0)
+ if err != nil {
+ return -1, fmt.Sprintf("socketpair error: %v", err)
+ }
+ defer syscall.Close(fds[0])
+ defer syscall.Close(fds[1])
+
+ cmd := exec.Command("/bin/fusermount", "--", dir)
+ cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3")
+
+ writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes")
+ defer writeFile.Close()
+ cmd.ExtraFiles = []*os.File{writeFile}
+
+ out, err := cmd.CombinedOutput()
+ if len(out) > 0 || err != nil {
+ return -1, fmt.Sprintf("fusermount: %q, %v", out, err)
+ }
+
+ readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads")
+ defer readFile.Close()
+ c, err := net.FileConn(readFile)
+ if err != nil {
+ return -1, fmt.Sprintf("FileConn from fusermount socket: %v", err)
+ }
+ defer c.Close()
+
+ uc, ok := c.(*net.UnixConn)
+ if !ok {
+ return -1, fmt.Sprintf("unexpected FileConn type; expected UnixConn, got %T", c)
+ }
+
+ buf := make([]byte, 32) // expect 1 byte
+ oob := make([]byte, 32) // expect 24 bytes
+ _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob)
+ scms, err := syscall.ParseSocketControlMessage(oob[:oobn])
+ if err != nil {
+ return -1, fmt.Sprintf("ParseSocketControlMessage: %v", err)
+ }
+ if len(scms) != 1 {
+ return -1, fmt.Sprintf("expected 1 SocketControlMessage; got scms = %#v", scms)
+ }
+ scm := scms[0]
+ gotFds, err := syscall.ParseUnixRights(&scm)
+ if err != nil {
+ return -1, fmt.Sprintf("syscall.ParseUnixRights: %v", err)
+ }
+ if len(gotFds) != 1 {
+ return -1, fmt.Sprintf("wanted 1 fd; got %#v", gotFds)
+ }
+ return gotFds[0], ""
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/serve.go b/vendor/github.com/mattermost/rsc/fuse/serve.go
new file mode 100644
index 000000000..fa4f27e3f
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/serve.go
@@ -0,0 +1,1022 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// FUSE service loop, for servers that wish to use it.
+
+package fuse
+
+import (
+ "fmt"
+ "hash/fnv"
+ "io"
+ "log"
+ "os"
+ "path"
+ "sync"
+ "syscall"
+ "time"
+)
+
+// TODO: FINISH DOCS
+
+// An Intr is a channel that signals that a request has been interrupted.
+// Being able to receive from the channel means the request has been
+// interrupted.
+type Intr chan struct{}
+
+func (Intr) String() string { return "fuse.Intr" }
+
+// An FS is the interface required of a file system.
+//
+// Root() (Node, Error)
+//
+// Root is called to obtain the Node for the file system root.
+//
+// Optional Methods
+//
+// An FS implementation may implement
+// additional methods to handle the corresponding FUSE requests:
+//
+// Init(req *InitRequest, resp *InitResponse) Error
+//
+// Init is called to initialize the FUSE connection.
+// It can inspect the request and adjust the response as desired.
+// The default response sets MaxReadahead to 0 and MaxWrite to 4096.
+// Init must return promptly.
+//
+// Statfs(resp *StatfsResponse, intr Intr) Error
+//
+// Statfs is called to obtain file system metadata. It should write that data to resp.
+//
+// Rename(req *RenameRequest, intr Intr) Error
+//
+// XXXX this is not implemented like this. Instead, Rename is a method
+// on the source dierctory node, and takes a newDir Node parameter. Fix it like this?
+// Rename is called to rename the file req.OldName in the directory req.OldDir to
+// become the file req.NewName in the directory req.NewDir.
+//
+type FS interface {
+ Root() (Node, Error)
+}
+
+// A Node is the interface required of a file or directory.
+// See the documentation for type FS for general information
+// pertaining to all methods.
+//
+// Getattr(resp *GetattrResponse, intr Intr) fuse.Error
+//
+// Getattr obtains the standard metadata for the receiver.
+// It should store that metadata in resp.
+//
+// Open(xxx, intr Intr) (Handle, fuse.Error)
+//
+// Open opens the receiver.
+// XXX note about access. XXX OpenFlags.
+// XXX note that the Node may be a file or directory.
+//
+// Optional Methods
+//
+// An Node implementation may implement additional methods
+// to handle the corresponding FUSE requests.
+//
+// These optional requests can be called for both file and directory nodes:
+//
+// Access
+//
+// Access checks whether the calling context has permission for
+// the given operations on the receiver. If so, Access should return nil. If not, Access should
+// return EPERM. Note that this call affects the result of the access(2) system call
+// but not the open(2) system call. If Access is not implemented, the Node behaves
+// as if it always returns nil (permission granted), relying on checks in Open instead.
+//
+// Getxattr
+//
+// Getxattr obtains an extended attribute for the receiver.
+// XXX
+//
+// Listxattr
+//
+// Listxattr lists the extended attributes recorded for the receiver.
+//
+// Removexattr
+//
+// Removexattr removes an extended attribute from the receiver.
+//
+// Setattr
+//
+// Setattr sets the standard metadata for the receiver.
+//
+// Setxattr
+//
+// Setxattr sets an extended attribute for the receiver.
+//
+// Optional Directory Methods
+//
+// These optional requests will be called only for directory nodes:
+//
+// Create(xxx)
+//
+// Create creates
+//
+// Link(xxx)
+//
+// Link XXX
+//
+// Lookup(name string, intr Intr) (Node, Error)
+//
+// Lookup looks up a specific entry in the receiver,
+// which must be a directory. Lookup should return a Node
+// corresponding to the entry. If the name does not exist in
+// the directory, Lookup should return nil, err.
+//
+// Lookup need not to handle the names "." and "..".
+//
+// Mkdir
+//
+// Mkdir creates XXX
+//
+// Mknod XXX
+//
+// XXX
+//
+// Remove
+//
+// Remove removes the entry with the given name from
+// the receiver, which must be a directory. The entry to be removed
+// may correspond to a file (unlink) or to a directory (rmdir).
+//
+// Symlink
+//
+// Symlink creates a new symbolic link in the receiver, which must be a directory.
+// The entry
+//
+// Optional Symlink Methods
+//
+// This optional request will be called only for symbolic link nodes:
+//
+// Readlink
+//
+// Readlink reads a symbolic link.
+type Node interface {
+ Attr() Attr
+}
+
+var startTime = time.Now()
+
+func nodeAttr(inode uint64, n Node) (attr Attr) {
+ attr = n.Attr()
+ if attr.Nlink == 0 {
+ attr.Nlink = 1
+ }
+ if attr.Atime.IsZero() {
+ attr.Atime = startTime
+ }
+ if attr.Mtime.IsZero() {
+ attr.Mtime = startTime
+ }
+ if attr.Ctime.IsZero() {
+ attr.Ctime = startTime
+ }
+ if attr.Crtime.IsZero() {
+ attr.Crtime = startTime
+ }
+ if attr.Inode == 0 {
+ attr.Inode = inode
+ }
+ return
+}
+
+// A Handle is the interface required of an opened file or directory.
+// See the documentation for type FS for general information
+// pertaining to all methods.
+//
+// Flush
+//
+// Flush is called each time the file or directory is closed. Because there can be
+// multiple file descriptors referring to a single opened file, Flush can be called
+// multiple times.
+//
+// Optional Methods
+//
+// A Handle implementation may implement additional methods to handle
+// the corresponding FUSE requests. The most common to implement are
+// Read, ReadDir, and Write.
+//
+// Fsync
+//
+// Getlk
+//
+// Read
+//
+// Readdir
+//
+// Release
+//
+// Setlk
+//
+// Setlkw
+//
+// Write
+//
+type Handle interface {
+}
+
+// Serve serves the FUSE connection by making calls to the methods
+// of fs and the Nodes and Handles it makes available. It returns only
+// when the connection has been closed or an unexpected error occurs.
+func (c *Conn) Serve(fs FS) error {
+ if c.req != nil {
+ panic("fuse: Serve called twice")
+ }
+ c.req = map[RequestID]*serveRequest{}
+
+ root, err := fs.Root()
+ if err != nil {
+ return fmt.Errorf("cannot obtain root node: %v", syscall.Errno(err.(Errno)).Error())
+ }
+ c.node = append(c.node, nil, &serveNode{name: "/", node: root})
+ c.handle = append(c.handle, nil)
+
+ for {
+ req, err := c.ReadRequest()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return err
+ }
+
+ go c.serve(fs, req)
+ }
+ return nil
+}
+
+type serveConn struct {
+ meta sync.Mutex
+ req map[RequestID]*serveRequest
+ node []*serveNode
+ handle []*serveHandle
+ freeNode []NodeID
+ freeHandle []HandleID
+ nodeGen uint64
+ nodeHandles []map[HandleID]bool // open handles for a node; slice index is NodeID
+}
+
+type serveRequest struct {
+ Request Request
+ Intr Intr
+}
+
+type serveNode struct {
+ name string
+ node Node
+ inode uint64
+ isDir bool
+}
+
+func (sn *serveNode) attr() (attr Attr) {
+ attr = nodeAttr(sn.inode, sn.node)
+ if attr.Inode == 0 {
+ sn.inode = hash(sn.name)
+ attr.Inode = sn.inode
+ }
+ sn.isDir = attr.Mode&os.ModeDir != 0
+ return
+}
+
+func hash(s string) uint64 {
+ f := fnv.New64()
+ f.Write([]byte(s))
+ return f.Sum64()
+}
+
+type serveHandle struct {
+ handle Handle
+ readData []byte
+ trunc bool
+ writeData []byte
+ nodeID NodeID
+}
+
+func (c *Conn) saveNode(name string, node Node) (id NodeID, gen uint64, sn *serveNode) {
+ sn = &serveNode{name: name, node: node}
+ c.meta.Lock()
+ if n := len(c.freeNode); n > 0 {
+ id = c.freeNode[n-1]
+ c.freeNode = c.freeNode[:n-1]
+ c.node[id] = sn
+ c.nodeGen++
+ } else {
+ id = NodeID(len(c.node))
+ c.node = append(c.node, sn)
+ }
+ gen = c.nodeGen
+ c.meta.Unlock()
+ return
+}
+
+func (c *Conn) saveHandle(handle Handle, nodeID NodeID) (id HandleID, shandle *serveHandle) {
+ c.meta.Lock()
+ shandle = &serveHandle{handle: handle, nodeID: nodeID}
+ if n := len(c.freeHandle); n > 0 {
+ id = c.freeHandle[n-1]
+ c.freeHandle = c.freeHandle[:n-1]
+ c.handle[id] = shandle
+ } else {
+ id = HandleID(len(c.handle))
+ c.handle = append(c.handle, shandle)
+ }
+
+ // Update mapping from node ID -> set of open Handle IDs.
+ for len(c.nodeHandles) <= int(nodeID) {
+ c.nodeHandles = append(c.nodeHandles, nil)
+ }
+ if c.nodeHandles[nodeID] == nil {
+ c.nodeHandles[nodeID] = make(map[HandleID]bool)
+ }
+ c.nodeHandles[nodeID][id] = true
+
+ c.meta.Unlock()
+ return
+}
+
+func (c *Conn) dropNode(id NodeID) {
+ c.meta.Lock()
+ c.node[id] = nil
+ if len(c.nodeHandles) > int(id) {
+ c.nodeHandles[id] = nil
+ }
+ c.freeNode = append(c.freeNode, id)
+ c.meta.Unlock()
+}
+
+func (c *Conn) dropHandle(id HandleID) {
+ c.meta.Lock()
+ h := c.handle[id]
+ delete(c.nodeHandles[h.nodeID], id)
+ c.handle[id] = nil
+ c.freeHandle = append(c.freeHandle, id)
+ c.meta.Unlock()
+}
+
+func (c *Conn) serve(fs FS, r Request) {
+ intr := make(Intr)
+ req := &serveRequest{Request: r, Intr: intr}
+
+ Debugf("<- %s", req)
+ var node Node
+ var handle Handle
+ var snode *serveNode
+ var shandle *serveHandle
+ c.meta.Lock()
+ hdr := r.Hdr()
+ if id := hdr.Node; id != 0 {
+ if id < NodeID(len(c.node)) {
+ snode = c.node[uint(id)]
+ }
+ if snode == nil {
+ c.meta.Unlock()
+ println("missing node", id, len(c.node), snode)
+ Debugf("-> %#x %v", hdr.ID, ESTALE)
+ r.RespondError(ESTALE)
+ return
+ }
+ node = snode.node
+ }
+ if id := r.handle(); id != 0 {
+ if id < HandleID(len(c.handle)) {
+ shandle = c.handle[uint(id)]
+ }
+ if shandle == nil {
+ println("missing handle", id, len(c.handle), shandle)
+ c.meta.Unlock()
+ Debugf("-> %#x %v", hdr.ID, ESTALE)
+ r.RespondError(ESTALE)
+ return
+ }
+ handle = shandle.handle
+ }
+ intr = make(chan struct{})
+ 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?
+ intr = nil
+ } else {
+ c.req[hdr.ID] = req
+ }
+ c.meta.Unlock()
+
+ // Call this before responding.
+ // After responding is too late: we might get another request
+ // with the same ID and be very confused.
+ done := func(resp interface{}) {
+ Debugf("-> %#x %v", hdr.ID, resp)
+ c.meta.Lock()
+ c.req[hdr.ID] = nil
+ c.meta.Unlock()
+ }
+
+ switch r := r.(type) {
+ default:
+ // Note: To FUSE, ENOSYS means "this server never implements this request."
+ // It would be inappropriate to return ENOSYS for other operations in this
+ // switch that might only be unavailable in some contexts, not all.
+ done(ENOSYS)
+ r.RespondError(ENOSYS)
+
+ // FS operations.
+ case *InitRequest:
+ s := &InitResponse{
+ MaxWrite: 4096,
+ }
+ if fs, ok := fs.(interface {
+ Init(*InitRequest, *InitResponse, Intr) Error
+ }); ok {
+ if err := fs.Init(r, s, intr); err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ }
+ done(s)
+ r.Respond(s)
+
+ case *StatfsRequest:
+ s := &StatfsResponse{}
+ if fs, ok := fs.(interface {
+ Statfs(*StatfsRequest, *StatfsResponse, Intr) Error
+ }); ok {
+ if err := fs.Statfs(r, s, intr); err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ }
+ done(s)
+ r.Respond(s)
+
+ // Node operations.
+ case *GetattrRequest:
+ s := &GetattrResponse{}
+ if n, ok := node.(interface {
+ Getattr(*GetattrRequest, *GetattrResponse, Intr) Error
+ }); ok {
+ if err := n.Getattr(r, s, intr); err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ } else {
+ s.AttrValid = 1 * time.Minute
+ s.Attr = snode.attr()
+ }
+ done(s)
+ r.Respond(s)
+
+ case *SetattrRequest:
+ s := &SetattrResponse{}
+
+ // Special-case truncation, if no other bits are set
+ // and the open Handles all have a WriteAll method.
+ if r.Valid&SetattrSize != 0 && r.Size == 0 {
+ type writeAll interface {
+ WriteAll([]byte, Intr) Error
+ }
+ switch r.Valid {
+ case SetattrLockOwner | SetattrSize, SetattrSize:
+ // Seen on Linux. Handle isn't set.
+ c.meta.Lock()
+ for hid := range c.nodeHandles[hdr.Node] {
+ shandle := c.handle[hid]
+ if _, ok := shandle.handle.(writeAll); ok {
+ shandle.trunc = true
+ }
+ }
+ c.meta.Unlock()
+ case SetattrHandle | SetattrSize:
+ // Seen on OS X; the Handle is provided.
+ if _, ok := handle.(writeAll); ok {
+ shandle.trunc = true
+ }
+ }
+ }
+
+ log.Printf("setattr %v", r)
+ if n, ok := node.(interface {
+ Setattr(*SetattrRequest, *SetattrResponse, Intr) Error
+ }); ok {
+ if err := n.Setattr(r, s, intr); err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ done(s)
+ r.Respond(s)
+ break
+ }
+
+ if s.AttrValid == 0 {
+ s.AttrValid = 1 * time.Minute
+ }
+ s.Attr = snode.attr()
+ done(s)
+ r.Respond(s)
+
+ case *SymlinkRequest:
+ s := &SymlinkResponse{}
+ n, ok := node.(interface {
+ Symlink(*SymlinkRequest, Intr) (Node, Error)
+ })
+ if !ok {
+ done(EIO) // XXX or EPERM like Mkdir?
+ r.RespondError(EIO)
+ break
+ }
+ n2, err := n.Symlink(r, intr)
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ c.saveLookup(&s.LookupResponse, snode, r.NewName, n2)
+ done(s)
+ r.Respond(s)
+
+ case *ReadlinkRequest:
+ n, ok := node.(interface {
+ Readlink(*ReadlinkRequest, Intr) (string, Error)
+ })
+ if !ok {
+ done(EIO) /// XXX or EPERM?
+ r.RespondError(EIO)
+ break
+ }
+ target, err := n.Readlink(r, intr)
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ done(target)
+ r.Respond(target)
+
+ case *LinkRequest:
+ n, ok := node.(interface {
+ Link(r *LinkRequest, old Node, intr Intr) (Node, Error)
+ })
+ if !ok {
+ log.Printf("Node %T doesn't implement fuse Link", node)
+ done(EIO) /// XXX or EPERM?
+ r.RespondError(EIO)
+ break
+ }
+ c.meta.Lock()
+ var oldNode *serveNode
+ if int(r.OldNode) < len(c.node) {
+ oldNode = c.node[r.OldNode]
+ }
+ c.meta.Unlock()
+ if oldNode == nil {
+ log.Printf("In LinkRequest, node %d not found", r.OldNode)
+ done(EIO)
+ r.RespondError(EIO)
+ break
+ }
+ n2, err := n.Link(r, oldNode.node, intr)
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ s := &LookupResponse{}
+ c.saveLookup(s, snode, r.NewName, n2)
+ done(s)
+ r.Respond(s)
+
+ case *RemoveRequest:
+ n, ok := node.(interface {
+ Remove(*RemoveRequest, Intr) Error
+ })
+ if !ok {
+ done(EIO) /// XXX or EPERM?
+ r.RespondError(EIO)
+ break
+ }
+ err := n.Remove(r, intr)
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ done(nil)
+ r.Respond()
+
+ case *AccessRequest:
+ if n, ok := node.(interface {
+ Access(*AccessRequest, Intr) Error
+ }); ok {
+ if err := n.Access(r, intr); err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ }
+ done(r)
+ r.Respond()
+
+ case *LookupRequest:
+ var n2 Node
+ var err Error
+ s := &LookupResponse{}
+ if n, ok := node.(interface {
+ Lookup(string, Intr) (Node, Error)
+ }); ok {
+ n2, err = n.Lookup(r.Name, intr)
+ } else if n, ok := node.(interface {
+ Lookup(*LookupRequest, *LookupResponse, Intr) (Node, Error)
+ }); ok {
+ n2, err = n.Lookup(r, s, intr)
+ } else {
+ done(ENOENT)
+ r.RespondError(ENOENT)
+ break
+ }
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ c.saveLookup(s, snode, r.Name, n2)
+ done(s)
+ r.Respond(s)
+
+ case *MkdirRequest:
+ s := &MkdirResponse{}
+ n, ok := node.(interface {
+ Mkdir(*MkdirRequest, Intr) (Node, Error)
+ })
+ if !ok {
+ done(EPERM)
+ r.RespondError(EPERM)
+ break
+ }
+ n2, err := n.Mkdir(r, intr)
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ c.saveLookup(&s.LookupResponse, snode, r.Name, n2)
+ done(s)
+ r.Respond(s)
+
+ case *OpenRequest:
+ s := &OpenResponse{Flags: OpenDirectIO}
+ var h2 Handle
+ if n, ok := node.(interface {
+ Open(*OpenRequest, *OpenResponse, Intr) (Handle, Error)
+ }); ok {
+ hh, err := n.Open(r, s, intr)
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ h2 = hh
+ } else {
+ h2 = node
+ }
+ s.Handle, _ = c.saveHandle(h2, hdr.Node)
+ done(s)
+ r.Respond(s)
+
+ case *CreateRequest:
+ n, ok := node.(interface {
+ Create(*CreateRequest, *CreateResponse, Intr) (Node, Handle, Error)
+ })
+ if !ok {
+ // If we send back ENOSYS, FUSE will try mknod+open.
+ done(EPERM)
+ r.RespondError(EPERM)
+ break
+ }
+ s := &CreateResponse{OpenResponse: OpenResponse{Flags: OpenDirectIO}}
+ n2, h2, err := n.Create(r, s, intr)
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ c.saveLookup(&s.LookupResponse, snode, r.Name, n2)
+ h, shandle := c.saveHandle(h2, hdr.Node)
+ s.Handle = h
+ shandle.trunc = true
+ done(s)
+ r.Respond(s)
+
+ case *GetxattrRequest, *SetxattrRequest, *ListxattrRequest, *RemovexattrRequest:
+ // TODO: Use n.
+ done(ENOSYS)
+ r.RespondError(ENOSYS)
+
+ case *ForgetRequest:
+ n, ok := node.(interface {
+ Forget()
+ })
+ if ok {
+ n.Forget()
+ }
+ c.dropNode(hdr.Node)
+ done(r)
+ r.Respond()
+
+ // Handle operations.
+ case *ReadRequest:
+ s := &ReadResponse{Data: make([]byte, 0, r.Size)}
+ if snode.isDir {
+ if h, ok := handle.(interface {
+ ReadDir(Intr) ([]Dirent, Error)
+ }); ok {
+ if shandle.readData == nil {
+ attr := snode.attr()
+ dirs, err := h.ReadDir(intr)
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ var data []byte
+ data = AppendDirent(data, Dirent{Inode: attr.Inode, Name: "."})
+ data = AppendDirent(data, Dirent{Inode: attr.Inode, Name: ".."})
+ for _, dir := range dirs {
+ if dir.Inode == 0 {
+ dir.Inode = hash(path.Join(snode.name, dir.Name))
+ }
+ data = AppendDirent(data, dir)
+ }
+ shandle.readData = data
+ }
+ HandleRead(r, s, shandle.readData)
+ done(s)
+ r.Respond(s)
+ break
+ }
+ } else {
+ if h, ok := handle.(interface {
+ ReadAll(Intr) ([]byte, Error)
+ }); ok {
+ if shandle.readData == nil {
+ data, err := h.ReadAll(intr)
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ if data == nil {
+ data = []byte{}
+ }
+ shandle.readData = data
+ }
+ HandleRead(r, s, shandle.readData)
+ done(s)
+ r.Respond(s)
+ break
+ }
+ }
+ h, ok := handle.(interface {
+ Read(*ReadRequest, *ReadResponse, Intr) Error
+ })
+ if !ok {
+ fmt.Printf("NO READ FOR %T\n", handle)
+ done(EIO)
+ r.RespondError(EIO)
+ break
+ }
+ if err := h.Read(r, s, intr); err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ done(s)
+ r.Respond(s)
+
+ case *WriteRequest:
+ s := &WriteResponse{}
+ if shandle.trunc && r.Offset == int64(len(shandle.writeData)) {
+ shandle.writeData = append(shandle.writeData, r.Data...)
+ s.Size = len(r.Data)
+ done(s)
+ r.Respond(s)
+ break
+ }
+ if h, ok := handle.(interface {
+ Write(*WriteRequest, *WriteResponse, Intr) Error
+ }); ok {
+ if err := h.Write(r, s, intr); err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ done(s)
+ r.Respond(s)
+ break
+ }
+ println("NO WRITE")
+ done(EIO)
+ r.RespondError(EIO)
+
+ case *FlushRequest:
+ if shandle.trunc {
+ h := handle.(interface {
+ WriteAll([]byte, Intr) Error
+ })
+ if err := h.WriteAll(shandle.writeData, intr); err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ shandle.writeData = nil
+ shandle.trunc = false
+ }
+ if h, ok := handle.(interface {
+ Flush(*FlushRequest, Intr) Error
+ }); ok {
+ if err := h.Flush(r, intr); err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ }
+ done(nil)
+ r.Respond()
+
+ case *ReleaseRequest:
+ // No matter what, release the handle.
+ c.dropHandle(r.handle())
+ if h, ok := handle.(interface {
+ Release(*ReleaseRequest, Intr) Error
+ }); ok {
+ if err := h.Release(r, intr); err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ }
+ done(nil)
+ r.Respond()
+
+ case *DestroyRequest:
+ fs, ok := fs.(interface {
+ Destroy()
+ })
+ if ok {
+ fs.Destroy()
+ }
+ done(nil)
+ r.Respond()
+
+ case *RenameRequest:
+ c.meta.Lock()
+ var newDirNode *serveNode
+ if int(r.NewDir) < len(c.node) {
+ newDirNode = c.node[r.NewDir]
+ }
+ c.meta.Unlock()
+ if newDirNode == nil {
+ println("RENAME NEW DIR NODE NOT FOUND")
+ done(EIO)
+ r.RespondError(EIO)
+ break
+ }
+ n, ok := node.(interface {
+ Rename(r *RenameRequest, newDir Node, intr Intr) Error
+ })
+ if !ok {
+ log.Printf("Node %T missing Rename method", node)
+ done(EIO) // XXX or EPERM like Mkdir?
+ r.RespondError(EIO)
+ break
+ }
+ err := n.Rename(r, newDirNode.node, intr)
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ done(nil)
+ r.Respond()
+
+ case *MknodRequest:
+ n, ok := node.(interface {
+ Mknod(r *MknodRequest, intr Intr) (Node, Error)
+ })
+ if !ok {
+ log.Printf("Node %T missing Mknod method", node)
+ done(EIO)
+ r.RespondError(EIO)
+ break
+ }
+ n2, err := n.Mknod(r, intr)
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ s := &LookupResponse{}
+ c.saveLookup(s, snode, r.Name, n2)
+ done(s)
+ r.Respond(s)
+
+ case *FsyncRequest:
+ n, ok := node.(interface {
+ Fsync(r *FsyncRequest, intr Intr) Error
+ })
+ if !ok {
+ log.Printf("Node %T missing Fsync method", node)
+ done(EIO)
+ r.RespondError(EIO)
+ break
+ }
+ err := n.Fsync(r, intr)
+ if err != nil {
+ done(err)
+ r.RespondError(err)
+ break
+ }
+ done(nil)
+ r.Respond()
+
+ /* case *FsyncdirRequest:
+ done(ENOSYS)
+ r.RespondError(ENOSYS)
+
+ case *GetlkRequest, *SetlkRequest, *SetlkwRequest:
+ done(ENOSYS)
+ r.RespondError(ENOSYS)
+
+ // One of a kind.
+ case *InterruptRequest:
+ c.meta.Lock()
+ ireq := c.req[r.OldID]
+ if ireq != nil && ireq.Intr != nil {
+ close(ireq.Intr)
+ ireq.Intr = nil
+ }
+ c.meta.Unlock()
+ done(nil)
+ r.Respond()
+
+ case *BmapRequest:
+ done(ENOSYS)
+ r.RespondError(ENOSYS)
+
+ case *SetvolnameRequest, *GetxtimesRequest, *ExchangeRequest:
+ done(ENOSYS)
+ r.RespondError(ENOSYS)
+ */
+ }
+}
+
+func (c *Conn) saveLookup(s *LookupResponse, snode *serveNode, elem string, n2 Node) {
+ name := path.Join(snode.name, elem)
+ var sn *serveNode
+ s.Node, s.Generation, sn = c.saveNode(name, n2)
+ if s.EntryValid == 0 {
+ s.EntryValid = 1 * time.Minute
+ }
+ if s.AttrValid == 0 {
+ s.AttrValid = 1 * time.Minute
+ }
+ s.Attr = sn.attr()
+}
+
+// HandleRead handles a read request assuming that data is the entire file content.
+// It adjusts the amount returned in resp according to req.Offset and req.Size.
+func HandleRead(req *ReadRequest, resp *ReadResponse, data []byte) {
+ if req.Offset >= int64(len(data)) {
+ data = nil
+ } else {
+ data = data[req.Offset:]
+ }
+ if len(data) > req.Size {
+ data = data[:req.Size]
+ }
+ n := copy(resp.Data[:req.Size], data)
+ resp.Data = resp.Data[:n]
+}
+
+// DataHandle returns a read-only Handle that satisfies reads
+// using the given data.
+func DataHandle(data []byte) Handle {
+ return &dataHandle{data}
+}
+
+type dataHandle struct {
+ data []byte
+}
+
+func (d *dataHandle) Read(intr Intr) ([]byte, Error) {
+ return d.data, nil
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/tree.go b/vendor/github.com/mattermost/rsc/fuse/tree.go
new file mode 100644
index 000000000..fec0a748f
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/tree.go
@@ -0,0 +1,93 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// FUSE directory tree, for servers that wish to use it with the service loop.
+
+package fuse
+
+import (
+ "os"
+ pathpkg "path"
+ "strings"
+)
+
+// A Tree implements a basic directory tree for FUSE.
+type Tree struct {
+ tree
+}
+
+func (t *Tree) Root() (Node, Error) {
+ return &t.tree, nil
+}
+
+// Add adds the path to the tree, resolving to the given node.
+// If path or a prefix of path has already been added to the tree,
+// Add panics.
+func (t *Tree) Add(path string, node Node) {
+ path = pathpkg.Clean("/" + path)[1:]
+ elems := strings.Split(path, "/")
+ dir := Node(&t.tree)
+ for i, elem := range elems {
+ dt, ok := dir.(*tree)
+ if !ok {
+ panic("fuse: Tree.Add for " + strings.Join(elems[:i], "/") + " and " + path)
+ }
+ n := dt.lookup(elem)
+ if n != nil {
+ if i+1 == len(elems) {
+ panic("fuse: Tree.Add for " + path + " conflicts with " + elem)
+ }
+ dir = n
+ } else {
+ if i+1 == len(elems) {
+ dt.add(elem, node)
+ } else {
+ dir = &tree{}
+ dt.add(elem, dir)
+ }
+ }
+ }
+}
+
+type treeDir struct {
+ name string
+ node Node
+}
+
+type tree struct {
+ dir []treeDir
+}
+
+func (t *tree) lookup(name string) Node {
+ for _, d := range t.dir {
+ if d.name == name {
+ return d.node
+ }
+ }
+ return nil
+}
+
+func (t *tree) add(name string, n Node) {
+ t.dir = append(t.dir, treeDir{name, n})
+}
+
+func (t *tree) Attr() Attr {
+ return Attr{Mode: os.ModeDir | 0555}
+}
+
+func (t *tree) Lookup(name string, intr Intr) (Node, Error) {
+ n := t.lookup(name)
+ if n != nil {
+ return n, nil
+ }
+ return nil, ENOENT
+}
+
+func (t *tree) ReadDir(intr Intr) ([]Dirent, Error) {
+ var out []Dirent
+ for _, d := range t.dir {
+ out = append(out, Dirent{Name: d.name})
+ }
+ return out, nil
+}