diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2022-08-17 20:48:50 -0600 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2022-08-17 22:41:20 -0600 |
commit | ff28ac9487ceb162bdef47f0639ce9a6c0cc7c28 (patch) | |
tree | 11560a7d2da1063137b8038d7399cf90e4b4f327 /lib/btrfsprogs/btrfsinspect/csums.go | |
parent | 1bea509616f5fc1749473945d304c9e6bae21869 (diff) |
rename: Move some files around
Diffstat (limited to 'lib/btrfsprogs/btrfsinspect/csums.go')
-rw-r--r-- | lib/btrfsprogs/btrfsinspect/csums.go | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/lib/btrfsprogs/btrfsinspect/csums.go b/lib/btrfsprogs/btrfsinspect/csums.go new file mode 100644 index 0000000..a062a77 --- /dev/null +++ b/lib/btrfsprogs/btrfsinspect/csums.go @@ -0,0 +1,316 @@ +// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package btrfsinspect + +import ( + "context" + "encoding/gob" + "io" + "math" + "os" + "runtime" + "strings" + "sync" + + "github.com/datawire/dlib/dgroup" + "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/btrfssum" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil" + "git.lukeshu.com/btrfs-progs-ng/lib/diskio" +) + +// ShortSum ////////////////////////////////////////////////////////// + +type ShortSum string + +// SumRun //////////////////////////////////////////////////////////// + +type SumRun[Addr btrfsvol.IntAddr[Addr]] struct { + // How big a ShortSum is in this Run. + ChecksumSize int + // Base address where this run starts. + Addr Addr + // All of the ShortSums in this run, concatenated together. + // + // This is a 'string' rather than a 'ShortSum' to make it hard + // to accidentally use it as a single sum. + Sums string +} + +func (run SumRun[Addr]) NumSums() int { + return len(run.Sums) / run.ChecksumSize +} + +func (run SumRun[Addr]) Size() btrfsvol.AddrDelta { + return btrfsvol.AddrDelta(run.NumSums()) * btrfsitem.CSumBlockSize +} + +// Get implements diskio.Sequence[int, ShortSum] +func (run SumRun[Addr]) Get(sumIdx int64) (ShortSum, error) { + if sumIdx < 0 || int(sumIdx) >= run.NumSums() { + return "", io.EOF + } + off := int(sumIdx) * run.ChecksumSize + return ShortSum(run.Sums[off : off+run.ChecksumSize]), nil +} + +func (run SumRun[Addr]) SumForAddr(addr Addr) (ShortSum, bool) { + if addr < run.Addr || addr >= run.Addr.Add(run.Size()) { + return "", false + } + off := int((addr-run.Addr)/btrfsitem.CSumBlockSize) * run.ChecksumSize + return ShortSum(run.Sums[off : off+run.ChecksumSize]), true +} + +func (run SumRun[Addr]) Walk(ctx context.Context, fn func(Addr, ShortSum) error) error { + for addr, off := run.Addr, 0; off < len(run.Sums); addr, off = addr+btrfsitem.CSumBlockSize, off+run.ChecksumSize { + if err := ctx.Err(); err != nil { + return err + } + if err := fn(addr, ShortSum(run.Sums[off:off+run.ChecksumSize])); err != nil { + return err + } + } + return nil +} + +// SumRunWithGaps //////////////////////////////////////////////////// + +type SumRunWithGaps[Addr btrfsvol.IntAddr[Addr]] struct { + Addr Addr + Size btrfsvol.AddrDelta + Runs []SumRun[Addr] +} + +func (sg SumRunWithGaps[Addr]) NumSums() int { + return int(sg.Size / btrfsitem.CSumBlockSize) +} + +func (sg SumRunWithGaps[Addr]) PctFull() float64 { + total := sg.NumSums() + var full int + for _, run := range sg.Runs { + full += run.NumSums() + } + return float64(full) / float64(total) +} + +func (sg SumRunWithGaps[Addr]) SumForAddr(addr Addr) (ShortSum, error) { + if addr < sg.Addr || addr >= sg.Addr.Add(sg.Size) { + return "", io.EOF + } + for _, run := range sg.Runs { + if run.Addr > addr { + return "", diskio.ErrWildcard + } + if run.Addr.Add(run.Size()) <= addr { + continue + } + off := int((addr-run.Addr)/btrfsitem.CSumBlockSize) * run.ChecksumSize + return ShortSum(run.Sums[off : off+run.ChecksumSize]), nil + } + return "", diskio.ErrWildcard +} + +// Get implements diskio.Sequence[int, ShortSum] +func (sg SumRunWithGaps[Addr]) Get(sumIdx int64) (ShortSum, error) { + addr := sg.Addr.Add(btrfsvol.AddrDelta(sumIdx) * btrfsitem.CSumBlockSize) + return sg.SumForAddr(addr) +} + +// AllSums /////////////////////////////////////////////////////////// + +type AllSums struct { + Logical []SumRun[btrfsvol.LogicalAddr] + Physical map[btrfsvol.DeviceID]SumRun[btrfsvol.PhysicalAddr] +} + +func (as AllSums) SumForPAddr(paddr btrfsvol.QualifiedPhysicalAddr) (ShortSum, bool) { + run, ok := as.Physical[paddr.Dev] + if !ok { + return "", false + } + return run.SumForAddr(paddr.Addr) +} + +func (as AllSums) RunForLAddr(laddr btrfsvol.LogicalAddr) (SumRun[btrfsvol.LogicalAddr], btrfsvol.LogicalAddr, bool) { + for _, run := range as.Logical { + if run.Addr > laddr { + return SumRun[btrfsvol.LogicalAddr]{}, run.Addr, false + } + if run.Addr.Add(run.Size()) <= laddr { + continue + } + return run, 0, true + } + return SumRun[btrfsvol.LogicalAddr]{}, math.MaxInt64, false +} + +func (as AllSums) SumForLAddr(laddr btrfsvol.LogicalAddr) (ShortSum, bool) { + run, _, ok := as.RunForLAddr(laddr) + if !ok { + return "", false + } + return run.SumForAddr(laddr) +} + +func (as AllSums) WalkLogical(ctx context.Context, fn func(btrfsvol.LogicalAddr, ShortSum) error) error { + for _, run := range as.Logical { + if err := run.Walk(ctx, fn); err != nil { + return err + } + } + return nil +} + +// Read/Write AllSums //////////////////////////////////////////////// + +func ReadAllSums(filename string) (AllSums, error) { + fh, err := os.Open(filename) + if err != nil { + return AllSums{}, err + } + defer fh.Close() + var val AllSums + if err := gob.NewDecoder(fh).Decode(&val); err != nil { + return AllSums{}, err + } + return val, nil +} + +func WriteAllSums(w io.Writer, sums AllSums) error { + return gob.NewEncoder(w).Encode(sums) +} + +func SumEverything(ctx context.Context, fs *btrfs.FS) (AllSums, error) { + var ret AllSums + + // ChecksumSize + var alg btrfssum.CSumType + var csumSize int + if err := func() error { + sb, err := fs.Superblock() + if err != nil { + return err + } + alg = sb.ChecksumType + csumSize = alg.Size() + return nil + }(); err != nil { + return ret, err + } + + // Logical + dlog.Info(ctx, "Walking CSUM_TREE...") + func() { + var curAddr btrfsvol.LogicalAddr + var curSums strings.Builder + btrfsutil.NewBrokenTrees(ctx, fs).TreeWalk(ctx, btrfs.CSUM_TREE_OBJECTID, + func(err *btrfs.TreeError) { + dlog.Error(ctx, err) + }, + btrfs.TreeWalkHandler{ + Item: func(path btrfs.TreePath, item btrfs.Item) error { + if item.Key.ItemType != btrfsitem.EXTENT_CSUM_KEY { + return nil + } + body := item.Body.(btrfsitem.ExtentCSum) + + for i, sum := range body.Sums { + laddr := btrfsvol.LogicalAddr(item.Key.Offset) + (btrfsvol.LogicalAddr(i) * btrfsitem.CSumBlockSize) + if laddr != curAddr+(btrfsvol.LogicalAddr(curSums.Len()/csumSize)*btrfsitem.CSumBlockSize) { + if curSums.Len() > 0 { + ret.Logical = append(ret.Logical, SumRun[btrfsvol.LogicalAddr]{ + ChecksumSize: csumSize, + Addr: curAddr, + Sums: curSums.String(), + }) + } + curAddr = laddr + curSums.Reset() + } + curSums.Write(sum[:csumSize]) + } + return nil + }, + }, + ) + if curSums.Len() > 0 { + ret.Logical = append(ret.Logical, SumRun[btrfsvol.LogicalAddr]{ + ChecksumSize: csumSize, + Addr: curAddr, + Sums: curSums.String(), + }) + } + }() + if err := ctx.Err(); err != nil { + return ret, err + } + dlog.Info(ctx, "... done walking") + runtime.GC() + dlog.Info(ctx, "... GC'd") + + // Physical + dlog.Info(ctx, "Summing devices...") + if err := func() error { + devs := fs.LV.PhysicalVolumes() + + var mu sync.Mutex + ret.Physical = make(map[btrfsvol.DeviceID]SumRun[btrfsvol.PhysicalAddr], len(devs)) + + grp := dgroup.NewGroup(ctx, dgroup.GroupConfig{}) + for devID, dev := range devs { + devID, dev := devID, dev + grp.Go(dev.Name(), func(ctx context.Context) error { + devSize := dev.Size() + numSums := int(devSize / btrfsitem.CSumBlockSize) + sums := make([]byte, numSums*csumSize) + lastPct := -1 + progress := func(curSum int) { + pct := int(100 * float64(curSum) / float64(numSums)) + if pct != lastPct || curSum == numSums { + dlog.Infof(ctx, "... dev[%q] summed %v%%", + dev.Name(), pct) + lastPct = pct + } + } + for i := 0; i < numSums; i++ { + if err := ctx.Err(); err != nil { + return err + } + progress(i) + sum, err := btrfsutil.ChecksumPhysical(dev, alg, btrfsvol.PhysicalAddr(i*btrfsitem.CSumBlockSize)) + if err != nil { + return err + } + copy(sums[i*csumSize:], sum[:csumSize]) + } + progress(numSums) + sumsStr := string(sums) + mu.Lock() + ret.Physical[devID] = SumRun[btrfsvol.PhysicalAddr]{ + ChecksumSize: csumSize, + Addr: 0, + Sums: sumsStr, + } + mu.Unlock() + return nil + }) + } + return grp.Wait() + }(); err != nil { + return ret, err + } + dlog.Info(ctx, "... done summing devices") + runtime.GC() + dlog.Info(ctx, "... GC'd") + + // Return + return ret, nil +} |