Use new FUSE API
Change-Id: I6b42407d92304fdd6ca14611235904bc04e62847
diff --git a/cmd/slothfs-gitilesfs/main.go b/cmd/slothfs-gitilesfs/main.go
index 2e9bfa1..30b3feb 100644
--- a/cmd/slothfs-gitilesfs/main.go
+++ b/cmd/slothfs-gitilesfs/main.go
@@ -24,7 +24,7 @@
"github.com/google/slothfs/cache"
"github.com/google/slothfs/fs"
"github.com/google/slothfs/gitiles"
- "github.com/hanwen/go-fuse/fuse/nodefs"
+ fusefs "github.com/hanwen/go-fuse/fs"
)
func main() {
@@ -64,12 +64,15 @@
}
root := fs.NewGitilesConfigFSRoot(cache, repoService, &opts)
- server, _, err := nodefs.MountRoot(mntDir, root, &nodefs.Options{
- EntryTimeout: time.Hour,
- NegativeTimeout: time.Hour,
- AttrTimeout: time.Hour,
- Debug: *debug,
- })
+ h := time.Hour
+ fuseOpts := &fusefs.Options{
+ EntryTimeout: &h,
+ NegativeTimeout: &h,
+ AttrTimeout: &h,
+ }
+ fuseOpts.Debug = *debug
+
+ server, err := fusefs.Mount(mntDir, root, fuseOpts)
if err != nil {
log.Fatalf("MountFileSystem: %v", err)
}
diff --git a/cmd/slothfs-hostfs/main.go b/cmd/slothfs-hostfs/main.go
index 68a80b6..1fa0436 100644
--- a/cmd/slothfs-hostfs/main.go
+++ b/cmd/slothfs-hostfs/main.go
@@ -24,7 +24,7 @@
"github.com/google/slothfs/cache"
"github.com/google/slothfs/fs"
"github.com/google/slothfs/gitiles"
- "github.com/hanwen/go-fuse/fuse/nodefs"
+ fusefs "github.com/hanwen/go-fuse/fs"
)
func main() {
@@ -57,12 +57,14 @@
log.Fatalf("NewService: %v", err)
}
- server, _, err := nodefs.MountRoot(mntDir, root, &nodefs.Options{
- EntryTimeout: time.Hour,
- NegativeTimeout: time.Hour,
- AttrTimeout: time.Hour,
- Debug: *debug,
- })
+ h := time.Hour
+ fuseOpts := &fusefs.Options{
+ EntryTimeout: &h,
+ NegativeTimeout: &h,
+ AttrTimeout: &h,
+ }
+ fuseOpts.Debug = *debug
+ server, err := fusefs.Mount(mntDir, root, fuseOpts)
if err != nil {
log.Fatalf("MountFileSystem: %v", err)
}
diff --git a/fs/fixture_test.go b/fs/fixture_test.go
index 92d41f7..0a1cdbc 100644
--- a/fs/fixture_test.go
+++ b/fs/fixture_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@
"github.com/google/slothfs/cache"
"github.com/google/slothfs/gitiles"
+ "github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse"
- "github.com/hanwen/go-fuse/fuse/nodefs"
)
type testFixture struct {
@@ -34,7 +34,7 @@
cache *cache.Cache
testServer *testServer
service *gitiles.Service
- root nodefs.Node
+ root fs.InodeEmbedder
}
func (f *testFixture) cleanup() {
@@ -52,52 +52,45 @@
if err != nil {
return nil, err
}
-
fixture := &testFixture{dir: d}
-
fixture.cache, err = cache.NewCache(filepath.Join(d, "/cache"), cache.Options{})
if err != nil {
return nil, err
}
-
fixture.testServer, err = newTestServer()
if err != nil {
return nil, err
}
-
fixture.service, err = gitiles.NewService(gitiles.Options{
Address: fmt.Sprintf("http://%s", fixture.testServer.addr),
})
if err != nil {
return nil, err
}
-
return fixture, nil
}
-func (f *testFixture) mount(root nodefs.Node) error {
+func (f *testFixture) mount(root fs.InodeEmbedder) error {
f.mntDir = filepath.Join(f.dir, "mnt")
if err := os.Mkdir(f.mntDir, 0755); err != nil {
return err
}
-
- fuseOpts := &nodefs.Options{
- EntryTimeout: time.Hour,
- NegativeTimeout: time.Hour,
- AttrTimeout: time.Hour,
+ t := time.Hour
+ fuseOpts := &fs.Options{
+ EntryTimeout: &t,
+ NegativeTimeout: &t,
+ AttrTimeout: &t,
}
-
+ fuseOpts.Debug = true
var err error
- f.server, _, err = nodefs.MountRoot(f.mntDir, root, fuseOpts)
+ f.server, err = fs.Mount(f.mntDir, root, fuseOpts)
if err != nil {
return err
}
-
if fuseDebug {
f.server.SetDebug(true)
}
go f.server.Serve()
-
f.root = root
return nil
}
diff --git a/fs/gitilesconfigfs.go b/fs/gitilesconfigfs.go
index 19ae65a..dad2b6a 100644
--- a/fs/gitilesconfigfs.go
+++ b/fs/gitilesconfigfs.go
@@ -15,31 +15,28 @@
package fs
import (
+ "context"
"encoding/hex"
"fmt"
"log"
+ "syscall"
"gopkg.in/src-d/go-git.v4/plumbing"
"github.com/google/slothfs/cache"
"github.com/google/slothfs/gitiles"
+ "github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse"
- "github.com/hanwen/go-fuse/fuse/nodefs"
)
type gitilesConfigFSRoot struct {
- nodefs.Node
+ fs.Inode
- fsConn *nodefs.FileSystemConnector
cache *cache.Cache
service *gitiles.RepoService
options GitilesOptions
}
-func (r *gitilesConfigFSRoot) OnMount(fsConn *nodefs.FileSystemConnector) {
- r.fsConn = fsConn
-}
-
func parseID(s string) (*plumbing.Hash, error) {
b, err := hex.DecodeString(s)
if err != nil || len(b) != 20 {
@@ -51,14 +48,16 @@
return &h, nil
}
-func (r *gitilesConfigFSRoot) Lookup(out *fuse.Attr, name string, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
+var _ = (fs.NodeLookuper)((*gitilesConfigFSRoot)(nil))
+
+func (r *gitilesConfigFSRoot) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
id, err := parseID(name)
if err != nil {
- return nil, fuse.ENOENT
+ return nil, syscall.ENOENT
}
- if ch := r.Inode().GetChild(name); ch != nil {
- return ch, fuse.OK
+ if ch := r.GetChild(name); ch != nil {
+ return ch, 0
}
tree, err := r.cache.Tree.Get(id)
@@ -66,7 +65,7 @@
tree, err = r.service.GetTree(id.String(), "/", true)
if err != nil {
log.Printf("GetTree(%s): %v", id, err)
- return nil, fuse.EIO
+ return nil, syscall.EIO
}
if err := r.cache.Tree.Add(id, tree); err != nil {
@@ -79,17 +78,18 @@
GitilesOptions: r.options,
}
newRoot := NewGitilesRoot(r.cache, tree, r.service, gro)
- ch := r.Inode().NewChild(id.String(), true, newRoot)
- out.Mode = fuse.S_IFDIR | 0755
+ ch := r.NewPersistentInode(
+ ctx,
+ newRoot,
+ fs.StableAttr{Mode: syscall.S_IFDIR})
- newRoot.OnMount(r.fsConn)
- return ch, fuse.OK
+ return ch, 0
}
// NewGitilesConfigFSRoot returns a root node for a filesystem that lazily
// instantiates a repository if you access any subdirectory named by a
// 40-byte hex SHA1.
-func NewGitilesConfigFSRoot(c *cache.Cache, service *gitiles.RepoService, options *GitilesOptions) nodefs.Node {
+func NewGitilesConfigFSRoot(c *cache.Cache, service *gitiles.RepoService, options *GitilesOptions) fs.InodeEmbedder {
// TODO(hanwen): nodefs.Node has an OnForget(), but it will
// never trigger for directories that have children. That
// means that we effectively never drop old trees. We can fix
@@ -97,7 +97,6 @@
// a periodic removal of all subtrees trees. Since the FS is
// read-only that should cause no ill effects.
return &gitilesConfigFSRoot{
- Node: nodefs.NewDefaultNode(),
cache: c,
service: service,
options: *options,
diff --git a/fs/gitilesfs.go b/fs/gitilesfs.go
index dd96b08..394e4ee 100644
--- a/fs/gitilesfs.go
+++ b/fs/gitilesfs.go
@@ -15,6 +15,7 @@
package fs
import (
+ "context"
"encoding/json"
"fmt"
"io"
@@ -22,6 +23,7 @@
"log"
"os"
"path/filepath"
+ "strings"
"sync"
"sync/atomic"
"syscall"
@@ -32,14 +34,14 @@
"github.com/google/slothfs/cache"
"github.com/google/slothfs/gitiles"
+ "github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse"
- "github.com/hanwen/go-fuse/fuse/nodefs"
)
// gitilesRoot is the root for a FUSE filesystem backed by a Gitiles
// service.
type gitilesRoot struct {
- nodefs.Node
+ fs.Inode
nodeCache *nodeCache
@@ -59,37 +61,9 @@
fetching map[plumbing.Hash]bool
}
-type linkNode struct {
- nodefs.Node
- linkTarget []byte
-}
-
-func (n *linkNode) Deletable() bool { return false }
-
-func newLinkNode(target string) *linkNode {
- return &linkNode{
- Node: nodefs.NewDefaultNode(),
- linkTarget: []byte(target),
- }
-}
-
-func (n *linkNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
- out.Size = uint64(len(n.linkTarget))
- out.Mode = fuse.S_IFLNK
-
- t := time.Unix(1, 0)
- out.SetTimes(nil, &t, nil)
-
- return fuse.OK
-}
-
-func (n *linkNode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
- return n.linkTarget, fuse.OK
-}
-
// gitilesNode represents a read-only blob in the FUSE filesystem.
type gitilesNode struct {
- nodefs.Node
+ fs.Inode
root *gitilesRoot
@@ -110,24 +84,15 @@
readCount uint32
}
-func (n *gitilesNode) Deletable() bool {
- return false
+var _ = (fs.NodeReadlinker)((*gitilesNode)(nil))
+
+func (n *gitilesNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
+ return n.linkTarget, 0
}
-func (n *gitilesNode) Utimens(file nodefs.File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status) {
- if mtime != nil {
- n.mtimeMu.Lock()
- n.mtime = *mtime
- n.mtimeMu.Unlock()
- }
- return fuse.OK
-}
+var _ = (fs.NodeGetattrer)((*gitilesNode)(nil))
-func (n *gitilesNode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
- return n.linkTarget, fuse.OK
-}
-
-func (n *gitilesNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
+func (n *gitilesNode) Getattr(ctx context.Context, h fs.FileHandle, out *fuse.AttrOut) (code syscall.Errno) {
out.Size = uint64(n.size)
out.Mode = n.mode
@@ -136,57 +101,86 @@
n.mtimeMu.Unlock()
out.SetTimes(nil, &t, nil)
- return fuse.OK
+ return 0
+}
+
+var _ = (fs.NodeSetattrer)((*gitilesNode)(nil))
+
+func (n *gitilesNode) Setattr(ctx context.Context, h fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) (code syscall.Errno) {
+ if 0 != in.Valid&(fuse.FATTR_MODE|
+ fuse.FATTR_UID|
+ fuse.FATTR_GID|
+ fuse.FATTR_SIZE|
+ fuse.FATTR_LOCKOWNER|
+ fuse.FATTR_CTIME) {
+ return syscall.ENOTSUP
+ }
+ if mt, ok := in.GetMTime(); ok {
+ n.mtimeMu.Lock()
+ n.mtime = mt
+ n.mtimeMu.Unlock()
+
+ return n.Getattr(ctx, h, out)
+ }
+ return 0
}
const xattrName = "user.gitsha1"
-func (n *gitilesNode) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) {
+var _ = (fs.NodeGetxattrer)((*gitilesNode)(nil))
+
+func (n *gitilesNode) Getxattr(ctx context.Context, attribute string, dest []byte) (uint32, syscall.Errno) {
if attribute != xattrName {
- return nil, fuse.ENODATA
+ return 0, syscall.ENODATA
}
- return []byte(n.id.String()), fuse.OK
+ sz := copy(dest, n.id.String())
+ return uint32(sz), 0
}
-func (n *gitilesNode) ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status) {
- return []string{xattrName}, fuse.OK
+var _ = (fs.NodeListxattrer)((*gitilesNode)(nil))
+
+func (n *gitilesNode) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) {
+ sz := copy(dest, xattrName)
+ dest[sz] = 0
+ return uint32(sz + 1), 0
}
-func (n *gitilesNode) Open(flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
+var _ = (fs.NodeOpener)((*gitilesNode)(nil))
+
+func (n *gitilesNode) Open(ctx context.Context, flags uint32) (h fs.FileHandle, fuseFlags uint32, code syscall.Errno) {
if n.root.handleLessIO {
// We say ENOSYS so FUSE on Linux uses handle-less I/O.
- return nil, fuse.ENOSYS
+ return nil, 0, syscall.ENOSYS
}
f, err := n.root.openFile(n.id, n.clone)
if err != nil {
- return nil, fuse.ToStatus(err)
+ return nil, 0, fs.ToErrno(err)
}
- return &nodefs.WithFlags{
- File: nodefs.NewLoopbackFile(f),
- FuseFlags: fuse.FOPEN_KEEP_CACHE,
- }, fuse.OK
+ return fs.NewLoopbackFile(int(f.Fd())), fuse.FOPEN_KEEP_CACHE, 0
}
-func (n *gitilesNode) Read(file nodefs.File, dest []byte, off int64, context *fuse.Context) (fuse.ReadResult, fuse.Status) {
+var _ = (fs.NodeReader)((*gitilesNode)(nil))
+
+func (n *gitilesNode) Read(ctx context.Context, file fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
if off == 0 {
atomic.AddUint32(&n.readCount, 1)
}
if n.root.handleLessIO {
- return n.handleLessRead(file, dest, off, context)
+ return n.handleLessRead(file, dest, off)
}
- return file.Read(dest, off)
+ return file.(fs.FileReader).Read(ctx, dest, off)
}
-func (n *gitilesNode) handleLessRead(file nodefs.File, dest []byte, off int64, context *fuse.Context) (fuse.ReadResult, fuse.Status) {
+func (n *gitilesNode) handleLessRead(file fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
// TODO(hanwen): for large files this is not efficient. Should
// have a cache of open file handles.
f, err := n.root.openFile(n.id, n.clone)
if err != nil {
- return nil, fuse.ToStatus(err)
+ return nil, fs.ToErrno(err)
}
m, err := f.ReadAt(dest, off)
@@ -194,7 +188,7 @@
err = nil
}
f.Close()
- return fuse.ReadResultData(dest[:m]), fuse.ToStatus(err)
+ return fuse.ReadResultData(dest[:m]), fs.ToErrno(err)
}
// openFile returns a file handle for the given blob. If `clone` is
@@ -290,37 +284,36 @@
// dataNode makes arbitrary data available as a file.
type dataNode struct {
- nodefs.Node
+ fs.Inode
data []byte
}
-func (n *dataNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
+var _ = (fs.NodeGetattrer)((*dataNode)(nil))
+
+func (n *dataNode) Getattr(ctx context.Context, file fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
out.Size = uint64(len(n.data))
out.Mode = fuse.S_IFREG | 0644
t := time.Unix(1, 0)
out.SetTimes(nil, &t, nil)
- return fuse.OK
+ return 0
}
-func (n *dataNode) Open(flags uint32, content *fuse.Context) (nodefs.File, fuse.Status) {
- return nodefs.NewDataFile(n.data), fuse.OK
+var _ = (fs.NodeOpener)((*gitilesNode)(nil))
+
+func (n *dataNode) Open(ctx context.Context, flags uint32) (fs.FileHandle, syscall.Errno) {
+ return fs.MemRegularFile{Data: n.data}, 0
}
-func (n *dataNode) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) {
- return nil, fuse.ENODATA
-}
+var _ = (fs.NodeGetxattrer)((*gitilesNode)(nil))
-func (n *dataNode) Deletable() bool { return false }
-
-func newDataNode(c []byte) nodefs.Node {
- return &dataNode{nodefs.NewDefaultNode(), c}
+func (n *dataNode) GetXAttr(ctx context.Context, attribute string) (data []byte, code syscall.Errno) {
+ return nil, syscall.ENODATA
}
// NewGitilesRoot returns the root node for a file system.
-func NewGitilesRoot(c *cache.Cache, tree *gitiles.Tree, service *gitiles.RepoService, options GitilesRevisionOptions) nodefs.Node {
+func NewGitilesRoot(c *cache.Cache, tree *gitiles.Tree, service *gitiles.RepoService, options GitilesRevisionOptions) *gitilesRoot {
r := &gitilesRoot{
- Node: newDirNode(),
service: service,
nodeCache: newNodeCache(),
cache: c,
@@ -335,63 +328,39 @@
return r
}
-func (r *gitilesRoot) Deletable() bool { return false }
+var _ = (fs.NodeGetxattrer)((*gitilesRoot)(nil))
-func (r *gitilesRoot) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) {
- return nil, fuse.ENODATA
+func (r *gitilesRoot) Getxattr(ctx context.Context, attribute string, data []byte) (sz uint32, code syscall.Errno) {
+ return 0, syscall.ENODATA
}
-func (r *gitilesRoot) OnMount(fsConn *nodefs.FileSystemConnector) {
- if err := r.onMount(fsConn); err != nil {
- log.Printf("onMount: %v", err)
- for k := range r.Inode().Children() {
- r.Inode().RmChild(k)
+func (r *gitilesRoot) pathTo(dir string) *fs.Inode {
+ p := &r.Inode
+ for _, c := range strings.Split(dir, "/") {
+ if len(c) == 0 {
+ continue
}
- r.Inode().NewChild("ERROR", false, newDataNode([]byte(err.Error())))
+ ch := p.GetChild(c)
+ if ch == nil {
+ ch = p.NewPersistentInode(context.Background(),
+ &fs.Inode{},
+ fs.StableAttr{Mode: syscall.S_IFDIR})
+ p.AddChild(c, ch, true)
+ }
+ p = ch
}
+ return p
}
-type dirNode struct {
- nodefs.Node
-}
+var _ = (fs.NodeOnAdder)((*gitilesRoot)(nil))
-// Implement Utimens so we don't create spurious "not implemented"
-// messages when directory targets for symlinks are touched.
-func (n *dirNode) Utimens(file nodefs.File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status) {
- return fuse.OK
-}
-
-func (n *dirNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
- out.Mode = fuse.S_IFDIR | 0755
- t := time.Unix(1, 0)
- out.SetTimes(nil, &t, nil)
- return fuse.OK
-}
-
-func (n *dirNode) Deletable() bool {
- return false
-}
-
-func newDirNode() nodefs.Node {
- return &dirNode{nodefs.NewDefaultNode()}
-}
-
-func (r *gitilesRoot) pathTo(fsConn *nodefs.FileSystemConnector, dir string) *nodefs.Inode {
- parent, left := fsConn.Node(r.Inode(), dir)
- for _, l := range left {
- ch := parent.NewChild(l, true, newDirNode())
- parent = ch
- }
- return parent
-}
-
-func (r *gitilesRoot) onMount(fsConn *nodefs.FileSystemConnector) error {
+func (r *gitilesRoot) OnAdd(ctx context.Context) {
for _, e := range r.tree.Entries {
if e.Type == "commit" {
// TODO(hanwen): support submodules. For now,
// we pretend we are plain git, which also
// leaves an empty directory in the place of a submodule.
- r.pathTo(fsConn, e.Name)
+ r.pathTo(e.Name)
continue
}
if e.Type != "blob" {
@@ -401,10 +370,10 @@
p := e.Name
dir, base := filepath.Split(p)
- parent := r.pathTo(fsConn, dir)
+ parent := r.pathTo(dir)
id, err := parseID(e.ID)
if err != nil {
- return err
+ return
}
// Determine if file should trigger a clone.
@@ -422,7 +391,6 @@
n := r.nodeCache.get(id, xbit)
if n == nil {
n = &gitilesNode{
- Node: nodefs.NewDefaultNode(),
id: *id,
mode: uint32(e.Mode),
clone: clone,
@@ -436,35 +404,45 @@
n.size = int64(*e.Size)
}
+ mode := uint32(syscall.S_IFREG)
if e.Target != nil {
n.linkTarget = []byte(*e.Target)
n.size = int64(len(n.linkTarget))
+ mode = syscall.S_IFLNK
}
r.shaMap[*id] = p
- parent.NewChild(base, false, n)
+
+ ch := parent.NewPersistentInode(ctx, n, fs.StableAttr{Mode: mode})
+ parent.AddChild(base, ch, true)
r.nodeCache.add(n)
} else {
- parent.AddChild(base, n.Inode())
+ parent.AddChild(base, n.EmbeddedInode(), true)
}
}
- slothfsNode := r.Inode().NewChild(".slothfs", true, newDirNode())
- slothfsNode.NewChild("treeID", false, newDataNode([]byte(r.tree.ID)))
+ slothfsNode := r.NewPersistentInode(ctx, &fs.Inode{}, fs.StableAttr{Mode: syscall.S_IFDIR})
+ r.AddChild(".slothfs", slothfsNode, true)
+ idFile := r.NewPersistentInode(ctx, &fs.MemRegularFile{
+ Data: []byte(r.tree.ID)}, fs.StableAttr{Mode: syscall.S_IFREG})
+
+ slothfsNode.AddChild("treeID", idFile, false)
treeContent, err := json.MarshalIndent(r.tree, "", " ")
if err != nil {
log.Panicf("json.Marshal: %v", err)
}
+ jsonFile := r.NewPersistentInode(ctx, &fs.MemRegularFile{
+ Data: treeContent}, fs.StableAttr{Mode: syscall.S_IFREG})
- slothfsNode.NewChild("tree.json", false, newDataNode([]byte(treeContent)))
+ slothfsNode.AddChild("tree.json", jsonFile, false)
// We don't need the tree data anymore.
r.tree = nil
- if fsConn.Server().KernelSettings().Flags&fuse.CAP_NO_OPEN_SUPPORT != 0 {
- r.handleLessIO = true
- }
- return nil
+ // XXX
+ // if fsConn.Server().KernelSettings().Flags&fuse.CAP_NO_OPEN_SUPPORT != 0 {
+ // r.handleLessIO = true
+ // }
}
diff --git a/fs/gitilesfs_test.go b/fs/gitilesfs_test.go
index f444292..ddac371 100644
--- a/fs/gitilesfs_test.go
+++ b/fs/gitilesfs_test.go
@@ -296,12 +296,12 @@
t.Fatal("mount", err)
}
- ch1 := fs.Inode().GetChild("AUTHORS")
+ ch1 := fs.GetChild("AUTHORS")
if ch1 == nil {
t.Fatalf("node for AUTHORS not found")
}
- ch2 := fs.Inode().GetChild("AUTHORS2")
+ ch2 := fs.GetChild("AUTHORS2")
if ch2 == nil {
t.Fatalf("node for AUTHORS2 not found")
}
@@ -309,7 +309,7 @@
if ch1 != ch2 {
t.Error("equal blobs did not share inodes.")
}
- ch3 := fs.Inode().GetChild("AUTHORSx")
+ ch3 := fs.GetChild("AUTHORSx")
if ch1 == ch3 {
t.Error("blob with different modes shared inode.")
}
@@ -430,7 +430,7 @@
}
// TODO(hanwen): is this race-detector sane?
- ch := fs.Inode().GetChild("testcase")
+ ch := fs.GetChild("testcase")
if ch == nil {
t.Fatalf("node for testcase/ not found")
}
@@ -439,9 +439,9 @@
t.Fatalf("node for addprefix.mk not found")
}
- giNode, ok := ch.Node().(*gitilesNode)
+ giNode, ok := ch.Operations().(*gitilesNode)
if !ok {
- t.Fatalf("got node type %T, want *gitilesNode", ch.Node())
+ t.Fatalf("got node type %T, want *gitilesNode", ch.Operations())
}
if giNode.clone {
@@ -477,14 +477,14 @@
}
}
- ch := fs.Inode().GetChild("AUTHORS")
+ ch := fs.GetChild("AUTHORS")
if ch == nil {
t.Fatalf("node for AUTHORS not found")
}
- giNode, ok := ch.Node().(*gitilesNode)
+ giNode, ok := ch.Operations().(*gitilesNode)
if !ok {
- t.Fatalf("got node type %T, want *gitilesNode", ch.Node())
+ t.Fatalf("got node type %T, want *gitilesNode", ch.Operations())
}
if c := atomic.LoadUint32(&giNode.readCount); c != 1 {
diff --git a/fs/gitileshostfs.go b/fs/gitileshostfs.go
index 7c83e6d..b283831 100644
--- a/fs/gitileshostfs.go
+++ b/fs/gitileshostfs.go
@@ -15,18 +15,20 @@
package fs
import (
+ "context"
"fmt"
"path/filepath"
"sort"
"strings"
+ "syscall"
"github.com/google/slothfs/cache"
"github.com/google/slothfs/gitiles"
- "github.com/hanwen/go-fuse/fuse/nodefs"
+ "github.com/hanwen/go-fuse/fs"
)
type hostFS struct {
- nodefs.Node
+ fs.Inode
cache *cache.Cache
service *gitiles.Service
@@ -60,7 +62,6 @@
}
return &hostFS{
- Node: nodefs.NewDefaultNode(),
projects: projMap,
cloneOptions: cloneOptions,
service: service,
@@ -68,34 +69,38 @@
}, nil
}
-func (h *hostFS) OnMount(fsConn *nodefs.FileSystemConnector) {
+var _ = (fs.NodeOnAdder)((*hostFS)(nil))
+
+func (h *hostFS) OnAdd(ctx context.Context) {
var keys []string
for k := range parents(h.projects) {
keys = append(keys, k)
}
sort.Strings(keys)
- nodes := map[string]*nodefs.Inode{
- "": h.Inode(),
+ nodes := map[string]*fs.Inode{
+ "": h.EmbeddedInode(),
}
for _, k := range keys {
if k == "." {
continue
}
+ // XXX should loop ?
d, nm := filepath.Split(k)
d = strings.TrimSuffix(d, "/")
parent := nodes[d]
- var node nodefs.Node
+ var node fs.InodeEmbedder
if p := h.projects[k]; p != nil {
node = h.newProjectNode(parent, p)
delete(h.projects, k)
- node.OnMount(fsConn)
} else {
- node = newDirNode()
+ node = &fs.Inode{}
}
- ch := parent.NewChild(nm, true, node)
+
+ ch := parent.NewPersistentInode(ctx, node, fs.StableAttr{Mode: syscall.S_IFDIR})
+ parent.AddChild(nm, ch, true)
nodes[k] = ch
}
@@ -105,13 +110,13 @@
parent := nodes[d]
node := h.newProjectNode(parent, p)
- node.OnMount(fsConn)
- parent.NewChild(nm, true, node)
+ ch := parent.NewPersistentInode(ctx, node, fs.StableAttr{Mode: syscall.S_IFDIR})
+ parent.AddChild(nm, ch, true)
}
}
-func (h *hostFS) newProjectNode(parent *nodefs.Inode, proj *gitiles.Project) nodefs.Node {
+func (h *hostFS) newProjectNode(parent *fs.Inode, proj *gitiles.Project) fs.InodeEmbedder {
repoService := h.service.NewRepoService(proj.Name)
opts := GitilesOptions{
CloneURL: proj.CloneURL,