diff options
Diffstat (limited to 'cmd/btrfs-rec/inspect/rebuildtrees')
-rw-r--r-- | cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go | 62 | ||||
-rw-r--r-- | cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go | 26 | ||||
-rw-r--r-- | cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go | 86 | ||||
-rw-r--r-- | cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wanttyp.go | 22 | ||||
-rw-r--r-- | cmd/btrfs-rec/inspect/rebuildtrees/scan.go | 105 |
5 files changed, 189 insertions, 112 deletions
diff --git a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go index ca1ce8c..0d25ac3 100644 --- a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go +++ b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go @@ -47,9 +47,7 @@ func (o keyAndTree) String() string { } type rebuilder struct { - sb btrfstree.Superblock - graph btrfsutil.Graph - keyIO *btrfsutil.KeyIO + scan ScanDevicesResult rebuilt *btrfsutil.RebuiltForrest @@ -67,9 +65,9 @@ type rebuilder struct { } type treeAugmentQueue struct { - zero map[Want]struct{} - single map[Want]btrfsvol.LogicalAddr - multi map[Want]containers.Set[btrfsvol.LogicalAddr] + zero map[want]struct{} + single map[want]btrfsvol.LogicalAddr + multi map[want]containers.Set[btrfsvol.LogicalAddr] } type Rebuilder interface { @@ -79,22 +77,20 @@ type Rebuilder interface { func NewRebuilder(ctx context.Context, fs *btrfs.FS, nodeList []btrfsvol.LogicalAddr) (Rebuilder, error) { ctx = dlog.WithField(ctx, "btrfs.inspect.rebuild-trees.step", "read-fs-data") - sb, nodeGraph, keyIO, err := ScanDevices(ctx, fs, nodeList) // ScanDevices does its own logging + scanData, err := ScanDevices(ctx, fs, nodeList) // ScanDevices does its own logging if err != nil { return nil, err } o := &rebuilder{ - sb: sb, - graph: nodeGraph, - keyIO: keyIO, + scan: scanData, } - o.rebuilt = btrfsutil.NewRebuiltForrest(sb, nodeGraph, keyIO, o) + o.rebuilt = btrfsutil.NewRebuiltForrest(fs, scanData.Superblock, scanData.Graph, forrestCallbacks{o}) return o, nil } func (o *rebuilder) ListRoots(ctx context.Context) map[btrfsprim.ObjID]containers.Set[btrfsvol.LogicalAddr] { - return o.rebuilt.ListRoots(ctx) + return o.rebuilt.RebuiltListRoots(ctx) } func (o *rebuilder) Rebuild(ctx context.Context) error { @@ -163,7 +159,7 @@ func (o *rebuilder) processTreeQueue(ctx context.Context) error { } // This will call o.AddedItem as nescessary, which // inserts to o.addedItemQueue. - _ = o.rebuilt.Tree(ctx, o.curKey.TreeID) + _ = o.rebuilt.RebuiltTree(ctx, o.curKey.TreeID) } return nil @@ -201,18 +197,18 @@ func (o *rebuilder) processAddedItemQueue(ctx context.Context) error { progressWriter.Set(progress) ctx := dlog.WithField(ctx, "btrfs.inspect.rebuild-trees.rebuild.settle.item", key) - tree := o.rebuilt.Tree(ctx, key.TreeID) - incPtr, ok := tree.Items(ctx).Load(key.Key) + tree := o.rebuilt.RebuiltTree(ctx, key.TreeID) + incPtr, ok := tree.RebuiltItems(ctx).Load(key.Key) if !ok { panic(fmt.Errorf("should not happen: failed to load already-added item: %v", key)) } - excPtr, ok := tree.PotentialItems(ctx).Load(key.Key) - if ok && tree.ShouldReplace(incPtr.Node, excPtr.Node) { - wantKey := WantWithTree{ + excPtr, ok := tree.RebuiltPotentialItems(ctx).Load(key.Key) + if ok && tree.RebuiltShouldReplace(incPtr.Node, excPtr.Node) { + wantKey := wantWithTree{ TreeID: key.TreeID, Key: wantFromKey(key.Key), } - o.wantAugment(ctx, wantKey, tree.LeafToRoots(ctx, excPtr.Node)) + o.wantAugment(ctx, wantKey, tree.RebuiltLeafToRoots(ctx, excPtr.Node)) progress.NumAugments = o.numAugments progress.NumAugmentTrees = len(o.augmentQueue) progressWriter.Set(progress) @@ -270,7 +266,7 @@ func (o *rebuilder) processSettledItemQueue(ctx context.Context) error { ctx := dlog.WithField(ctx, "btrfs.inspect.rebuild-trees.rebuild.process.item", key) item := keyAndBody{ keyAndTree: key, - Body: o.rebuilt.Tree(ctx, key.TreeID).ReadItem(ctx, key.Key), + Body: o.rebuilt.RebuiltTree(ctx, key.TreeID).ReadItem(ctx, key.Key), } select { case itemChan <- item: @@ -286,7 +282,7 @@ func (o *rebuilder) processSettledItemQueue(ctx context.Context) error { ctx := dlog.WithField(ctx, "btrfs.inspect.rebuild-trees.rebuild.process.item", item.keyAndTree) o.curKey.TreeID = item.TreeID o.curKey.Key.Val = item.Key - btrfscheck.HandleItem(ctx, o, item.TreeID, btrfstree.Item{ + btrfscheck.HandleItem(ctx, graphCallbacks{o}, item.TreeID, btrfstree.Item{ Key: item.Key, Body: item.Body, }) @@ -337,7 +333,7 @@ func (o *rebuilder) processAugmentQueue(ctx context.Context) error { progressWriter.Set(progress) // This will call o.AddedItem as nescessary, which // inserts to o.addedItemQueue. - o.rebuilt.Tree(ctx, treeID).AddRoot(ctx, nodeAddr) + o.rebuilt.RebuiltTree(ctx, treeID).RebuiltAddRoot(ctx, nodeAddr) progress.N++ } } @@ -385,8 +381,8 @@ func (o *rebuilder) resolveTreeAugments(ctx context.Context, treeID btrfsprim.Ob } else { choices[choice] = ChoiceInfo{ Count: 1, - Distance: discardOK(o.rebuilt.Tree(ctx, treeID).COWDistance(o.graph.Nodes[choice].Owner)), - Generation: o.graph.Nodes[choice].Generation, + Distance: discardOK(o.rebuilt.RebuiltTree(ctx, treeID).RebuiltCOWDistance(o.scan.Graph.Nodes[choice].Owner)), + Generation: o.scan.Graph.Nodes[choice].Generation, } } } @@ -399,8 +395,8 @@ func (o *rebuilder) resolveTreeAugments(ctx context.Context, treeID btrfsprim.Ob } else { choices[choice] = ChoiceInfo{ Count: 1, - Distance: discardOK(o.rebuilt.Tree(ctx, treeID).COWDistance(o.graph.Nodes[choice].Owner)), - Generation: o.graph.Nodes[choice].Generation, + Distance: discardOK(o.rebuilt.RebuiltTree(ctx, treeID).RebuiltCOWDistance(o.scan.Graph.Nodes[choice].Owner)), + Generation: o.scan.Graph.Nodes[choice].Generation, } } } @@ -520,7 +516,7 @@ func (o *rebuilder) resolveTreeAugments(ctx context.Context, treeID btrfsprim.Ob //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -func (queue *treeAugmentQueue) has(wantKey Want) bool { +func (queue *treeAugmentQueue) has(wantKey want) bool { if queue != nil { if queue.zero != nil { if _, ok := queue.zero[wantKey]; ok { @@ -541,7 +537,7 @@ func (queue *treeAugmentQueue) has(wantKey Want) bool { return false } -func (queue *treeAugmentQueue) store(wantKey Want, choices containers.Set[btrfsvol.LogicalAddr]) { +func (queue *treeAugmentQueue) store(wantKey want, choices containers.Set[btrfsvol.LogicalAddr]) { if len(choices) == 0 && wantKey.OffsetType > offsetExact { // This wantKey is unlikely to come up again, so it's // not worth the RAM of storing a negative result. @@ -550,27 +546,27 @@ func (queue *treeAugmentQueue) store(wantKey Want, choices containers.Set[btrfsv switch len(choices) { case 0: if queue.zero == nil { - queue.zero = make(map[Want]struct{}) + queue.zero = make(map[want]struct{}) } queue.zero[wantKey] = struct{}{} case 1: if queue.single == nil { - queue.single = make(map[Want]btrfsvol.LogicalAddr) + queue.single = make(map[want]btrfsvol.LogicalAddr) } queue.single[wantKey] = choices.TakeOne() default: if queue.multi == nil { - queue.multi = make(map[Want]containers.Set[btrfsvol.LogicalAddr]) + queue.multi = make(map[want]containers.Set[btrfsvol.LogicalAddr]) } queue.multi[wantKey] = choices } } -func (o *rebuilder) hasAugment(wantKey WantWithTree) bool { +func (o *rebuilder) hasAugment(wantKey wantWithTree) bool { return o.augmentQueue[wantKey.TreeID].has(wantKey.Key) } -func (o *rebuilder) wantAugment(ctx context.Context, wantKey WantWithTree, choices containers.Set[btrfsvol.LogicalAddr]) { +func (o *rebuilder) wantAugment(ctx context.Context, wantKey wantWithTree, choices containers.Set[btrfsvol.LogicalAddr]) { if o.augmentQueue[wantKey.TreeID] == nil { o.augmentQueue[wantKey.TreeID] = new(treeAugmentQueue) } diff --git a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go index a422a47..92b5ee5 100644 --- a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go +++ b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_treecb.go @@ -13,8 +13,12 @@ import ( "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" ) +type forrestCallbacks struct { + *rebuilder +} + // AddedItem implements btrfsutil.RebuiltForrestCallbacks. -func (o *rebuilder) AddedItem(_ context.Context, tree btrfsprim.ObjID, key btrfsprim.Key) { +func (o forrestCallbacks) AddedItem(_ context.Context, tree btrfsprim.ObjID, key btrfsprim.Key) { o.addedItemQueue.Insert(keyAndTree{ TreeID: tree, Key: key, @@ -22,17 +26,17 @@ func (o *rebuilder) AddedItem(_ context.Context, tree btrfsprim.ObjID, key btrfs } // AddedRoot implements btrfsutil.RebuiltForrestCallbacks. -func (o *rebuilder) AddedRoot(_ context.Context, tree btrfsprim.ObjID, _ btrfsvol.LogicalAddr) { +func (o forrestCallbacks) AddedRoot(_ context.Context, tree btrfsprim.ObjID, _ btrfsvol.LogicalAddr) { if retries := o.retryItemQueue[tree]; retries != nil { o.addedItemQueue.InsertFrom(retries) } } // LookupRoot implements btrfsutil.RebuiltForrestCallbacks. -func (o *rebuilder) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, ok bool) { - wantKey := WantWithTree{ +func (o forrestCallbacks) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offset btrfsprim.Generation, item btrfsitem.Root, ok bool) { + wantKey := wantWithTree{ TreeID: btrfsprim.ROOT_TREE_OBJECTID, - Key: Want{ + Key: want{ ObjectID: tree, ItemType: btrfsitem.ROOT_ITEM_KEY, OffsetType: offsetAny, @@ -44,13 +48,13 @@ func (o *rebuilder) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offse o.enqueueRetry(btrfsprim.ROOT_TREE_OBJECTID) return 0, btrfsitem.Root{}, false } - itemBody := o.rebuilt.Tree(ctx, wantKey.TreeID).ReadItem(ctx, foundKey) + itemBody := o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).ReadItem(ctx, foundKey) defer itemBody.Free() switch itemBody := itemBody.(type) { case *btrfsitem.Root: return btrfsprim.Generation(foundKey.Offset), *itemBody, true case *btrfsitem.Error: - o.FSErr(ctx, fmt.Errorf("error decoding item: %v: %w", foundKey, itemBody.Err)) + graphCallbacks(o).FSErr(ctx, fmt.Errorf("error decoding item: %v: %w", foundKey, itemBody.Err)) return 0, btrfsitem.Root{}, false default: // This is a panic because the item decoder should not emit ROOT_ITEM items as anything but @@ -60,8 +64,8 @@ func (o *rebuilder) LookupRoot(ctx context.Context, tree btrfsprim.ObjID) (offse } // LookupUUID implements btrfsutil.RebuiltForrestCallbacks. -func (o *rebuilder) LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, ok bool) { - wantKey := WantWithTree{ +func (o forrestCallbacks) LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btrfsprim.ObjID, ok bool) { + wantKey := wantWithTree{ TreeID: btrfsprim.UUID_TREE_OBJECTID, Key: wantFromKey(btrfsitem.UUIDToKey(uuid)), } @@ -70,13 +74,13 @@ func (o *rebuilder) LookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id btr o.enqueueRetry(btrfsprim.UUID_TREE_OBJECTID) return 0, false } - itemBody := o.rebuilt.Tree(ctx, wantKey.TreeID).ReadItem(ctx, wantKey.Key.Key()) + itemBody := o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).ReadItem(ctx, wantKey.Key.Key()) defer itemBody.Free() switch itemBody := itemBody.(type) { case *btrfsitem.UUIDMap: return itemBody.ObjID, true case *btrfsitem.Error: - o.FSErr(ctx, fmt.Errorf("error decoding item: %v: %w", wantKey, itemBody.Err)) + graphCallbacks(o).FSErr(ctx, fmt.Errorf("error decoding item: %v: %w", wantKey, itemBody.Err)) return 0, false default: // This is a panic because the item decoder should not emit UUID_SUBVOL items as anything but diff --git a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go index 704f4ee..eff2a83 100644 --- a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go +++ b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go @@ -19,16 +19,20 @@ import ( "git.lukeshu.com/btrfs-progs-ng/lib/containers" ) +type graphCallbacks struct { + *rebuilder +} + // FSErr implements btrfscheck.GraphCallbacks. -func (*rebuilder) FSErr(ctx context.Context, e error) { +func (graphCallbacks) FSErr(ctx context.Context, e error) { dlog.Errorf(ctx, "filesystem error: %v", e) } // Want implements btrfscheck.GraphCallbacks. -func (o *rebuilder) Want(ctx context.Context, reason string, treeID btrfsprim.ObjID, objID btrfsprim.ObjID, typ btrfsprim.ItemType) { - wantKey := WantWithTree{ +func (o graphCallbacks) Want(ctx context.Context, reason string, treeID btrfsprim.ObjID, objID btrfsprim.ObjID, typ btrfsprim.ItemType) { + wantKey := wantWithTree{ TreeID: treeID, - Key: Want{ + Key: want{ ObjectID: objID, ItemType: typ, OffsetType: offsetAny, @@ -38,8 +42,8 @@ func (o *rebuilder) Want(ctx context.Context, reason string, treeID btrfsprim.Ob o._want(ctx, wantKey) } -func (o *rebuilder) _want(ctx context.Context, wantKey WantWithTree) (key btrfsprim.Key, ok bool) { - if o.rebuilt.Tree(ctx, wantKey.TreeID) == nil { +func (o *rebuilder) _want(ctx context.Context, wantKey wantWithTree) (key btrfsprim.Key, ok bool) { + if o.rebuilt.RebuiltTree(ctx, wantKey.TreeID) == nil { o.enqueueRetry(wantKey.TreeID) return btrfsprim.Key{}, false } @@ -47,7 +51,7 @@ func (o *rebuilder) _want(ctx context.Context, wantKey WantWithTree) (key btrfsp // check if we already have it tgt := wantKey.Key.Key() - if key, _, ok := o.rebuilt.Tree(ctx, wantKey.TreeID).Items(ctx).Search(func(key btrfsprim.Key, _ btrfsutil.ItemPtr) int { + if key, _, ok := o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltItems(ctx).Search(func(key btrfsprim.Key, _ btrfsutil.ItemPtr) int { key.Offset = 0 return tgt.Compare(key) }); ok { @@ -60,13 +64,13 @@ func (o *rebuilder) _want(ctx context.Context, wantKey WantWithTree) (key btrfsp return btrfsprim.Key{}, false } wants := make(containers.Set[btrfsvol.LogicalAddr]) - o.rebuilt.Tree(ctx, wantKey.TreeID).PotentialItems(ctx).Subrange( + o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltPotentialItems(ctx).Subrange( func(k btrfsprim.Key, _ btrfsutil.ItemPtr) int { k.Offset = 0 return tgt.Compare(k) }, func(_ btrfsprim.Key, v btrfsutil.ItemPtr) bool { - wants.InsertFrom(o.rebuilt.Tree(ctx, wantKey.TreeID).LeafToRoots(ctx, v.Node)) + wants.InsertFrom(o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltLeafToRoots(ctx, v.Node)) return true }) o.wantAugment(ctx, wantKey, wants) @@ -74,10 +78,10 @@ func (o *rebuilder) _want(ctx context.Context, wantKey WantWithTree) (key btrfsp } // WantOff implements btrfscheck.GraphCallbacks. -func (o *rebuilder) WantOff(ctx context.Context, reason string, treeID btrfsprim.ObjID, objID btrfsprim.ObjID, typ btrfsprim.ItemType, off uint64) { - wantKey := WantWithTree{ +func (o graphCallbacks) WantOff(ctx context.Context, reason string, treeID btrfsprim.ObjID, objID btrfsprim.ObjID, typ btrfsprim.ItemType, off uint64) { + wantKey := wantWithTree{ TreeID: treeID, - Key: Want{ + Key: want{ ObjectID: objID, ItemType: typ, OffsetType: offsetExact, @@ -88,8 +92,8 @@ func (o *rebuilder) WantOff(ctx context.Context, reason string, treeID btrfsprim o._wantOff(ctx, wantKey) } -func (o *rebuilder) _wantOff(ctx context.Context, wantKey WantWithTree) (ok bool) { - if o.rebuilt.Tree(ctx, wantKey.TreeID) == nil { +func (o *rebuilder) _wantOff(ctx context.Context, wantKey wantWithTree) (ok bool) { + if o.rebuilt.RebuiltTree(ctx, wantKey.TreeID) == nil { o.enqueueRetry(wantKey.TreeID) return false } @@ -97,7 +101,7 @@ func (o *rebuilder) _wantOff(ctx context.Context, wantKey WantWithTree) (ok bool // check if we already have it tgt := wantKey.Key.Key() - if _, ok := o.rebuilt.Tree(ctx, wantKey.TreeID).Items(ctx).Load(tgt); ok { + if _, ok := o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltItems(ctx).Load(tgt); ok { return true } @@ -107,10 +111,10 @@ func (o *rebuilder) _wantOff(ctx context.Context, wantKey WantWithTree) (ok bool return false } wants := make(containers.Set[btrfsvol.LogicalAddr]) - o.rebuilt.Tree(ctx, wantKey.TreeID).PotentialItems(ctx).Subrange( + o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltPotentialItems(ctx).Subrange( func(k btrfsprim.Key, _ btrfsutil.ItemPtr) int { return tgt.Compare(k) }, func(_ btrfsprim.Key, v btrfsutil.ItemPtr) bool { - wants.InsertFrom(o.rebuilt.Tree(ctx, wantKey.TreeID).LeafToRoots(ctx, v.Node)) + wants.InsertFrom(o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltLeafToRoots(ctx, v.Node)) return true }) o.wantAugment(ctx, wantKey, wants) @@ -118,10 +122,10 @@ func (o *rebuilder) _wantOff(ctx context.Context, wantKey WantWithTree) (ok bool } // WantDirIndex implements btrfscheck.GraphCallbacks. -func (o *rebuilder) WantDirIndex(ctx context.Context, reason string, treeID btrfsprim.ObjID, objID btrfsprim.ObjID, name []byte) { - wantKey := WantWithTree{ +func (o graphCallbacks) WantDirIndex(ctx context.Context, reason string, treeID btrfsprim.ObjID, objID btrfsprim.ObjID, name []byte) { + wantKey := wantWithTree{ TreeID: treeID, - Key: Want{ + Key: want{ ObjectID: objID, ItemType: btrfsitem.DIR_INDEX_KEY, OffsetType: offsetName, @@ -130,7 +134,7 @@ func (o *rebuilder) WantDirIndex(ctx context.Context, reason string, treeID btrf } ctx = withWant(ctx, logFieldItemWant, reason, wantKey) - if o.rebuilt.Tree(ctx, treeID) == nil { + if o.rebuilt.RebuiltTree(ctx, treeID) == nil { o.enqueueRetry(treeID) return } @@ -139,13 +143,13 @@ func (o *rebuilder) WantDirIndex(ctx context.Context, reason string, treeID btrf tgt := wantKey.Key.Key() found := false - o.rebuilt.Tree(ctx, treeID).Items(ctx).Subrange( + o.rebuilt.RebuiltTree(ctx, treeID).RebuiltItems(ctx).Subrange( func(key btrfsprim.Key, _ btrfsutil.ItemPtr) int { key.Offset = 0 return tgt.Compare(key) }, func(_ btrfsprim.Key, ptr btrfsutil.ItemPtr) bool { - if itemName, ok := o.keyIO.Names[ptr]; ok && bytes.Equal(itemName, name) { + if itemName, ok := o.scan.Names[ptr]; ok && bytes.Equal(itemName, name) { found = true } return !found @@ -160,21 +164,21 @@ func (o *rebuilder) WantDirIndex(ctx context.Context, reason string, treeID btrf return } wants := make(containers.Set[btrfsvol.LogicalAddr]) - o.rebuilt.Tree(ctx, treeID).PotentialItems(ctx).Subrange( + o.rebuilt.RebuiltTree(ctx, treeID).RebuiltPotentialItems(ctx).Subrange( func(key btrfsprim.Key, _ btrfsutil.ItemPtr) int { key.Offset = 0 return tgt.Compare(key) }, func(_ btrfsprim.Key, ptr btrfsutil.ItemPtr) bool { - if itemName, ok := o.keyIO.Names[ptr]; ok && bytes.Equal(itemName, name) { - wants.InsertFrom(o.rebuilt.Tree(ctx, treeID).LeafToRoots(ctx, ptr.Node)) + if itemName, ok := o.scan.Names[ptr]; ok && bytes.Equal(itemName, name) { + wants.InsertFrom(o.rebuilt.RebuiltTree(ctx, treeID).RebuiltLeafToRoots(ctx, ptr.Node)) } return true }) o.wantAugment(ctx, wantKey, wants) } -func (o *rebuilder) _walkRange( +func (o graphCallbacks) _walkRange( ctx context.Context, items *containers.SortedMap[btrfsprim.Key, btrfsutil.ItemPtr], treeID, objID btrfsprim.ObjID, typ btrfsprim.ItemType, @@ -203,7 +207,7 @@ func (o *rebuilder) _walkRange( } }, func(runKey btrfsprim.Key, runPtr btrfsutil.ItemPtr) bool { - runSizeAndErr, ok := o.keyIO.Sizes[runPtr] + runSizeAndErr, ok := o.scan.Sizes[runPtr] if !ok { panic(fmt.Errorf("should not happen: %v (%v) did not have a size recorded", runPtr, keyAndTree{TreeID: treeID, Key: runKey})) @@ -239,14 +243,14 @@ func (a gap) Compare(b gap) int { return containers.NativeCompare(a.Beg, b.Beg) } -func (o *rebuilder) _wantRange( +func (o graphCallbacks) _wantRange( ctx context.Context, reason string, treeID btrfsprim.ObjID, objID btrfsprim.ObjID, typ btrfsprim.ItemType, beg, end uint64, ) { - wantKey := WantWithTree{ + wantKey := wantWithTree{ TreeID: treeID, - Key: Want{ + Key: want{ ObjectID: objID, ItemType: typ, OffsetType: offsetAny, @@ -255,7 +259,7 @@ func (o *rebuilder) _wantRange( ctx = withWant(ctx, logFieldItemWant, reason, wantKey) wantKey.Key.OffsetType = offsetRange - if o.rebuilt.Tree(ctx, treeID) == nil { + if o.rebuilt.RebuiltTree(ctx, treeID) == nil { o.enqueueRetry(treeID) return } @@ -271,7 +275,7 @@ func (o *rebuilder) _wantRange( }) o._walkRange( ctx, - o.rebuilt.Tree(ctx, treeID).Items(ctx), + o.rebuilt.RebuiltTree(ctx, treeID).RebuiltItems(ctx), treeID, objID, typ, beg, end, func(runKey btrfsprim.Key, _ btrfsutil.ItemPtr, runBeg, runEnd uint64) { var overlappingGaps []*containers.RBNode[gap] @@ -316,7 +320,7 @@ func (o *rebuilder) _wantRange( if gaps.Len() == 0 { return } - potentialItems := o.rebuilt.Tree(ctx, treeID).PotentialItems(ctx) + potentialItems := o.rebuilt.RebuiltTree(ctx, treeID).RebuiltPotentialItems(ctx) gaps.Range(func(rbNode *containers.RBNode[gap]) bool { gap := rbNode.Value last := gap.Beg @@ -336,7 +340,7 @@ func (o *rebuilder) _wantRange( wantKey.Key.OffsetLow = gap.Beg wantKey.Key.OffsetHigh = gap.End wantCtx := withWant(ctx, logFieldItemWant, reason, wantKey) - o.wantAugment(wantCtx, wantKey, o.rebuilt.Tree(wantCtx, treeID).LeafToRoots(wantCtx, v.Node)) + o.wantAugment(wantCtx, wantKey, o.rebuilt.RebuiltTree(wantCtx, treeID).RebuiltLeafToRoots(wantCtx, v.Node)) last = runEnd }) if last < gap.End { @@ -353,10 +357,10 @@ func (o *rebuilder) _wantRange( // WantCSum implements btrfscheck.GraphCallbacks. // // interval is [beg, end) -func (o *rebuilder) WantCSum(ctx context.Context, reason string, inodeTree, inode btrfsprim.ObjID, beg, end btrfsvol.LogicalAddr) { - inodeWant := WantWithTree{ +func (o graphCallbacks) WantCSum(ctx context.Context, reason string, inodeTree, inode btrfsprim.ObjID, beg, end btrfsvol.LogicalAddr) { + inodeWant := wantWithTree{ TreeID: inodeTree, - Key: Want{ + Key: want{ ObjectID: inode, ItemType: btrfsitem.INODE_ITEM_KEY, OffsetType: offsetExact, @@ -368,11 +372,11 @@ func (o *rebuilder) WantCSum(ctx context.Context, reason string, inodeTree, inod o.enqueueRetry(inodeTree) return } - inodePtr, ok := o.rebuilt.Tree(inodeCtx, inodeTree).Items(inodeCtx).Load(inodeWant.Key.Key()) + inodePtr, ok := o.rebuilt.RebuiltTree(inodeCtx, inodeTree).RebuiltItems(inodeCtx).Load(inodeWant.Key.Key()) if !ok { panic(fmt.Errorf("should not happen: could not load key: %v", inodeWant)) } - inodeFlags, ok := o.keyIO.Flags[inodePtr] + inodeFlags, ok := o.scan.Flags[inodePtr] if !ok { panic(fmt.Errorf("should not happen: INODE_ITEM did not have flags recorded")) } @@ -392,7 +396,7 @@ func (o *rebuilder) WantCSum(ctx context.Context, reason string, inodeTree, inod } // WantFileExt implements btrfscheck.GraphCallbacks. -func (o *rebuilder) WantFileExt(ctx context.Context, reason string, treeID btrfsprim.ObjID, ino btrfsprim.ObjID, size int64) { +func (o graphCallbacks) WantFileExt(ctx context.Context, reason string, treeID btrfsprim.ObjID, ino btrfsprim.ObjID, size int64) { o._wantRange( ctx, reason, treeID, ino, btrfsprim.EXTENT_DATA_KEY, diff --git a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wanttyp.go b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wanttyp.go index 8fe8a49..6c9d72b 100644 --- a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wanttyp.go +++ b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wanttyp.go @@ -23,8 +23,8 @@ const ( offsetName ) -type Want struct { - // TODO(lukeshu): Delete the 'Want' type in favor of +type want struct { + // TODO(lukeshu): Delete the 'want' type in favor of // btrfstree.Search. ObjectID btrfsprim.ObjID ItemType btrfsprim.ItemType @@ -34,7 +34,7 @@ type Want struct { OffsetName string } -func (a Want) Compare(b Want) int { +func (a want) Compare(b want) int { if d := containers.NativeCompare(a.ObjectID, b.ObjectID); d != 0 { return d } @@ -56,7 +56,7 @@ func (a Want) Compare(b Want) int { return 0 } -func (o Want) Key() btrfsprim.Key { +func (o want) Key() btrfsprim.Key { return btrfsprim.Key{ ObjectID: o.ObjectID, ItemType: o.ItemType, @@ -64,8 +64,8 @@ func (o Want) Key() btrfsprim.Key { } } -func wantFromKey(k btrfsprim.Key) Want { - return Want{ +func wantFromKey(k btrfsprim.Key) want { + return want{ ObjectID: k.ObjectID, ItemType: k.ItemType, OffsetType: offsetExact, @@ -73,7 +73,7 @@ func wantFromKey(k btrfsprim.Key) Want { } } -func (o Want) String() string { +func (o want) String() string { switch o.OffsetType { case offsetAny: return fmt.Sprintf("{%v %v ?}", o.ObjectID, o.ItemType) @@ -88,12 +88,12 @@ func (o Want) String() string { } } -type WantWithTree struct { +type wantWithTree struct { TreeID btrfsprim.ObjID - Key Want + Key want } -func (o WantWithTree) String() string { +func (o wantWithTree) String() string { return fmt.Sprintf("tree=%v key=%v", o.TreeID, o.Key) } @@ -102,7 +102,7 @@ const ( logFieldTreeWant = "btrfs.util.rebuilt-forrest.add-tree.want" ) -func withWant(ctx context.Context, logField, reason string, wantKey WantWithTree) context.Context { +func withWant(ctx context.Context, logField, reason string, wantKey wantWithTree) context.Context { ctx = dlog.WithField(ctx, logField+".reason", reason) ctx = dlog.WithField(ctx, logField+".key", wantKey) return ctx diff --git a/cmd/btrfs-rec/inspect/rebuildtrees/scan.go b/cmd/btrfs-rec/inspect/rebuildtrees/scan.go index ba56c5b..ada9f6f 100644 --- a/cmd/btrfs-rec/inspect/rebuildtrees/scan.go +++ b/cmd/btrfs-rec/inspect/rebuildtrees/scan.go @@ -6,11 +6,14 @@ package rebuildtrees import ( "context" + "fmt" "time" "github.com/datawire/dlib/dlog" "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/btrfsutil" @@ -18,11 +21,31 @@ import ( "git.lukeshu.com/btrfs-progs-ng/lib/textui" ) -func ScanDevices(ctx context.Context, fs *btrfs.FS, nodeList []btrfsvol.LogicalAddr) (btrfstree.Superblock, btrfsutil.Graph, *btrfsutil.KeyIO, error) { +type SizeAndErr struct { + Size uint64 + Err error +} + +type FlagsAndErr struct { + NoDataSum bool + Err error +} + +type ScanDevicesResult struct { + Superblock btrfstree.Superblock + + Graph btrfsutil.Graph + + Flags map[btrfsutil.ItemPtr]FlagsAndErr // INODE_ITEM + Names map[btrfsutil.ItemPtr][]byte // DIR_INDEX + Sizes map[btrfsutil.ItemPtr]SizeAndErr // EXTENT_CSUM and EXTENT_DATA +} + +func ScanDevices(ctx context.Context, fs *btrfs.FS, nodeList []btrfsvol.LogicalAddr) (ScanDevicesResult, error) { dlog.Info(ctx, "Reading superblock...") sb, err := fs.Superblock() if err != nil { - return btrfstree.Superblock{}, btrfsutil.Graph{}, nil, err + return ScanDevicesResult{}, err } dlog.Infof(ctx, "Reading node data from FS...") @@ -33,26 +56,32 @@ func ScanDevices(ctx context.Context, fs *btrfs.FS, nodeList []btrfsvol.LogicalA dlog.WithField(ctx, "btrfs.inspect.rebuild-trees.read.substep", "read-nodes"), dlog.LogLevelInfo, textui.Tunable(1*time.Second)) - nodeGraph := btrfsutil.NewGraph(*sb) - keyIO := btrfsutil.NewKeyIO(fs, *sb) + ret := ScanDevicesResult{ + Superblock: *sb, + + Graph: btrfsutil.NewGraph(*sb), + + Flags: make(map[btrfsutil.ItemPtr]FlagsAndErr), + Names: make(map[btrfsutil.ItemPtr][]byte), + Sizes: make(map[btrfsutil.ItemPtr]SizeAndErr), + } progressWriter.Set(stats) for _, laddr := range nodeList { if err := ctx.Err(); err != nil { - return btrfstree.Superblock{}, btrfsutil.Graph{}, nil, err + return ScanDevicesResult{}, err } - nodeRef, err := btrfstree.ReadNode[btrfsvol.LogicalAddr](fs, *sb, laddr, btrfstree.NodeExpectations{ - LAddr: containers.Optional[btrfsvol.LogicalAddr]{OK: true, Val: laddr}, + node, err := btrfstree.ReadNode[btrfsvol.LogicalAddr](fs, *sb, laddr, btrfstree.NodeExpectations{ + LAddr: containers.OptionalValue(laddr), }) if err != nil { - btrfstree.FreeNodeRef(nodeRef) - return btrfstree.Superblock{}, btrfsutil.Graph{}, nil, err + node.Free() + return ScanDevicesResult{}, err } - nodeGraph.InsertNode(nodeRef) - keyIO.InsertNode(nodeRef) + ret.insertNode(node) - btrfstree.FreeNodeRef(nodeRef) + node.Free() stats.N++ progressWriter.Set(stats) @@ -64,10 +93,54 @@ func ScanDevices(ctx context.Context, fs *btrfs.FS, nodeList []btrfsvol.LogicalA dlog.Info(ctx, "... done reading node data") ctx = dlog.WithField(ctx, "btrfs.inspect.rebuild-trees.read.substep", "check") - if err := nodeGraph.FinalCheck(ctx, fs, *sb); err != nil { - return btrfstree.Superblock{}, btrfsutil.Graph{}, nil, err + if err := ret.Graph.FinalCheck(ctx, fs, *sb); err != nil { + return ScanDevicesResult{}, err } - keyIO.SetGraph(*nodeGraph) - return *sb, *nodeGraph, keyIO, nil + return ret, nil +} + +func (o *ScanDevicesResult) insertNode(node *btrfstree.Node) { + o.Graph.InsertNode(node) + for i, item := range node.BodyLeaf { + ptr := btrfsutil.ItemPtr{ + Node: node.Head.Addr, + Slot: i, + } + switch itemBody := item.Body.(type) { + case *btrfsitem.Inode: + o.Flags[ptr] = FlagsAndErr{ + NoDataSum: itemBody.Flags.Has(btrfsitem.INODE_NODATASUM), + Err: nil, + } + case *btrfsitem.DirEntry: + if item.Key.ItemType == btrfsprim.DIR_INDEX_KEY { + o.Names[ptr] = append([]byte(nil), itemBody.Name...) + } + case *btrfsitem.ExtentCSum: + o.Sizes[ptr] = SizeAndErr{ + Size: uint64(itemBody.Size()), + Err: nil, + } + case *btrfsitem.FileExtent: + size, err := itemBody.Size() + o.Sizes[ptr] = SizeAndErr{ + Size: uint64(size), + Err: err, + } + case *btrfsitem.Error: + switch item.Key.ItemType { + case btrfsprim.INODE_ITEM_KEY: + o.Flags[ptr] = FlagsAndErr{ + Err: fmt.Errorf("error decoding item: ptr=%v (tree=%v key=%v): %w", + ptr, node.Head.Owner, item.Key, itemBody.Err), + } + case btrfsprim.EXTENT_CSUM_KEY, btrfsprim.EXTENT_DATA_KEY: + o.Sizes[ptr] = SizeAndErr{ + Err: fmt.Errorf("error decoding item: ptr=%v (tree=%v key=%v): %w", + ptr, node.Head.Owner, item.Key, itemBody.Err), + } + } + } + } } |