summaryrefslogtreecommitdiff
path: root/lib/btrfsprogs/btrfsinspect/rebuildmappings/csumitems.go
blob: eaa6f06dcdfac8ab97bf9d7ccc6b4a7a53c06410 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright (C) 2022  Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later

package rebuildmappings

import (
	"context"
	"strings"

	"github.com/datawire/dlib/dlog"

	"git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
	"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfssum"
	"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
	"git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsinspect"
	"git.lukeshu.com/btrfs-progs-ng/lib/maps"
	"git.lukeshu.com/btrfs-progs-ng/lib/slices"
)

func MapLogicalSums(ctx context.Context, scanResults btrfsinspect.ScanDevicesResult) btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr] {
	dlog.Info(ctx, "Mapping the logical address space...")
	type record struct {
		Gen btrfs.Generation
		Sum btrfssum.ShortSum
	}
	addrspace := make(map[btrfsvol.LogicalAddr]record)
	var sumSize int
	for _, devResults := range scanResults {
		sumSize = devResults.Checksums.ChecksumSize
		for _, sumItem := range devResults.FoundExtentCSums {
			_ = sumItem.Sums.Walk(ctx, func(pos btrfsvol.LogicalAddr, sum btrfssum.ShortSum) error {
				new := record{
					Gen: sumItem.Generation,
					Sum: sum,
				}
				if old, ok := addrspace[pos]; ok {
					switch {
					case old.Gen > new.Gen:
						// do nothing
					case old.Gen < new.Gen:
						addrspace[pos] = new
					case old.Gen == new.Gen:
						if old != new {
							dlog.Errorf(ctx, "mismatch of laddr=%v sum: %v != %v", pos, old, new)
						}
					}
				} else {
					addrspace[pos] = new
				}
				return nil
			})
		}
	}
	dlog.Info(ctx, "... done mapping")

	var flattened btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr]
	if len(addrspace) == 0 {
		return flattened
	}

	dlog.Info(ctx, "Flattening the map ...")
	var curAddr btrfsvol.LogicalAddr
	var curSums strings.Builder
	for _, laddr := range maps.SortedKeys(addrspace) {
		if laddr != curAddr+(btrfsvol.LogicalAddr(curSums.Len()/sumSize)*btrfssum.BlockSize) {
			if curSums.Len() > 0 {
				flattened.Runs = append(flattened.Runs, btrfssum.SumRun[btrfsvol.LogicalAddr]{
					ChecksumSize: sumSize,
					Addr:         curAddr,
					Sums:         btrfssum.ShortSum(curSums.String()),
				})
			}
			curAddr = laddr
			curSums.Reset()
		}
		curSums.WriteString(string(addrspace[laddr].Sum))
	}
	if curSums.Len() > 0 {
		flattened.Runs = append(flattened.Runs, btrfssum.SumRun[btrfsvol.LogicalAddr]{
			ChecksumSize: sumSize,
			Addr:         curAddr,
			Sums:         btrfssum.ShortSum(curSums.String()),
		})
	}
	flattened.Addr = flattened.Runs[0].Addr
	last := flattened.Runs[len(flattened.Runs)-1]
	end := last.Addr.Add(last.Size())
	flattened.Size = end.Sub(flattened.Addr)
	dlog.Info(ctx, "... done flattening")

	return flattened
}

func SumsForLogicalRegion(sums btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr], beg btrfsvol.LogicalAddr, size btrfsvol.AddrDelta) btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr] {
	runs := btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr]{
		Addr: beg,
		Size: size,
	}
	for laddr := beg; laddr < beg.Add(size); {
		run, next, ok := sums.RunForAddr(laddr)
		if !ok {
			laddr = next
			continue
		}
		off := int((laddr-run.Addr)/btrfssum.BlockSize) * run.ChecksumSize
		deltaAddr := slices.Min[btrfsvol.AddrDelta](
			size-laddr.Sub(beg),
			btrfsvol.AddrDelta((len(run.Sums)-off)/run.ChecksumSize)*btrfssum.BlockSize)
		deltaOff := int(deltaAddr/btrfssum.BlockSize) * run.ChecksumSize
		runs.Runs = append(runs.Runs, btrfssum.SumRun[btrfsvol.LogicalAddr]{
			ChecksumSize: run.ChecksumSize,
			Addr:         laddr,
			Sums:         run.Sums[off : off+deltaOff],
		})
		laddr = laddr.Add(deltaAddr)
	}
	return runs
}