1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-06-29 01:12:24 +08:00

godeps: update bazil.org/fuse

fuse: Attr() now has a Context parameter and error return value

~GOPATH/src/bazil.org/fuse:master$ git shortlog 48c34fb7780b88aca1696bf865508f6703aa47f1..e4fcc9a2c7567d1c42861deebeb483315d222262
Tommi Virtanen (8):
      Remove dead code
      Make saveLookup take Context, return error
      Make serveNode.attr take Context, return error
      Make nodeAttr take Context, return error
      API change: Move attribute validity time inside Attr
      Set attribute validity default time in one place
      API change: Attr method takes Context, returns error
      Set LookupResponse validity times up front, instead of after the handler
This commit is contained in:
Henry
2015-05-28 22:09:26 +02:00
parent 97d75891b0
commit e9074beb6d
16 changed files with 407 additions and 139 deletions

2
Godeps/Godeps.json generated
View File

@ -7,7 +7,7 @@
"Deps": [ "Deps": [
{ {
"ImportPath": "bazil.org/fuse", "ImportPath": "bazil.org/fuse",
"Rev": "79d103f5608724e3ccee0a10daccc5e4aff03591" "Rev": "e4fcc9a2c7567d1c42861deebeb483315d222262"
}, },
{ {
"ImportPath": "code.google.com/p/go-uuid/uuid", "ImportPath": "code.google.com/p/go-uuid/uuid",

View File

@ -1,4 +1,4 @@
Copyright (c) 2013, 2014 Tommi Virtanen. Copyright (c) 2013-2015 Tommi Virtanen.
Copyright (c) 2009, 2011, 2012 The Go Authors. Copyright (c) 2009, 2011, 2012 The Go Authors.
All rights reserved. All rights reserved.

View File

@ -15,7 +15,7 @@ Heres how to get going:
Website: http://bazil.org/fuse/ 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 API docs: http://godoc.org/bazil.org/fuse

View File

@ -43,8 +43,10 @@ var _ = fs.NodeStringLookuper(benchDir{})
var _ = fs.Handle(benchDir{}) var _ = fs.Handle(benchDir{})
var _ = fs.HandleReadDirAller(benchDir{}) var _ = fs.HandleReadDirAller(benchDir{})
func (benchDir) Attr() fuse.Attr { func (benchDir) Attr(ctx context.Context, a *fuse.Attr) error {
return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0555} a.Inode = 1
a.Mode = os.ModeDir | 0555
return nil
} }
func (d benchDir) Lookup(ctx context.Context, name string) (fs.Node, error) { 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.HandleReader(benchFile{})
var _ = fs.HandleWriter(benchFile{}) var _ = fs.HandleWriter(benchFile{})
func (benchFile) Attr() fuse.Attr { func (benchFile) Attr(ctx context.Context, a *fuse.Attr) error {
return fuse.Attr{Inode: 2, Mode: 0644, Size: 9999999999999999} 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) { func (f benchFile) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {

View File

@ -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. // File can be embedded in a struct to make it look like a file.
type File struct{} 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. // Dir can be embedded in a struct to make it look like a directory.
type Dir struct{} 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. // ChildMap is a directory with child nodes looked up from a map.
type ChildMap map[string]fs.Node type ChildMap map[string]fs.Node
@ -35,8 +41,9 @@ type ChildMap map[string]fs.Node
var _ = fs.Node(ChildMap{}) var _ = fs.Node(ChildMap{})
var _ = fs.NodeStringLookuper(ChildMap{}) var _ = fs.NodeStringLookuper(ChildMap{})
func (f ChildMap) Attr() fuse.Attr { func (f ChildMap) Attr(ctx context.Context, a *fuse.Attr) error {
return fuse.Attr{Mode: os.ModeDir | 0777} a.Mode = os.ModeDir | 0777
return nil
} }
func (f ChildMap) Lookup(ctx context.Context, name string) (fs.Node, error) { func (f ChildMap) Lookup(ctx context.Context, name string) (fs.Node, error) {

View File

@ -7,7 +7,9 @@ import (
"fmt" "fmt"
"hash/fnv" "hash/fnv"
"io" "io"
"log"
"reflect" "reflect"
"runtime"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -55,9 +57,6 @@ type FSDestroyer interface {
// Linux only sends this request for block device backed (fuseblk) // Linux only sends this request for block device backed (fuseblk)
// filesystems, to allow them to flush writes to disk before the // filesystems, to allow them to flush writes to disk before the
// unmount completes. // unmount completes.
//
// On normal FUSE filesystems, use Forget of the root Node to
// do actions at unmount time.
Destroy() Destroy()
} }
@ -87,7 +86,8 @@ type FSInodeGenerator interface {
// Other FUSE requests can be handled by implementing methods from the // Other FUSE requests can be handled by implementing methods from the
// Node* interfaces, for example NodeOpener. // Node* interfaces, for example NodeOpener.
type Node interface { 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 { type NodeGetattrer interface {
@ -192,6 +192,11 @@ type NodeCreater interface {
} }
type NodeForgetter 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() Forget()
} }
@ -236,24 +241,17 @@ type NodeRemovexattrer interface {
var startTime = time.Now() var startTime = time.Now()
func nodeAttr(n Node) (attr fuse.Attr) { func nodeAttr(ctx context.Context, n Node, attr *fuse.Attr) error {
attr = n.Attr() attr.Valid = attrValidTime
if attr.Nlink == 0 {
attr.Nlink = 1 attr.Nlink = 1
}
if attr.Atime.IsZero() {
attr.Atime = startTime attr.Atime = startTime
}
if attr.Mtime.IsZero() {
attr.Mtime = startTime attr.Mtime = startTime
}
if attr.Ctime.IsZero() {
attr.Ctime = startTime attr.Ctime = startTime
}
if attr.Crtime.IsZero() {
attr.Crtime = startTime attr.Crtime = startTime
if err := n.Attr(ctx, attr); err != nil {
return err
} }
return return nil
} }
// A Handle is the interface required of an opened file or directory. // 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. // See fuse.Debug for the rules that log functions must follow.
Debug func(msg interface{}) 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 // Serve serves the FUSE connection by making calls to the methods
// of fs and the Nodes and Handles it makes available. It returns only // of fs and the Nodes and Handles it makes available. It returns only
// when the connection has been closed or an unexpected error occurs. // when the connection has been closed or an unexpected error occurs.
func (s *Server) Serve(c *fuse.Conn) error { func (s *Server) Serve(c *fuse.Conn) error {
defer s.wg.Wait() // Wait for worker goroutines to complete before return
sc := serveConn{ sc := serveConn{
fs: s.FS, fs: s.FS,
debug: s.Debug, debug: s.Debug,
@ -358,7 +361,11 @@ func (s *Server) Serve(c *fuse.Conn) error {
return err return err
} }
go sc.serve(req) s.wg.Add(1)
go func() {
defer s.wg.Done()
sc.serve(req)
}()
} }
return nil return nil
} }
@ -398,12 +405,12 @@ type serveNode struct {
refs uint64 refs uint64
} }
func (sn *serveNode) attr() (attr fuse.Attr) { func (sn *serveNode) attr(ctx context.Context, attr *fuse.Attr) error {
attr = nodeAttr(sn.node) err := nodeAttr(ctx, sn.node, attr)
if attr.Inode == 0 { if attr.Inode == 0 {
attr.Inode = sn.inode attr.Inode = sn.inode
} }
return return err
} }
type serveHandle struct { 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) 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) { func (c *serveConn) serve(r fuse.Request) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@ -725,6 +756,22 @@ func (c *serveConn) serve(r fuse.Request) {
c.meta.Unlock() 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) { switch r := r.(type) {
default: default:
// Note: To FUSE, ENOSYS means "this server never implements this request." // Note: To FUSE, ENOSYS means "this server never implements this request."
@ -771,8 +818,11 @@ func (c *serveConn) serve(r fuse.Request) {
break break
} }
} else { } else {
s.AttrValid = attrValidTime if err := snode.attr(ctx, &s.Attr); err != nil {
s.Attr = snode.attr() done(err)
r.RespondError(err)
break
}
} }
done(s) done(s)
r.Respond(s) r.Respond(s)
@ -790,15 +840,17 @@ func (c *serveConn) serve(r fuse.Request) {
break break
} }
if s.AttrValid == 0 { if err := snode.attr(ctx, &s.Attr); err != nil {
s.AttrValid = attrValidTime done(err)
r.RespondError(err)
break
} }
s.Attr = snode.attr()
done(s) done(s)
r.Respond(s) r.Respond(s)
case *fuse.SymlinkRequest: case *fuse.SymlinkRequest:
s := &fuse.SymlinkResponse{} s := &fuse.SymlinkResponse{}
initLookupResponse(&s.LookupResponse)
n, ok := node.(NodeSymlinker) n, ok := node.(NodeSymlinker)
if !ok { if !ok {
done(fuse.EIO) // XXX or EPERM like Mkdir? done(fuse.EIO) // XXX or EPERM like Mkdir?
@ -811,7 +863,11 @@ func (c *serveConn) serve(r fuse.Request) {
r.RespondError(err) r.RespondError(err)
break 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) done(s)
r.Respond(s) r.Respond(s)
@ -860,7 +916,12 @@ func (c *serveConn) serve(r fuse.Request) {
break break
} }
s := &fuse.LookupResponse{} 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) done(s)
r.Respond(s) r.Respond(s)
@ -895,6 +956,7 @@ func (c *serveConn) serve(r fuse.Request) {
var n2 Node var n2 Node
var err error var err error
s := &fuse.LookupResponse{} s := &fuse.LookupResponse{}
initLookupResponse(s)
if n, ok := node.(NodeStringLookuper); ok { if n, ok := node.(NodeStringLookuper); ok {
n2, err = n.Lookup(ctx, r.Name) n2, err = n.Lookup(ctx, r.Name)
} else if n, ok := node.(NodeRequestLookuper); ok { } else if n, ok := node.(NodeRequestLookuper); ok {
@ -909,12 +971,17 @@ func (c *serveConn) serve(r fuse.Request) {
r.RespondError(err) r.RespondError(err)
break 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) done(s)
r.Respond(s) r.Respond(s)
case *fuse.MkdirRequest: case *fuse.MkdirRequest:
s := &fuse.MkdirResponse{} s := &fuse.MkdirResponse{}
initLookupResponse(&s.LookupResponse)
n, ok := node.(NodeMkdirer) n, ok := node.(NodeMkdirer)
if !ok { if !ok {
done(fuse.EPERM) done(fuse.EPERM)
@ -927,7 +994,11 @@ func (c *serveConn) serve(r fuse.Request) {
r.RespondError(err) r.RespondError(err)
break 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) done(s)
r.Respond(s) r.Respond(s)
@ -958,13 +1029,18 @@ func (c *serveConn) serve(r fuse.Request) {
break break
} }
s := &fuse.CreateResponse{OpenResponse: fuse.OpenResponse{}} s := &fuse.CreateResponse{OpenResponse: fuse.OpenResponse{}}
initLookupResponse(&s.LookupResponse)
n2, h2, err := n.Create(ctx, r, s) n2, h2, err := n.Create(ctx, r, s)
if err != nil { if err != nil {
done(err) done(err)
r.RespondError(err) r.RespondError(err)
break 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) s.Handle = c.saveHandle(h2, hdr.Node)
done(s) done(s)
r.Respond(s) r.Respond(s)
@ -1240,7 +1316,12 @@ func (c *serveConn) serve(r fuse.Request) {
break break
} }
s := &fuse.LookupResponse{} 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) done(s)
r.Respond(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) { func (c *serveConn) saveLookup(ctx context.Context, s *fuse.LookupResponse, snode *serveNode, elem string, n2 Node) error {
s.Attr = nodeAttr(n2) if err := nodeAttr(ctx, n2, &s.Attr); err != nil {
return err
}
if s.Attr.Inode == 0 { if s.Attr.Inode == 0 {
s.Attr.Inode = c.dynamicInode(snode.inode, elem) s.Attr.Inode = c.dynamicInode(snode.inode, elem)
} }
s.Node, s.Generation = c.saveNode(s.Attr.Inode, n2) s.Node, s.Generation = c.saveNode(s.Attr.Inode, n2)
if s.EntryValid == 0 { return nil
s.EntryValid = entryValidTime
}
if s.AttrValid == 0 {
s.AttrValid = attrValidTime
}
} }
// DataHandle returns a read-only Handle that satisfies reads // DataHandle returns a read-only Handle that satisfies reads

View File

@ -10,6 +10,7 @@ import (
"os/exec" "os/exec"
"runtime" "runtime"
"strings" "strings"
"sync"
"syscall" "syscall"
"testing" "testing"
"time" "time"
@ -42,12 +43,18 @@ type symlink struct {
target string 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. // fifo can be embedded in a struct to make it look like a named pipe.
type fifo struct{} 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{} 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{} type testStatFS struct{}
func (f testStatFS) Root() (fs.Node, error) { func (f testStatFS) Root() (fs.Node, error) {
return f, nil return f, nil
} }
func (f testStatFS) Attr() fuse.Attr { func (f testStatFS) Attr(ctx context.Context, a *fuse.Attr) error {
return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0777} a.Inode = 1
a.Mode = os.ModeDir | 0777
return nil
} }
func (f testStatFS) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error { 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 return f, nil
} }
func (root) Attr() fuse.Attr { func (root) Attr(ctx context.Context, a *fuse.Attr) error {
return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0555} a.Inode = 1
a.Mode = os.ModeDir | 0555
return nil
} }
func TestStatRoot(t *testing.T) { func TestStatRoot(t *testing.T) {
@ -196,11 +250,10 @@ type readAll struct {
const hi = "hello, world" const hi = "hello, world"
func (readAll) Attr() fuse.Attr { func (readAll) Attr(ctx context.Context, a *fuse.Attr) error {
return fuse.Attr{ a.Mode = 0666
Mode: 0666, a.Size = uint64(len(hi))
Size: uint64(len(hi)), return nil
}
} }
func (readAll) ReadAll(ctx context.Context) ([]byte, error) { func (readAll) ReadAll(ctx context.Context) ([]byte, error) {
@ -234,11 +287,10 @@ type readWithHandleRead struct {
fstestutil.File fstestutil.File
} }
func (readWithHandleRead) Attr() fuse.Attr { func (readWithHandleRead) Attr(ctx context.Context, a *fuse.Attr) error {
return fuse.Attr{ a.Mode = 0666
Mode: 0666, a.Size = uint64(len(hi))
Size: uint64(len(hi)), return nil
}
} }
func (readWithHandleRead) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { func (readWithHandleRead) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
@ -772,11 +824,10 @@ type dataHandleTest struct {
fstestutil.File fstestutil.File
} }
func (dataHandleTest) Attr() fuse.Attr { func (dataHandleTest) Attr(ctx context.Context, a *fuse.Attr) error {
return fuse.Attr{ a.Mode = 0666
Mode: 0666, a.Size = uint64(len(hi))
Size: uint64(len(hi)), return nil
}
} }
func (dataHandleTest) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) { 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{} hanging chan struct{}
} }
func (interrupt) Attr() fuse.Attr { func (interrupt) Attr(ctx context.Context, a *fuse.Attr) error {
return fuse.Attr{ a.Mode = 0666
Mode: 0666, a.Size = 1
Size: 1, return nil
}
} }
func (it *interrupt) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { 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 // Test Mmap writing
type inMemoryFile struct { type inMemoryFile struct {
mu sync.Mutex
data []byte data []byte
} }
func (f *inMemoryFile) Attr() fuse.Attr { func (f *inMemoryFile) bytes() []byte {
return fuse.Attr{ f.mu.Lock()
Mode: 0666, defer f.mu.Unlock()
Size: uint64(len(f.data)),
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 { 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) fuseutil.HandleRead(req, resp, f.data)
return nil return nil
} }
func (f *inMemoryFile) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error { 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) resp.Size = copy(f.data[req.Offset:], req.Data)
return nil return nil
} }
@ -1709,7 +1775,7 @@ func TestMmap(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
got := w.data got := w.bytes()
if g, e := len(got), mmapSize; g != e { if g, e := len(got), mmapSize; g != e {
t.Fatalf("bad write length: %d != %d", 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) 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)
}
}

View File

@ -77,8 +77,9 @@ func (t *tree) add(name string, n Node) {
t.dir = append(t.dir, treeDir{name, n}) t.dir = append(t.dir, treeDir{name, n})
} }
func (t *tree) Attr() fuse.Attr { func (t *tree) Attr(ctx context.Context, a *fuse.Attr) error {
return fuse.Attr{Mode: os.ModeDir | 0555} a.Mode = os.ModeDir | 0555
return nil
} }
func (t *tree) Lookup(ctx context.Context, name string) (Node, error) { func (t *tree) Lookup(ctx context.Context, name string) (Node, error) {

View File

@ -1,4 +1,5 @@
// See the file LICENSE for copyright and licensing information. // See the file LICENSE for copyright and licensing information.
// Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c, // Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c,
// which carries this notice: // which carries this notice:
// //
@ -45,7 +46,7 @@
// The required and optional methods for the FS, Node, and Handle interfaces // The required and optional methods for the FS, Node, and Handle interfaces
// have the general form // 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 // where Op is the name of a FUSE operation. Op reads request
// parameters from req and writes results to resp. An operation whose // parameters from req and writes results to resp. An operation whose
@ -67,7 +68,7 @@
// can implement ErrorNumber to control the errno returned. Without // can implement ErrorNumber to control the errno returned. Without
// ErrorNumber, a generic errno (EIO) is returned. // 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. // response.
// //
// Interrupted Operations // Interrupted Operations
@ -1064,6 +1065,8 @@ func (r *AccessRequest) Respond() {
// An Attr is the metadata for a single file or directory. // An Attr is the metadata for a single file or directory.
type Attr struct { type Attr struct {
Valid time.Duration // how long Attr can be cached
Inode uint64 // inode number Inode uint64 // inode number
Size uint64 // size in bytes Size uint64 // size in bytes
Blocks uint64 // size in blocks Blocks uint64 // size in blocks
@ -1143,8 +1146,8 @@ func (r *GetattrRequest) String() string {
func (r *GetattrRequest) Respond(resp *GetattrResponse) { func (r *GetattrRequest) Respond(resp *GetattrResponse) {
out := &attrOut{ out := &attrOut{
outHeader: outHeader{Unique: uint64(r.ID)}, outHeader: outHeader{Unique: uint64(r.ID)},
AttrValid: uint64(resp.AttrValid / time.Second), AttrValid: uint64(resp.Attr.Valid / time.Second),
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond), AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(), Attr: resp.Attr.attr(),
} }
r.respond(&out.outHeader, unsafe.Sizeof(*out)) r.respond(&out.outHeader, unsafe.Sizeof(*out))
@ -1152,7 +1155,6 @@ func (r *GetattrRequest) Respond(resp *GetattrResponse) {
// A GetattrResponse is the response to a GetattrRequest. // A GetattrResponse is the response to a GetattrRequest.
type GetattrResponse struct { type GetattrResponse struct {
AttrValid time.Duration // how long Attr can be cached
Attr Attr // file attributes Attr Attr // file attributes
} }
@ -1333,8 +1335,8 @@ func (r *LookupRequest) Respond(resp *LookupResponse) {
Generation: resp.Generation, Generation: resp.Generation,
EntryValid: uint64(resp.EntryValid / time.Second), EntryValid: uint64(resp.EntryValid / time.Second),
EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond), EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
AttrValid: uint64(resp.AttrValid / time.Second), AttrValid: uint64(resp.Attr.Valid / time.Second),
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond), AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(), Attr: resp.Attr.attr(),
} }
r.respond(&out.outHeader, unsafe.Sizeof(*out)) r.respond(&out.outHeader, unsafe.Sizeof(*out))
@ -1345,7 +1347,6 @@ type LookupResponse struct {
Node NodeID Node NodeID
Generation uint64 Generation uint64
EntryValid time.Duration EntryValid time.Duration
AttrValid time.Duration
Attr Attr Attr Attr
} }
@ -1409,8 +1410,8 @@ func (r *CreateRequest) Respond(resp *CreateResponse) {
Generation: resp.Generation, Generation: resp.Generation,
EntryValid: uint64(resp.EntryValid / time.Second), EntryValid: uint64(resp.EntryValid / time.Second),
EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond), EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
AttrValid: uint64(resp.AttrValid / time.Second), AttrValid: uint64(resp.Attr.Valid / time.Second),
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond), AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(), Attr: resp.Attr.attr(),
Fh: uint64(resp.Handle), Fh: uint64(resp.Handle),
@ -1451,8 +1452,8 @@ func (r *MkdirRequest) Respond(resp *MkdirResponse) {
Generation: resp.Generation, Generation: resp.Generation,
EntryValid: uint64(resp.EntryValid / time.Second), EntryValid: uint64(resp.EntryValid / time.Second),
EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond), EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
AttrValid: uint64(resp.AttrValid / time.Second), AttrValid: uint64(resp.Attr.Valid / time.Second),
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond), AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(), Attr: resp.Attr.attr(),
} }
r.respond(&out.outHeader, unsafe.Sizeof(*out)) r.respond(&out.outHeader, unsafe.Sizeof(*out))
@ -1776,8 +1777,8 @@ func (r *SetattrRequest) String() string {
func (r *SetattrRequest) Respond(resp *SetattrResponse) { func (r *SetattrRequest) Respond(resp *SetattrResponse) {
out := &attrOut{ out := &attrOut{
outHeader: outHeader{Unique: uint64(r.ID)}, outHeader: outHeader{Unique: uint64(r.ID)},
AttrValid: uint64(resp.AttrValid / time.Second), AttrValid: uint64(resp.Attr.Valid / time.Second),
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond), AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(), Attr: resp.Attr.attr(),
} }
r.respond(&out.outHeader, unsafe.Sizeof(*out)) r.respond(&out.outHeader, unsafe.Sizeof(*out))
@ -1785,7 +1786,6 @@ func (r *SetattrRequest) Respond(resp *SetattrResponse) {
// A SetattrResponse is the response to a SetattrRequest. // A SetattrResponse is the response to a SetattrRequest.
type SetattrResponse struct { type SetattrResponse struct {
AttrValid time.Duration // how long Attr can be cached
Attr Attr // file attributes Attr Attr // file attributes
} }
@ -1855,8 +1855,8 @@ func (r *SymlinkRequest) Respond(resp *SymlinkResponse) {
Generation: resp.Generation, Generation: resp.Generation,
EntryValid: uint64(resp.EntryValid / time.Second), EntryValid: uint64(resp.EntryValid / time.Second),
EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond), EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
AttrValid: uint64(resp.AttrValid / time.Second), AttrValid: uint64(resp.Attr.Valid / time.Second),
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond), AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(), Attr: resp.Attr.attr(),
} }
r.respond(&out.outHeader, unsafe.Sizeof(*out)) r.respond(&out.outHeader, unsafe.Sizeof(*out))
@ -1903,8 +1903,8 @@ func (r *LinkRequest) Respond(resp *LookupResponse) {
Generation: resp.Generation, Generation: resp.Generation,
EntryValid: uint64(resp.EntryValid / time.Second), EntryValid: uint64(resp.EntryValid / time.Second),
EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond), EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
AttrValid: uint64(resp.AttrValid / time.Second), AttrValid: uint64(resp.Attr.Valid / time.Second),
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond), AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(), Attr: resp.Attr.attr(),
} }
r.respond(&out.outHeader, unsafe.Sizeof(*out)) r.respond(&out.outHeader, unsafe.Sizeof(*out))
@ -1948,8 +1948,8 @@ func (r *MknodRequest) Respond(resp *LookupResponse) {
Generation: resp.Generation, Generation: resp.Generation,
EntryValid: uint64(resp.EntryValid / time.Second), EntryValid: uint64(resp.EntryValid / time.Second),
EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond), EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
AttrValid: uint64(resp.AttrValid / time.Second), AttrValid: uint64(resp.Attr.Valid / time.Second),
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond), AttrValidNsec: uint32(resp.Attr.Valid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(), Attr: resp.Attr.attr(),
} }
r.respond(&out.outHeader, unsafe.Sizeof(*out)) r.respond(&out.outHeader, unsafe.Sizeof(*out))

View File

@ -1,6 +1,6 @@
// See the file LICENSE for copyright and licensing information. // 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 This file defines the kernel interface of FUSE
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>

View File

@ -63,8 +63,10 @@ func (FS) Root() (fs.Node, error) {
// Dir implements both Node and Handle for the root directory. // Dir implements both Node and Handle for the root directory.
type Dir struct{} type Dir struct{}
func (Dir) Attr() fuse.Attr { func (Dir) Attr(ctx context.Context, a *fuse.Attr) error {
return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0555} a.Inode = 1
a.Mode = os.ModeDir | 0555
return nil
} }
func (Dir) Lookup(ctx context.Context, name string) (fs.Node, error) { func (Dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
@ -87,8 +89,11 @@ type File struct{}
const greeting = "hello, world\n" const greeting = "hello, world\n"
func (File) Attr() fuse.Attr { func (File) Attr(ctx context.Context, a *fuse.Attr) error {
return fuse.Attr{Inode: 2, Mode: 0444, Size: uint64(len(greeting))} a.Inode = 2
a.Mode = 0444
a.Size = uint64(len(greeting))
return nil
} }
func (File) ReadAll(ctx context.Context) ([]byte, error) { func (File) ReadAll(ctx context.Context) ([]byte, error) {

View File

@ -1,13 +1,37 @@
package fuse package fuse
import ( import (
"bufio"
"fmt" "fmt"
"io"
"log"
"net" "net"
"os" "os"
"os/exec" "os/exec"
"sync"
"syscall" "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) { func mount(dir string, conf *MountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) {
// linux mount is never delayed // linux mount is never delayed
close(ready) close(ready)
@ -31,9 +55,25 @@ func mount(dir string, conf *MountConfig, ready chan<- struct{}, errp *error) (f
defer writeFile.Close() defer writeFile.Close()
cmd.ExtraFiles = []*os.File{writeFile} cmd.ExtraFiles = []*os.File{writeFile}
out, err := cmd.CombinedOutput() var wg sync.WaitGroup
if len(out) > 0 || err != nil { stdout, err := cmd.StdoutPipe()
return nil, fmt.Errorf("fusermount: %q, %v", out, err) 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") readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads")

View File

@ -155,7 +155,10 @@ func TestMountOptionAllowRootThenAllowOther(t *testing.T) {
type unwritableFile struct{} 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) { func TestMountOptionDefaultPermissions(t *testing.T) {
if runtime.GOOS == "freebsd" { if runtime.GOOS == "freebsd" {

View File

@ -6,6 +6,7 @@ package ipns
import ( import (
"errors" "errors"
"fmt"
"os" "os"
"strings" "strings"
@ -106,9 +107,10 @@ func CreateRoot(ipfs *core.IpfsNode, keys []ci.PrivKey, ipfspath, ipnspath strin
} }
// Attr returns file attributes. // Attr returns file attributes.
func (*Root) Attr() fuse.Attr { func (*Root) Attr(ctx context.Context, a *fuse.Attr) error {
log.Debug("Root Attr") 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. // Lookup performs a lookup under this node.
@ -215,29 +217,31 @@ type File struct {
} }
// Attr returns the attributes of a given node. // 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") log.Debug("Directory Attr")
return fuse.Attr{ *a = fuse.Attr{
Mode: os.ModeDir | 0555, Mode: os.ModeDir | 0555,
Uid: uint32(os.Getuid()), Uid: uint32(os.Getuid()),
Gid: uint32(os.Getgid()), Gid: uint32(os.Getgid()),
} }
return nil
} }
// Attr returns the attributes of a given node. // 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") log.Debug("File Attr")
size, err := fi.fi.Size() size, err := fi.fi.Size()
if err != nil { if err != nil {
// In this case, the dag node in question may not be unixfs // 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), Mode: os.FileMode(0666),
Size: uint64(size), Size: uint64(size),
Uid: uint32(os.Getuid()), Uid: uint32(os.Getuid()),
Gid: uint32(os.Getgid()), Gid: uint32(os.Getgid()),
} }
return nil
} }
// Lookup performs a lookup under this node. // Lookup performs a lookup under this node.

View File

@ -14,11 +14,12 @@ type Link struct {
Target string Target string
} }
func (l *Link) Attr() fuse.Attr { func (l *Link) Attr(ctx context.Context, a *fuse.Attr) error {
log.Debug("Link attr.") log.Debug("Link attr.")
return fuse.Attr{ *a = fuse.Attr{
Mode: os.ModeSymlink | 0555, Mode: os.ModeSymlink | 0555,
} }
return nil
} }
func (l *Link) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) { func (l *Link) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {

View File

@ -4,6 +4,7 @@
package readonly package readonly
import ( import (
"fmt"
"io" "io"
"os" "os"
@ -43,8 +44,9 @@ type Root struct {
} }
// Attr returns file attributes. // Attr returns file attributes.
func (*Root) Attr() fuse.Attr { func (*Root) Attr(ctx context.Context, a *fuse.Attr) error {
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. // Lookup performs a lookup under this node.
@ -85,21 +87,23 @@ func (s *Node) loadData() error {
} }
// Attr returns the attributes of a given node. // 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.") log.Debug("Node attr.")
if s.cached == nil { if s.cached == nil {
s.loadData() if err := s.loadData(); err != nil {
return fmt.Errorf("readonly: loadData() failed: %s", err)
}
} }
switch s.cached.GetType() { switch s.cached.GetType() {
case ftpb.Data_Directory: case ftpb.Data_Directory:
return fuse.Attr{ *a = fuse.Attr{
Mode: os.ModeDir | 0555, Mode: os.ModeDir | 0555,
Uid: uint32(os.Getuid()), Uid: uint32(os.Getuid()),
Gid: uint32(os.Getgid()), Gid: uint32(os.Getgid()),
} }
case ftpb.Data_File: case ftpb.Data_File:
size := s.cached.GetFilesize() size := s.cached.GetFilesize()
return fuse.Attr{ *a = fuse.Attr{
Mode: 0444, Mode: 0444,
Size: uint64(size), Size: uint64(size),
Blocks: uint64(len(s.Nd.Links)), Blocks: uint64(len(s.Nd.Links)),
@ -107,7 +111,7 @@ func (s *Node) Attr() fuse.Attr {
Gid: uint32(os.Getgid()), Gid: uint32(os.Getgid()),
} }
case ftpb.Data_Raw: case ftpb.Data_Raw:
return fuse.Attr{ *a = fuse.Attr{
Mode: 0444, Mode: 0444,
Size: uint64(len(s.cached.GetData())), Size: uint64(len(s.cached.GetData())),
Blocks: uint64(len(s.Nd.Links)), Blocks: uint64(len(s.Nd.Links)),
@ -116,9 +120,9 @@ func (s *Node) Attr() fuse.Attr {
} }
default: default:
log.Debug("Invalid data type.") return fmt.Errorf("Invalid data type - %s", s.cached.GetType())
return fuse.Attr{}
} }
return nil
} }
// Lookup performs a lookup under this node. // Lookup performs a lookup under this node.