summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-08-28 11:44:08 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-08-28 14:40:22 -0600
commita40e81b3b629198a4c30ad18f7544c7d513da287 (patch)
tree8291e76d36821652f98d4110f3abb33ceb1de7b1 /lib
parent732394dfe160705f80c136aa8696d180165b485c (diff)
btrfs: Rework TreePath to allow correctly checking the owner tree
Diffstat (limited to 'lib')
-rw-r--r--lib/btrfs/io2_lv.go4
-rw-r--r--lib/btrfs/io3_btree.go394
-rw-r--r--lib/btrfs/types_node.go9
-rw-r--r--lib/btrfsprogs/btrfsinspect/print_tree.go2
-rw-r--r--lib/btrfsprogs/btrfsrepair/clearnodes.go6
-rw-r--r--lib/btrfsprogs/btrfsutil/broken_btree.go39
6 files changed, 245 insertions, 209 deletions
diff --git a/lib/btrfs/io2_lv.go b/lib/btrfs/io2_lv.go
index facd88c..7fa003e 100644
--- a/lib/btrfs/io2_lv.go
+++ b/lib/btrfs/io2_lv.go
@@ -24,6 +24,10 @@ type FS struct {
cacheSuperblocks []*diskio.Ref[btrfsvol.PhysicalAddr, Superblock]
cacheSuperblock *Superblock
+
+ cacheObjID2UUID map[ObjID]UUID
+ cacheUUID2ObjID map[UUID]ObjID
+ cacheTreeParent map[ObjID]UUID
}
var _ diskio.File[btrfsvol.LogicalAddr] = (*FS)(nil)
diff --git a/lib/btrfs/io3_btree.go b/lib/btrfs/io3_btree.go
index 682e355..154f800 100644
--- a/lib/btrfs/io3_btree.go
+++ b/lib/btrfs/io3_btree.go
@@ -70,121 +70,138 @@ var _ Trees = (*FS)(nil)
// - For .Item() callbacks, the last element will always have a
// NodeAddr of 0.
//
-// For example, given the tree structure
+// For example, a path through a tree, with the associated PathElems:
//
-// [superblock]
+// [superblock: tree=B, lvl=3, gen=6]
// |
-// | <------------------------------------------ pathElem={idx:-1, addr:0x01, lvl:3, gen:6}
-// |
-// +[0x01]-----------+
-// | lvl=3 gen=6 |
-// +-+-+-+-+-+-+-+-+-+
-// |1|2|3|4|5|6|7|8|9|
-// +---+---+---+---+-+
-// |
-// | <------------------------------ pathElem={idx:8, addr:0x02, lvl:2: gen:6}
+// | <------------------------------------------ pathElem={from_tree:B, from_gen=6, from_idx=-1,
+// | to_addr:0x01, to_lvl=3}
+// +[0x01]-------------+
+// | lvl=3 gen=6 own=B |
+// +-+-+-+-+-+-+-+-+-+-+
+// |0|1|2|3|4|5|6|7|8|9|
+// +-+-+-+-+-+-+-+-+-+-+
// |
-// +[0x02]-----------+
-// | lvl=2 gen=5 |
-// +-+-+-+-+-+-+-+-+-+
-// |1|2|3|4|5|6|7|8|9|
-// +---+---+---+---+-+
-// |
-// | <-------------------- pathElem={idx:7, addr:0x03, lvl:1: gen:5}
+// | <------------------------------ pathElem:{from_tree:B, from_gen:6, from_idx:7,
+// | to_addr:0x02, to_lvl:2}
+// +[0x02]--------------+
+// | lvl=2 gen=5 own=B |
+// +-+-+-+-+-+-+-+-+-+-+
+// |0|1|2|3|4|5|6|7|8|9|
+// +-+-+-+-+-+-+-+-+-+-+
// |
-// +[0x03]-----------+
-// | lvl=1 gen=5 |
-// +-+-+-+-+-+-+-+-+-+
-// |1|2|3|4|5|6|7|8|9|
-// +---+---+---+---+-+
+// | <-------------------- pathElem={from_tree:B, from_gen:5, from_idx:6,
+// | to_addr:0x03, to_lvl:1}
+// +[0x03]-------------+
+// | lvl=1 gen=5 own=A |
+// +-+-+-+-+-+-+-+-+-+-+
+// |0|1|2|3|4|5|6|7|8|9|
+// +-+-+-+-+-+-+-+-+-+-+
// |
-// | <---------------- pathElem={idx:4, addr:0x04, lvl:0: gen:5}
-// |
-// +[0x04]-----------+
-// | lvl=0 gen=2 |
-// +-+-+-+-+-+-+-+-+-+
-// |1|2|3|4|5|6|7|8|9|
-// +---+---+---+---+-+
-// |
-// | <--------------- pathElem={idx:5, addr:0, lvl:0, gen:2}
+// | <---------------- pathElem={from_tree:A, from_gen:5, from_idx:3,
+// | to_addr:0x04, to_lvl:0}
+// +[0x04]-------------+
+// | lvl=0 gen=2 own=A |
+// +-+-+-+-+-+-+-+-+-+-+
+// |0|1|2|3|4|5|6|7|8|9|
+// +-+-+-+-+-+-+-+-+-+-+
// |
+// | <--------------- pathElem={from_tree:A, from_gen:2, from_idx:1,
+// | to_addr:0, to_lvl:0}
// [item]
-//
-// the path would be
-//
-// {
-// TreeID: …,
-// Nodes: {-1, 0x01, 3, 6}→{8, 0x02, 2, 6}→{7, 0x03, 1, 5}→{4, 0x04, 5}→{2, 0, 0, 2},
-// }
-type TreePath struct {
- TreeID ObjID
- Nodes []TreePathElem
-}
+type TreePath []TreePathElem
-// A TreePathElem essentially represents a KeyPointer.
+// A TreePathElem essentially represents a KeyPointer. If there is an
+// error looking up the tree root, everything but FromTree is zero.
type TreePathElem struct {
- // ItemIdx is the index of this KeyPointer in the parent Node;
- // or -1 if this is the root and there is no KeyPointer.
- ItemIdx int
- // NodeAddr is the address of the node that the KeyPointer
- // points at, or 0 if this is a leaf item and nothing is
+ // FromTree is the owning tree ID of the parent node; or the
+ // well-known tree ID if this is the root.
+ FromTree ObjID
+ // FromGeneration is the generation of the parent node the
+ // parent node; or generation stored in the superblock if this
+ // is the root.
+ FromGeneration Generation
+ // FromItemIdx is the index of this KeyPointer in the parent
+ // Node; or -1 if this is the root and there is no KeyPointer.
+ FromItemIdx int
+
+ // ToNodeAddr is the address of the node that the KeyPointer
+ // points at, or 0 if this is a leaf item and nothing is being
+ // pointed at.
+ ToNodeAddr btrfsvol.LogicalAddr
+ // ToNodeLevel is the expected or actual level of the node at
+ // ToNodeAddr, or 0 if this is a leaf item and nothing is
// being pointed at.
- NodeAddr btrfsvol.LogicalAddr
- // NodeLevel is the expected or actual level of the node at
- // NodeAddr.
- NodeLevel uint8
- // NodeParentGeneration is the generation of the parent node;
- // the maximum expected generation of the child node.
- NodeParentGeneration Generation
+ ToNodeLevel uint8
}
func (elem TreePathElem) writeNodeTo(w io.Writer) {
- fmt.Fprintf(w, "node:%d@%v", elem.NodeLevel, elem.NodeAddr)
+ fmt.Fprintf(w, "node:%d@%v", elem.ToNodeLevel, elem.ToNodeAddr)
}
func (path TreePath) String() string {
- var ret strings.Builder
- fmt.Fprintf(&ret, "%s->", path.TreeID.Format(btrfsitem.ROOT_ITEM_KEY))
- if len(path.Nodes) == 0 {
- ret.WriteString("(empty-path)")
+ if len(path) == 0 {
+ return "(empty-path)"
} else {
- path.Nodes[0].writeNodeTo(&ret)
- for _, elem := range path.Nodes[1:] {
- fmt.Fprintf(&ret, "[%v]", elem.ItemIdx)
- if elem.NodeAddr != 0 {
+ var ret strings.Builder
+ fmt.Fprintf(&ret, "%s->", path[0].FromTree.Format(btrfsitem.ROOT_ITEM_KEY))
+ if len(path) == 1 && path[0] == (TreePathElem{FromTree: path[0].FromTree}) {
+ ret.WriteString("(empty-path)")
+ } else {
+ path[0].writeNodeTo(&ret)
+ }
+ for _, elem := range path[1:] {
+ fmt.Fprintf(&ret, "[%v]", elem.FromItemIdx)
+ if elem.ToNodeAddr != 0 {
ret.WriteString("->")
elem.writeNodeTo(&ret)
}
}
+ return ret.String()
}
- return ret.String()
}
func (path TreePath) DeepCopy() TreePath {
- return TreePath{
- TreeID: path.TreeID,
- Nodes: append([]TreePathElem(nil), path.Nodes...),
- }
-}
-
-func (path TreePath) Append(elem TreePathElem) TreePath {
- path.Nodes = append(path.Nodes, elem)
- return path
+ return append(TreePath(nil), path...)
}
func (path TreePath) Parent() TreePath {
- path.Nodes = path.Nodes[:len(path.Nodes)-1]
- return path
+ return path[:len(path)-1]
}
-// path.Node(x) is like path.Nodes[x], but negative values of x move
-// down from the end of path.Nodes (similar to how lists work in many
-// other languages, such as Python).
+// path.Node(x) is like &path[x], but negative values of x move down
+// from the end of path (similar to how lists work in many other
+// languages, such as Python).
func (path TreePath) Node(x int) *TreePathElem {
if x < 0 {
- x += len(path.Nodes)
+ x += len(path)
+ }
+ return &path[x]
+}
+
+func (fs *FS) populateTreeUUIDs(ctx context.Context) {
+ if fs.cacheObjID2UUID == nil || fs.cacheUUID2ObjID == nil || fs.cacheTreeParent == nil {
+ fs.cacheObjID2UUID = make(map[ObjID]UUID)
+ fs.cacheUUID2ObjID = make(map[UUID]ObjID)
+ fs.cacheTreeParent = make(map[ObjID]UUID)
+ fs.TreeWalk(ctx, ROOT_TREE_OBJECTID,
+ func(err *TreeError) {
+ // do nothing
+ },
+ TreeWalkHandler{
+ Item: func(_ TreePath, item Item) error {
+ itemBody, ok := item.Body.(btrfsitem.Root)
+ if !ok {
+ return nil
+ }
+ fs.cacheObjID2UUID[item.Key.ObjectID] = itemBody.UUID
+ fs.cacheTreeParent[item.Key.ObjectID] = itemBody.ParentUUID
+ fs.cacheUUID2ObjID[itemBody.UUID] = item.Key.ObjectID
+ return nil
+ },
+ },
+ )
}
- return &path.Nodes[x]
}
func (fs *FS) ReadNode(path TreePath) (*diskio.Ref[btrfsvol.LogicalAddr, Node], error) {
@@ -193,11 +210,31 @@ func (fs *FS) ReadNode(path TreePath) (*diskio.Ref[btrfsvol.LogicalAddr, Node],
return nil, fmt.Errorf("btrfs.FS.ReadNode: %w", err)
}
- return ReadNode[btrfsvol.LogicalAddr](fs, *sb, path.Node(-1).NodeAddr, NodeExpectations{
- LAddr: containers.Optional[btrfsvol.LogicalAddr]{OK: true, Val: path.Node(-1).NodeAddr},
- Level: containers.Optional[uint8]{OK: true, Val: path.Node(-1).NodeLevel},
- MaxGeneration: containers.Optional[Generation]{OK: true, Val: path.Node(-1).NodeParentGeneration},
- Owner: containers.Optional[ObjID]{OK: true, Val: path.TreeID},
+ potentialOwners := []ObjID{
+ path.Node(-1).FromTree,
+ }
+ if potentialOwners[0] >= FIRST_FREE_OBJECTID {
+ ctx := context.TODO()
+ fs.populateTreeUUIDs(ctx)
+ for potentialOwners[len(potentialOwners)-1] >= FIRST_FREE_OBJECTID {
+ objID := potentialOwners[len(potentialOwners)-1]
+ parentUUID := fs.cacheTreeParent[objID]
+ if parentUUID == (UUID{}) {
+ break
+ }
+ parentObjID, ok := fs.cacheUUID2ObjID[parentUUID]
+ if !ok {
+ break
+ }
+ potentialOwners = append(potentialOwners, parentObjID)
+ }
+ }
+
+ return ReadNode[btrfsvol.LogicalAddr](fs, *sb, path.Node(-1).ToNodeAddr, NodeExpectations{
+ LAddr: containers.Optional[btrfsvol.LogicalAddr]{OK: true, Val: path.Node(-1).ToNodeAddr},
+ Level: containers.Optional[uint8]{OK: true, Val: path.Node(-1).ToNodeLevel},
+ MaxGeneration: containers.Optional[Generation]{OK: true, Val: path.Node(-1).FromGeneration},
+ Owner: potentialOwners,
})
}
@@ -300,37 +337,24 @@ type TreeWalkHandler struct {
// TreeWalk implements the 'Trees' interface.
func (fs *FS) TreeWalk(ctx context.Context, treeID ObjID, errHandle func(*TreeError), cbs TreeWalkHandler) {
- path := TreePath{
- TreeID: treeID,
- }
rootInfo, err := LookupTreeRoot(fs, treeID)
if err != nil {
- errHandle(&TreeError{Path: path, Err: err})
+ errHandle(&TreeError{Path: TreePath{{FromTree: treeID}}, Err: err})
return
}
- path = path.Append(TreePathElem{
- ItemIdx: -1,
- NodeAddr: rootInfo.RootNode,
- NodeLevel: rootInfo.Level,
- NodeParentGeneration: rootInfo.Generation,
- })
- fs.treeWalk(ctx, path, errHandle, cbs)
+ fs.RawTreeWalk(ctx, *rootInfo, errHandle, cbs)
}
// TreeWalk is a utility function to help with implementing the 'Trees'
// interface.
func (fs *FS) RawTreeWalk(ctx context.Context, rootInfo TreeRoot, errHandle func(*TreeError), cbs TreeWalkHandler) {
- path := TreePath{
- TreeID: rootInfo.TreeID,
- Nodes: []TreePathElem{
- {
- ItemIdx: -1,
- NodeAddr: rootInfo.RootNode,
- NodeLevel: rootInfo.Level,
- NodeParentGeneration: rootInfo.Generation,
- },
- },
- }
+ path := TreePath{{
+ FromTree: rootInfo.TreeID,
+ FromGeneration: rootInfo.Generation,
+ FromItemIdx: -1,
+ ToNodeAddr: rootInfo.RootNode,
+ ToNodeLevel: rootInfo.Level,
+ }}
fs.treeWalk(ctx, path, errHandle, cbs)
}
@@ -338,7 +362,7 @@ func (fs *FS) treeWalk(ctx context.Context, path TreePath, errHandle func(*TreeE
if ctx.Err() != nil {
return
}
- if path.Node(-1).NodeAddr == 0 {
+ if path.Node(-1).ToNodeAddr == 0 {
return
}
@@ -372,11 +396,12 @@ func (fs *FS) treeWalk(ctx context.Context, path TreePath, errHandle func(*TreeE
}
if node != nil {
for i, item := range node.Data.BodyInternal {
- itemPath := path.Append(TreePathElem{
- ItemIdx: i,
- NodeAddr: item.BlockPtr,
- NodeLevel: node.Data.Head.Level - 1,
- NodeParentGeneration: node.Data.Head.Generation,
+ itemPath := append(path, TreePathElem{
+ FromTree: node.Data.Head.Owner,
+ FromGeneration: node.Data.Head.Generation,
+ FromItemIdx: i,
+ ToNodeAddr: item.BlockPtr,
+ ToNodeLevel: node.Data.Head.Level - 1,
})
if cbs.PreKeyPointer != nil {
if err := cbs.PreKeyPointer(itemPath, item); err != nil {
@@ -397,8 +422,10 @@ func (fs *FS) treeWalk(ctx context.Context, path TreePath, errHandle func(*TreeE
}
}
for i, item := range node.Data.BodyLeaf {
- itemPath := path.Append(TreePathElem{
- ItemIdx: i,
+ itemPath := append(path, TreePathElem{
+ FromTree: node.Data.Head.Owner,
+ FromGeneration: node.Data.Head.Generation,
+ FromItemIdx: i,
})
if errBody, isErr := item.Body.(btrfsitem.Error); isErr {
if cbs.BadItem == nil {
@@ -431,19 +458,15 @@ func (fs *FS) treeWalk(ctx context.Context, path TreePath, errHandle func(*TreeE
}
func (fs *FS) treeSearch(treeRoot TreeRoot, fn func(Key, uint32) int) (TreePath, *diskio.Ref[btrfsvol.LogicalAddr, Node], error) {
- path := TreePath{
- TreeID: treeRoot.TreeID,
- Nodes: []TreePathElem{
- {
- ItemIdx: -1,
- NodeAddr: treeRoot.RootNode,
- NodeLevel: treeRoot.Level,
- NodeParentGeneration: treeRoot.Generation,
- },
- },
- }
+ path := TreePath{{
+ FromTree: treeRoot.TreeID,
+ FromGeneration: treeRoot.Generation,
+ FromItemIdx: -1,
+ ToNodeAddr: treeRoot.RootNode,
+ ToNodeLevel: treeRoot.Level,
+ }}
for {
- if path.Node(-1).NodeAddr == 0 {
+ if path.Node(-1).ToNodeAddr == 0 {
return TreePath{}, nil, iofs.ErrNotExist
}
node, err := fs.ReadNode(path)
@@ -468,11 +491,12 @@ func (fs *FS) treeSearch(treeRoot TreeRoot, fn func(Key, uint32) int) (TreePath,
if !ok {
return TreePath{}, nil, iofs.ErrNotExist
}
- path = path.Append(TreePathElem{
- ItemIdx: lastGood,
- NodeAddr: node.Data.BodyInternal[lastGood].BlockPtr,
- NodeLevel: node.Data.Head.Level - 1,
- NodeParentGeneration: node.Data.Head.Generation,
+ path = append(path, TreePathElem{
+ FromTree: node.Data.Head.Owner,
+ FromGeneration: node.Data.Head.Generation,
+ FromItemIdx: lastGood,
+ ToNodeAddr: node.Data.BodyInternal[lastGood].BlockPtr,
+ ToNodeLevel: node.Data.Head.Level - 1,
})
} else {
// leaf node
@@ -493,8 +517,10 @@ func (fs *FS) treeSearch(treeRoot TreeRoot, fn func(Key, uint32) int) (TreePath,
if !ok {
return TreePath{}, nil, iofs.ErrNotExist
}
- path = path.Append(TreePathElem{
- ItemIdx: idx,
+ path = append(path, TreePathElem{
+ FromTree: node.Data.Head.Owner,
+ FromGeneration: node.Data.Head.Generation,
+ FromItemIdx: idx,
})
return path, node, nil
}
@@ -506,46 +532,49 @@ func (fs *FS) prev(path TreePath, node *diskio.Ref[btrfsvol.LogicalAddr, Node])
path = path.DeepCopy()
// go up
- for path.Node(-1).ItemIdx < 1 {
- path.Nodes = path.Nodes[:len(path.Nodes)-1]
- if len(path.Nodes) == 0 {
+ for path.Node(-1).FromItemIdx < 1 {
+ path = path.Parent()
+ if len(path) == 0 {
return TreePath{}, nil, nil
}
}
// go left
- path.Node(-1).ItemIdx--
- if path.Node(-1).NodeAddr != 0 {
- if node.Addr != path.Node(-2).NodeAddr {
+ path.Node(-1).FromItemIdx--
+ if path.Node(-1).ToNodeAddr != 0 {
+ if node.Addr != path.Node(-2).ToNodeAddr {
node, err = fs.ReadNode(path.Parent())
if err != nil {
return TreePath{}, nil, err
}
- path.Node(-1).NodeAddr = node.Data.BodyInternal[path.Node(-1).ItemIdx].BlockPtr
+ path.Node(-1).ToNodeAddr = node.Data.BodyInternal[path.Node(-1).FromItemIdx].BlockPtr
}
}
// go down
- for path.Node(-1).NodeAddr != 0 {
- if node.Addr != path.Node(-1).NodeAddr {
+ for path.Node(-1).ToNodeAddr != 0 {
+ if node.Addr != path.Node(-1).ToNodeAddr {
node, err = fs.ReadNode(path)
if err != nil {
return TreePath{}, nil, err
}
}
if node.Data.Head.Level > 0 {
- path = path.Append(TreePathElem{
- ItemIdx: len(node.Data.BodyInternal) - 1,
- NodeAddr: node.Data.BodyInternal[len(node.Data.BodyInternal)-1].BlockPtr,
- NodeLevel: node.Data.Head.Level - 1,
- NodeParentGeneration: node.Data.Head.Generation,
+ path = append(path, TreePathElem{
+ FromTree: node.Data.Head.Owner,
+ FromGeneration: node.Data.Head.Generation,
+ FromItemIdx: len(node.Data.BodyInternal) - 1,
+ ToNodeAddr: node.Data.BodyInternal[len(node.Data.BodyInternal)-1].BlockPtr,
+ ToNodeLevel: node.Data.Head.Level - 1,
})
} else {
- path = path.Append(TreePathElem{
- ItemIdx: len(node.Data.BodyLeaf) - 1,
+ path = append(path, TreePathElem{
+ FromTree: node.Data.Head.Owner,
+ FromGeneration: node.Data.Head.Generation,
+ FromItemIdx: len(node.Data.BodyLeaf) - 1,
})
}
}
// return
- if node.Addr != path.Node(-2).NodeAddr {
+ if node.Addr != path.Node(-2).ToNodeAddr {
node, err = fs.ReadNode(path.Parent())
if err != nil {
return TreePath{}, nil, err
@@ -559,61 +588,64 @@ func (fs *FS) next(path TreePath, node *diskio.Ref[btrfsvol.LogicalAddr, Node])
path = path.DeepCopy()
// go up
- if node.Addr != path.Node(-2).NodeAddr {
+ if node.Addr != path.Node(-2).ToNodeAddr {
node, err = fs.ReadNode(path.Parent())
if err != nil {
return TreePath{}, nil, err
}
- path.Node(-2).NodeLevel = node.Data.Head.Level
+ path.Node(-2).ToNodeLevel = node.Data.Head.Level
}
- for path.Node(-1).ItemIdx+1 >= int(node.Data.Head.NumItems) {
- path.Nodes = path.Nodes[:len(path.Nodes)-1]
- if len(path.Nodes) == 1 {
+ for path.Node(-1).FromItemIdx+1 >= int(node.Data.Head.NumItems) {
+ path = path.Parent()
+ if len(path) == 1 {
return TreePath{}, nil, nil
}
- if node.Addr != path.Node(-2).NodeAddr {
+ if node.Addr != path.Node(-2).ToNodeAddr {
node, err = fs.ReadNode(path.Parent())
if err != nil {
return TreePath{}, nil, err
}
- path.Node(-2).NodeLevel = node.Data.Head.Level
+ path.Node(-2).ToNodeLevel = node.Data.Head.Level
}
}
// go right
- path.Node(-1).ItemIdx++
- if path.Node(-1).NodeAddr != 0 {
- if node.Addr != path.Node(-2).NodeAddr {
+ path.Node(-1).FromItemIdx++
+ if path.Node(-1).ToNodeAddr != 0 {
+ if node.Addr != path.Node(-2).ToNodeAddr {
node, err = fs.ReadNode(path.Parent())
if err != nil {
return TreePath{}, nil, err
}
- path.Node(-1).NodeAddr = node.Data.BodyInternal[path.Node(-1).ItemIdx].BlockPtr
+ path.Node(-1).ToNodeAddr = node.Data.BodyInternal[path.Node(-1).FromItemIdx].BlockPtr
}
}
// go down
- for path.Node(-1).NodeAddr != 0 {
- if node.Addr != path.Node(-1).NodeAddr {
+ for path.Node(-1).ToNodeAddr != 0 {
+ if node.Addr != path.Node(-1).ToNodeAddr {
node, err = fs.ReadNode(path)
if err != nil {
return TreePath{}, nil, err
}
- path.Node(-1).NodeLevel = node.Data.Head.Level
+ path.Node(-1).ToNodeLevel = node.Data.Head.Level
}
if node.Data.Head.Level > 0 {
- path = path.Append(TreePathElem{
- ItemIdx: 0,
- NodeAddr: node.Data.BodyInternal[len(node.Data.BodyInternal)-1].BlockPtr,
- NodeLevel: node.Data.Head.Level - 1,
- NodeParentGeneration: node.Data.Head.Generation,
+ path = append(path, TreePathElem{
+ FromTree: node.Data.Head.Owner,
+ FromGeneration: node.Data.Head.Generation,
+ FromItemIdx: 0,
+ ToNodeAddr: node.Data.BodyInternal[len(node.Data.BodyInternal)-1].BlockPtr,
+ ToNodeLevel: node.Data.Head.Level - 1,
})
} else {
- path = path.Append(TreePathElem{
- ItemIdx: 0,
+ path = append(path, TreePathElem{
+ FromTree: node.Data.Head.Owner,
+ FromGeneration: node.Data.Head.Generation,
+ FromItemIdx: 0,
})
}
}
// return
- if node.Addr != path.Node(-2).NodeAddr {
+ if node.Addr != path.Node(-2).ToNodeAddr {
node, err = fs.ReadNode(path.Parent())
if err != nil {
return TreePath{}, nil, err
@@ -632,7 +664,7 @@ func (fs *FS) TreeSearch(treeID ObjID, fn func(Key, uint32) int) (Item, error) {
if err != nil {
return Item{}, err
}
- return node.Data.BodyLeaf[path.Node(-1).ItemIdx], nil
+ return node.Data.BodyLeaf[path.Node(-1).FromItemIdx], nil
}
// KeySearch returns a comparator suitable to be passed to TreeSearch.
@@ -661,7 +693,7 @@ func (fs *FS) TreeSearchAll(treeID ObjID, fn func(Key, uint32) int) ([]Item, err
if err != nil {
return nil, err
}
- middleItem := middleNode.Data.BodyLeaf[middlePath.Node(-1).ItemIdx]
+ middleItem := middleNode.Data.BodyLeaf[middlePath.Node(-1).FromItemIdx]
var ret = []Item{middleItem}
var errs derror.MultiError
@@ -671,10 +703,10 @@ func (fs *FS) TreeSearchAll(treeID ObjID, fn func(Key, uint32) int) ([]Item, err
errs = append(errs, err)
break
}
- if len(prevPath.Nodes) == 0 {
+ if len(prevPath) == 0 {
break
}
- prevItem := prevNode.Data.BodyLeaf[prevPath.Node(-1).ItemIdx]
+ prevItem := prevNode.Data.BodyLeaf[prevPath.Node(-1).FromItemIdx]
if fn(prevItem.Key, prevItem.BodySize) != 0 {
break
}
@@ -687,10 +719,10 @@ func (fs *FS) TreeSearchAll(treeID ObjID, fn func(Key, uint32) int) ([]Item, err
errs = append(errs, err)
break
}
- if len(nextPath.Nodes) == 0 {
+ if len(nextPath) == 0 {
break
}
- nextItem := nextNode.Data.BodyLeaf[nextPath.Node(-1).ItemIdx]
+ nextItem := nextNode.Data.BodyLeaf[nextPath.Node(-1).FromItemIdx]
if fn(nextItem.Key, nextItem.BodySize) != 0 {
break
}
diff --git a/lib/btrfs/types_node.go b/lib/btrfs/types_node.go
index e7be341..7d66f60 100644
--- a/lib/btrfs/types_node.go
+++ b/lib/btrfs/types_node.go
@@ -16,6 +16,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/containers"
"git.lukeshu.com/btrfs-progs-ng/lib/diskio"
"git.lukeshu.com/btrfs-progs-ng/lib/fmtutil"
+ "git.lukeshu.com/btrfs-progs-ng/lib/slices"
)
type NodeFlags uint64
@@ -384,7 +385,7 @@ type NodeExpectations struct {
// Things knowable from the parent.
Level containers.Optional[uint8]
MaxGeneration containers.Optional[Generation]
- Owner containers.Optional[ObjID]
+ Owner []ObjID
}
func ReadNode[Addr ~int64](fs diskio.File[Addr], sb Superblock, addr Addr, exp NodeExpectations) (*diskio.Ref[Addr, Node], error) {
@@ -435,9 +436,9 @@ func ReadNode[Addr ~int64](fs diskio.File[Addr], sb Superblock, addr Addr, exp N
return nodeRef, fmt.Errorf("btrfs.ReadNode: node@%v: expected generation<=%v but claims to be generation=%v",
addr, exp.MaxGeneration.Val, nodeRef.Data.Head.Generation)
}
- if exp.Owner.OK && nodeRef.Data.Head.Owner != exp.Owner.Val {
- return nodeRef, fmt.Errorf("btrfs.ReadNode: node@%v: expected owner=%v but claims to have owner=%v",
- addr, exp.Owner.Val, nodeRef.Data.Head.Owner)
+ if len(exp.Owner) > 0 && !slices.Contains(nodeRef.Data.Head.Owner, exp.Owner) {
+ return nodeRef, fmt.Errorf("btrfs.ReadNode: node@%v: expected owner in %v but claims to have owner=%v",
+ addr, exp.Owner, nodeRef.Data.Head.Owner)
}
// parse (main)
diff --git a/lib/btrfsprogs/btrfsinspect/print_tree.go b/lib/btrfsprogs/btrfsinspect/print_tree.go
index 4dd03c4..1568245 100644
--- a/lib/btrfsprogs/btrfsinspect/print_tree.go
+++ b/lib/btrfsprogs/btrfsinspect/print_tree.go
@@ -109,7 +109,7 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfs.Ob
return nil
},
Item: func(path btrfs.TreePath, item btrfs.Item) error {
- i := path.Node(-1).ItemIdx
+ i := path.Node(-1).FromItemIdx
bs, _ := binstruct.Marshal(item.Body)
itemSize := uint32(len(bs))
itemOffset -= itemSize
diff --git a/lib/btrfsprogs/btrfsrepair/clearnodes.go b/lib/btrfsprogs/btrfsrepair/clearnodes.go
index 9f1e42c..2258f66 100644
--- a/lib/btrfsprogs/btrfsrepair/clearnodes.go
+++ b/lib/btrfsprogs/btrfsrepair/clearnodes.go
@@ -54,10 +54,10 @@ func ClearBadNodes(ctx context.Context, fs *btrfs.FS) error {
Flags: btrfs.NodeWritten,
BackrefRev: btrfs.MixedBackrefRev,
ChunkTreeUUID: chunkTreeUUID,
- Generation: 0,
- Owner: path.TreeID,
+ Generation: path.Node(-1).FromGeneration,
+ Owner: path.Node(-1).FromTree,
NumItems: 0,
- Level: path.Node(-1).NodeLevel,
+ Level: path.Node(-1).ToNodeLevel,
},
}
node.Data.Head.Checksum, err = node.Data.CalculateChecksum()
diff --git a/lib/btrfsprogs/btrfsutil/broken_btree.go b/lib/btrfsprogs/btrfsutil/broken_btree.go
index 70eb1de..3a8fb4b 100644
--- a/lib/btrfsprogs/btrfsutil/broken_btree.go
+++ b/lib/btrfsprogs/btrfsutil/broken_btree.go
@@ -47,32 +47,31 @@ func keyMm(key btrfs.Key) btrfs.Key {
func span(fs *btrfs.FS, path btrfs.TreePath) (btrfs.Key, btrfs.Key) {
// tree root error
- if len(path.Nodes) == 0 {
+ if len(path) == 0 {
return btrfs.Key{}, maxKey
}
// item error
- if path.Node(-1).NodeAddr == 0 {
+ if path.Node(-1).ToNodeAddr == 0 {
// If we got an item error, then the node is readable
- node, _ := fs.ReadNode(path.Parent())
- key := node.Data.BodyLeaf[path.Node(-1).ItemIdx].Key
+ node, _ := fs.ReadNode(path[:len(path)-1])
+ key := node.Data.BodyLeaf[path.Node(-1).FromItemIdx].Key
return key, key
}
// node error
//
// assume that path.Node(-1).NodeAddr is not readable, but that path.Node(-2).NodeAddr is.
- if len(path.Nodes) == 1 {
+ if len(path) == 1 {
return btrfs.Key{}, maxKey
}
parentNode, _ := fs.ReadNode(path.Parent())
- low := parentNode.Data.BodyInternal[path.Node(-1).ItemIdx].Key
+ low := parentNode.Data.BodyInternal[path.Node(-1).FromItemIdx].Key
var high btrfs.Key
- if path.Node(-1).ItemIdx+1 < len(parentNode.Data.BodyInternal) {
- high = keyMm(parentNode.Data.BodyInternal[path.Node(-1).ItemIdx+1].Key)
+ if path.Node(-1).FromItemIdx+1 < len(parentNode.Data.BodyInternal) {
+ high = keyMm(parentNode.Data.BodyInternal[path.Node(-1).FromItemIdx+1].Key)
} else {
- parentPath := path.DeepCopy()
- parentPath.Nodes = parentPath.Nodes[:len(parentPath.Nodes)-1]
+ parentPath := path.Parent().DeepCopy()
_, high = span(fs, parentPath)
}
return low, high
@@ -162,12 +161,12 @@ func (bt *brokenTrees) treeIndex(treeID btrfs.ObjID) cachedIndex {
bt.ctx,
*treeRoot,
func(err *btrfs.TreeError) {
- if len(err.Path.Nodes) > 0 && err.Path.Node(-1).NodeAddr == 0 {
+ if len(err.Path) > 0 && err.Path.Node(-1).ToNodeAddr == 0 {
// This is a panic because on the filesystems I'm working with it more likely
// indicates a bug in my item parser than a problem with the filesystem.
panic(fmt.Errorf("TODO: error parsing item: %w", err))
}
- invLvl := len(err.Path.Nodes)
+ invLvl := len(err.Path)
lvlErrs, ok := cacheEntry.Errors[invLvl]
if !ok {
lvlErrs = make(map[btrfs.Key][]spanError)
@@ -231,7 +230,7 @@ func (bt *brokenTrees) TreeSearch(treeID btrfs.ObjID, fn func(btrfs.Key, uint32)
return btrfs.Item{}, err
}
- item := node.Data.BodyLeaf[indexItem.Value.Path.Node(-1).ItemIdx]
+ item := node.Data.BodyLeaf[indexItem.Value.Path.Node(-1).FromItemIdx]
return item, nil
}
@@ -251,14 +250,14 @@ func (bt *brokenTrees) TreeSearchAll(treeID btrfs.ObjID, fn func(btrfs.Key, uint
ret := make([]btrfs.Item, len(indexItems))
var node *diskio.Ref[btrfsvol.LogicalAddr, btrfs.Node]
for i := range indexItems {
- if node == nil || node.Addr != indexItems[i].Path.Node(-2).NodeAddr {
+ if node == nil || node.Addr != indexItems[i].Path.Node(-2).ToNodeAddr {
var err error
node, err = bt.inner.ReadNode(indexItems[i].Path.Parent())
if err != nil {
return nil, err
}
}
- ret[i] = node.Data.BodyLeaf[indexItems[i].Path.Node(-1).ItemIdx]
+ ret[i] = node.Data.BodyLeaf[indexItems[i].Path.Node(-1).FromItemIdx]
}
var errs derror.MultiError
@@ -288,9 +287,9 @@ func (bt *brokenTrees) TreeWalk(ctx context.Context, treeID btrfs.ObjID, errHand
index := bt.treeIndex(treeID)
if index.TreeRootErr != nil {
errHandle(&btrfs.TreeError{
- Path: btrfs.TreePath{
- TreeID: treeID,
- },
+ Path: btrfs.TreePath{{
+ FromTree: treeID,
+ }},
Err: index.TreeRootErr,
})
return
@@ -304,7 +303,7 @@ func (bt *brokenTrees) TreeWalk(ctx context.Context, treeID btrfs.ObjID, errHand
return bt.ctx.Err()
}
if cbs.Item != nil {
- if node == nil || node.Addr != indexItem.Value.Path.Node(-2).NodeAddr {
+ if node == nil || node.Addr != indexItem.Value.Path.Node(-2).ToNodeAddr {
var err error
node, err = bt.inner.ReadNode(indexItem.Value.Path.Parent())
if err != nil {
@@ -312,7 +311,7 @@ func (bt *brokenTrees) TreeWalk(ctx context.Context, treeID btrfs.ObjID, errHand
return nil
}
}
- item := node.Data.BodyLeaf[indexItem.Value.Path.Node(-1).ItemIdx]
+ item := node.Data.BodyLeaf[indexItem.Value.Path.Node(-1).FromItemIdx]
if err := cbs.Item(indexItem.Value.Path, item); err != nil {
errHandle(&btrfs.TreeError{Path: indexItem.Value.Path, Err: err})
}