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,