diff options
Diffstat (limited to 'cmd/btrfs-mount/subvol.go')
-rw-r--r-- | cmd/btrfs-mount/subvol.go | 207 |
1 files changed, 139 insertions, 68 deletions
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 } |