// Copyright (C) 2022 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later package rebuildnodes /* import ( "fmt" "reflect" "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/containers" "git.lukeshu.com/btrfs-progs-ng/lib/slices" ) type RebuiltNode struct { Errs containers.Set[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) a.Errs.InsertFrom(b.Errs) 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).ToNodeGeneration jGen := badNodes[j].Path.Node(-1).ToNodeGeneration 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{ Errs: containers.NewSet[string]( badNode.Err, ), MinKey: min, MaxKey: max, InTrees: containers.NewSet[btrfsprim.ObjID]( path.Node(-1).FromTree, ), 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).ToNodeGeneration, 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 } */