diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 43c6f91f6..88b7e712a 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -7,7 +7,7 @@ "Deps": [ { "ImportPath": "bazil.org/fuse", - "Rev": "79d103f5608724e3ccee0a10daccc5e4aff03591" + "Rev": "e4fcc9a2c7567d1c42861deebeb483315d222262" }, { "ImportPath": "code.google.com/p/go-uuid/uuid", diff --git a/Godeps/_workspace/src/bazil.org/fuse/LICENSE b/Godeps/_workspace/src/bazil.org/fuse/LICENSE index d369cb822..4ac7cd838 100644 --- a/Godeps/_workspace/src/bazil.org/fuse/LICENSE +++ b/Godeps/_workspace/src/bazil.org/fuse/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013, 2014 Tommi Virtanen. +Copyright (c) 2013-2015 Tommi Virtanen. Copyright (c) 2009, 2011, 2012 The Go Authors. All rights reserved. diff --git a/Godeps/_workspace/src/bazil.org/fuse/README.md b/Godeps/_workspace/src/bazil.org/fuse/README.md index 471b2b258..8c6d556ee 100644 --- a/Godeps/_workspace/src/bazil.org/fuse/README.md +++ b/Godeps/_workspace/src/bazil.org/fuse/README.md @@ -15,7 +15,7 @@ Here’s how to get going: Website: http://bazil.org/fuse/ -Github repository: https://github.com/bazillion/fuse +Github repository: https://github.com/bazil/fuse API docs: http://godoc.org/bazil.org/fuse diff --git a/Godeps/_workspace/src/bazil.org/fuse/fs/bench/bench_test.go b/Godeps/_workspace/src/bazil.org/fuse/fs/bench/bench_test.go index 2596446ad..6cab7dff0 100644 --- a/Godeps/_workspace/src/bazil.org/fuse/fs/bench/bench_test.go +++ b/Godeps/_workspace/src/bazil.org/fuse/fs/bench/bench_test.go @@ -43,8 +43,10 @@ var _ = fs.NodeStringLookuper(benchDir{}) var _ = fs.Handle(benchDir{}) var _ = fs.HandleReadDirAller(benchDir{}) -func (benchDir) Attr() fuse.Attr { - return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0555} +func (benchDir) Attr(ctx context.Context, a *fuse.Attr) error { + a.Inode = 1 + a.Mode = os.ModeDir | 0555 + return nil } func (d benchDir) Lookup(ctx context.Context, name string) (fs.Node, error) { @@ -72,8 +74,11 @@ var _ = fs.Handle(benchFile{}) var _ = fs.HandleReader(benchFile{}) var _ = fs.HandleWriter(benchFile{}) -func (benchFile) Attr() fuse.Attr { - return fuse.Attr{Inode: 2, Mode: 0644, Size: 9999999999999999} +func (benchFile) Attr(ctx context.Context, a *fuse.Attr) error { + a.Inode = 2 + a.Mode = 0644 + a.Size = 9999999999999999 + return nil } func (f benchFile) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) { diff --git a/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/testfs.go b/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/testfs.go index 169f20059..f86bdbe54 100644 --- a/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/testfs.go +++ b/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/testfs.go @@ -22,12 +22,18 @@ func (f SimpleFS) Root() (fs.Node, error) { // File can be embedded in a struct to make it look like a file. type File struct{} -func (f File) Attr() fuse.Attr { return fuse.Attr{Mode: 0666} } +func (f File) Attr(ctx context.Context, a *fuse.Attr) error { + a.Mode = 0666 + return nil +} // Dir can be embedded in a struct to make it look like a directory. type Dir struct{} -func (f Dir) Attr() fuse.Attr { return fuse.Attr{Mode: os.ModeDir | 0777} } +func (f Dir) Attr(ctx context.Context, a *fuse.Attr) error { + a.Mode = os.ModeDir | 0777 + return nil +} // ChildMap is a directory with child nodes looked up from a map. type ChildMap map[string]fs.Node @@ -35,8 +41,9 @@ type ChildMap map[string]fs.Node var _ = fs.Node(ChildMap{}) var _ = fs.NodeStringLookuper(ChildMap{}) -func (f ChildMap) Attr() fuse.Attr { - return fuse.Attr{Mode: os.ModeDir | 0777} +func (f ChildMap) Attr(ctx context.Context, a *fuse.Attr) error { + a.Mode = os.ModeDir | 0777 + return nil } func (f ChildMap) Lookup(ctx context.Context, name string) (fs.Node, error) { diff --git a/Godeps/_workspace/src/bazil.org/fuse/fs/serve.go b/Godeps/_workspace/src/bazil.org/fuse/fs/serve.go index beb62bfd1..3eab002cc 100644 --- a/Godeps/_workspace/src/bazil.org/fuse/fs/serve.go +++ b/Godeps/_workspace/src/bazil.org/fuse/fs/serve.go @@ -7,7 +7,9 @@ import ( "fmt" "hash/fnv" "io" + "log" "reflect" + "runtime" "strings" "sync" "time" @@ -55,9 +57,6 @@ type FSDestroyer interface { // Linux only sends this request for block device backed (fuseblk) // filesystems, to allow them to flush writes to disk before the // unmount completes. - // - // On normal FUSE filesystems, use Forget of the root Node to - // do actions at unmount time. Destroy() } @@ -87,7 +86,8 @@ type FSInodeGenerator interface { // Other FUSE requests can be handled by implementing methods from the // Node* interfaces, for example NodeOpener. type Node interface { - Attr() fuse.Attr + // Attr fills attr with the standard metadata for the node. + Attr(ctx context.Context, attr *fuse.Attr) error } type NodeGetattrer interface { @@ -192,6 +192,11 @@ type NodeCreater interface { } type NodeForgetter interface { + // Forget about this node. This node will not receive further + // method calls. + // + // Forget is not necessarily seen on unmount, as all nodes are + // implicitly forgotten as part part of the unmount. Forget() } @@ -236,24 +241,17 @@ type NodeRemovexattrer interface { var startTime = time.Now() -func nodeAttr(n Node) (attr fuse.Attr) { - attr = n.Attr() - if attr.Nlink == 0 { - attr.Nlink = 1 +func nodeAttr(ctx context.Context, n Node, attr *fuse.Attr) error { + attr.Valid = attrValidTime + attr.Nlink = 1 + attr.Atime = startTime + attr.Mtime = startTime + attr.Ctime = startTime + attr.Crtime = startTime + if err := n.Attr(ctx, attr); err != nil { + return err } - 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 - } - return + return nil } // A Handle is the interface required of an opened file or directory. @@ -323,12 +321,17 @@ type Server struct { // // See fuse.Debug for the rules that log functions must follow. Debug func(msg interface{}) + + // Used to ensure worker goroutines finish before Serve returns + wg sync.WaitGroup } // 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 (s *Server) Serve(c *fuse.Conn) error { + defer s.wg.Wait() // Wait for worker goroutines to complete before return + sc := serveConn{ fs: s.FS, debug: s.Debug, @@ -358,7 +361,11 @@ func (s *Server) Serve(c *fuse.Conn) error { return err } - go sc.serve(req) + s.wg.Add(1) + go func() { + defer s.wg.Done() + sc.serve(req) + }() } return nil } @@ -398,12 +405,12 @@ type serveNode struct { refs uint64 } -func (sn *serveNode) attr() (attr fuse.Attr) { - attr = nodeAttr(sn.node) +func (sn *serveNode) attr(ctx context.Context, attr *fuse.Attr) error { + err := nodeAttr(ctx, sn.node, attr) if attr.Inode == 0 { attr.Inode = sn.inode } - return + return err } type serveHandle struct { @@ -646,6 +653,30 @@ func (m *renameNewDirNodeNotFound) String() string { return fmt.Sprintf("In RenameRequest (request %#x), node %d not found", m.Request.Hdr().ID, m.In.NewDir) } +type handlerPanickedError struct { + Request interface{} + Err interface{} +} + +var _ error = handlerPanickedError{} + +func (h handlerPanickedError) Error() string { + return fmt.Sprintf("handler panicked: %v", h.Err) +} + +var _ fuse.ErrorNumber = handlerPanickedError{} + +func (h handlerPanickedError) Errno() fuse.Errno { + if err, ok := h.Err.(fuse.ErrorNumber); ok { + return err.Errno() + } + return fuse.DefaultErrno +} + +func initLookupResponse(s *fuse.LookupResponse) { + s.EntryValid = entryValidTime +} + func (c *serveConn) serve(r fuse.Request) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -725,6 +756,22 @@ func (c *serveConn) serve(r fuse.Request) { c.meta.Unlock() } + defer func() { + if rec := recover(); rec != nil { + const size = 1 << 16 + buf := make([]byte, size) + n := runtime.Stack(buf, false) + buf = buf[:n] + log.Printf("fuse: panic in handler for %v: %v\n%s", r, rec, buf) + err := handlerPanickedError{ + Request: r, + Err: rec, + } + done(err) + r.RespondError(err) + } + }() + switch r := r.(type) { default: // Note: To FUSE, ENOSYS means "this server never implements this request." @@ -771,8 +818,11 @@ func (c *serveConn) serve(r fuse.Request) { break } } else { - s.AttrValid = attrValidTime - s.Attr = snode.attr() + if err := snode.attr(ctx, &s.Attr); err != nil { + done(err) + r.RespondError(err) + break + } } done(s) r.Respond(s) @@ -790,15 +840,17 @@ func (c *serveConn) serve(r fuse.Request) { break } - if s.AttrValid == 0 { - s.AttrValid = attrValidTime + if err := snode.attr(ctx, &s.Attr); err != nil { + done(err) + r.RespondError(err) + break } - s.Attr = snode.attr() done(s) r.Respond(s) case *fuse.SymlinkRequest: s := &fuse.SymlinkResponse{} + initLookupResponse(&s.LookupResponse) n, ok := node.(NodeSymlinker) if !ok { done(fuse.EIO) // XXX or EPERM like Mkdir? @@ -811,7 +863,11 @@ func (c *serveConn) serve(r fuse.Request) { r.RespondError(err) break } - c.saveLookup(&s.LookupResponse, snode, r.NewName, n2) + if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.NewName, n2); err != nil { + done(err) + r.RespondError(err) + break + } done(s) r.Respond(s) @@ -860,7 +916,12 @@ func (c *serveConn) serve(r fuse.Request) { break } s := &fuse.LookupResponse{} - c.saveLookup(s, snode, r.NewName, n2) + initLookupResponse(s) + if err := c.saveLookup(ctx, s, snode, r.NewName, n2); err != nil { + done(err) + r.RespondError(err) + break + } done(s) r.Respond(s) @@ -895,6 +956,7 @@ func (c *serveConn) serve(r fuse.Request) { var n2 Node var err error s := &fuse.LookupResponse{} + initLookupResponse(s) if n, ok := node.(NodeStringLookuper); ok { n2, err = n.Lookup(ctx, r.Name) } else if n, ok := node.(NodeRequestLookuper); ok { @@ -909,12 +971,17 @@ func (c *serveConn) serve(r fuse.Request) { r.RespondError(err) break } - c.saveLookup(s, snode, r.Name, n2) + if err := c.saveLookup(ctx, s, snode, r.Name, n2); err != nil { + done(err) + r.RespondError(err) + break + } done(s) r.Respond(s) case *fuse.MkdirRequest: s := &fuse.MkdirResponse{} + initLookupResponse(&s.LookupResponse) n, ok := node.(NodeMkdirer) if !ok { done(fuse.EPERM) @@ -927,7 +994,11 @@ func (c *serveConn) serve(r fuse.Request) { r.RespondError(err) break } - c.saveLookup(&s.LookupResponse, snode, r.Name, n2) + if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.Name, n2); err != nil { + done(err) + r.RespondError(err) + break + } done(s) r.Respond(s) @@ -958,13 +1029,18 @@ func (c *serveConn) serve(r fuse.Request) { break } s := &fuse.CreateResponse{OpenResponse: fuse.OpenResponse{}} + initLookupResponse(&s.LookupResponse) n2, h2, err := n.Create(ctx, r, s) if err != nil { done(err) r.RespondError(err) break } - c.saveLookup(&s.LookupResponse, snode, r.Name, n2) + if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.Name, n2); err != nil { + done(err) + r.RespondError(err) + break + } s.Handle = c.saveHandle(h2, hdr.Node) done(s) r.Respond(s) @@ -1240,7 +1316,12 @@ func (c *serveConn) serve(r fuse.Request) { break } s := &fuse.LookupResponse{} - c.saveLookup(s, snode, r.Name, n2) + initLookupResponse(s) + if err := c.saveLookup(ctx, s, snode, r.Name, n2); err != nil { + done(err) + r.RespondError(err) + break + } done(s) r.Respond(s) @@ -1290,19 +1371,16 @@ func (c *serveConn) serve(r fuse.Request) { } } -func (c *serveConn) saveLookup(s *fuse.LookupResponse, snode *serveNode, elem string, n2 Node) { - s.Attr = nodeAttr(n2) +func (c *serveConn) saveLookup(ctx context.Context, s *fuse.LookupResponse, snode *serveNode, elem string, n2 Node) error { + if err := nodeAttr(ctx, n2, &s.Attr); err != nil { + return err + } if s.Attr.Inode == 0 { s.Attr.Inode = c.dynamicInode(snode.inode, elem) } s.Node, s.Generation = c.saveNode(s.Attr.Inode, n2) - if s.EntryValid == 0 { - s.EntryValid = entryValidTime - } - if s.AttrValid == 0 { - s.AttrValid = attrValidTime - } + return nil } // DataHandle returns a read-only Handle that satisfies reads diff --git a/Godeps/_workspace/src/bazil.org/fuse/fs/serve_test.go b/Godeps/_workspace/src/bazil.org/fuse/fs/serve_test.go index e6407df6d..b15f90698 100644 --- a/Godeps/_workspace/src/bazil.org/fuse/fs/serve_test.go +++ b/Godeps/_workspace/src/bazil.org/fuse/fs/serve_test.go @@ -10,6 +10,7 @@ import ( "os/exec" "runtime" "strings" + "sync" "syscall" "testing" "time" @@ -42,12 +43,18 @@ type symlink struct { target string } -func (f symlink) Attr() fuse.Attr { return fuse.Attr{Mode: os.ModeSymlink | 0666} } +func (f symlink) Attr(ctx context.Context, a *fuse.Attr) error { + a.Mode = os.ModeSymlink | 0666 + return nil +} // fifo can be embedded in a struct to make it look like a named pipe. type fifo struct{} -func (f fifo) Attr() fuse.Attr { return fuse.Attr{Mode: os.ModeNamedPipe | 0666} } +func (f fifo) Attr(ctx context.Context, a *fuse.Attr) error { + a.Mode = os.ModeNamedPipe | 0666 + return nil +} type badRootFS struct{} @@ -79,14 +86,59 @@ func TestRootErr(t *testing.T) { } } +type testPanic struct{} + +type panicSentinel struct{} + +var _ error = panicSentinel{} + +func (panicSentinel) Error() string { return "just a test" } + +var _ fuse.ErrorNumber = panicSentinel{} + +func (panicSentinel) Errno() fuse.Errno { + return fuse.Errno(syscall.ENAMETOOLONG) +} + +func (f testPanic) Root() (fs.Node, error) { + return f, nil +} + +func (f testPanic) Attr(ctx context.Context, a *fuse.Attr) error { + a.Inode = 1 + a.Mode = os.ModeDir | 0777 + return nil +} + +func (f testPanic) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error { + panic(panicSentinel{}) +} + +func TestPanic(t *testing.T) { + t.Parallel() + mnt, err := fstestutil.MountedT(t, testPanic{}) + if err != nil { + t.Fatal(err) + } + defer mnt.Close() + + var st syscall.Statfs_t + err = syscall.Statfs(mnt.Dir, &st) + if g, e := err, syscall.ENAMETOOLONG; g != e { + t.Fatalf("wrong error from panicking handler: %v != %v", g, e) + } +} + type testStatFS struct{} func (f testStatFS) Root() (fs.Node, error) { return f, nil } -func (f testStatFS) Attr() fuse.Attr { - return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0777} +func (f testStatFS) Attr(ctx context.Context, a *fuse.Attr) error { + a.Inode = 1 + a.Mode = os.ModeDir | 0777 + return nil } func (f testStatFS) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error { @@ -148,8 +200,10 @@ func (f root) Root() (fs.Node, error) { return f, nil } -func (root) Attr() fuse.Attr { - return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0555} +func (root) Attr(ctx context.Context, a *fuse.Attr) error { + a.Inode = 1 + a.Mode = os.ModeDir | 0555 + return nil } func TestStatRoot(t *testing.T) { @@ -196,11 +250,10 @@ type readAll struct { const hi = "hello, world" -func (readAll) Attr() fuse.Attr { - return fuse.Attr{ - Mode: 0666, - Size: uint64(len(hi)), - } +func (readAll) Attr(ctx context.Context, a *fuse.Attr) error { + a.Mode = 0666 + a.Size = uint64(len(hi)) + return nil } func (readAll) ReadAll(ctx context.Context) ([]byte, error) { @@ -234,11 +287,10 @@ type readWithHandleRead struct { fstestutil.File } -func (readWithHandleRead) Attr() fuse.Attr { - return fuse.Attr{ - Mode: 0666, - Size: uint64(len(hi)), - } +func (readWithHandleRead) Attr(ctx context.Context, a *fuse.Attr) error { + a.Mode = 0666 + a.Size = uint64(len(hi)) + return nil } func (readWithHandleRead) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { @@ -772,11 +824,10 @@ type dataHandleTest struct { fstestutil.File } -func (dataHandleTest) Attr() fuse.Attr { - return fuse.Attr{ - Mode: 0666, - Size: uint64(len(hi)), - } +func (dataHandleTest) Attr(ctx context.Context, a *fuse.Attr) error { + a.Mode = 0666 + a.Size = uint64(len(hi)) + return nil } func (dataHandleTest) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) { @@ -811,11 +862,10 @@ type interrupt struct { hanging chan struct{} } -func (interrupt) Attr() fuse.Attr { - return fuse.Attr{ - Mode: 0666, - Size: 1, - } +func (interrupt) Attr(ctx context.Context, a *fuse.Attr) error { + a.Mode = 0666 + a.Size = 1 + return nil } func (it *interrupt) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { @@ -1609,22 +1659,38 @@ func TestCustomErrno(t *testing.T) { // Test Mmap writing type inMemoryFile struct { + mu sync.Mutex data []byte } -func (f *inMemoryFile) Attr() fuse.Attr { - return fuse.Attr{ - Mode: 0666, - Size: uint64(len(f.data)), - } +func (f *inMemoryFile) bytes() []byte { + f.mu.Lock() + defer f.mu.Unlock() + + return f.data +} + +func (f *inMemoryFile) Attr(ctx context.Context, a *fuse.Attr) error { + f.mu.Lock() + defer f.mu.Unlock() + + a.Mode = 0666 + a.Size = uint64(len(f.data)) + return nil } func (f *inMemoryFile) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { + f.mu.Lock() + defer f.mu.Unlock() + fuseutil.HandleRead(req, resp, f.data) return nil } func (f *inMemoryFile) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error { + f.mu.Lock() + defer f.mu.Unlock() + resp.Size = copy(f.data[req.Offset:], req.Data) return nil } @@ -1709,7 +1775,7 @@ func TestMmap(t *testing.T) { t.Fatal(err) } - got := w.data + got := w.bytes() if g, e := len(got), mmapSize; g != e { t.Fatalf("bad write length: %d != %d", g, e) } @@ -1797,3 +1863,57 @@ func TestDirectWrite(t *testing.T) { t.Errorf("write = %q, want %q", got, hi) } } + +// Test Attr + +// attrUnlinked is a file that is unlinked (Nlink==0). +type attrUnlinked struct { + fstestutil.File +} + +var _ fs.Node = attrUnlinked{} + +func (f attrUnlinked) Attr(ctx context.Context, a *fuse.Attr) error { + if err := f.File.Attr(ctx, a); err != nil { + return err + } + a.Nlink = 0 + return nil +} + +func TestAttrUnlinked(t *testing.T) { + t.Parallel() + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": attrUnlinked{}}}) + + fi, err := os.Stat(mnt.Dir + "/child") + if err != nil { + t.Fatalf("Stat failed with %v", err) + } + switch stat := fi.Sys().(type) { + case *syscall.Stat_t: + if stat.Nlink != 0 { + t.Errorf("wrong link count: %v", stat.Nlink) + } + } +} + +// Test behavior when Attr method fails + +type attrBad struct { +} + +var _ fs.Node = attrBad{} + +func (attrBad) Attr(ctx context.Context, attr *fuse.Attr) error { + return fuse.Errno(syscall.ENAMETOOLONG) +} + +func TestAttrBad(t *testing.T) { + t.Parallel() + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": attrBad{}}}) + + _, err = os.Stat(mnt.Dir + "/child") + if nerr, ok := err.(*os.PathError); !ok || nerr.Err != syscall.ENAMETOOLONG { + t.Fatalf("wrong error: %v", err) + } +} diff --git a/Godeps/_workspace/src/bazil.org/fuse/fs/tree.go b/Godeps/_workspace/src/bazil.org/fuse/fs/tree.go index 3d35559aa..fc47ee321 100644 --- a/Godeps/_workspace/src/bazil.org/fuse/fs/tree.go +++ b/Godeps/_workspace/src/bazil.org/fuse/fs/tree.go @@ -77,8 +77,9 @@ func (t *tree) add(name string, n Node) { t.dir = append(t.dir, treeDir{name, n}) } -func (t *tree) Attr() fuse.Attr { - return fuse.Attr{Mode: os.ModeDir | 0555} +func (t *tree) Attr(ctx context.Context, a *fuse.Attr) error { + a.Mode = os.ModeDir | 0555 + return nil } func (t *tree) Lookup(ctx context.Context, name string) (Node, error) { diff --git a/Godeps/_workspace/src/bazil.org/fuse/fuse.go b/Godeps/_workspace/src/bazil.org/fuse/fuse.go index a642309b9..794ad91a2 100644 --- a/Godeps/_workspace/src/bazil.org/fuse/fuse.go +++ b/Godeps/_workspace/src/bazil.org/fuse/fuse.go @@ -1,4 +1,5 @@ // See the file LICENSE for copyright and licensing information. + // Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c, // which carries this notice: // @@ -45,7 +46,7 @@ // The required and optional methods for the FS, Node, and Handle interfaces // have the general form // -// Op(ctx context.Context, req *OpRequest, resp *OpResponse) Error +// Op(ctx context.Context, req *OpRequest, resp *OpResponse) error // // where Op is the name of a FUSE operation. Op reads request // parameters from req and writes results to resp. An operation whose @@ -67,7 +68,7 @@ // can implement ErrorNumber to control the errno returned. Without // ErrorNumber, a generic errno (EIO) is returned. // -// Errors messages will be visible in the debug log as part of the +// Error messages will be visible in the debug log as part of the // response. // // Interrupted Operations @@ -1064,6 +1065,8 @@ func (r *AccessRequest) Respond() { // An Attr is the metadata for a single file or directory. type Attr struct { + Valid time.Duration // how long Attr can be cached + Inode uint64 // inode number Size uint64 // size in bytes Blocks uint64 // size in blocks @@ -1143,8 +1146,8 @@ func (r *GetattrRequest) String() string { 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), + AttrValid: uint64(resp.Attr.Valid / time.Second), + AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond), Attr: resp.Attr.attr(), } r.respond(&out.outHeader, unsafe.Sizeof(*out)) @@ -1152,8 +1155,7 @@ func (r *GetattrRequest) Respond(resp *GetattrResponse) { // A GetattrResponse is the response to a GetattrRequest. type GetattrResponse struct { - AttrValid time.Duration // how long Attr can be cached - Attr Attr // file attributes + Attr Attr // file attributes } func (r *GetattrResponse) String() string { @@ -1333,8 +1335,8 @@ func (r *LookupRequest) Respond(resp *LookupResponse) { 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), + AttrValid: uint64(resp.Attr.Valid / time.Second), + AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond), Attr: resp.Attr.attr(), } r.respond(&out.outHeader, unsafe.Sizeof(*out)) @@ -1345,7 +1347,6 @@ type LookupResponse struct { Node NodeID Generation uint64 EntryValid time.Duration - AttrValid time.Duration Attr Attr } @@ -1409,8 +1410,8 @@ func (r *CreateRequest) Respond(resp *CreateResponse) { 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), + AttrValid: uint64(resp.Attr.Valid / time.Second), + AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond), Attr: resp.Attr.attr(), Fh: uint64(resp.Handle), @@ -1451,8 +1452,8 @@ func (r *MkdirRequest) Respond(resp *MkdirResponse) { 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), + AttrValid: uint64(resp.Attr.Valid / time.Second), + AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond), Attr: resp.Attr.attr(), } r.respond(&out.outHeader, unsafe.Sizeof(*out)) @@ -1776,8 +1777,8 @@ func (r *SetattrRequest) String() string { 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), + AttrValid: uint64(resp.Attr.Valid / time.Second), + AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond), Attr: resp.Attr.attr(), } r.respond(&out.outHeader, unsafe.Sizeof(*out)) @@ -1785,8 +1786,7 @@ func (r *SetattrRequest) Respond(resp *SetattrResponse) { // A SetattrResponse is the response to a SetattrRequest. type SetattrResponse struct { - AttrValid time.Duration // how long Attr can be cached - Attr Attr // file attributes + Attr Attr // file attributes } func (r *SetattrResponse) String() string { @@ -1855,8 +1855,8 @@ func (r *SymlinkRequest) Respond(resp *SymlinkResponse) { 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), + AttrValid: uint64(resp.Attr.Valid / time.Second), + AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond), Attr: resp.Attr.attr(), } r.respond(&out.outHeader, unsafe.Sizeof(*out)) @@ -1903,8 +1903,8 @@ func (r *LinkRequest) Respond(resp *LookupResponse) { 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), + AttrValid: uint64(resp.Attr.Valid / time.Second), + AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond), Attr: resp.Attr.attr(), } r.respond(&out.outHeader, unsafe.Sizeof(*out)) @@ -1948,8 +1948,8 @@ func (r *MknodRequest) Respond(resp *LookupResponse) { 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), + AttrValid: uint64(resp.Attr.Valid / time.Second), + AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond), Attr: resp.Attr.attr(), } r.respond(&out.outHeader, unsafe.Sizeof(*out)) diff --git a/Godeps/_workspace/src/bazil.org/fuse/fuse_kernel.go b/Godeps/_workspace/src/bazil.org/fuse/fuse_kernel.go index 5fba53dbf..b04c89a9e 100644 --- a/Godeps/_workspace/src/bazil.org/fuse/fuse_kernel.go +++ b/Godeps/_workspace/src/bazil.org/fuse/fuse_kernel.go @@ -1,6 +1,6 @@ // See the file LICENSE for copyright and licensing information. -// Derived from FUSE's fuse_kernel.h +// Derived from FUSE's fuse_kernel.h, which carries this notice: /* This file defines the kernel interface of FUSE Copyright (C) 2001-2007 Miklos Szeredi diff --git a/Godeps/_workspace/src/bazil.org/fuse/hellofs/hello.go b/Godeps/_workspace/src/bazil.org/fuse/hellofs/hello.go index 874e45884..45b644e4d 100644 --- a/Godeps/_workspace/src/bazil.org/fuse/hellofs/hello.go +++ b/Godeps/_workspace/src/bazil.org/fuse/hellofs/hello.go @@ -63,8 +63,10 @@ func (FS) Root() (fs.Node, error) { // Dir implements both Node and Handle for the root directory. type Dir struct{} -func (Dir) Attr() fuse.Attr { - return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0555} +func (Dir) Attr(ctx context.Context, a *fuse.Attr) error { + a.Inode = 1 + a.Mode = os.ModeDir | 0555 + return nil } func (Dir) Lookup(ctx context.Context, name string) (fs.Node, error) { @@ -87,8 +89,11 @@ type File struct{} const greeting = "hello, world\n" -func (File) Attr() fuse.Attr { - return fuse.Attr{Inode: 2, Mode: 0444, Size: uint64(len(greeting))} +func (File) Attr(ctx context.Context, a *fuse.Attr) error { + a.Inode = 2 + a.Mode = 0444 + a.Size = uint64(len(greeting)) + return nil } func (File) ReadAll(ctx context.Context) ([]byte, error) { diff --git a/Godeps/_workspace/src/bazil.org/fuse/mount_linux.go b/Godeps/_workspace/src/bazil.org/fuse/mount_linux.go index 0748c0a5d..36b81dbeb 100644 --- a/Godeps/_workspace/src/bazil.org/fuse/mount_linux.go +++ b/Godeps/_workspace/src/bazil.org/fuse/mount_linux.go @@ -1,13 +1,37 @@ package fuse import ( + "bufio" "fmt" + "io" + "log" "net" "os" "os/exec" + "sync" "syscall" ) +func lineLogger(wg *sync.WaitGroup, prefix string, r io.ReadCloser) { + defer wg.Done() + + scanner := bufio.NewScanner(r) + for scanner.Scan() { + switch line := scanner.Text(); line { + case `fusermount: failed to open /etc/fuse.conf: Permission denied`: + // Silence this particular message, it occurs way too + // commonly and isn't very relevant to whether the mount + // succeeds or not. + continue + default: + log.Printf("%s: %s", prefix, line) + } + } + if err := scanner.Err(); err != nil { + log.Printf("%s, error reading: %v", prefix, err) + } +} + func mount(dir string, conf *MountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) { // linux mount is never delayed close(ready) @@ -31,9 +55,25 @@ func mount(dir string, conf *MountConfig, ready chan<- struct{}, errp *error) (f defer writeFile.Close() cmd.ExtraFiles = []*os.File{writeFile} - out, err := cmd.CombinedOutput() - if len(out) > 0 || err != nil { - return nil, fmt.Errorf("fusermount: %q, %v", out, err) + var wg sync.WaitGroup + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, fmt.Errorf("setting up fusermount stderr: %v", err) + } + stderr, err := cmd.StderrPipe() + if err != nil { + return nil, fmt.Errorf("setting up fusermount stderr: %v", err) + } + + if err := cmd.Start(); err != nil { + return nil, fmt.Errorf("fusermount: %v", err) + } + wg.Add(2) + go lineLogger(&wg, "mount helper output", stdout) + go lineLogger(&wg, "mount helper error", stderr) + wg.Wait() + if err := cmd.Wait(); err != nil { + return nil, fmt.Errorf("fusermount: %v", err) } readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads") diff --git a/Godeps/_workspace/src/bazil.org/fuse/options_test.go b/Godeps/_workspace/src/bazil.org/fuse/options_test.go index 5387f34a2..a28dfa067 100644 --- a/Godeps/_workspace/src/bazil.org/fuse/options_test.go +++ b/Godeps/_workspace/src/bazil.org/fuse/options_test.go @@ -155,7 +155,10 @@ func TestMountOptionAllowRootThenAllowOther(t *testing.T) { type unwritableFile struct{} -func (f unwritableFile) Attr() fuse.Attr { return fuse.Attr{Mode: 0000} } +func (f unwritableFile) Attr(ctx context.Context, a *fuse.Attr) error { + a.Mode = 0000 + return nil +} func TestMountOptionDefaultPermissions(t *testing.T) { if runtime.GOOS == "freebsd" { diff --git a/fuse/ipns/ipns_unix.go b/fuse/ipns/ipns_unix.go index 6cf39c9f8..8eb4bc134 100644 --- a/fuse/ipns/ipns_unix.go +++ b/fuse/ipns/ipns_unix.go @@ -6,6 +6,7 @@ package ipns import ( "errors" + "fmt" "os" "strings" @@ -106,9 +107,10 @@ func CreateRoot(ipfs *core.IpfsNode, keys []ci.PrivKey, ipfspath, ipnspath strin } // Attr returns file attributes. -func (*Root) Attr() fuse.Attr { +func (*Root) Attr(ctx context.Context, a *fuse.Attr) error { log.Debug("Root Attr") - return fuse.Attr{Mode: os.ModeDir | 0111} // -rw+x + *a = fuse.Attr{Mode: os.ModeDir | 0111} // -rw+x + return nil } // Lookup performs a lookup under this node. @@ -215,29 +217,31 @@ type File struct { } // Attr returns the attributes of a given node. -func (d *Directory) Attr() fuse.Attr { +func (d *Directory) Attr(ctx context.Context, a *fuse.Attr) error { log.Debug("Directory Attr") - return fuse.Attr{ + *a = fuse.Attr{ Mode: os.ModeDir | 0555, Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid()), } + return nil } // Attr returns the attributes of a given node. -func (fi *File) Attr() fuse.Attr { +func (fi *File) Attr(ctx context.Context, a *fuse.Attr) error { log.Debug("File Attr") size, err := fi.fi.Size() if err != nil { // In this case, the dag node in question may not be unixfs - log.Critical("Failed to get file size: %s", err) + return fmt.Errorf("fuse/ipns: failed to get file.Size(): %s", err) } - return fuse.Attr{ + *a = fuse.Attr{ Mode: os.FileMode(0666), Size: uint64(size), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid()), } + return nil } // Lookup performs a lookup under this node. diff --git a/fuse/ipns/link_unix.go b/fuse/ipns/link_unix.go index 9198941da..a8414a365 100644 --- a/fuse/ipns/link_unix.go +++ b/fuse/ipns/link_unix.go @@ -14,11 +14,12 @@ type Link struct { Target string } -func (l *Link) Attr() fuse.Attr { +func (l *Link) Attr(ctx context.Context, a *fuse.Attr) error { log.Debug("Link attr.") - return fuse.Attr{ + *a = fuse.Attr{ Mode: os.ModeSymlink | 0555, } + return nil } func (l *Link) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) { diff --git a/fuse/readonly/readonly_unix.go b/fuse/readonly/readonly_unix.go index 8df5d06c9..fe8321a8a 100644 --- a/fuse/readonly/readonly_unix.go +++ b/fuse/readonly/readonly_unix.go @@ -4,6 +4,7 @@ package readonly import ( + "fmt" "io" "os" @@ -43,8 +44,9 @@ type Root struct { } // Attr returns file attributes. -func (*Root) Attr() fuse.Attr { - return fuse.Attr{Mode: os.ModeDir | 0111} // -rw+x +func (*Root) Attr(ctx context.Context, a *fuse.Attr) error { + *a = fuse.Attr{Mode: os.ModeDir | 0111} // -rw+x + return nil } // Lookup performs a lookup under this node. @@ -85,21 +87,23 @@ func (s *Node) loadData() error { } // Attr returns the attributes of a given node. -func (s *Node) Attr() fuse.Attr { +func (s *Node) Attr(ctx context.Context, a *fuse.Attr) error { log.Debug("Node attr.") if s.cached == nil { - s.loadData() + if err := s.loadData(); err != nil { + return fmt.Errorf("readonly: loadData() failed: %s", err) + } } switch s.cached.GetType() { case ftpb.Data_Directory: - return fuse.Attr{ + *a = fuse.Attr{ Mode: os.ModeDir | 0555, Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid()), } case ftpb.Data_File: size := s.cached.GetFilesize() - return fuse.Attr{ + *a = fuse.Attr{ Mode: 0444, Size: uint64(size), Blocks: uint64(len(s.Nd.Links)), @@ -107,7 +111,7 @@ func (s *Node) Attr() fuse.Attr { Gid: uint32(os.Getgid()), } case ftpb.Data_Raw: - return fuse.Attr{ + *a = fuse.Attr{ Mode: 0444, Size: uint64(len(s.cached.GetData())), Blocks: uint64(len(s.Nd.Links)), @@ -116,9 +120,9 @@ func (s *Node) Attr() fuse.Attr { } default: - log.Debug("Invalid data type.") - return fuse.Attr{} + return fmt.Errorf("Invalid data type - %s", s.cached.GetType()) } + return nil } // Lookup performs a lookup under this node.