summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-03-30 12:14:34 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2023-03-30 12:14:34 -0600
commit3d5e080385ed64ca5e0810263acc2d9970f14baa (patch)
treeaf7a1e957d0ffcae3e2ba3d3a11a8c64a545a65f
parente62b128e088346e891b8b2a5e6458cf77abc9d02 (diff)
parentc7b6460ee9b3c07c13c973cbc8c8f690560fefc6 (diff)
Merge branch 'lukeshu/tree-api-pt3-forrest'
-rw-r--r--cmd/btrfs-rec/inspect/dumptrees/print_tree.go96
-rw-r--r--cmd/btrfs-rec/inspect/lsfiles/lsfiles.go9
-rw-r--r--cmd/btrfs-rec/inspect/mount/mount.go2
-rw-r--r--cmd/btrfs-rec/inspect_lsfiles.go2
-rw-r--r--cmd/btrfs-rec/inspect_lstrees.go4
-rw-r--r--cmd/btrfs-rec/inspect_spewitems.go7
-rw-r--r--lib/btrfs/btrfstree/btree.go133
-rw-r--r--lib/btrfs/btrfstree/btree_forrest.go84
-rw-r--r--lib/btrfs/btrfstree/btree_tree.go37
-rw-r--r--lib/btrfs/csums.go9
-rw-r--r--lib/btrfs/io2_lv.go54
-rw-r--r--lib/btrfs/io3_btree.go37
-rw-r--r--lib/btrfs/io4_fs.go52
-rw-r--r--lib/btrfsutil/old_rebuilt_forrest.go123
-rw-r--r--lib/btrfsutil/walk.go53
15 files changed, 364 insertions, 338 deletions
diff --git a/cmd/btrfs-rec/inspect/dumptrees/print_tree.go b/cmd/btrfs-rec/inspect/dumptrees/print_tree.go
index 75797a8..735a50b 100644
--- a/cmd/btrfs-rec/inspect/dumptrees/print_tree.go
+++ b/cmd/btrfs-rec/inspect/dumptrees/print_tree.go
@@ -23,7 +23,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/textui"
)
-func DumpTrees(ctx context.Context, out io.Writer, fs *btrfs.FS) {
+func DumpTrees(ctx context.Context, out io.Writer, fs btrfs.ReadableFS) {
superblock, err := fs.Superblock()
if err != nil {
dlog.Error(ctx, err)
@@ -46,45 +46,44 @@ func DumpTrees(ctx context.Context, out io.Writer, fs *btrfs.FS) {
textui.Fprintf(out, "block group tree\n")
printTree(ctx, out, fs, btrfsprim.BLOCK_GROUP_TREE_OBJECTID)
}
- fs.TreeWalk(
- ctx,
- btrfsprim.ROOT_TREE_OBJECTID,
- func(err *btrfstree.TreeError) {
- dlog.Error(ctx, err)
- },
- btrfstree.TreeWalkHandler{
- Item: func(_ btrfstree.Path, item btrfstree.Item) {
- if item.Key.ItemType != btrfsitem.ROOT_ITEM_KEY {
- return
- }
- treeName, ok := map[btrfsprim.ObjID]string{
- btrfsprim.ROOT_TREE_OBJECTID: "root",
- btrfsprim.EXTENT_TREE_OBJECTID: "extent",
- btrfsprim.CHUNK_TREE_OBJECTID: "chunk",
- btrfsprim.DEV_TREE_OBJECTID: "device",
- btrfsprim.FS_TREE_OBJECTID: "fs",
- btrfsprim.ROOT_TREE_DIR_OBJECTID: "directory",
- btrfsprim.CSUM_TREE_OBJECTID: "checksum",
- btrfsprim.ORPHAN_OBJECTID: "orphan",
- btrfsprim.TREE_LOG_OBJECTID: "log",
- btrfsprim.TREE_LOG_FIXUP_OBJECTID: "log fixup",
- btrfsprim.TREE_RELOC_OBJECTID: "reloc",
- btrfsprim.DATA_RELOC_TREE_OBJECTID: "data reloc",
- btrfsprim.EXTENT_CSUM_OBJECTID: "extent checksum",
- btrfsprim.QUOTA_TREE_OBJECTID: "quota",
- btrfsprim.UUID_TREE_OBJECTID: "uuid",
- btrfsprim.FREE_SPACE_TREE_OBJECTID: "free space",
- btrfsprim.MULTIPLE_OBJECTIDS: "multiple",
- btrfsprim.BLOCK_GROUP_TREE_OBJECTID: "block group",
- }[item.Key.ObjectID]
- if !ok {
- treeName = "file"
- }
- textui.Fprintf(out, "%v tree key %v \n", treeName, item.Key.Format(btrfsprim.ROOT_TREE_OBJECTID))
- printTree(ctx, out, fs, item.Key.ObjectID)
- },
- },
- )
+ rootTree, err := fs.ForrestLookup(ctx, btrfsprim.ROOT_TREE_OBJECTID)
+ if err != nil {
+ dlog.Errorf(ctx, "root tree: %v", err)
+ } else {
+ if err := rootTree.TreeRange(ctx, func(item btrfstree.Item) bool {
+ if item.Key.ItemType != btrfsitem.ROOT_ITEM_KEY {
+ return true
+ }
+ treeName, ok := map[btrfsprim.ObjID]string{
+ btrfsprim.ROOT_TREE_OBJECTID: "root",
+ btrfsprim.EXTENT_TREE_OBJECTID: "extent",
+ btrfsprim.CHUNK_TREE_OBJECTID: "chunk",
+ btrfsprim.DEV_TREE_OBJECTID: "device",
+ btrfsprim.FS_TREE_OBJECTID: "fs",
+ btrfsprim.ROOT_TREE_DIR_OBJECTID: "directory",
+ btrfsprim.CSUM_TREE_OBJECTID: "checksum",
+ btrfsprim.ORPHAN_OBJECTID: "orphan",
+ btrfsprim.TREE_LOG_OBJECTID: "log",
+ btrfsprim.TREE_LOG_FIXUP_OBJECTID: "log fixup",
+ btrfsprim.TREE_RELOC_OBJECTID: "reloc",
+ btrfsprim.DATA_RELOC_TREE_OBJECTID: "data reloc",
+ btrfsprim.EXTENT_CSUM_OBJECTID: "extent checksum",
+ btrfsprim.QUOTA_TREE_OBJECTID: "quota",
+ btrfsprim.UUID_TREE_OBJECTID: "uuid",
+ btrfsprim.FREE_SPACE_TREE_OBJECTID: "free space",
+ btrfsprim.MULTIPLE_OBJECTIDS: "multiple",
+ btrfsprim.BLOCK_GROUP_TREE_OBJECTID: "block group",
+ }[item.Key.ObjectID]
+ if !ok {
+ treeName = "file"
+ }
+ textui.Fprintf(out, "%v tree key %v \n", treeName, item.Key.Format(btrfsprim.ROOT_TREE_OBJECTID))
+ printTree(ctx, out, fs, item.Key.ObjectID)
+ return true
+ }); err != nil {
+ dlog.Errorf(ctx, "iterating over root tree: %v", err)
+ }
+ }
textui.Fprintf(out, "total bytes %v\n", superblock.TotalBytes)
textui.Fprintf(out, "bytes used %v\n", superblock.BytesUsed)
textui.Fprintf(out, "uuid %v\n", superblock.FSUUID)
@@ -95,7 +94,7 @@ var nodeHeaderSize = binstruct.StaticSize(btrfstree.NodeHeader{})
// printTree mimics btrfs-progs
// kernel-shared/print-tree.c:btrfs_print_tree() and
// kernel-shared/print-tree.c:btrfs_print_leaf()
-func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfsprim.ObjID) {
+func printTree(ctx context.Context, out io.Writer, fs btrfs.ReadableFS, treeID btrfsprim.ObjID) {
var itemOffset uint32
handlers := btrfstree.TreeWalkHandler{
Node: func(path btrfstree.Path, node *btrfstree.Node) {
@@ -357,14 +356,13 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri
},
}
handlers.BadItem = handlers.Item
- fs.TreeWalk(
- ctx,
- treeID,
- func(err *btrfstree.TreeError) {
- dlog.Error(ctx, err)
- },
- handlers,
- )
+
+ tree, err := fs.ForrestLookup(ctx, treeID)
+ if err != nil {
+ dlog.Errorf(ctx, "tree %v: %v", treeID, err)
+ return
+ }
+ tree.TreeWalk(ctx, handlers)
}
// printHeaderInfo mimics btrfs-progs kernel-shared/print-tree.c:print_header_info()
diff --git a/cmd/btrfs-rec/inspect/lsfiles/lsfiles.go b/cmd/btrfs-rec/inspect/lsfiles/lsfiles.go
index af8f690..d172e2e 100644
--- a/cmd/btrfs-rec/inspect/lsfiles/lsfiles.go
+++ b/cmd/btrfs-rec/inspect/lsfiles/lsfiles.go
@@ -20,9 +20,6 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfstree"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
- "git.lukeshu.com/btrfs-progs-ng/lib/diskio"
"git.lukeshu.com/btrfs-progs-ng/lib/maps"
"git.lukeshu.com/btrfs-progs-ng/lib/textui"
)
@@ -30,11 +27,7 @@ import (
func LsFiles(
ctx context.Context,
out io.Writer,
- fs interface {
- btrfstree.TreeOperator
- Superblock() (*btrfstree.Superblock, error)
- diskio.ReaderAt[btrfsvol.LogicalAddr]
- },
+ fs btrfs.ReadableFS,
) (err error) {
defer func() {
if _err := derror.PanicToError(recover()); _err != nil {
diff --git a/cmd/btrfs-rec/inspect/mount/mount.go b/cmd/btrfs-rec/inspect/mount/mount.go
index 143131a..28363ed 100644
--- a/cmd/btrfs-rec/inspect/mount/mount.go
+++ b/cmd/btrfs-rec/inspect/mount/mount.go
@@ -54,7 +54,7 @@ func MountRO(ctx context.Context, fs *btrfs.FS, mountpoint string, noChecksums b
rootSubvol := &subvolume{
Subvolume: btrfs.NewSubvolume(
ctx,
- btrfsutil.NewOldRebuiltForrest(ctx, fs),
+ btrfsutil.NewOldRebuiltForrest(fs),
btrfsprim.FS_TREE_OBJECTID,
noChecksums,
),
diff --git a/cmd/btrfs-rec/inspect_lsfiles.go b/cmd/btrfs-rec/inspect_lsfiles.go
index 00a4873..abc73bc 100644
--- a/cmd/btrfs-rec/inspect_lsfiles.go
+++ b/cmd/btrfs-rec/inspect_lsfiles.go
@@ -32,7 +32,7 @@ func init() {
return lsfiles.LsFiles(
cmd.Context(),
out,
- btrfsutil.NewOldRebuiltForrest(cmd.Context(), fs))
+ btrfsutil.NewOldRebuiltForrest(fs))
}),
})
}
diff --git a/cmd/btrfs-rec/inspect_lstrees.go b/cmd/btrfs-rec/inspect_lstrees.go
index 5887983..0eb45ba 100644
--- a/cmd/btrfs-rec/inspect_lstrees.go
+++ b/cmd/btrfs-rec/inspect_lstrees.go
@@ -71,10 +71,10 @@ func init() {
treeItemCnt = make(map[btrfsitem.Type]int)
textui.Fprintf(os.Stdout, "tree id=%v name=%q\n", treeID, name)
},
- Err: func(_ *btrfsutil.WalkError) {
+ BadTree: func(_ string, _ btrfsprim.ObjID, _ error) {
treeErrCnt++
},
- TreeWalkHandler: btrfstree.TreeWalkHandler{
+ Tree: btrfstree.TreeWalkHandler{
Node: func(path btrfstree.Path, node *btrfstree.Node) {
visitedNodes.Insert(node.Head.Addr)
},
diff --git a/cmd/btrfs-rec/inspect_spewitems.go b/cmd/btrfs-rec/inspect_spewitems.go
index c3a1e6b..94d34d9 100644
--- a/cmd/btrfs-rec/inspect_spewitems.go
+++ b/cmd/btrfs-rec/inspect_spewitems.go
@@ -13,6 +13,7 @@ import (
"github.com/spf13/cobra"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfstree"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfsutil"
"git.lukeshu.com/btrfs-progs-ng/lib/textui"
@@ -30,10 +31,10 @@ func init() {
spew.DisablePointerAddresses = true
btrfsutil.WalkAllTrees(ctx, fs, btrfsutil.WalkAllTreesHandler{
- Err: func(err *btrfsutil.WalkError) {
- dlog.Error(ctx, err)
+ BadTree: func(name string, id btrfsprim.ObjID, err error) {
+ dlog.Errorf(ctx, "%v: %v", name, err)
},
- TreeWalkHandler: btrfstree.TreeWalkHandler{
+ Tree: btrfstree.TreeWalkHandler{
Item: func(path btrfstree.Path, item btrfstree.Item) {
textui.Fprintf(os.Stdout, "%s = ", path)
spew.Dump(item)
diff --git a/lib/btrfs/btrfstree/btree.go b/lib/btrfs/btrfstree/btree.go
index 19c7c68..25259c0 100644
--- a/lib/btrfs/btrfstree/btree.go
+++ b/lib/btrfs/btrfstree/btree.go
@@ -14,7 +14,62 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
)
+type Forrest interface {
+ // ForrestLookup returns (nil, ErrNoTree) if the tree does not
+ // exist but there is otherwise no error.
+ ForrestLookup(ctx context.Context, treeID btrfsprim.ObjID) (Tree, error)
+}
+
type Tree interface {
+ // TreeLookup looks up the Item for a given key.
+ //
+ // If no such Item exists, but there is otherwise no error,
+ // then ErrNoItem is returned.
+ TreeLookup(ctx context.Context, key btrfsprim.Key) (Item, error)
+
+ // TreeSearch searches the Tree for a value for which
+ // `search.Search(itemKey, itemSize) == 0`.
+ //
+ // : + + + 0 0 0 - - -
+ // : ^ ^ ^
+ // : any of
+ //
+ // You can conceptualize `search.Search` as subtraction:
+ //
+ // func(strawKey btrfsprim.Key, strawSize uint32) int {
+ // return needle - straw
+ // }
+ //
+ // `search.Search` may be called with key/size pairs that do not
+ // correspond to an existing Item (for key-pointers in
+ // interior nodes in the tree); in which case the size
+ // math.MaxUint32.
+ //
+ // If no such Item exists, but there is otherwise no error,
+ // then an error that is ErrNoItem is returned.
+ TreeSearch(ctx context.Context, search TreeSearcher) (Item, error)
+
+ // TreeRange iterates over the Tree in order, calling
+ // `handleFn` for each Item.
+ TreeRange(ctx context.Context, handleFn func(Item) bool) error
+
+ // TreeSubrange iterates over the Tree in order, calling
+ // `handleFn` for all Items for which `search.Search` returns
+ // 0.
+ //
+ // `search.Search` may be called with key/size pairs that do
+ // not correspond to an existing Item (for key-pointers in
+ // interior nodes in the tree); in which case the size
+ // math.MaxUint32.
+ //
+ // If handleFn is called for fewer than `min` items, an error
+ // that is ErrNoItem is returned.
+ TreeSubrange(ctx context.Context,
+ min int,
+ search TreeSearcher,
+ handleFn func(Item) bool,
+ ) error
+
// CheckOwner returns whether it is permissible for a node
// with .Head.Owner=owner and .Head.Generation=gen to be in
// this tree.
@@ -23,6 +78,33 @@ type Tree interface {
// specifies whether it should return an error (false) or nil
// (true).
TreeCheckOwner(ctx context.Context, failOpen bool, owner btrfsprim.ObjID, gen btrfsprim.Generation) error
+
+ // TreeWalk is a lower-level call than TreeSubrange. Use with
+ // hesitancy.
+ //
+ // It walks a Tree, triggering callbacks for every node, key-pointer,
+ // and item; as well as for any errors encountered.
+ //
+ // If the Tree is valid, then everything is walked in key-order; but
+ // if the Tree is broken, then ordering is not guaranteed.
+ //
+ // Canceling the Context causes TreeWalk to return early; no values
+ // from the Context are used.
+ //
+ // The lifecycle of callbacks is:
+ //
+ // 000 (read superblock) (maybe cbs.BadSuperblock())
+ //
+ // 001 (read node)
+ // 002 cbs.Node() or cbs.BadNode()
+ // if interior:
+ // for kp in node.items:
+ // 003a if cbs.PreKeyPointer == nil || cbs.PreKeyPointer() {
+ // 004b (recurse)
+ // else:
+ // for item in node.items:
+ // 003b cbs.Item() or cbs.BadItem()
+ TreeWalk(ctx context.Context, cbs TreeWalkHandler)
}
type TreeSearcher interface {
@@ -55,57 +137,6 @@ type TreeWalkHandler struct {
BadItem func(Path, Item)
}
-// TreeOperator is an interface for performing basic btree operations.
-type TreeOperator interface {
- // TreeWalk walks a tree, triggering callbacks for every node,
- // key-pointer, and item; as well as for any errors encountered.
- //
- // If the tree is valid, then everything is walked in key-order; but
- // if the tree is broken, then ordering is not guaranteed.
- //
- // Canceling the Context causes TreeWalk to return early; no values
- // from the Context are used.
- //
- // The lifecycle of callbacks is:
- //
- // 000 (read superblock) (maybe cbs.BadSuperblock())
- //
- // 001 (read node)
- // 002 cbs.Node() or cbs.BadNode()
- // if interior:
- // for kp in node.items:
- // 003a if cbs.PreKeyPointer == nil || cbs.PreKeyPointer() {
- // 004b (recurse)
- // else:
- // for item in node.items:
- // 003b cbs.Item() or cbs.BadItem()
- TreeWalk(ctx context.Context, treeID btrfsprim.ObjID, errHandle func(*TreeError), cbs TreeWalkHandler)
-
- TreeLookup(treeID btrfsprim.ObjID, key btrfsprim.Key) (Item, error)
- TreeSearch(treeID btrfsprim.ObjID, search TreeSearcher) (Item, error)
-
- // If some items are able to be read, but there is an error reading the
- // full set, then it might return *both* a list of items and an error.
- //
- // If the tree is not found, an error that is ErrNoTree is
- // returned.
- //
- // If no such item is found, an error that is ErrNoItem is
- // returned.
- TreeSearchAll(treeID btrfsprim.ObjID, search TreeSearcher) ([]Item, error)
-}
-
-type TreeError struct {
- Path Path
- Err error
-}
-
-func (e *TreeError) Unwrap() error { return e.Err }
-
-func (e *TreeError) Error() string {
- return fmt.Sprintf("%v: %v", e.Path, e.Err)
-}
-
type NodeSource interface {
Superblock() (*Superblock, error)
AcquireNode(ctx context.Context, addr btrfsvol.LogicalAddr, exp NodeExpectations) (*Node, error)
diff --git a/lib/btrfs/btrfstree/btree_forrest.go b/lib/btrfs/btrfstree/btree_forrest.go
index 1875c2f..1a80504 100644
--- a/lib/btrfs/btrfstree/btree_forrest.go
+++ b/lib/btrfs/btrfstree/btree_forrest.go
@@ -14,6 +14,8 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
)
+// LookupTreeRoot //////////////////////////////////////////////////////////////
+
// A TreeRoot is more-or-less a btrfsitem.Root, but simpler; returned by
// LookupTreeRoot.
type TreeRoot struct {
@@ -28,8 +30,16 @@ type TreeRoot struct {
}
// LookupTreeRoot is a utility function to help with implementing the
-// 'TreeOperator' interface.
-func LookupTreeRoot(_ context.Context, fs TreeOperator, sb Superblock, treeID btrfsprim.ObjID) (*TreeRoot, error) {
+// 'Forrest' interface.
+//
+// The 'forrest' passed to LookupTreeRoot must handle:
+//
+// forrest.ForrestLookup(ctx, btrfsprim.ROOT_TREE_OBJECTID)
+//
+// It is OK for forrest.ForrestLookup to recurse and call
+// LookupTreeRoot, as LookupTreeRoot will not call ForrestLookup for
+// ROOT_TREE_OBJECTID; so it will not be an infinite recursion.
+func LookupTreeRoot(ctx context.Context, forrest Forrest, sb Superblock, treeID btrfsprim.ObjID) (*TreeRoot, error) {
switch treeID {
case btrfsprim.ROOT_TREE_OBJECTID:
return &TreeRoot{
@@ -60,7 +70,11 @@ func LookupTreeRoot(_ context.Context, fs TreeOperator, sb Superblock, treeID bt
Generation: sb.BlockGroupRootGeneration,
}, nil
default:
- rootItem, err := fs.TreeSearch(btrfsprim.ROOT_TREE_OBJECTID, SearchRootItem(treeID))
+ rootTree, err := forrest.ForrestLookup(ctx, btrfsprim.ROOT_TREE_OBJECTID)
+ if err != nil {
+ return nil, fmt.Errorf("tree %s: %w", treeID.Format(btrfsprim.ROOT_TREE_OBJECTID), err)
+ }
+ rootItem, err := rootTree.TreeSearch(ctx, SearchRootItem(treeID))
if err != nil {
if errors.Is(err, ErrNoItem) {
err = fmt.Errorf("%w: %s", ErrNoTree, err)
@@ -87,69 +101,37 @@ func LookupTreeRoot(_ context.Context, fs TreeOperator, sb Superblock, treeID bt
}
}
-type TreeOperatorImpl struct {
+// RawForrest //////////////////////////////////////////////////////////////////
+
+// RawForrest implements Forrest.
+type RawForrest struct {
NodeSource NodeSource
}
-func (fs TreeOperatorImpl) RawTree(ctx context.Context, treeID btrfsprim.ObjID) (*RawTree, error) {
- sb, err := fs.NodeSource.Superblock()
+var _ Forrest = RawForrest{}
+
+// RawTree is a variant of ForrestLookup that returns a concrete type
+// instead of an interface.
+func (forrest RawForrest) RawTree(ctx context.Context, treeID btrfsprim.ObjID) (*RawTree, error) {
+ sb, err := forrest.NodeSource.Superblock()
if err != nil {
return nil, err
}
- rootInfo, err := LookupTreeRoot(ctx, fs, *sb, treeID)
+ rootInfo, err := LookupTreeRoot(ctx, forrest, *sb, treeID)
if err != nil {
return nil, err
}
return &RawTree{
- Forrest: fs,
+ Forrest: forrest,
TreeRoot: *rootInfo,
}, nil
}
-// TreeWalk implements the 'TreeOperator' interface.
-func (fs TreeOperatorImpl) TreeWalk(ctx context.Context, treeID btrfsprim.ObjID, errHandle func(*TreeError), cbs TreeWalkHandler) {
- tree, err := fs.RawTree(ctx, treeID)
- if err != nil {
- errHandle(&TreeError{Path: Path{PathRoot{TreeID: treeID}}, Err: err})
- return
- }
- tree.TreeWalk(ctx, cbs)
-}
-
-// TreeLookup implements the 'TreeOperator' interface.
-func (fs TreeOperatorImpl) TreeLookup(treeID btrfsprim.ObjID, key btrfsprim.Key) (Item, error) {
- ctx := context.TODO()
- tree, err := fs.RawTree(ctx, treeID)
- if err != nil {
- return Item{}, err
- }
- return tree.TreeLookup(ctx, key)
-}
-
-// TreeSearch implements the 'TreeOperator' interface.
-func (fs TreeOperatorImpl) TreeSearch(treeID btrfsprim.ObjID, searcher TreeSearcher) (Item, error) {
- ctx := context.TODO()
- tree, err := fs.RawTree(ctx, treeID)
- if err != nil {
- return Item{}, err
- }
- return tree.TreeSearch(ctx, searcher)
-}
-
-// TreeSearchAll implements the 'TreeOperator' interface.
-func (fs TreeOperatorImpl) TreeSearchAll(treeID btrfsprim.ObjID, searcher TreeSearcher) ([]Item, error) {
- ctx := context.TODO()
- tree, err := fs.RawTree(ctx, treeID)
+// ForrestLookup implements Forrest.
+func (forrest RawForrest) ForrestLookup(ctx context.Context, treeID btrfsprim.ObjID) (Tree, error) {
+ tree, err := forrest.RawTree(ctx, treeID)
if err != nil {
return nil, err
}
-
- var ret []Item
- err = tree.TreeSubrange(ctx, 1, searcher, func(item Item) bool {
- item.Body = item.Body.CloneItem()
- ret = append(ret, item)
- return true
- })
-
- return ret, err
+ return tree, nil
}
diff --git a/lib/btrfs/btrfstree/btree_tree.go b/lib/btrfs/btrfstree/btree_tree.go
index 27d6548..7cfa257 100644
--- a/lib/btrfs/btrfstree/btree_tree.go
+++ b/lib/btrfs/btrfstree/btree_tree.go
@@ -16,11 +16,15 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/slices"
)
+// RawTree implements Tree.
type RawTree struct {
- Forrest TreeOperatorImpl
+ Forrest RawForrest
TreeRoot
}
+var _ Tree = (*RawTree)(nil)
+
+// TreeWalk implements the 'Tree' interface.
func (tree *RawTree) TreeWalk(ctx context.Context, cbs TreeWalkHandler) {
sb, err := tree.Forrest.NodeSource.Superblock()
if err != nil {
@@ -161,6 +165,7 @@ func searchKP(haystack []KeyPointer, searchFn func(key btrfsprim.Key, size uint3
return KeyPointer{}, false
}
+// TreeSearch implements the 'Tree' interface.
func (tree *RawTree) TreeSearch(ctx context.Context, searcher TreeSearcher) (Item, error) {
ctx, cancel := context.WithCancel(ctx)
var retErr error
@@ -206,10 +211,40 @@ func (tree *RawTree) TreeSearch(ctx context.Context, searcher TreeSearcher) (Ite
return ret, retErr
}
+// TreeLookup implements the 'Tree' interface.
func (tree *RawTree) TreeLookup(ctx context.Context, key btrfsprim.Key) (Item, error) {
return tree.TreeSearch(ctx, SearchExactKey(key))
}
+// TreeRange implements the 'Tree' interface.
+func (tree *RawTree) TreeRange(ctx context.Context, handleFn func(Item) bool) error {
+ ctx, cancel := context.WithCancel(ctx)
+ var errs derror.MultiError
+
+ tree.TreeWalk(ctx, TreeWalkHandler{
+ BadNode: func(path Path, _ *Node, err error) bool {
+ errs = append(errs, fmt.Errorf("%v: %w", path, err))
+ return false
+ },
+ Item: func(_ Path, item Item) {
+ if !handleFn(item) {
+ cancel()
+ }
+ },
+ BadItem: func(_ Path, item Item) {
+ if !handleFn(item) {
+ cancel()
+ }
+ },
+ })
+
+ if len(errs) > 0 {
+ return errs
+ }
+ return nil
+}
+
+// TreeSubrange implements the 'Tree' interface.
func (tree *RawTree) TreeSubrange(ctx context.Context, min int, searcher TreeSearcher, handleFn func(Item) bool) error {
ctx, cancel := context.WithCancel(ctx)
var errs derror.MultiError
diff --git a/lib/btrfs/csums.go b/lib/btrfs/csums.go
index 8515d12..c5bcfd6 100644
--- a/lib/btrfs/csums.go
+++ b/lib/btrfs/csums.go
@@ -5,6 +5,7 @@
package btrfs
import (
+ "context"
"fmt"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem"
@@ -47,8 +48,12 @@ func ChecksumQualifiedPhysical(fs *FS, alg btrfssum.CSumType, paddr btrfsvol.Qua
return ChecksumPhysical(dev, alg, paddr.Addr)
}
-func LookupCSum(fs btrfstree.TreeOperator, alg btrfssum.CSumType, laddr btrfsvol.LogicalAddr) (btrfssum.SumRun[btrfsvol.LogicalAddr], error) {
- item, err := fs.TreeSearch(btrfsprim.CSUM_TREE_OBJECTID, btrfstree.SearchCSum(laddr, alg.Size()))
+func LookupCSum(ctx context.Context, fs btrfstree.Forrest, alg btrfssum.CSumType, laddr btrfsvol.LogicalAddr) (btrfssum.SumRun[btrfsvol.LogicalAddr], error) {
+ csumTree, err := fs.ForrestLookup(ctx, btrfsprim.CSUM_TREE_OBJECTID)
+ if err != nil {
+ return btrfssum.SumRun[btrfsvol.LogicalAddr]{}, err
+ }
+ item, err := csumTree.TreeSearch(ctx, btrfstree.SearchCSum(laddr, alg.Size()))
if err != nil {
return btrfssum.SumRun[btrfsvol.LogicalAddr]{}, err
}
diff --git a/lib/btrfs/io2_lv.go b/lib/btrfs/io2_lv.go
index 44378a0..e9de215 100644
--- a/lib/btrfs/io2_lv.go
+++ b/lib/btrfs/io2_lv.go
@@ -161,37 +161,39 @@ func (fs *FS) initDev(ctx context.Context, sb btrfstree.Superblock) error {
}
}
}
+ chunkTree, err := fs.ForrestLookup(ctx, btrfsprim.CHUNK_TREE_OBJECTID)
+ if err != nil {
+ return err
+ }
+
var errs derror.MultiError
- fs.TreeWalk(ctx, btrfsprim.CHUNK_TREE_OBJECTID,
- func(err *btrfstree.TreeError) {
- errs = append(errs, err)
- },
- btrfstree.TreeWalkHandler{
- Item: func(_ btrfstree.Path, item btrfstree.Item) {
- if item.Key.ItemType != btrfsitem.CHUNK_ITEM_KEY {
- return
- }
- switch itemBody := item.Body.(type) {
- case *btrfsitem.Chunk:
- for _, mapping := range itemBody.Mappings(item.Key) {
- if err := fs.LV.AddMapping(mapping); err != nil {
- errs = append(errs, err)
- }
- }
- case *btrfsitem.Error:
- // do nothing
- default:
- // This is a panic because the item decoder should not emit CHUNK_ITEM items as
- // anything but btrfsitem.Chunk or btrfsitem.Error without this code also being
- // updated.
- panic(fmt.Errorf("should not happen: CHUNK_ITEM has unexpected item type: %T", itemBody))
+ if err := chunkTree.TreeRange(ctx, func(item btrfstree.Item) bool {
+ if item.Key.ItemType != btrfsitem.CHUNK_ITEM_KEY {
+ return true
+ }
+ switch itemBody := item.Body.(type) {
+ case *btrfsitem.Chunk:
+ for _, mapping := range itemBody.Mappings(item.Key) {
+ if err := fs.LV.AddMapping(mapping); err != nil {
+ errs = append(errs, err)
}
- },
- },
- )
+ }
+ case *btrfsitem.Error:
+ // do nothing
+ default:
+ // This is a panic because the item decoder should not emit CHUNK_ITEM items as
+ // anything but btrfsitem.Chunk or btrfsitem.Error without this code also being
+ // updated.
+ panic(fmt.Errorf("should not happen: CHUNK_ITEM has unexpected item type: %T", itemBody))
+ }
+ return true
+ }); err != nil {
+ errs = append(errs, err)
+ }
if len(errs) > 0 {
return errs
}
+
return nil
}
diff --git a/lib/btrfs/io3_btree.go b/lib/btrfs/io3_btree.go
index 030ea41..01797df 100644
--- a/lib/btrfs/io3_btree.go
+++ b/lib/btrfs/io3_btree.go
@@ -12,6 +12,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfstree"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
"git.lukeshu.com/btrfs-progs-ng/lib/containers"
+ "git.lukeshu.com/btrfs-progs-ng/lib/diskio"
"git.lukeshu.com/btrfs-progs-ng/lib/textui"
)
@@ -73,26 +74,32 @@ func (fs *FS) readNode(_ context.Context, addr btrfsvol.LogicalAddr, nodeEntry *
var _ btrfstree.NodeSource = (*FS)(nil)
-// btrfstree.TreeOperator //////////////////////////////////////////////////////
+// btrfstree.Forrest ///////////////////////////////////////////////////////////
-// TreeWalk implements btrfstree.TreeOperator.
-func (fs *FS) TreeWalk(ctx context.Context, treeID btrfsprim.ObjID, errHandle func(*btrfstree.TreeError), cbs btrfstree.TreeWalkHandler) {
- btrfstree.TreeOperatorImpl{NodeSource: fs}.TreeWalk(ctx, treeID, errHandle, cbs)
+// RawTree is a variant of ForrestLookup that returns a concrete type
+// instead of an interface.
+func (fs *FS) RawTree(ctx context.Context, treeID btrfsprim.ObjID) (*btrfstree.RawTree, error) {
+ return btrfstree.RawForrest{NodeSource: fs}.RawTree(ctx, treeID)
}
-// TreeLookup implements btrfstree.TreeOperator.
-func (fs *FS) TreeLookup(treeID btrfsprim.ObjID, key btrfsprim.Key) (btrfstree.Item, error) {
- return btrfstree.TreeOperatorImpl{NodeSource: fs}.TreeLookup(treeID, key)
+// ForrestLookup implements btree.Forrest.
+func (fs *FS) ForrestLookup(ctx context.Context, treeID btrfsprim.ObjID) (btrfstree.Tree, error) {
+ return btrfstree.RawForrest{NodeSource: fs}.ForrestLookup(ctx, treeID)
}
-// TreeSearch implements btrfstree.TreeOperator.
-func (fs *FS) TreeSearch(treeID btrfsprim.ObjID, searcher btrfstree.TreeSearcher) (btrfstree.Item, error) {
- return btrfstree.TreeOperatorImpl{NodeSource: fs}.TreeSearch(treeID, searcher)
-}
+var _ btrfstree.Forrest = (*FS)(nil)
+
+// ReadableFS //////////////////////////////////////////////////////////////////
+
+type ReadableFS interface {
+ btrfstree.Forrest
+
+ Superblock() (*btrfstree.Superblock, error)
+
+ Name() string
-// TreeSearchAll implements btrfstree.TreeOperator.
-func (fs *FS) TreeSearchAll(treeID btrfsprim.ObjID, searcher btrfstree.TreeSearcher) ([]btrfstree.Item, error) {
- return btrfstree.TreeOperatorImpl{NodeSource: fs}.TreeSearchAll(treeID, searcher)
+ // For reading file contents.
+ diskio.ReaderAt[btrfsvol.LogicalAddr]
}
-var _ btrfstree.TreeOperator = (*FS)(nil)
+var _ ReadableFS = (*FS)(nil)
diff --git a/lib/btrfs/io4_fs.go b/lib/btrfs/io4_fs.go
index a611f35..9b70713 100644
--- a/lib/btrfs/io4_fs.go
+++ b/lib/btrfs/io4_fs.go
@@ -20,7 +20,6 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfstree"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
"git.lukeshu.com/btrfs-progs-ng/lib/containers"
- "git.lukeshu.com/btrfs-progs-ng/lib/diskio"
"git.lukeshu.com/btrfs-progs-ng/lib/maps"
"git.lukeshu.com/btrfs-progs-ng/lib/slices"
"git.lukeshu.com/btrfs-progs-ng/lib/textui"
@@ -63,17 +62,14 @@ type File struct {
}
type Subvolume struct {
- ctx context.Context //nolint:containedctx // don't have an option while keeping the same API
- fs interface {
- btrfstree.TreeOperator
- Superblock() (*btrfstree.Superblock, error)
- diskio.ReaderAt[btrfsvol.LogicalAddr]
- }
+ ctx context.Context //nolint:containedctx // don't have an option while keeping the same API
+ fs ReadableFS
TreeID btrfsprim.ObjID
noChecksums bool
- rootInfo btrfstree.TreeRoot
rootErr error
+ rootInfo btrfstree.TreeRoot
+ tree btrfstree.Tree
bareInodeCache containers.Cache[btrfsprim.ObjID, BareInode]
fullInodeCache containers.Cache[btrfsprim.ObjID, FullInode]
@@ -83,31 +79,26 @@ type Subvolume struct {
func NewSubvolume(
ctx context.Context,
- fs interface {
- btrfstree.TreeOperator
- Superblock() (*btrfstree.Superblock, error)
- diskio.ReaderAt[btrfsvol.LogicalAddr]
- },
+ fs ReadableFS,
treeID btrfsprim.ObjID,
noChecksums bool,
) *Subvolume {
sv := &Subvolume{
+ ctx: ctx,
fs: fs,
TreeID: treeID,
noChecksums: noChecksums,
}
- sb, err := sv.fs.Superblock()
- if err != nil {
- sv.rootErr = err
- return sv
- }
- rootInfo, err := btrfstree.LookupTreeRoot(ctx, sv.fs, *sb, sv.TreeID)
+ tree, err := sv.fs.ForrestLookup(ctx, sv.TreeID)
if err != nil {
sv.rootErr = err
return sv
}
+ sb, _ := sv.fs.Superblock()
+ rootInfo, _ := btrfstree.LookupTreeRoot(ctx, sv.fs, *sb, sv.TreeID)
sv.rootInfo = *rootInfo
+ sv.tree = tree
sv.bareInodeCache = containers.NewARCache[btrfsprim.ObjID, BareInode](textui.Tunable(128),
containers.SourceFunc[btrfsprim.ObjID, BareInode](sv.loadBareInode))
@@ -142,11 +133,11 @@ func (sv *Subvolume) ReleaseBareInode(inode btrfsprim.ObjID) {
sv.bareInodeCache.Release(inode)
}
-func (sv *Subvolume) loadBareInode(_ context.Context, inode btrfsprim.ObjID, val *BareInode) {
+func (sv *Subvolume) loadBareInode(ctx context.Context, inode btrfsprim.ObjID, val *BareInode) {
*val = BareInode{
Inode: inode,
}
- item, err := sv.fs.TreeLookup(sv.TreeID, btrfsprim.Key{
+ item, err := sv.tree.TreeLookup(ctx, btrfsprim.Key{
ObjectID: inode,
ItemType: btrfsitem.INODE_ITEM_KEY,
Offset: 0,
@@ -180,21 +171,14 @@ func (sv *Subvolume) ReleaseFullInode(inode btrfsprim.ObjID) {
sv.fullInodeCache.Release(inode)
}
-func (sv *Subvolume) loadFullInode(_ context.Context, inode btrfsprim.ObjID, val *FullInode) {
+func (sv *Subvolume) loadFullInode(ctx context.Context, inode btrfsprim.ObjID, val *FullInode) {
*val = FullInode{
BareInode: BareInode{
Inode: inode,
},
XAttrs: make(map[string]string),
}
- items, err := sv.fs.TreeSearchAll(sv.TreeID, btrfstree.SearchObject(inode))
- if err != nil {
- val.Errs = append(val.Errs, err)
- if len(items) == 0 {
- return
- }
- }
- for _, item := range items {
+ if err := sv.tree.TreeSubrange(ctx, 1, btrfstree.SearchObject(inode), func(item btrfstree.Item) bool {
switch item.Key.ItemType {
case btrfsitem.INODE_ITEM_KEY:
switch itemBody := item.Body.(type) {
@@ -203,7 +187,7 @@ func (sv *Subvolume) loadFullInode(_ context.Context, inode btrfsprim.ObjID, val
if !reflect.DeepEqual(itemBody, *val.InodeItem) {
val.Errs = append(val.Errs, fmt.Errorf("multiple inodes"))
}
- continue
+ return true
}
bodyCopy := itemBody.Clone()
val.InodeItem = &bodyCopy
@@ -222,8 +206,12 @@ func (sv *Subvolume) loadFullInode(_ context.Context, inode btrfsprim.ObjID, val
panic(fmt.Errorf("should not happen: XATTR_ITEM has unexpected item type: %T", itemBody))
}
default:
+ item.Body = item.Body.CloneItem()
val.OtherItems = append(val.OtherItems, item)
}
+ return true
+ }); err != nil {
+ val.Errs = append(val.Errs, err)
}
}
@@ -503,7 +491,7 @@ func (file *File) maybeShortReadAt(dat []byte, off int64) (int, error) {
return 0, err
}
if !file.SV.noChecksums {
- sumRun, err := LookupCSum(file.SV.fs, sb.ChecksumType, blockBeg)
+ sumRun, err := LookupCSum(file.SV.ctx, file.SV.fs, sb.ChecksumType, blockBeg)
if err != nil {
return 0, fmt.Errorf("checksum@%v: %w", blockBeg, err)
}
diff --git a/lib/btrfsutil/old_rebuilt_forrest.go b/lib/btrfsutil/old_rebuilt_forrest.go
index d17aa4a..58333df 100644
--- a/lib/btrfsutil/old_rebuilt_forrest.go
+++ b/lib/btrfsutil/old_rebuilt_forrest.go
@@ -85,7 +85,6 @@ func newOldRebuiltTree() oldRebuiltTree {
}
type OldRebuiltForrest struct {
- ctx context.Context //nolint:containedctx // don't have an option while keeping the same API
inner *btrfs.FS
// btrfsprim.ROOT_TREE_OBJECTID
@@ -96,35 +95,43 @@ type OldRebuiltForrest struct {
trees map[btrfsprim.ObjID]oldRebuiltTree
}
-var _ btrfstree.TreeOperator = (*OldRebuiltForrest)(nil)
+var _ btrfs.ReadableFS = (*OldRebuiltForrest)(nil)
// NewOldRebuiltForrest wraps a *btrfs.FS to support looking up
// information from broken trees.
//
-// Of the btrfstree.TreeOperator methods:
+// Of the btrfstree.Tree methods:
//
-// - TreeWalk works on broken trees
-// - TreeLookup relies on the tree being properly ordered (which a
+// - TreeRange works on broken trees
+// - TreeSubrange relies on the tree being properly ordered (which a
// broken tree might not be).
// - TreeSearch relies on the tree being properly ordered (which a
// broken tree might not be).
-// - TreeSearchAll relies on the tree being properly ordered (which a
-// broken tree might not be), and a bad node may cause it to not
-// return a truncated list of results.
+// - TreeLookup relies on the tree being properly ordered (which a
+// broken tree might not be).
//
-// NewOldRebuiltForrest attempts to remedy these deficiencies by using
-// .TreeWalk to build an out-of-FS index of all of the items in the
-// tree, and re-implements TreeLookup, TreeSearch, and TreeSearchAll
+// NewOldRebuiltForrest attempts to remedy these deficiencies by
+// building an out-of-FS index of all of the items in the tree, and
+// re-implements TreeLookup, TreeSearch, TreeSubrange, and TreeRange
// using that index.
-func NewOldRebuiltForrest(ctx context.Context, inner *btrfs.FS) *OldRebuiltForrest {
+func NewOldRebuiltForrest(inner *btrfs.FS) *OldRebuiltForrest {
return &OldRebuiltForrest{
- ctx: ctx,
inner: inner,
}
}
-// RebuiltTree returns a handle for an individual tree. An error is
-// indicated by the ret.RootErr member.
+// ForrestLookup implements btrfstree.Forrest.
+func (bt *OldRebuiltForrest) ForrestLookup(ctx context.Context, treeID btrfsprim.ObjID) (btrfstree.Tree, error) {
+ tree := bt.RebuiltTree(ctx, treeID)
+ if tree.RootErr != nil {
+ return nil, tree.RootErr
+ }
+ return tree, nil
+}
+
+// RebuiltTree is a variant of ForrestLookup that returns a concrete
+// type instead of an interface. An error is indicated by the
+// ret.RootErr member.
func (bt *OldRebuiltForrest) RebuiltTree(ctx context.Context, treeID btrfsprim.ObjID) oldRebuiltTree {
if treeID == btrfsprim.ROOT_TREE_OBJECTID {
bt.rootTreeMu.Lock()
@@ -172,7 +179,7 @@ func (bt *OldRebuiltForrest) rawTreeWalk(ctx context.Context, treeID btrfsprim.O
return
}
tree := &btrfstree.RawTree{
- Forrest: btrfstree.TreeOperatorImpl{NodeSource: bt.inner},
+ Forrest: btrfstree.RawForrest{NodeSource: bt.inner},
TreeRoot: *root,
}
@@ -238,8 +245,8 @@ func (tree oldRebuiltTree) addErrs(fn func(btrfsprim.Key, uint32) int, err error
return errs
}
-func (bt *OldRebuiltForrest) readNode(nodeInfo nodeInfo) *btrfstree.Node {
- node, err := bt.inner.AcquireNode(bt.ctx, nodeInfo.LAddr, btrfstree.NodeExpectations{
+func (bt *OldRebuiltForrest) readNode(ctx context.Context, nodeInfo nodeInfo) *btrfstree.Node {
+ node, err := bt.inner.AcquireNode(ctx, nodeInfo.LAddr, btrfstree.NodeExpectations{
LAddr: containers.OptionalValue(nodeInfo.LAddr),
Level: containers.OptionalValue(nodeInfo.Level),
Generation: containers.OptionalValue(nodeInfo.Generation),
@@ -261,22 +268,13 @@ func (bt *OldRebuiltForrest) readNode(nodeInfo nodeInfo) *btrfstree.Node {
return node
}
-// TreeLookup implements btrfstree.TreeOperator.
-func (bt *OldRebuiltForrest) TreeLookup(treeID btrfsprim.ObjID, key btrfsprim.Key) (btrfstree.Item, error) {
- return bt.RebuiltTree(bt.ctx, treeID).treeLookup(bt.ctx, key)
-}
-
-func (tree oldRebuiltTree) treeLookup(ctx context.Context, key btrfsprim.Key) (btrfstree.Item, error) {
- return tree.treeSearch(ctx, btrfstree.SearchExactKey(key))
-}
-
-// TreeSearch implements btrfstree.TreeOperator.
-func (bt *OldRebuiltForrest) TreeSearch(treeID btrfsprim.ObjID, searcher btrfstree.TreeSearcher) (btrfstree.Item, error) {
- return bt.RebuiltTree(bt.ctx, treeID).treeSearch(bt.ctx, searcher)
+// TreeLookup implements btrfstree.Tree.
+func (tree oldRebuiltTree) TreeLookup(ctx context.Context, key btrfsprim.Key) (btrfstree.Item, error) {
+ return tree.TreeSearch(ctx, btrfstree.SearchExactKey(key))
}
// TreeSearch implements btrfstree.Tree.
-func (tree oldRebuiltTree) treeSearch(_ context.Context, searcher btrfstree.TreeSearcher) (btrfstree.Item, error) {
+func (tree oldRebuiltTree) TreeSearch(ctx context.Context, searcher btrfstree.TreeSearcher) (btrfstree.Item, error) {
if tree.RootErr != nil {
return btrfstree.Item{}, tree.RootErr
}
@@ -288,7 +286,7 @@ func (tree oldRebuiltTree) treeSearch(_ context.Context, searcher btrfstree.Tree
return btrfstree.Item{}, fmt.Errorf("item with %s: %w", searcher, tree.addErrs(searcher.Search, btrfstree.ErrNoItem))
}
- node := tree.forrest.readNode(indexItem.Value.Node)
+ node := tree.forrest.readNode(ctx, indexItem.Value.Node)
defer tree.forrest.inner.ReleaseNode(node)
item := node.BodyLeaf[indexItem.Value.Slot]
@@ -299,24 +297,28 @@ func (tree oldRebuiltTree) treeSearch(_ context.Context, searcher btrfstree.Tree
return item, nil
}
-// TreeSearchAll implements btrfstree.TreeOperator.
-func (bt *OldRebuiltForrest) TreeSearchAll(treeID btrfsprim.ObjID, searcher btrfstree.TreeSearcher) ([]btrfstree.Item, error) {
- tree := bt.RebuiltTree(bt.ctx, treeID)
+// TreeRange implements btrfstree.Tree.
+func (tree oldRebuiltTree) TreeRange(ctx context.Context, handleFn func(btrfstree.Item) bool) error {
if tree.RootErr != nil {
- return nil, tree.RootErr
+ return tree.RootErr
}
- var ret []btrfstree.Item
- err := tree.treeSubrange(bt.ctx, 1, searcher, func(item btrfstree.Item) bool {
- item.Body = item.Body.CloneItem()
- ret = append(ret, item)
- return true
- })
+ var node *btrfstree.Node
+ tree.Items.Range(
+ func(rbnode *containers.RBNode[oldRebuiltTreeValue]) bool {
+ if node == nil || node.Head.Addr != rbnode.Value.Node.LAddr {
+ tree.forrest.inner.ReleaseNode(node)
+ node = tree.forrest.readNode(ctx, rbnode.Value.Node)
+ }
+ return handleFn(node.BodyLeaf[rbnode.Value.Slot])
+ })
+ tree.forrest.inner.ReleaseNode(node)
- return ret, err
+ return tree.addErrs(func(btrfsprim.Key, uint32) int { return 0 }, nil)
}
-func (tree oldRebuiltTree) treeSubrange(_ context.Context, min int, searcher btrfstree.TreeSearcher, handleFn func(btrfstree.Item) bool) error {
+// TreeSubrange implements btrfstree.Tree.
+func (tree oldRebuiltTree) TreeSubrange(ctx context.Context, min int, searcher btrfstree.TreeSearcher, handleFn func(btrfstree.Item) bool) error {
var node *btrfstree.Node
var cnt int
tree.Items.Subrange(
@@ -327,7 +329,7 @@ func (tree oldRebuiltTree) treeSubrange(_ context.Context, min int, searcher btr
cnt++
if node == nil || node.Head.Addr != rbNode.Value.Node.LAddr {
tree.forrest.inner.ReleaseNode(node)
- node = tree.forrest.readNode(rbNode.Value.Node)
+ node = tree.forrest.readNode(ctx, rbNode.Value.Node)
}
return handleFn(node.BodyLeaf[rbNode.Value.Slot])
})
@@ -344,21 +346,9 @@ func (tree oldRebuiltTree) treeSubrange(_ context.Context, min int, searcher btr
return err
}
-// TreeWalk implements btrfstree.TreeOperator. It doesn't actually
-// visit nodes or keypointers (just items).
-func (bt *OldRebuiltForrest) TreeWalk(ctx context.Context, treeID btrfsprim.ObjID, errHandle func(*btrfstree.TreeError), cbs btrfstree.TreeWalkHandler) {
- tree := bt.RebuiltTree(ctx, treeID)
- if tree.RootErr != nil {
- errHandle(&btrfstree.TreeError{
- Path: btrfstree.Path{btrfstree.PathRoot{TreeID: treeID}},
- Err: tree.RootErr,
- })
- return
- }
- tree.treeWalk(ctx, cbs)
-}
-
-func (tree oldRebuiltTree) treeWalk(ctx context.Context, cbs btrfstree.TreeWalkHandler) {
+// TreeWalk implements btrfstree.Tree. It doesn't actually visit
+// nodes or keypointers (just items).
+func (tree oldRebuiltTree) TreeWalk(ctx context.Context, cbs btrfstree.TreeWalkHandler) {
if cbs.Item == nil && cbs.BadItem == nil {
return
}
@@ -367,12 +357,10 @@ func (tree oldRebuiltTree) treeWalk(ctx context.Context, cbs btrfstree.TreeWalkH
if ctx.Err() != nil {
return false
}
- if tree.forrest.ctx.Err() != nil {
- return false
- }
+
if node == nil || node.Head.Addr != indexItem.Value.Node.LAddr {
tree.forrest.inner.ReleaseNode(node)
- node = tree.forrest.readNode(indexItem.Value.Node)
+ node = tree.forrest.readNode(ctx, indexItem.Value.Node)
}
item := node.BodyLeaf[indexItem.Value.Slot]
@@ -415,6 +403,11 @@ func (bt *OldRebuiltForrest) ReadAt(p []byte, off btrfsvol.LogicalAddr) (int, er
return bt.inner.ReadAt(p, off)
}
+// Name implements btrfs.ReadableFS.
+func (bt *OldRebuiltForrest) Name() string {
+ return bt.inner.Name()
+}
+
// TreeCheckOwner implements btrfstree.Tree.
func (tree oldRebuiltTree) TreeCheckOwner(ctx context.Context, failOpen bool, owner btrfsprim.ObjID, gen btrfsprim.Generation) error {
var uuidTree oldRebuiltTree
@@ -439,7 +432,7 @@ func (tree oldRebuiltTree) TreeCheckOwner(ctx context.Context, failOpen bool, ow
return nil //nolint:nilerr // fail open
}
}
- parentIDItem, err := uuidTree.treeLookup(ctx, btrfsitem.UUIDToKey(tree.ParentUUID))
+ parentIDItem, err := uuidTree.TreeLookup(ctx, btrfsitem.UUIDToKey(tree.ParentUUID))
if err != nil {
if failOpen {
return nil
diff --git a/lib/btrfsutil/walk.go b/lib/btrfsutil/walk.go
index b05218c..caa1e4c 100644
--- a/lib/btrfsutil/walk.go
+++ b/lib/btrfsutil/walk.go
@@ -8,35 +8,23 @@ import (
"context"
"fmt"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfstree"
)
-type WalkError struct {
- TreeName string
- Err *btrfstree.TreeError
-}
-
-func (e *WalkError) Unwrap() error { return e.Err }
-
-func (e *WalkError) Error() string {
- return fmt.Sprintf("%v: %v", e.TreeName, e.Err)
-}
-
type WalkAllTreesHandler struct {
- Err func(*WalkError)
- // Callbacks for entire trees
PreTree func(name string, id btrfsprim.ObjID)
+ BadTree func(name string, id btrfsprim.ObjID, err error)
+ Tree btrfstree.TreeWalkHandler
PostTree func(name string, id btrfsprim.ObjID)
- // Callbacks for nodes or smaller
- btrfstree.TreeWalkHandler
}
-// WalkAllTrees walks all trees in a *btrfs.FS. Rather than returning
-// an error, it calls errCb each time an error is encountered. The
-// error will always be of type WalkError.
-func WalkAllTrees(ctx context.Context, fs btrfstree.TreeOperator, cbs WalkAllTreesHandler) {
+// WalkAllTrees walks all trees in a btrfs.ReadableFS. Rather than
+// returning an error, it calls the appropriate "BadXXX" callback
+// (BadTree, BadNode, BadItem) each time an error is encountered.
+func WalkAllTrees(ctx context.Context, fs btrfs.ReadableFS, cbs WalkAllTreesHandler) {
var treeName string
trees := []struct {
@@ -60,8 +48,8 @@ func WalkAllTrees(ctx context.Context, fs btrfstree.TreeOperator, cbs WalkAllTre
ID: btrfsprim.BLOCK_GROUP_TREE_OBJECTID,
},
}
- origItem := cbs.Item
- cbs.Item = func(path btrfstree.Path, item btrfstree.Item) {
+ origItem := cbs.Tree.Item
+ cbs.Tree.Item = func(path btrfstree.Path, item btrfstree.Item) {
if item.Key.ItemType == btrfsitem.ROOT_ITEM_KEY {
trees = append(trees, struct {
Name string
@@ -78,19 +66,22 @@ func WalkAllTrees(ctx context.Context, fs btrfstree.TreeOperator, cbs WalkAllTre
}
for i := 0; i < len(trees); i++ {
- tree := trees[i]
- treeName = tree.Name
+ treeInfo := trees[i]
+ treeName = treeInfo.Name
if cbs.PreTree != nil {
- cbs.PreTree(treeName, tree.ID)
+ cbs.PreTree(treeName, treeInfo.ID)
+ }
+ tree, err := fs.ForrestLookup(ctx, treeInfo.ID)
+ switch {
+ case err != nil:
+ if cbs.BadTree != nil {
+ cbs.BadTree(treeName, treeInfo.ID, err)
+ }
+ default:
+ tree.TreeWalk(ctx, cbs.Tree)
}
- fs.TreeWalk(
- ctx,
- tree.ID,
- func(err *btrfstree.TreeError) { cbs.Err(&WalkError{TreeName: treeName, Err: err}) },
- cbs.TreeWalkHandler,
- )
if cbs.PostTree != nil {
- cbs.PostTree(treeName, tree.ID)
+ cbs.PostTree(treeName, treeInfo.ID)
}
}
}