Commit 994fc0fe authored by Jonathon Reinhart's avatar Jonathon Reinhart Committed by Han-Wen Nienhuys

nodefs: Change defaultNode.GetAttr() to call file.GetAttr() if not nil

This enhances nodes which embed defaultNode (which is most of them),
allowing those implementations to omit a GetAttr() method. Without this,
when the kernel calls GetAttr() for an open file, FUSE will return a
size of zero, causing a read() of the file to always return 0, even
though fuse returns a nonzero number of bytes.

One can leverage this behavior to create a Linux procfs-like file, which
stat shows to be zero bytes, but can be successfully read.

N.B. This behavior is only useful if the FileSystemConnector Options
specify AttrTimeout of zero. Otherwise, the result of the first call to
GetAttr() (file == nil) is cached, and the kernel allows zero bytes to
be read. With this caching disabled, the next GetAttr() call (file !=
nil) will be used to control the number of readable bytes.
parent a2e4f919
......@@ -126,6 +126,9 @@ func (n *defaultNode) ListXAttr(context *fuse.Context) (attrs []string, code fus
}
func (n *defaultNode) GetAttr(out *fuse.Attr, file File, context *fuse.Context) (code fuse.Status) {
if file != nil {
return file.GetAttr(out)
}
if n.Inode().IsDir() {
out.Mode = fuse.S_IFDIR | 0755
} else {
......
// Copyright 2016 the Go-FUSE 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 test
import (
"io/ioutil"
"os"
"path"
"testing"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/internal/testutil"
)
func TestDefaultNodeGetAttr(t *testing.T) {
dir := testutil.TempDir()
defer os.RemoveAll(dir)
opts := &nodefs.Options{
// Note: defaultNode.GetAttr() calling file.GetAttr() is only useful if
// AttrTimeout is zero.
// See https://github.com/JonathonReinhart/gitlab-fuse/issues/2
Owner: fuse.CurrentOwner(),
}
root := nodefs.NewDefaultNode()
s, _, err := nodefs.MountRoot(dir, root, opts)
if err != nil {
t.Fatalf("MountRoot: %v", err)
}
go s.Serve()
if err := s.WaitMount(); err != nil {
t.Fatal("WaitMount", err)
}
defer s.Unmount()
// Attach another custom node type
root.Inode().NewChild("foo", false, &myNode{
Node: nodefs.NewDefaultNode(),
content: []byte("success"),
})
filepath := path.Join(dir, "foo")
// NewDefaultNode() should provide for stat that indicates 0-byte regular file
fi, err := os.Stat(filepath)
if err != nil {
t.Fatalf("Stat: %v", err)
}
if mode := (fi.Mode() & os.ModeType); mode != 0 {
// Mode() & ModeType should be zero for regular files
t.Fatalf("Unexpected mode: %#o", mode)
}
if size := fi.Size(); size != 0 {
t.Fatalf("Unexpected size: %d", size)
}
// But when we open the file, we should get the content
content, err := ioutil.ReadFile(filepath)
if err != nil {
t.Fatalf("ReadFile: %v", err)
}
if string(content) != "success" {
t.Fatalf("Unexpected content: %v", content)
}
}
type myNode struct {
nodefs.Node
content []byte
}
func (n *myNode) Open(flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
return nodefs.NewDataFile(n.content), fuse.OK
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment