summaryrefslogtreecommitdiff
path: root/cmd/btrfs-rec/inspect/rebuildmappings/process_sums_physical.go
blob: 5f8d9328027b348664cc73adaf36466e62d01ee2 (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
// Copyright (C) 2022-2023  Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later

package rebuildmappings

import (
	"context"
	"sort"

	"golang.org/x/exp/constraints"

	"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/maps"
)

func extractPhysicalSums(scanResults ScanDevicesResult) map[btrfsvol.DeviceID]btrfssum.SumRun[btrfsvol.PhysicalAddr] {
	ret := make(map[btrfsvol.DeviceID]btrfssum.SumRun[btrfsvol.PhysicalAddr], len(scanResults))
	for devID, devResults := range scanResults {
		ret[devID] = devResults.Checksums
	}
	return ret
}

type physicalRegion struct {
	Beg, End btrfsvol.PhysicalAddr
}

func listUnmappedPhysicalRegions(fs *btrfs.FS) map[btrfsvol.DeviceID][]physicalRegion {
	regions := make(map[btrfsvol.DeviceID][]physicalRegion)
	pos := make(map[btrfsvol.DeviceID]btrfsvol.PhysicalAddr)
	mappings := fs.LV.Mappings()
	sort.Slice(mappings, func(i, j int) bool {
		return mappings[i].PAddr.Compare(mappings[j].PAddr) < 0
	})
	for _, mapping := range mappings {
		if pos[mapping.PAddr.Dev] < mapping.PAddr.Addr {
			regions[mapping.PAddr.Dev] = append(regions[mapping.PAddr.Dev], physicalRegion{
				Beg: pos[mapping.PAddr.Dev],
				End: mapping.PAddr.Addr,
			})
		}
		if pos[mapping.PAddr.Dev] < mapping.PAddr.Addr.Add(mapping.Size) {
			pos[mapping.PAddr.Dev] = mapping.PAddr.Addr.Add(mapping.Size)
		}
	}
	for devID, dev := range fs.LV.PhysicalVolumes() {
		devSize := dev.Size()
		if pos[devID] < devSize {
			regions[devID] = append(regions[devID], physicalRegion{
				Beg: pos[devID],
				End: devSize,
			})
		}
	}
	return regions
}

func roundUp[T constraints.Integer](x, multiple T) T {
	return ((x + multiple - 1) / multiple) * multiple
}

func walkUnmappedPhysicalRegions(ctx context.Context,
	physicalSums map[btrfsvol.DeviceID]btrfssum.SumRun[btrfsvol.PhysicalAddr],
	gaps map[btrfsvol.DeviceID][]physicalRegion,
	fn func(btrfsvol.DeviceID, btrfssum.SumRun[btrfsvol.PhysicalAddr]) error,
) error {
	for _, devID := range maps.SortedKeys(gaps) {
		for _, gap := range gaps[devID] {
			if err := ctx.Err(); err != nil {
				return err
			}
			begAddr := roundUp(gap.Beg, btrfssum.BlockSize)
			begOff := int(begAddr/btrfssum.BlockSize) * physicalSums[devID].ChecksumSize
			endOff := int(gap.End/btrfssum.BlockSize) * physicalSums[devID].ChecksumSize
			if err := fn(devID, btrfssum.SumRun[btrfsvol.PhysicalAddr]{
				ChecksumSize: physicalSums[devID].ChecksumSize,
				Addr:         begAddr,
				Sums:         physicalSums[devID].Sums[begOff:endOff],
			}); err != nil {
				return err
			}
		}
	}
	return nil
}