summaryrefslogtreecommitdiff
path: root/cmd/btrfs-mount
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-07-05 12:26:45 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-07-08 00:15:09 -0600
commitb392ad64a8fd04d20b35ad21d2d4ea3ff2778e3f (patch)
treefea515cb26435c767320ecb7b07fe1fec01ce388 /cmd/btrfs-mount
parent08a2266c346edc20ad7bfdda62dc8f685f0be27d (diff)
wip file io
Diffstat (limited to 'cmd/btrfs-mount')
-rw-r--r--cmd/btrfs-mount/lru.go73
-rw-r--r--cmd/btrfs-mount/subvol.go207
-rw-r--r--cmd/btrfs-mount/subvol_fuse.go18
3 files changed, 220 insertions, 78 deletions
diff --git a/cmd/btrfs-mount/lru.go b/cmd/btrfs-mount/lru.go
new file mode 100644
index 0000000..bf91c5e
--- /dev/null
+++ b/cmd/btrfs-mount/lru.go
@@ -0,0 +1,73 @@
+package main
+
+import (
+ "sync"
+
+ lru "github.com/hashicorp/golang-lru"
+)
+
+type LRUCache[K comparable, V any] struct {
+ initOnce sync.Once
+ inner *lru.ARCCache
+}
+
+func (c *LRUCache[K, V]) init() {
+ c.initOnce.Do(func() {
+ c.inner, _ = lru.NewARC(128)
+ })
+}
+
+func (c *LRUCache[K, V]) Add(key K, value V) {
+ c.init()
+ c.inner.Add(key, value)
+}
+func (c *LRUCache[K, V]) Contains(key K) bool {
+ c.init()
+ return c.inner.Contains(key)
+}
+func (c *LRUCache[K, V]) Get(key K) (value V, ok bool) {
+ c.init()
+ _value, ok := c.inner.Get(key)
+ if ok {
+ value = _value.(V)
+ }
+ return value, ok
+}
+func (c *LRUCache[K, V]) Keys() []K {
+ c.init()
+ untyped := c.inner.Keys()
+ typed := make([]K, len(untyped))
+ for i := range untyped {
+ typed[i] = untyped[i].(K)
+ }
+ return typed
+}
+func (c *LRUCache[K, V]) Len() int {
+ c.init()
+ return c.inner.Len()
+}
+func (c *LRUCache[K, V]) Peek(key K) (value V, ok bool) {
+ c.init()
+ _value, ok := c.inner.Peek(key)
+ if ok {
+ value = _value.(V)
+ }
+ return value, ok
+}
+func (c *LRUCache[K, V]) Purge() {
+ c.init()
+ c.inner.Purge()
+}
+func (c *LRUCache[K, V]) Remove(key K) {
+ c.init()
+ c.inner.Remove(key)
+}
+
+func (c *LRUCache[K, V]) GetOrElse(key K, fn func() V) V {
+ var value V
+ var ok bool
+ for value, ok = c.Get(key); !ok; value, ok = c.Get(key) {
+ c.Add(key, fn())
+ }
+ return value
+}
diff --git a/cmd/btrfs-mount/subvol.go b/cmd/btrfs-mount/subvol.go
index e4e0460..4e741ad 100644
--- a/cmd/btrfs-mount/subvol.go
+++ b/cmd/btrfs-mount/subvol.go
@@ -7,8 +7,8 @@ import (
"sync"
"github.com/datawire/dlib/dcontext"
+ "github.com/datawire/dlib/derror"
"github.com/datawire/dlib/dlog"
- lru "github.com/hashicorp/golang-lru"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseutil"
@@ -18,6 +18,28 @@ import (
"lukeshu.com/btrfs-tools/pkg/util"
)
+type bareInode struct {
+ Inode btrfs.ObjID
+ InodeItem *btrfsitem.Inode
+ Errs derror.MultiError
+}
+
+type fullInode struct {
+ bareInode
+ OtherItems []btrfs.Item
+}
+
+type dir struct {
+ fullInode
+ ChildrenByName map[string]btrfsitem.DirEntry
+ ChildrenByIndex map[uint64]btrfsitem.DirEntry
+}
+
+type file struct {
+ fullInode
+ // TODO
+}
+
type Subvolume struct {
FS *btrfs.FS
DeviceName string
@@ -28,8 +50,10 @@ type Subvolume struct {
rootVal btrfsitem.Root
rootErr error
- inodeCache *lru.ARCCache
- dirCache *lru.ARCCache
+ bareInodeCache LRUCache[btrfs.ObjID, *bareInode]
+ fullInodeCache LRUCache[btrfs.ObjID, *fullInode]
+ dirCache LRUCache[btrfs.ObjID, *dir]
+ fileCache LRUCache[btrfs.ObjID, *file]
subvolumeFUSE
}
@@ -79,11 +103,6 @@ func (sv *Subvolume) init() {
}
sv.rootVal = rootBody
-
- sv.inodeCache, _ = lru.NewARC(128)
- sv.dirCache, _ = lru.NewARC(128)
-
- sv.subvolumeFUSE.init()
})
}
@@ -97,73 +116,108 @@ func (sv *Subvolume) getFSTree() (btrfsvol.LogicalAddr, error) {
return sv.rootVal.ByteNr, sv.rootErr
}
-func (sv *Subvolume) loadInode(inode btrfs.ObjID) (btrfsitem.Inode, error) {
- tree, err := sv.getFSTree()
- if err != nil {
- return btrfsitem.Inode{}, nil
- }
- if ret, ok := sv.inodeCache.Get(inode); ok {
- return ret.(btrfsitem.Inode), nil
- }
- item, err := sv.FS.TreeLookup(tree, btrfs.Key{
- ObjectID: inode,
- ItemType: btrfsitem.INODE_ITEM_KEY,
- Offset: 0,
- })
- if err != nil {
- return btrfsitem.Inode{}, err
- }
+func (sv *Subvolume) loadBareInode(inode btrfs.ObjID) (*bareInode, error) {
+ val := sv.bareInodeCache.GetOrElse(inode, func() (val *bareInode) {
+ val = &bareInode{
+ Inode: inode,
+ }
+ tree, err := sv.getFSTree()
+ if err != nil {
+ val.Errs = append(val.Errs, err)
+ return
+ }
+ item, err := sv.FS.TreeLookup(tree, btrfs.Key{
+ ObjectID: inode,
+ ItemType: btrfsitem.INODE_ITEM_KEY,
+ Offset: 0,
+ })
+ if err != nil {
+ val.Errs = append(val.Errs, err)
+ return
+ }
- itemBody, ok := item.Body.(btrfsitem.Inode)
- if !ok {
- return btrfsitem.Inode{}, fmt.Errorf("malformed inode")
- }
+ itemBody, ok := item.Body.(btrfsitem.Inode)
+ if !ok {
+ val.Errs = append(val.Errs, fmt.Errorf("malformed inode"))
+ return
+ }
+ val.InodeItem = &itemBody
- sv.inodeCache.Add(inode, itemBody)
- return itemBody, nil
+ return
+ })
+ if val.InodeItem == nil {
+ return nil, val.Errs
+ }
+ return val, nil
}
-type dir struct {
- Inode btrfs.ObjID
- InodeDat *btrfsitem.Inode
- ChildrenByName map[string]btrfsitem.DirEntry
- ChildrenByIndex map[uint64]btrfsitem.DirEntry
- Errs []error
+func (sv *Subvolume) loadFullInode(inode btrfs.ObjID) (*fullInode, error) {
+ val := sv.fullInodeCache.GetOrElse(inode, func() (val *fullInode) {
+ val = &fullInode{
+ bareInode: bareInode{
+ Inode: inode,
+ },
+ }
+ tree, err := sv.getFSTree()
+ if err != nil {
+ val.Errs = append(val.Errs, err)
+ return
+ }
+ items, err := sv.FS.TreeSearchAll(tree, func(key btrfs.Key) int {
+ return util.CmpUint(inode, key.ObjectID)
+ })
+ if err != nil {
+ val.Errs = append(val.Errs, err)
+ if len(items) == 0 {
+ return
+ }
+ }
+ for _, item := range items {
+ switch item.Head.Key.ItemType {
+ case btrfsitem.INODE_ITEM_KEY:
+ itemBody := item.Body.(btrfsitem.Inode)
+ if val.InodeItem != nil {
+ if !reflect.DeepEqual(itemBody, *val.InodeItem) {
+ val.Errs = append(val.Errs, fmt.Errorf("multiple inodes"))
+ }
+ continue
+ }
+ val.InodeItem = &itemBody
+ default:
+ val.OtherItems = append(val.OtherItems, item)
+ }
+ }
+ return
+ })
+ if val.InodeItem == nil && val.OtherItems == nil {
+ return nil, val.Errs
+ }
+ return val, nil
}
func (sv *Subvolume) loadDir(inode btrfs.ObjID) (*dir, error) {
- tree, err := sv.getFSTree()
- if err != nil {
- return nil, err
- }
- if ret, ok := sv.dirCache.Get(inode); ok {
- return ret.(*dir), nil
- }
- ret := &dir{
- Inode: inode,
- ChildrenByName: make(map[string]btrfsitem.DirEntry),
- ChildrenByIndex: make(map[uint64]btrfsitem.DirEntry),
- }
- items, err := sv.FS.TreeSearchAll(tree, func(key btrfs.Key) int {
- return util.CmpUint(inode, key.ObjectID)
- })
- if err != nil {
- if len(items) == 0 {
- return nil, err
+ val := sv.dirCache.GetOrElse(inode, func() (val *dir) {
+ val = new(dir)
+ fullInode, err := sv.loadFullInode(inode)
+ if err != nil {
+ val.Errs = append(val.Errs, err)
+ return
}
- ret.Errs = append(ret.Errs, err)
+ val.fullInode = *fullInode
+ val.populate()
+ return
+ })
+ if val.Inode == 0 {
+ return nil, val.Errs
}
- for _, item := range items {
+ return val, nil
+}
+
+func (ret *dir) populate() {
+ ret.ChildrenByName = make(map[string]btrfsitem.DirEntry)
+ ret.ChildrenByIndex = make(map[uint64]btrfsitem.DirEntry)
+ for _, item := range ret.OtherItems {
switch item.Head.Key.ItemType {
- case btrfsitem.INODE_ITEM_KEY:
- itemBody := item.Body.(btrfsitem.Inode)
- if ret.InodeDat != nil {
- if !reflect.DeepEqual(itemBody, *ret.InodeDat) {
- ret.Errs = append(ret.Errs, fmt.Errorf("multiple inodes"))
- }
- continue
- }
- ret.InodeDat = &itemBody
case btrfsitem.INODE_REF_KEY:
// TODO
case btrfsitem.DIR_ITEM_KEY:
@@ -205,7 +259,7 @@ func (sv *Subvolume) loadDir(inode btrfs.ObjID) (*dir, error) {
}
//case btrfsitem.XATTR_ITEM_KEY:
default:
- ret.Errs = append(ret.Errs, fmt.Errorf("TODO: handle item type %v", item.Head.Key.ItemType))
+ panic(fmt.Errorf("TODO: handle item type %v", item.Head.Key.ItemType))
}
}
entriesWithIndexes := make(map[string]struct{})
@@ -230,6 +284,23 @@ func (sv *Subvolume) loadDir(inode btrfs.ObjID) (*dir, error) {
nextIndex++
}
}
- sv.dirCache.Add(inode, ret)
- return ret, nil
+ return
+}
+
+func (sv *Subvolume) loadFile(inode btrfs.ObjID) (*file, error) {
+ val := sv.fileCache.GetOrElse(inode, func() (val *file) {
+ val = new(file)
+ fullInode, err := sv.loadFullInode(inode)
+ if err != nil {
+ val.Errs = append(val.Errs, err)
+ return
+ }
+ val.fullInode = *fullInode
+ // TODO
+ return
+ })
+ if val.Inode == 0 {
+ return nil, val.Errs
+ }
+ return val, nil
}
diff --git a/cmd/btrfs-mount/subvol_fuse.go b/cmd/btrfs-mount/subvol_fuse.go
index 8227ee2..7500e0b 100644
--- a/cmd/btrfs-mount/subvol_fuse.go
+++ b/cmd/btrfs-mount/subvol_fuse.go
@@ -19,7 +19,7 @@ type dirState struct {
}
type fileState struct {
- InodeItem btrfsitem.Inode
+ File *file
}
type subvolumeFUSE struct {
@@ -29,8 +29,6 @@ type subvolumeFUSE struct {
fileHandles util.SyncMap[fuseops.HandleID, *fileState]
}
-func (sv *subvolumeFUSE) init() {}
-
func (sv *subvolumeFUSE) newHandle() fuseops.HandleID {
return fuseops.HandleID(atomic.AddUint64(&sv.lastHandle, 1))
}
@@ -93,14 +91,14 @@ func (sv *Subvolume) LookUpInode(_ context.Context, op *fuseops.LookUpInodeOp) e
if entry.Location.ItemType != btrfsitem.INODE_ITEM_KEY {
return fmt.Errorf("child %q is not an inode: %w", op.Name, syscall.ENOSYS)
}
- inodeItem, err := sv.loadInode(entry.Location.ObjectID)
+ bareInode, err := sv.loadBareInode(entry.Location.ObjectID)
if err != nil {
return err
}
op.Entry = fuseops.ChildInodeEntry{
Child: fuseops.InodeID(entry.Location.ObjectID),
- Generation: fuseops.GenerationNumber(inodeItem.Sequence),
- Attributes: inodeItemToFUSE(inodeItem),
+ Generation: fuseops.GenerationNumber(bareInode.InodeItem.Sequence),
+ Attributes: inodeItemToFUSE(*bareInode.InodeItem),
}
return nil
}
@@ -114,12 +112,12 @@ func (sv *Subvolume) GetInodeAttributes(_ context.Context, op *fuseops.GetInodeA
op.Inode = fuseops.InodeID(inode)
}
- inodeItem, err := sv.loadInode(btrfs.ObjID(op.Inode))
+ bareInode, err := sv.loadBareInode(btrfs.ObjID(op.Inode))
if err != nil {
return err
}
- op.Attributes = inodeItemToFUSE(inodeItem)
+ op.Attributes = inodeItemToFUSE(*bareInode.InodeItem)
return nil
}
@@ -186,13 +184,13 @@ func (sv *Subvolume) ReleaseDirHandle(_ context.Context, op *fuseops.ReleaseDirH
}
func (sv *Subvolume) OpenFile(_ context.Context, op *fuseops.OpenFileOp) error {
- inodeItem, err := sv.loadInode(btrfs.ObjID(op.Inode))
+ file, err := sv.loadFile(btrfs.ObjID(op.Inode))
if err != nil {
return err
}
handle := sv.newHandle()
sv.fileHandles.Store(handle, &fileState{
- InodeItem: inodeItem,
+ File: file,
})
op.Handle = handle
op.KeepPageCache = true