From fdbe325563be8e223ed8b9f2a89426cf39f4903e Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sun, 16 Apr 2023 23:35:04 -0600 Subject: btrfs: Subvolume: loadFile: Fix errors around extent gaps --- lib/btrfs/io4_fs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/btrfs/io4_fs.go b/lib/btrfs/io4_fs.go index 9b70713..1b7837e 100644 --- a/lib/btrfs/io4_fs.go +++ b/lib/btrfs/io4_fs.go @@ -423,7 +423,7 @@ func (sv *Subvolume) loadFile(_ context.Context, inode btrfsprim.ObjID, file *Fi if err != nil { file.Errs = append(file.Errs, fmt.Errorf("extent %v: %w", extent.OffsetWithinFile, err)) } - pos += size + pos = extent.OffsetWithinFile + size } if file.InodeItem != nil && pos != file.InodeItem.NumBytes { if file.InodeItem.NumBytes > pos { -- cgit v1.2.3 From 5e7fe4c3d25fdecb3b5d4ad5f752fb0a6bddac85 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 17 Apr 2023 15:43:53 -0600 Subject: btrfs: FS: AcquireNode: Shorten errors --- lib/btrfs/io3_btree.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/btrfs/io3_btree.go b/lib/btrfs/io3_btree.go index c9e1d79..7518561 100644 --- a/lib/btrfs/io3_btree.go +++ b/lib/btrfs/io3_btree.go @@ -6,7 +6,6 @@ package btrfs import ( "context" - "fmt" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfstree" @@ -44,7 +43,7 @@ func (fs *FS) AcquireNode(ctx context.Context, addr btrfsvol.LogicalAddr, exp bt if nodeEntry.node != nil { if err := exp.Check(nodeEntry.node); err != nil { fs.cacheNodes.Release(addr) - return nil, fmt.Errorf("btrfs.FS.AcquireNode: node@%v: %w", addr, err) + return nil, err } } -- cgit v1.2.3 From 1a979d52855911980acc0b6f366d808c5734c054 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 17 Apr 2023 15:53:42 -0600 Subject: btrfstree: Wrap the error for UUID lookup failures --- lib/btrfs/btrfstree/btree_tree.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/btrfs/btrfstree/btree_tree.go b/lib/btrfs/btrfstree/btree_tree.go index b9b9201..b071b7d 100644 --- a/lib/btrfs/btrfstree/btree_tree.go +++ b/lib/btrfs/btrfstree/btree_tree.go @@ -328,7 +328,8 @@ func (tree *RawTree) TreeParentID(ctx context.Context) (btrfsprim.ObjID, btrfspr } parentIDItem, err := uuidTree.TreeLookup(ctx, btrfsitem.UUIDToKey(tree.ParentUUID)) if err != nil { - return 0, 0, err + return 0, 0, fmt.Errorf("tree %s: failed to look up UUID: %v: %w", + tree.ID.Format(btrfsprim.ROOT_TREE_OBJECTID), tree.ParentUUID, err) } switch parentIDBody := parentIDItem.Body.(type) { case *btrfsitem.UUIDMap: -- cgit v1.2.3 From 1efce3f90371d12bbb429b1810ce3a5c53e4d4f6 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 17 Apr 2023 08:07:09 -0600 Subject: btrfsutil: RebuiltTree: Fix potential infinite loop in RebuiltCOWDistance --- lib/btrfsutil/rebuilt_tree.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/btrfsutil/rebuilt_tree.go b/lib/btrfsutil/rebuilt_tree.go index 3036938..a0f5932 100644 --- a/lib/btrfsutil/rebuilt_tree.go +++ b/lib/btrfsutil/rebuilt_tree.go @@ -506,11 +506,12 @@ func (tree *RebuiltTree) RebuiltAddRoot(ctx context.Context, rootNode btrfsvol.L // RebuiltCOWDistance returns how many COW-snapshots down the 'tree' // is from the 'parent'. func (tree *RebuiltTree) RebuiltCOWDistance(parentID btrfsprim.ObjID) (dist int, ok bool) { + root := tree.ancestorRoot for { if parentID == tree.ID { return dist, true } - if tree.Parent == nil { + if tree.Parent == nil || tree.ID == root { return 0, false } tree = tree.Parent -- cgit v1.2.3 From eb19c7c4d4a1a8b49ea4423b706edff651768447 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 17 Apr 2023 08:09:05 -0600 Subject: btrfsutil: RebuiltTree: Don't have laxAncestors hide UUID errors --- lib/btrfsutil/rebuilt_forrest.go | 7 +++++-- lib/btrfsutil/rebuilt_tree.go | 9 +++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/btrfsutil/rebuilt_forrest.go b/lib/btrfsutil/rebuilt_forrest.go index 900e725..5741072 100644 --- a/lib/btrfsutil/rebuilt_forrest.go +++ b/lib/btrfsutil/rebuilt_forrest.go @@ -223,8 +223,11 @@ func (ts *RebuiltForrest) rebuildTree(ctx context.Context, treeID btrfsprim.ObjI ts.trees[treeID].ParentGen = rootOff parentID, ok := ts.cb.LookupUUID(ctx, rootItem.ParentUUID) if !ok { - if !ts.laxAncestors { - ts.trees[treeID].rootErr = fmt.Errorf("failed to look up UUID: %v", rootItem.ParentUUID) + err := fmt.Errorf("failed to look up UUID: %v", rootItem.ParentUUID) + if ts.laxAncestors { + ts.trees[treeID].parentErr = err + } else { + ts.trees[treeID].rootErr = err } return } diff --git a/lib/btrfsutil/rebuilt_tree.go b/lib/btrfsutil/rebuilt_tree.go index a0f5932..0598e05 100644 --- a/lib/btrfsutil/rebuilt_tree.go +++ b/lib/btrfsutil/rebuilt_tree.go @@ -34,6 +34,7 @@ type RebuiltTree struct { Root btrfsvol.LogicalAddr Parent *RebuiltTree ParentGen btrfsprim.Generation // offset of this tree's root item + parentErr error forrest *RebuiltForrest // mutable @@ -553,10 +554,14 @@ var _ btrfstree.Tree = (*RebuiltTree)(nil) // TreeParentID implements btrfstree.Tree. func (tree *RebuiltTree) TreeParentID(_ context.Context) (btrfsprim.ObjID, btrfsprim.Generation, error) { - if tree.Parent == nil { + switch { + case tree.parentErr != nil: + return 0, 0, tree.parentErr + case tree.Parent == nil: return 0, 0, nil + default: + return tree.Parent.ID, tree.ParentGen, nil } - return tree.Parent.ID, tree.ParentGen, nil } // TreeLookup implements btrfstree.Tree. -- cgit v1.2.3 From a74c2cd52a12614565349577b8a5a6fbdcf337c3 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sat, 4 Mar 2023 09:42:44 -0700 Subject: btrfsutil: RebuiltTree: Properly track errors for the btrfstree.Tree API --- lib/btrfs/btrfstree/path.go | 6 +- lib/btrfsutil/rebuilt_tree.go | 285 ++++++++++++++++++++++++++++++++++++++++-- lib/textui/log.go | 8 +- 3 files changed, 281 insertions(+), 18 deletions(-) diff --git a/lib/btrfs/btrfstree/path.go b/lib/btrfs/btrfstree/path.go index 327a39b..3fd43c8 100644 --- a/lib/btrfs/btrfstree/path.go +++ b/lib/btrfs/btrfstree/path.go @@ -134,7 +134,7 @@ func (path Path) String() string { return ret.String() } -func checkOwner( +func CheckOwner( ctx context.Context, forrest Forrest, treeID btrfsprim.ObjID, ownerToCheck btrfsprim.ObjID, genToCheck btrfsprim.Generation, ) error { @@ -194,7 +194,7 @@ func (path Path) NodeExpectations(ctx context.Context) (_ btrfsvol.LogicalAddr, Level: containers.OptionalValue(lastElem.ToLevel), Generation: containers.OptionalValue(lastElem.ToGeneration), Owner: func(owner btrfsprim.ObjID, gen btrfsprim.Generation) error { - return checkOwner(ctx, firstElem.Forrest, lastElem.TreeID, + return CheckOwner(ctx, firstElem.Forrest, lastElem.TreeID, owner, gen) }, MinItem: containers.OptionalValue(btrfsprim.Key{}), @@ -206,7 +206,7 @@ func (path Path) NodeExpectations(ctx context.Context) (_ btrfsvol.LogicalAddr, Level: containers.OptionalValue(lastElem.ToLevel), Generation: containers.OptionalValue(lastElem.ToGeneration), Owner: func(owner btrfsprim.ObjID, gen btrfsprim.Generation) error { - return checkOwner(ctx, firstElem.Forrest, lastElem.FromTree, + return CheckOwner(ctx, firstElem.Forrest, lastElem.FromTree, owner, gen) }, MinItem: containers.OptionalValue(lastElem.ToMinKey), diff --git a/lib/btrfsutil/rebuilt_tree.go b/lib/btrfsutil/rebuilt_tree.go index 0598e05..8336c85 100644 --- a/lib/btrfsutil/rebuilt_tree.go +++ b/lib/btrfsutil/rebuilt_tree.go @@ -10,6 +10,7 @@ import ( "sync" "time" + "github.com/datawire/dlib/derror" "github.com/datawire/dlib/dlog" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem" @@ -45,7 +46,7 @@ type RebuiltTree struct { Roots containers.Set[btrfsvol.LogicalAddr] - // There are 3 more mutable "members" that are protected by + // There are 4 more mutable "members" that are protected by // `mu`; but they live in a shared Cache. They are all // derived from tree.Roots, which is why it's OK if they get // evicted. @@ -53,12 +54,14 @@ type RebuiltTree struct { // 1. tree.acquireNodeIndex() = tree.forrest.nodeIndex.Acquire(tree.ID) // 2. tree.RebuiltAcquireItems() = tree.forrest.incItems.Acquire(tree.ID) // 3. tree.RebuiltAcquirePotentialItems() = tree.forrest.excItems.Acquire(tree.ID) + // 4. tree.addErrs() = tree.forrest.errors.Acquire(tree.ID) } type rebuiltSharedCache struct { nodeIndex containers.Cache[btrfsprim.ObjID, rebuiltNodeIndex] incItems containers.Cache[btrfsprim.ObjID, containers.SortedMap[btrfsprim.Key, ItemPtr]] excItems containers.Cache[btrfsprim.ObjID, containers.SortedMap[btrfsprim.Key, ItemPtr]] + errors containers.Cache[btrfsprim.ObjID, containers.IntervalTree[btrfsprim.Key, rebuiltTreeError]] } func makeRebuiltSharedCache(forrest *RebuiltForrest) rebuiltSharedCache { @@ -81,6 +84,12 @@ func makeRebuiltSharedCache(forrest *RebuiltForrest) rebuiltSharedCache { func(ctx context.Context, treeID btrfsprim.ObjID, excItems *containers.SortedMap[btrfsprim.Key, ItemPtr]) { *excItems = forrest.trees[treeID].uncachedExcItems(ctx) })) + ret.errors = containers.NewARCache[btrfsprim.ObjID, containers.IntervalTree[btrfsprim.Key, rebuiltTreeError]]( + textui.Tunable(8), + containers.SourceFunc[btrfsprim.ObjID, containers.IntervalTree[btrfsprim.Key, rebuiltTreeError]]( + func(ctx context.Context, treeID btrfsprim.ObjID, errs *containers.IntervalTree[btrfsprim.Key, rebuiltTreeError]) { + *errs = forrest.trees[treeID].uncachedErrors(ctx) + })) return ret } @@ -387,6 +396,245 @@ func (tree *RebuiltTree) uncachedItems(ctx context.Context, inc bool) containers return index } +// evictable member 4: .addErrs() ////////////////////////////////////////////////////////////////////////////////////// + +type rebuiltTreeError struct { + Min btrfsprim.Key + Max btrfsprim.Key + Node btrfsvol.LogicalAddr + Err error +} + +func (e rebuiltTreeError) Error() string { + return fmt.Sprintf("keys %v-%v: node@%v: %v", e.Min, e.Max, e.Node, e.Err) +} + +func (e rebuiltTreeError) Unwrap() error { + return e.Err +} + +type errorStats struct { + Nodes textui.Portion[int] + NumErrs int +} + +func (s errorStats) String() string { + return textui.Sprintf("%v (%v errs)", + s.Nodes, s.NumErrs) +} + +func (tree *RebuiltTree) uncachedErrors(ctx context.Context) containers.IntervalTree[btrfsprim.Key, rebuiltTreeError] { + ctx = dlog.WithField(ctx, "btrfs.util.rebuilt-tree.index-errors", fmt.Sprintf("tree=%v", tree.ID)) + + tree.mu.RLock() + defer tree.mu.RUnlock() + + errs := containers.IntervalTree[btrfsprim.Key, rebuiltTreeError]{ + MinFn: func(err rebuiltTreeError) btrfsprim.Key { + return err.Min + }, + MaxFn: func(err rebuiltTreeError) btrfsprim.Key { + return err.Max + }, + } + + nodeIndex := tree.acquireNodeIndex(ctx) + defer tree.releaseNodeIndex() + + nodesToProcess := make(containers.Set[btrfsvol.LogicalAddr], len(nodeIndex.nodeToRoots)) + for node := range nodeIndex.nodeToRoots { + if !maps.HaveAnyKeysInCommon(nodeIndex.nodeToRoots[node], tree.Roots) { + continue + } + nodesToProcess.Insert(node) + for _, kp := range tree.forrest.graph.EdgesFrom[node] { + nodesToProcess.Insert(kp.ToNode) + } + } + for root := range tree.Roots { + // For BadNodes that are roots. + nodesToProcess.Insert(root) + } + + var stats errorStats + stats.Nodes.D = len(nodesToProcess) + progressWriter := textui.NewProgress[errorStats](ctx, dlog.LogLevelInfo, textui.Tunable(1*time.Second)) + progressWriter.Set(stats) + + var kps []*GraphEdge + for i, node := range maps.SortedKeys(nodesToProcess) { + stats.Nodes.N = i + progressWriter.Set(stats) + + // `node` may either be in the tree, or just pointed + // to by a different node in the tree. Decide whether + // it's in the tree, and gather all of the + // key-pointers in the tree that point to it. + inTree := maps.HaveAnyKeysInCommon(nodeIndex.nodeToRoots[node], tree.Roots) + kps = kps[:0] + for _, kp := range tree.forrest.graph.EdgesTo[node] { + if !maps.HaveAnyKeysInCommon(nodeIndex.nodeToRoots[kp.FromNode], tree.Roots) { + continue + } + kps = append(kps, kp) + } + + // Look at all key-pointers to decide what our + // expectations are. + var ( + expLevel = make(containers.Set[uint8], len(kps)) + expGen = make(containers.Set[btrfsprim.Generation], len(kps)) + expTree = make(containers.Set[btrfsprim.ObjID], len(kps)) + loMinItem = btrfsprim.MaxKey // lowest kp.ToKey seen + hiMinItem = btrfsprim.Key{} // highest kp.ToKey seen + loMaxItem = btrfsprim.MaxKey // lowest nodeToRoots[node][root] seen + hiMaxItem = btrfsprim.Key{} // highest nodeToRoots[node][root] seen + ) + expTree.Insert(tree.ID) + if len(kps) == 0 { + // This is a root. + loMinItem = btrfsprim.Key{} + hiMaxItem = btrfsprim.MaxKey + } else { + // expLevel, expGen, loMinItem, hiMinItem, + for _, kp := range kps { + expLevel.Insert(kp.ToLevel) + expGen.Insert(kp.ToGeneration) + expTree.Insert(kp.FromTree) + if kp.ToKey.Compare(loMinItem) < 0 { + loMinItem = kp.ToKey + } + if kp.ToKey.Compare(hiMinItem) > 0 { + hiMinItem = kp.ToKey + } + } + // loMaxItem, hiMaxItem + if !inTree { + for _, kp := range kps { + for root, rootInfo := range nodeIndex.nodeToRoots[kp.FromNode] { + if !tree.Roots.Has(root) { + continue + } + if kp.FromSlot+1 < len(tree.forrest.graph.EdgesFrom[kp.FromNode]) { + rootInfo.loMaxItem = tree.forrest.graph.EdgesFrom[kp.FromNode][kp.FromSlot+1].ToKey.Mm() + rootInfo.hiMaxItem = rootInfo.loMaxItem + } + if loMaxItem.Compare(rootInfo.loMaxItem) > 0 { + loMaxItem = rootInfo.loMaxItem + } + if hiMaxItem.Compare(rootInfo.hiMaxItem) < 0 { + hiMaxItem = rootInfo.hiMaxItem + } + } + } + } else { + // As an optimization, we can look at this node's rootInfo directly. + // This should be equivalent to the above loop for `!inTree`, but is + // faster. + for root, rootInfo := range nodeIndex.nodeToRoots[node] { + if !tree.Roots.Has(root) { + continue + } + if loMaxItem.Compare(rootInfo.loMaxItem) > 0 { + loMaxItem = rootInfo.loMaxItem + } + if hiMaxItem.Compare(rootInfo.hiMaxItem) < 0 { + hiMaxItem = rootInfo.hiMaxItem + } + } + } + } + + // Assemble all of that in to a btrfstree.NodeExpectations. + var nodeErrs derror.MultiError + exp := btrfstree.NodeExpectations{ + LAddr: containers.OptionalValue(node), + MinItem: containers.OptionalValue(hiMinItem), + MaxItem: containers.OptionalValue(loMaxItem), + Owner: func(owner btrfsprim.ObjID, gen btrfsprim.Generation) error { + var ownerErrs derror.MultiError + for _, kpTree := range maps.SortedKeys(expTree) { + if err := btrfstree.CheckOwner(ctx, tree.forrest, kpTree, owner, gen); err != nil { + ownerErrs = append(ownerErrs, err) + } + } + if len(ownerErrs) > 0 { + return ownerErrs + } + return nil + }, + } + switch len(expLevel) { + case 0: + // do nothing + case 1: + exp.Level = containers.OptionalValue(expLevel.TakeOne()) + default: + nodeErrs = append(nodeErrs, + fmt.Errorf("multiple KPs request different node levels: %v (actual: %v)", + maps.SortedKeys(expLevel), tree.forrest.graph.Nodes[node].Level)) + } + switch len(expGen) { + case 0: + // do nothing + case 1: + exp.Generation = containers.OptionalValue(expGen.TakeOne()) + default: + nodeErrs = append(nodeErrs, + fmt.Errorf("multiple KPs request different node generations: %v (actual: %v)", + maps.SortedKeys(expGen), tree.forrest.graph.Nodes[node].Generation)) + } + + // Check those expectations. + if hiMaxItem.Compare(loMinItem) < 0 { + nodeErrs = append(nodeErrs, + fmt.Errorf("loMinItem:%v > hiMaxItem:%v", loMinItem, hiMaxItem)) + loMinItem = btrfsprim.Key{} + hiMaxItem = btrfsprim.MaxKey + } + if err := tree.forrest.graph.BadNodes[node]; err != nil { + nodeErrs = append(nodeErrs, err) + } else if err := tree.forrest.graph.Nodes[node].CheckExpectations(tree.forrest.graph, exp); err != nil { + nodeErrs = append(nodeErrs, err) + } + + if len(nodeErrs) > 0 { + errs.Insert(rebuiltTreeError{ + Min: loMinItem, + Max: hiMaxItem, + Node: node, + Err: nodeErrs, + }) + + stats.NumErrs++ + progressWriter.Set(stats) + } + } + stats.Nodes.N = stats.Nodes.D + progressWriter.Set(stats) + progressWriter.Done() + + return errs +} + +func (tree *RebuiltTree) addErrs(ctx context.Context, fn func(btrfsprim.Key, uint32) int, err error) error { + var errs derror.MultiError + tree.forrest.errors.Acquire(ctx, tree.ID).Subrange( + func(k btrfsprim.Key) int { return fn(k, 0) }, + func(v rebuiltTreeError) bool { + errs = append(errs, v) + return true + }) + tree.forrest.errors.Release(tree.ID) + if len(errs) == 0 { + return err + } + if err != nil { + errs = append(errs, err) + } + return errs +} + // main public API ///////////////////////////////////////////////////////////////////////////////////////////////////// func (tree *RebuiltTree) RebuiltShouldReplace(oldNode, newNode btrfsvol.LogicalAddr) bool { @@ -497,6 +745,7 @@ func (tree *RebuiltTree) RebuiltAddRoot(ctx context.Context, rootNode btrfsvol.L tree.Roots.Insert(rootNode) tree.forrest.incItems.Delete(tree.ID) // force re-gen tree.forrest.excItems.Delete(tree.ID) // force re-gen + tree.forrest.errors.Delete(tree.ID) // force re-gen if shouldFlush { tree.forrest.flushNegativeCache(ctx) @@ -565,8 +814,6 @@ func (tree *RebuiltTree) TreeParentID(_ context.Context) (btrfsprim.ObjID, btrfs } // TreeLookup implements btrfstree.Tree. -// -// BUG(lukeshu): Errors in the tree are not ever returned. func (tree *RebuiltTree) TreeLookup(ctx context.Context, key btrfsprim.Key) (btrfstree.Item, error) { return tree.TreeSearch(ctx, btrfstree.SearchExactKey(key)) } @@ -574,16 +821,19 @@ func (tree *RebuiltTree) TreeLookup(ctx context.Context, key btrfsprim.Key) (btr // TreeSearch implements btrfstree.Tree. It is a thin wrapper around // tree.RebuiltItems(ctx).Search (to do the search) and // tree.TreeLookup (to read item bodies). -// -// BUG(lukeshu): Errors in the tree are not ever returned. func (tree *RebuiltTree) TreeSearch(ctx context.Context, searcher btrfstree.TreeSearcher) (btrfstree.Item, error) { + tree.forrest.commitTrees(ctx, tree.ID) + tree.initRoots(ctx) + tree.mu.RLock() + defer tree.mu.RUnlock() + _, ptr, ok := tree.RebuiltAcquireItems(ctx).Search(func(_ btrfsprim.Key, ptr ItemPtr) int { straw := tree.forrest.graph.Nodes[ptr.Node].Items[ptr.Slot] return searcher.Search(straw.Key, straw.Size) }) tree.RebuiltReleaseItems() if !ok { - return btrfstree.Item{}, fmt.Errorf("item with %s: %w", searcher, btrfstree.ErrNoItem) + return btrfstree.Item{}, fmt.Errorf("item with %s: %w", searcher, tree.addErrs(ctx, searcher.Search, btrfstree.ErrNoItem)) } return tree.forrest.readItem(ctx, ptr), nil } @@ -591,26 +841,32 @@ func (tree *RebuiltTree) TreeSearch(ctx context.Context, searcher btrfstree.Tree // TreeRange implements btrfstree.Tree. It is a thin wrapper around // tree.RebuiltItems(ctx).Range (to do the iteration) and // tree.TreeLookup (to read item bodies). -// -// BUG(lukeshu): Errors in the tree are not ever returned. func (tree *RebuiltTree) TreeRange(ctx context.Context, handleFn func(btrfstree.Item) bool) error { + tree.forrest.commitTrees(ctx, tree.ID) + tree.initRoots(ctx) + tree.mu.RLock() + defer tree.mu.RUnlock() + tree.RebuiltAcquireItems(ctx).Range(func(_ btrfsprim.Key, ptr ItemPtr) bool { return handleFn(tree.forrest.readItem(ctx, ptr)) }) tree.RebuiltReleaseItems() - return nil + return tree.addErrs(ctx, func(btrfsprim.Key, uint32) int { return 0 }, nil) } // TreeSubrange implements btrfstree.Tree. It is a thin wrapper // around tree.RebuiltItems(ctx).Subrange (to do the iteration) and // tree.TreeLookup (to read item bodies). -// -// BUG(lukeshu): Errors in the tree are not ever returned. func (tree *RebuiltTree) TreeSubrange(ctx context.Context, min int, searcher btrfstree.TreeSearcher, handleFn func(btrfstree.Item) bool, ) error { + tree.forrest.commitTrees(ctx, tree.ID) + tree.initRoots(ctx) + tree.mu.RLock() + defer tree.mu.RUnlock() + var cnt int tree.RebuiltAcquireItems(ctx).Subrange( func(_ btrfsprim.Key, ptr ItemPtr) int { @@ -624,8 +880,13 @@ func (tree *RebuiltTree) TreeSubrange(ctx context.Context, ) tree.RebuiltReleaseItems() + var err error if cnt < min { - return btrfstree.ErrNoItem + err = btrfstree.ErrNoItem + } + err = tree.addErrs(ctx, searcher.Search, err) + if err != nil { + return fmt.Errorf("items with %s: %w", searcher, err) } return nil diff --git a/lib/textui/log.go b/lib/textui/log.go index 25bb4be..a26dd5f 100644 --- a/lib/textui/log.go +++ b/lib/textui/log.go @@ -338,12 +338,14 @@ func fieldOrd(key string) int { // btrfsutil.RebuiltForrest //////////////////////////////////////////// case "btrfs.util.rebuilt-forrest.add-tree": - return -7 + return -8 case "btrfs.util.rebuilt-forrest.add-tree.want.key": - return -6 + return -7 case "btrfs.util.rebuilt-forrest.add-tree.want.reason": - return -5 + return -6 case "btrfs.util.rebuilt-tree.add-root": + return -5 + case "btrfs.util.rebuilt-tree.index-errors": return -4 case "btrfs.util.rebuilt-tree.index-inc-items": return -3 -- cgit v1.2.3 From 2fec63f72ded515dd51b08245f344385adbe34fb Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 17 Apr 2023 15:53:20 -0600 Subject: btrfsutil: RebuiltForrest: Have LookupUUID return an error --- cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go | 9 +++++---- lib/btrfsutil/rebuilt_callbacks.go | 12 ++++++------ lib/btrfsutil/rebuilt_forrest.go | 6 +++--- lib/btrfsutil/rebuilt_forrest_test.go | 17 +++++++++-------- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go index 15ad42c..cfef623 100644 --- a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go +++ b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go @@ -10,6 +10,7 @@ import ( "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/btrfsutil" ) @@ -67,7 +68,7 @@ func (o forrestCallbacks) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) } // LookupUUID implements btrfsutil.RebuiltForrestCallbacks. -func (o forrestCallbacks) LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, ok bool) { +func (o forrestCallbacks) LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, err error) { wantKey := wantWithTree{ TreeID: btrfsprim.UUID_TREE_OBJECTID, Key: wantFromKey(btrfsitem.UUIDToKey(uuid)), @@ -75,16 +76,16 @@ func (o forrestCallbacks) LookupUUID(ctx context.Context, uuid btrfsprim.UUID) ( ctx = withWant(ctx, logFieldTreeWant, "resolve parent UUID", wantKey) if !o._wantOff(ctx, wantKey) { o.enqueueRetry(btrfsprim.UUID_TREE_OBJECTID) - return 0, false + return 0, btrfstree.ErrNoItem } item, _ := discardErr(o.rebuilt.RebuiltTree(ctx, wantKey.TreeID)).TreeLookup(ctx, wantKey.Key.Key()) defer item.Body.Free() switch itemBody := item.Body.(type) { case *btrfsitem.UUIDMap: - return itemBody.ObjID, true + return itemBody.ObjID, nil case *btrfsitem.Error: graphCallbacks(o).FSErr(ctx, fmt.Errorf("error decoding item: %v: %w", wantKey, itemBody.Err)) - return 0, false + return 0, itemBody.Err default: // This is a panic because the item decoder should not emit UUID_SUBVOL items as anything but // btrfsitem.UUIDMap or btrfsitem.Error without this code also being updated. diff --git a/lib/btrfsutil/rebuilt_callbacks.go b/lib/btrfsutil/rebuilt_callbacks.go index 0b51d08..9ed3177 100644 --- a/lib/btrfsutil/rebuilt_callbacks.go +++ b/lib/btrfsutil/rebuilt_callbacks.go @@ -17,7 +17,7 @@ import ( type RebuiltForrestCallbacks interface { AddedRoot(ctx context.Context, tree btrfsprim.ObjID, root btrfsvol.LogicalAddr) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, ok bool) - LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, ok bool) + LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, err error) } type RebuiltForrestExtendedCallbacks interface { @@ -54,22 +54,22 @@ func (cb noopRebuiltForrestCallbacks) LookupRoot(ctx context.Context, tree btrfs } } -func (cb noopRebuiltForrestCallbacks) LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, ok bool) { +func (cb noopRebuiltForrestCallbacks) LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, err error) { uuidTree, err := cb.forrest.ForrestLookup(ctx, btrfsprim.UUID_TREE_OBJECTID) if err != nil { - return 0, false + return 0, err } tgt := btrfsitem.UUIDToKey(uuid) item, err := uuidTree.TreeLookup(ctx, tgt) if err != nil { - return 0, false + return 0, err } defer item.Body.Free() switch itemBody := item.Body.(type) { case *btrfsitem.UUIDMap: - return itemBody.ObjID, true + return itemBody.ObjID, nil case *btrfsitem.Error: - return 0, false + return 0, itemBody.Err default: // This is a panic because the item decoder should not emit UUID_SUBVOL items as anything but // btrfsitem.UUIDMap or btrfsitem.Error without this code also being updated. diff --git a/lib/btrfsutil/rebuilt_forrest.go b/lib/btrfsutil/rebuilt_forrest.go index 5741072..3c53af7 100644 --- a/lib/btrfsutil/rebuilt_forrest.go +++ b/lib/btrfsutil/rebuilt_forrest.go @@ -221,9 +221,9 @@ func (ts *RebuiltForrest) rebuildTree(ctx context.Context, treeID btrfsprim.ObjI ts.trees[treeID].UUID = rootItem.UUID if rootItem.ParentUUID != (btrfsprim.UUID{}) { ts.trees[treeID].ParentGen = rootOff - parentID, ok := ts.cb.LookupUUID(ctx, rootItem.ParentUUID) - if !ok { - err := fmt.Errorf("failed to look up UUID: %v", rootItem.ParentUUID) + parentID, err := ts.cb.LookupUUID(ctx, rootItem.ParentUUID) + if err != nil { + err := fmt.Errorf("failed to look up UUID: %v: %w", rootItem.ParentUUID, err) if ts.laxAncestors { ts.trees[treeID].parentErr = err } else { diff --git a/lib/btrfsutil/rebuilt_forrest_test.go b/lib/btrfsutil/rebuilt_forrest_test.go index 8bbb50a..ef0ed72 100644 --- a/lib/btrfsutil/rebuilt_forrest_test.go +++ b/lib/btrfsutil/rebuilt_forrest_test.go @@ -13,6 +13,7 @@ import ( "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" ) @@ -20,7 +21,7 @@ type rebuiltForrestCallbacks struct { addedItem func(ctx context.Context, tree btrfsprim.ObjID, key btrfsprim.Key) addedRoot func(ctx context.Context, tree btrfsprim.ObjID, root btrfsvol.LogicalAddr) lookupRoot func(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, ok bool) - lookupUUID func(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, ok bool) + lookupUUID func(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, err error) } func (cbs rebuiltForrestCallbacks) AddedItem(ctx context.Context, tree btrfsprim.ObjID, key btrfsprim.Key) { @@ -35,7 +36,7 @@ func (cbs rebuiltForrestCallbacks) LookupRoot(ctx context.Context, tree btrfspri return cbs.lookupRoot(ctx, tree) } -func (cbs rebuiltForrestCallbacks) LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, ok bool) { +func (cbs rebuiltForrestCallbacks) LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, err error) { return cbs.lookupUUID(ctx, uuid) } @@ -96,13 +97,13 @@ func TestRebuiltTreeCycles(t *testing.T) { } return 0, btrfsitem.Root{}, false }, - lookupUUID: func(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, ok bool) { + lookupUUID: func(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, err error) { for _, root := range roots { if root.UUID == uuid { - return root.ID, true + return root.ID, nil } } - return 0, false + return 0, btrfstree.ErrNoItem }, } @@ -209,13 +210,13 @@ func TestRebuiltTreeParentErr(t *testing.T) { } return 0, btrfsitem.Root{}, false }, - lookupUUID: func(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, ok bool) { + lookupUUID: func(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, err error) { for _, root := range roots { if root.UUID == uuid { - return root.ID, true + return root.ID, nil } } - return 0, false + return 0, btrfstree.ErrNoItem }, } -- cgit v1.2.3 From 3a7fec8e6fe4d36c976540f88bce2c186dd47d1f Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 17 Apr 2023 18:02:31 -0600 Subject: btrfsutil: RebuiltTree: Dedup ownership errors --- lib/btrfsutil/rebuilt_tree.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/btrfsutil/rebuilt_tree.go b/lib/btrfsutil/rebuilt_tree.go index 8336c85..97308a3 100644 --- a/lib/btrfsutil/rebuilt_tree.go +++ b/lib/btrfsutil/rebuilt_tree.go @@ -552,14 +552,18 @@ func (tree *RebuiltTree) uncachedErrors(ctx context.Context) containers.Interval MinItem: containers.OptionalValue(hiMinItem), MaxItem: containers.OptionalValue(loMaxItem), Owner: func(owner btrfsprim.ObjID, gen btrfsprim.Generation) error { - var ownerErrs derror.MultiError + byStr := make(map[string]error) for _, kpTree := range maps.SortedKeys(expTree) { if err := btrfstree.CheckOwner(ctx, tree.forrest, kpTree, owner, gen); err != nil { - ownerErrs = append(ownerErrs, err) + byStr[err.Error()] = err } } - if len(ownerErrs) > 0 { - return ownerErrs + if len(byStr) > 0 { + byPos := make(derror.MultiError, 0, len(byStr)) + for _, str := range maps.SortedKeys(byStr) { + byPos = append(byPos, byStr[str]) + } + return byPos } return nil }, -- cgit v1.2.3 From 6b521d08bbb75430432834e5fcd2cb1da3de1e2e Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 17 Apr 2023 18:21:04 -0600 Subject: btrfsutil: RebuiltForrest: Have LookupRoot return an error --- cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go | 8 ++++---- lib/btrfsutil/rebuilt_callbacks.go | 12 ++++++------ lib/btrfsutil/rebuilt_forrest.go | 6 +++--- lib/btrfsutil/rebuilt_forrest_test.go | 20 ++++++++++---------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go index cfef623..b8205ae 100644 --- a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go +++ b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go @@ -37,7 +37,7 @@ func (o forrestCallbacks) AddedRoot(_ context.Context, tree btrfsprim.ObjID, _ b } // LookupRoot implements btrfsutil.RebuiltForrestCallbacks. -func (o forrestCallbacks) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, _item btrfsitem.Root, ok bool) { +func (o forrestCallbacks) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, _item btrfsitem.Root, err error) { wantKey := wantWithTree{ TreeID: btrfsprim.ROOT_TREE_OBJECTID, Key: want{ @@ -50,16 +50,16 @@ func (o forrestCallbacks) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) foundKey, ok := o._want(ctx, wantKey) if !ok { o.enqueueRetry(btrfsprim.ROOT_TREE_OBJECTID) - return 0, btrfsitem.Root{}, false + return 0, btrfsitem.Root{}, btrfstree.ErrNoItem } item, _ := discardErr(o.rebuilt.RebuiltTree(ctx, wantKey.TreeID)).TreeLookup(ctx, foundKey) defer item.Body.Free() switch itemBody := item.Body.(type) { case *btrfsitem.Root: - return btrfsprim.Generation(foundKey.Offset), *itemBody, true + return btrfsprim.Generation(foundKey.Offset), *itemBody, nil case *btrfsitem.Error: graphCallbacks(o).FSErr(ctx, fmt.Errorf("error decoding item: %v: %w", foundKey, itemBody.Err)) - return 0, btrfsitem.Root{}, false + return 0, btrfsitem.Root{}, itemBody.Err default: // This is a panic because the item decoder should not emit ROOT_ITEM items as anything but // btrfsitem.Root or btrfsitem.Error without this code also being updated. diff --git a/lib/btrfsutil/rebuilt_callbacks.go b/lib/btrfsutil/rebuilt_callbacks.go index 9ed3177..b5fbc91 100644 --- a/lib/btrfsutil/rebuilt_callbacks.go +++ b/lib/btrfsutil/rebuilt_callbacks.go @@ -16,7 +16,7 @@ import ( type RebuiltForrestCallbacks interface { AddedRoot(ctx context.Context, tree btrfsprim.ObjID, root btrfsvol.LogicalAddr) - LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, ok bool) + LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, err error) LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, err error) } @@ -32,21 +32,21 @@ type noopRebuiltForrestCallbacks struct { func (noopRebuiltForrestCallbacks) AddedRoot(context.Context, btrfsprim.ObjID, btrfsvol.LogicalAddr) { } -func (cb noopRebuiltForrestCallbacks) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, _item btrfsitem.Root, ok bool) { +func (cb noopRebuiltForrestCallbacks) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, _item btrfsitem.Root, err error) { rootTree, err := cb.forrest.ForrestLookup(ctx, btrfsprim.ROOT_TREE_OBJECTID) if err != nil { - return 0, btrfsitem.Root{}, false + return 0, btrfsitem.Root{}, err } item, err := rootTree.TreeSearch(ctx, btrfstree.SearchRootItem(tree)) if err != nil { - return 0, btrfsitem.Root{}, false + return 0, btrfsitem.Root{}, err } defer item.Body.Free() switch itemBody := item.Body.(type) { case *btrfsitem.Root: - return btrfsprim.Generation(item.Key.Offset), *itemBody, true + return btrfsprim.Generation(item.Key.Offset), *itemBody, nil case *btrfsitem.Error: - return 0, btrfsitem.Root{}, false + return 0, btrfsitem.Root{}, itemBody.Err default: // This is a panic because the item decoder should not emit ROOT_ITEM items as anything but // btrfsitem.Root or btrfsitem.Error without this code also being updated. diff --git a/lib/btrfsutil/rebuilt_forrest.go b/lib/btrfsutil/rebuilt_forrest.go index 3c53af7..3e40103 100644 --- a/lib/btrfsutil/rebuilt_forrest.go +++ b/lib/btrfsutil/rebuilt_forrest.go @@ -212,9 +212,9 @@ func (ts *RebuiltForrest) rebuildTree(ctx context.Context, treeID btrfsprim.ObjI sb, _ := ts.Superblock() ts.trees[treeID].Root = sb.BlockGroupRoot default: - rootOff, rootItem, ok := ts.cb.LookupRoot(ctx, treeID) - if !ok { - ts.trees[treeID].rootErr = btrfstree.ErrNoTree + rootOff, rootItem, err := ts.cb.LookupRoot(ctx, treeID) + if err != nil { + ts.trees[treeID].rootErr = fmt.Errorf("%w: %s", btrfstree.ErrNoTree, err) return } ts.trees[treeID].Root = rootItem.ByteNr diff --git a/lib/btrfsutil/rebuilt_forrest_test.go b/lib/btrfsutil/rebuilt_forrest_test.go index ef0ed72..86ae9a7 100644 --- a/lib/btrfsutil/rebuilt_forrest_test.go +++ b/lib/btrfsutil/rebuilt_forrest_test.go @@ -20,7 +20,7 @@ import ( type rebuiltForrestCallbacks struct { addedItem func(ctx context.Context, tree btrfsprim.ObjID, key btrfsprim.Key) addedRoot func(ctx context.Context, tree btrfsprim.ObjID, root btrfsvol.LogicalAddr) - lookupRoot func(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, ok bool) + lookupRoot func(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, err error) lookupUUID func(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, err error) } @@ -32,7 +32,7 @@ func (cbs rebuiltForrestCallbacks) AddedRoot(ctx context.Context, tree btrfsprim cbs.addedRoot(ctx, tree, root) } -func (cbs rebuiltForrestCallbacks) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, ok bool) { +func (cbs rebuiltForrestCallbacks) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, err error) { return cbs.lookupRoot(ctx, tree) } @@ -85,17 +85,17 @@ func TestRebuiltTreeCycles(t *testing.T) { addedRoot: func(ctx context.Context, tree btrfsprim.ObjID, root btrfsvol.LogicalAddr) { // do nothing }, - lookupRoot: func(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, ok bool) { + lookupRoot: func(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, err error) { for _, root := range roots { if root.ID == tree { return root.ParentGen, btrfsitem.Root{ Generation: 2000, UUID: root.UUID, ParentUUID: root.ParentUUID, - }, true + }, nil } } - return 0, btrfsitem.Root{}, false + return 0, btrfsitem.Root{}, btrfstree.ErrNoItem }, lookupUUID: func(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, err error) { for _, root := range roots { @@ -194,10 +194,10 @@ func TestRebuiltTreeParentErr(t *testing.T) { addedRoot: func(ctx context.Context, tree btrfsprim.ObjID, root btrfsvol.LogicalAddr) { // do nothing }, - lookupRoot: func(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, ok bool) { + lookupRoot: func(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, err error) { if tree == 304 { // Force a fault. - return 0, btrfsitem.Root{}, false + return 0, btrfsitem.Root{}, btrfstree.ErrNoItem } for _, root := range roots { if root.ID == tree { @@ -205,10 +205,10 @@ func TestRebuiltTreeParentErr(t *testing.T) { Generation: 2000, UUID: root.UUID, ParentUUID: root.ParentUUID, - }, true + }, nil } } - return 0, btrfsitem.Root{}, false + return 0, btrfsitem.Root{}, btrfstree.ErrNoItem }, lookupUUID: func(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, err error) { for _, root := range roots { @@ -225,7 +225,7 @@ func TestRebuiltTreeParentErr(t *testing.T) { rfs := NewRebuiltForrest(nil, Graph{}, cbs, false) tree, err := rfs.RebuiltTree(ctx, 305) - assert.EqualError(t, err, `failed to rebuild parent tree: 304: tree does not exist`) + assert.EqualError(t, err, `failed to rebuild parent tree: 304: tree does not exist: item does not exist`) assert.Nil(t, tree) }) -- cgit v1.2.3 From 7bd942c1e5f8d4a18f7e1ac866191d8f1aa31d30 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 17 Apr 2023 19:06:26 -0600 Subject: btrfsutil: RebuiltForrest: Fuss with error messages --- lib/btrfsutil/rebuilt_forrest.go | 9 ++++++--- lib/btrfsutil/rebuilt_forrest_test.go | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/btrfsutil/rebuilt_forrest.go b/lib/btrfsutil/rebuilt_forrest.go index 3e40103..79d8beb 100644 --- a/lib/btrfsutil/rebuilt_forrest.go +++ b/lib/btrfsutil/rebuilt_forrest.go @@ -214,7 +214,8 @@ func (ts *RebuiltForrest) rebuildTree(ctx context.Context, treeID btrfsprim.ObjI default: rootOff, rootItem, err := ts.cb.LookupRoot(ctx, treeID) if err != nil { - ts.trees[treeID].rootErr = fmt.Errorf("%w: %s", btrfstree.ErrNoTree, err) + ts.trees[treeID].rootErr = fmt.Errorf("tree %s: %w: %s", + treeID.Format(btrfsprim.ROOT_TREE_OBJECTID), btrfstree.ErrNoTree, err) return } ts.trees[treeID].Root = rootItem.ByteNr @@ -223,7 +224,8 @@ func (ts *RebuiltForrest) rebuildTree(ctx context.Context, treeID btrfsprim.ObjI ts.trees[treeID].ParentGen = rootOff parentID, err := ts.cb.LookupUUID(ctx, rootItem.ParentUUID) if err != nil { - err := fmt.Errorf("failed to look up UUID: %v: %w", rootItem.ParentUUID, err) + err := fmt.Errorf("tree %s: failed to look up UUID: %v: %w", + treeID.Format(btrfsprim.ROOT_TREE_OBJECTID), rootItem.ParentUUID, err) if ts.laxAncestors { ts.trees[treeID].parentErr = err } else { @@ -238,7 +240,8 @@ func (ts *RebuiltForrest) rebuildTree(ctx context.Context, treeID btrfsprim.ObjI ts.trees[treeID].ancestorLoop = true return case !ts.laxAncestors && ts.trees[treeID].Parent.rootErr != nil: - ts.trees[treeID].rootErr = fmt.Errorf("failed to rebuild parent tree: %v: %w", parentID, ts.trees[treeID].Parent.rootErr) + ts.trees[treeID].rootErr = fmt.Errorf("tree %s: failed to rebuild parent: %w", + treeID.Format(btrfsprim.ROOT_TREE_OBJECTID), ts.trees[treeID].Parent.rootErr) return } } diff --git a/lib/btrfsutil/rebuilt_forrest_test.go b/lib/btrfsutil/rebuilt_forrest_test.go index 86ae9a7..bb7b698 100644 --- a/lib/btrfsutil/rebuilt_forrest_test.go +++ b/lib/btrfsutil/rebuilt_forrest_test.go @@ -225,7 +225,7 @@ func TestRebuiltTreeParentErr(t *testing.T) { rfs := NewRebuiltForrest(nil, Graph{}, cbs, false) tree, err := rfs.RebuiltTree(ctx, 305) - assert.EqualError(t, err, `failed to rebuild parent tree: 304: tree does not exist: item does not exist`) + assert.EqualError(t, err, `tree 305: failed to rebuild parent: tree 304: tree does not exist: item does not exist`) assert.Nil(t, tree) }) -- cgit v1.2.3