summaryrefslogtreecommitdiff
path: root/lib/btrfsprogs/btrfsinspect/rebuildnodes/s3_reinit.go
diff options
context:
space:
mode:
Diffstat (limited to 'lib/btrfsprogs/btrfsinspect/rebuildnodes/s3_reinit.go')
-rw-r--r--lib/btrfsprogs/btrfsinspect/rebuildnodes/s3_reinit.go137
1 files changed, 137 insertions, 0 deletions
diff --git a/lib/btrfsprogs/btrfsinspect/rebuildnodes/s3_reinit.go b/lib/btrfsprogs/btrfsinspect/rebuildnodes/s3_reinit.go
new file mode 100644
index 0000000..a4b8f0f
--- /dev/null
+++ b/lib/btrfsprogs/btrfsinspect/rebuildnodes/s3_reinit.go
@@ -0,0 +1,137 @@
+// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package rebuildnodes
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+ "sort"
+
+ "github.com/datawire/dlib/dlog"
+
+ "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/containers"
+ "git.lukeshu.com/btrfs-progs-ng/lib/slices"
+)
+
+type RebuiltNode struct {
+ Err string
+ MinKey, MaxKey btrfsprim.Key
+ InTrees containers.Set[btrfsprim.ObjID]
+ btrfstree.Node
+}
+
+func (a RebuiltNode) Compat(b RebuiltNode) bool {
+ a.Node.Head.Generation = b.Node.Head.Generation
+ return reflect.DeepEqual(a.Node, b.Node)
+}
+
+func (a RebuiltNode) Merge(b RebuiltNode) (RebuiltNode, error) {
+ if !a.Compat(b) {
+ switch {
+ case a.Node.Head.Generation > b.Node.Head.Generation:
+ return a, nil
+ case a.Node.Head.Generation < b.Node.Head.Generation:
+ return b, nil
+ default:
+ return a, fmt.Errorf("mismatch: %v != %v", a, b)
+ }
+ }
+
+ // take the broadest region
+ if a.MinKey.Cmp(b.MinKey) > 0 { // if a.MinKey > b.MinKey {
+ a.MinKey = b.MinKey // take the min of the two
+ }
+ if a.MaxKey.Cmp(b.MaxKey) < 0 { // if a.MaxKey < b.MaxKey {
+ a.MaxKey = b.MaxKey // take the min of the two
+ }
+
+ // take the highest generation
+ a.Node.Head.Generation = slices.Max(a.Node.Head.Generation, b.Node.Head.Generation)
+
+ // take the union
+ a.InTrees.InsertFrom(b.InTrees)
+
+ return a, nil
+}
+
+func reInitBrokenNodes(ctx context.Context, fs _FS, badNodes []badNode) (map[btrfsvol.LogicalAddr]*RebuiltNode, error) {
+ dlog.Info(ctx, "Re-initializing bad nodes...")
+
+ sb, err := fs.Superblock()
+ if err != nil {
+ return nil, err
+ }
+ chunkTreeUUID, ok := getChunkTreeUUID(ctx, fs)
+ if !ok {
+ return nil, fmt.Errorf("could not look up chunk tree UUID")
+ }
+
+ sort.Slice(badNodes, func(i, j int) bool {
+ iGen := badNodes[i].Path.Node(-1).FromGeneration
+ jGen := badNodes[j].Path.Node(-1).FromGeneration
+ switch {
+ case iGen < jGen:
+ return true
+ case iGen > jGen:
+ return false
+ default:
+ iAddr := badNodes[i].Path.Node(-1).ToNodeAddr
+ jAddr := badNodes[j].Path.Node(-1).ToNodeAddr
+ return iAddr < jAddr
+ }
+ })
+
+ lastPct := -1
+ progress := func(done int) {
+ pct := int(100 * float64(done) / float64(len(badNodes)))
+ if pct != lastPct || done == len(badNodes) {
+ dlog.Infof(ctx, "... %v%% (%v/%v)",
+ pct, done, len(badNodes))
+ lastPct = pct
+ }
+ }
+
+ rebuiltNodes := make(map[btrfsvol.LogicalAddr]*RebuiltNode)
+ for i, badNode := range badNodes {
+ progress(i)
+ path := badNode.Path
+
+ min, max := spanOfTreePath(fs, path)
+ node := RebuiltNode{
+ Err: err.Error(),
+ MinKey: min,
+ MaxKey: max,
+ InTrees: containers.Set[btrfsprim.ObjID]{path.Node(-1).FromTree: struct{}{}},
+ Node: btrfstree.Node{
+ Size: sb.NodeSize,
+ ChecksumType: sb.ChecksumType,
+ Head: btrfstree.NodeHeader{
+ MetadataUUID: sb.EffectiveMetadataUUID(),
+ Addr: path.Node(-1).ToNodeAddr,
+ ChunkTreeUUID: chunkTreeUUID,
+ //Owner: TBD, // see RebuiltNode.InTrees
+ Generation: path.Node(-1).FromGeneration,
+ Level: path.Node(-1).ToNodeLevel,
+ },
+ },
+ }
+ if other, ok := rebuiltNodes[path.Node(-1).ToNodeAddr]; ok {
+ *other, err = other.Merge(node)
+ if err != nil {
+ dlog.Errorf(ctx, "... %v", err)
+ }
+ } else {
+ rebuiltNodes[path.Node(-1).ToNodeAddr] = &node
+ }
+ }
+ progress(len(badNodes))
+
+ dlog.Infof(ctx, "... initialized %d nodes", len(rebuiltNodes))
+ return rebuiltNodes, nil
+}