Merge pull request #4020 from greatroar/fuse-inode
fuse: Better inode generation
This commit is contained in:
commit
57d8eedb88
4 changed files with 95 additions and 12 deletions
|
@ -182,7 +182,7 @@ func (d *dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
|||
}
|
||||
|
||||
ret = append(ret, fuse.Dirent{
|
||||
Inode: fs.GenerateDynamicInode(d.inode, name),
|
||||
Inode: inodeFromNode(d.inode, node),
|
||||
Type: typ,
|
||||
Name: name,
|
||||
})
|
||||
|
@ -204,15 +204,16 @@ func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
|||
debug.Log(" Lookup(%v) -> not found", name)
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
inode := inodeFromNode(d.inode, node)
|
||||
switch node.Type {
|
||||
case "dir":
|
||||
return newDir(d.root, fs.GenerateDynamicInode(d.inode, name), d.inode, node)
|
||||
return newDir(d.root, inode, d.inode, node)
|
||||
case "file":
|
||||
return newFile(d.root, fs.GenerateDynamicInode(d.inode, name), node)
|
||||
return newFile(d.root, inode, node)
|
||||
case "symlink":
|
||||
return newLink(d.root, fs.GenerateDynamicInode(d.inode, name), node)
|
||||
return newLink(d.root, inode, node)
|
||||
case "dev", "chardev", "fifo", "socket":
|
||||
return newOther(d.root, fs.GenerateDynamicInode(d.inode, name), node)
|
||||
return newOther(d.root, inode, node)
|
||||
default:
|
||||
debug.Log(" node %v has unknown type %v", name, node.Type)
|
||||
return nil, fuse.ENOENT
|
||||
|
|
|
@ -118,7 +118,7 @@ func TestFuseFile(t *testing.T) {
|
|||
}
|
||||
root := &Root{repo: repo, blobCache: bloblru.New(blobCacheSize)}
|
||||
|
||||
inode := fs.GenerateDynamicInode(1, "foo")
|
||||
inode := inodeFromNode(1, node)
|
||||
f, err := newFile(root, inode, node)
|
||||
rtest.OK(t, err)
|
||||
of, err := f.Open(context.TODO(), nil, nil)
|
||||
|
@ -161,8 +161,8 @@ func TestFuseDir(t *testing.T) {
|
|||
ChangeTime: time.Unix(1606773732, 0),
|
||||
ModTime: time.Unix(1606773733, 0),
|
||||
}
|
||||
parentInode := fs.GenerateDynamicInode(0, "parent")
|
||||
inode := fs.GenerateDynamicInode(1, "foo")
|
||||
parentInode := inodeFromName(0, "parent")
|
||||
inode := inodeFromName(1, "foo")
|
||||
d, err := newDir(root, inode, parentInode, node)
|
||||
rtest.OK(t, err)
|
||||
|
||||
|
@ -219,3 +219,40 @@ func testTopUIDGID(t *testing.T, cfg Config, repo restic.Repository, uid, gid ui
|
|||
rtest.Equals(t, uint32(0), attr.Uid)
|
||||
rtest.Equals(t, uint32(0), attr.Gid)
|
||||
}
|
||||
|
||||
func TestInodeFromNode(t *testing.T) {
|
||||
node := &restic.Node{Name: "foo.txt", Type: "chardev", Links: 2}
|
||||
ino1 := inodeFromNode(1, node)
|
||||
ino2 := inodeFromNode(2, node)
|
||||
rtest.Assert(t, ino1 == ino2, "inodes %d, %d of hard links differ", ino1, ino2)
|
||||
|
||||
node.Links = 1
|
||||
ino1 = inodeFromNode(1, node)
|
||||
ino2 = inodeFromNode(2, node)
|
||||
rtest.Assert(t, ino1 != ino2, "same inode %d but different parent", ino1)
|
||||
}
|
||||
|
||||
var sink uint64
|
||||
|
||||
func BenchmarkInode(b *testing.B) {
|
||||
for _, sub := range []struct {
|
||||
name string
|
||||
node restic.Node
|
||||
}{
|
||||
{
|
||||
name: "no_hard_links",
|
||||
node: restic.Node{Name: "a somewhat long-ish filename.svg.bz2", Type: "fifo"},
|
||||
},
|
||||
{
|
||||
name: "hard_link",
|
||||
node: restic.Node{Name: "some other filename", Type: "file", Links: 2},
|
||||
},
|
||||
} {
|
||||
b.Run(sub.name, func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink = inodeFromNode(1, &sub.node)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
44
internal/fuse/inode.go
Normal file
44
internal/fuse/inode.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
//go:build darwin || freebsd || linux
|
||||
// +build darwin freebsd linux
|
||||
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
)
|
||||
|
||||
// inodeFromName generates an inode number for a file in a meta dir.
|
||||
func inodeFromName(parent uint64, name string) uint64 {
|
||||
inode := parent ^ xxhash.Sum64String(cleanupNodeName(name))
|
||||
|
||||
// Inode 0 is invalid and 1 is the root. Remap those.
|
||||
if inode < 2 {
|
||||
inode += 2
|
||||
}
|
||||
return inode
|
||||
}
|
||||
|
||||
// inodeFromNode generates an inode number for a file within a snapshot.
|
||||
func inodeFromNode(parent uint64, node *restic.Node) (inode uint64) {
|
||||
if node.Links > 1 && node.Type != "dir" {
|
||||
// If node has hard links, give them all the same inode,
|
||||
// irrespective of the parent.
|
||||
var buf [16]byte
|
||||
binary.LittleEndian.PutUint64(buf[:8], node.DeviceID)
|
||||
binary.LittleEndian.PutUint64(buf[8:], node.Inode)
|
||||
inode = xxhash.Sum64(buf[:])
|
||||
} else {
|
||||
// Else, use the name and the parent inode.
|
||||
// node.{DeviceID,Inode} may not even be reliable.
|
||||
inode = parent ^ xxhash.Sum64String(cleanupNodeName(node.Name))
|
||||
}
|
||||
|
||||
// Inode 0 is invalid and 1 is the root. Remap those.
|
||||
if inode < 2 {
|
||||
inode += 2
|
||||
}
|
||||
return inode
|
||||
}
|
|
@ -78,7 +78,7 @@ func (d *SnapshotsDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
|||
|
||||
for name, entry := range meta.names {
|
||||
d := fuse.Dirent{
|
||||
Inode: fs.GenerateDynamicInode(d.inode, name),
|
||||
Inode: inodeFromName(d.inode, name),
|
||||
Name: name,
|
||||
Type: fuse.DT_Dir,
|
||||
}
|
||||
|
@ -104,12 +104,13 @@ func (d *SnapshotsDir) Lookup(ctx context.Context, name string) (fs.Node, error)
|
|||
|
||||
entry := meta.names[name]
|
||||
if entry != nil {
|
||||
inode := inodeFromName(d.inode, name)
|
||||
if entry.linkTarget != "" {
|
||||
return newSnapshotLink(d.root, fs.GenerateDynamicInode(d.inode, name), entry.linkTarget, entry.snapshot)
|
||||
return newSnapshotLink(d.root, inode, entry.linkTarget, entry.snapshot)
|
||||
} else if entry.snapshot != nil {
|
||||
return newDirFromSnapshot(d.root, fs.GenerateDynamicInode(d.inode, name), entry.snapshot)
|
||||
return newDirFromSnapshot(d.root, inode, entry.snapshot)
|
||||
} else {
|
||||
return NewSnapshotsDir(d.root, fs.GenerateDynamicInode(d.inode, name), d.inode, d.dirStruct, d.prefix+"/"+name), nil
|
||||
return NewSnapshotsDir(d.root, inode, d.inode, d.dirStruct, d.prefix+"/"+name), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue