From 4b4d3fe609ecb99cf43aa4a70787bfa2113e6018 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 15 Jun 2022 23:26:54 -0600 Subject: dump-tree: Sync up to 5.18.1; implement csum types --- cmd/btrfs-dump-tree/main.go | 4 +-- pkg/btrfs/crc32c.go | 28 ---------------- pkg/btrfs/crc32c_test.go | 35 ------------------- pkg/btrfs/csum.go | 78 +++++++++++++++++++++++++++++++++++++++++++ pkg/btrfs/csum_test.go | 35 +++++++++++++++++++ pkg/btrfs/types_btree.go | 17 +++++++--- pkg/btrfs/types_superblock.go | 4 +-- pkg/btrfsmisc/print_tree.go | 4 +-- 8 files changed, 132 insertions(+), 73 deletions(-) delete mode 100644 pkg/btrfs/crc32c.go delete mode 100644 pkg/btrfs/crc32c_test.go create mode 100644 pkg/btrfs/csum.go create mode 100644 pkg/btrfs/csum_test.go diff --git a/cmd/btrfs-dump-tree/main.go b/cmd/btrfs-dump-tree/main.go index e32d677..3c7bcda 100644 --- a/cmd/btrfs-dump-tree/main.go +++ b/cmd/btrfs-dump-tree/main.go @@ -16,7 +16,7 @@ func main() { } } -const version = "5.17" +const version = "5.18.1" func Main(imgfilename string) (err error) { maybeSetErr := func(_err error) { @@ -49,7 +49,7 @@ func Main(imgfilename string) (err error) { return err } - fmt.Printf("btrfs-progs v%v \n", version) + fmt.Printf("btrfs-progs v%v\n", version) if superblock.Data.RootTree != 0 { fmt.Printf("root tree\n") if err := btrfsmisc.PrintTree(fs, superblock.Data.RootTree); err != nil { diff --git a/pkg/btrfs/crc32c.go b/pkg/btrfs/crc32c.go deleted file mode 100644 index 52058e8..0000000 --- a/pkg/btrfs/crc32c.go +++ /dev/null @@ -1,28 +0,0 @@ -package btrfs - -import ( - "encoding/binary" - "encoding/hex" - "fmt" - "hash/crc32" - - "lukeshu.com/btrfs-tools/pkg/util" -) - -type CSum [0x20]byte - -func (csum CSum) String() string { - return hex.EncodeToString(csum[:]) -} - -func (csum CSum) Format(f fmt.State, verb rune) { - util.FormatByteArrayStringer(csum, csum[:], f, verb) -} - -func CRC32c(data []byte) CSum { - crc := crc32.Update(0, crc32.MakeTable(crc32.Castagnoli), data) - - var ret CSum - binary.LittleEndian.PutUint32(ret[:], crc) - return ret -} diff --git a/pkg/btrfs/crc32c_test.go b/pkg/btrfs/crc32c_test.go deleted file mode 100644 index 335b3cc..0000000 --- a/pkg/btrfs/crc32c_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package btrfs_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "lukeshu.com/btrfs-tools/pkg/btrfs" -) - -func TestCSumFormat(t *testing.T) { - t.Parallel() - type TestCase struct { - InputSum btrfs.CSum - InputFmt string - Output string - } - csum := btrfs.CSum{0xbd, 0x7b, 0x41, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} - testcases := map[string]TestCase{ - "s": TestCase{InputSum: csum, InputFmt: "%s", Output: "bd7b41f400000000000000000000000000000000000000000000000000000000"}, - "x": TestCase{InputSum: csum, InputFmt: "%x", Output: "bd7b41f400000000000000000000000000000000000000000000000000000000"}, - "v": TestCase{InputSum: csum, InputFmt: "%v", Output: "bd7b41f400000000000000000000000000000000000000000000000000000000"}, - "70s": TestCase{InputSum: csum, InputFmt: "|% 70s", Output: "| bd7b41f400000000000000000000000000000000000000000000000000000000"}, - "#180v": TestCase{InputSum: csum, InputFmt: "%#180v", Output: " btrfs.CSum{0xbd, 0x7b, 0x41, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}"}, - } - for tcName, tc := range testcases { - tc := tc - t.Run(tcName, func(t *testing.T) { - t.Parallel() - actual := fmt.Sprintf(tc.InputFmt, tc.InputSum) - assert.Equal(t, tc.Output, actual) - }) - } -} diff --git a/pkg/btrfs/csum.go b/pkg/btrfs/csum.go new file mode 100644 index 0000000..6245fa4 --- /dev/null +++ b/pkg/btrfs/csum.go @@ -0,0 +1,78 @@ +package btrfs + +import ( + "encoding/binary" + "encoding/hex" + "fmt" + "hash/crc32" + + "lukeshu.com/btrfs-tools/pkg/util" +) + +type CSum [0x20]byte + +func (csum CSum) String() string { + return hex.EncodeToString(csum[:]) +} + +func (csum CSum) Fmt(typ CSumType) string { + return hex.EncodeToString(csum[:typ.Size()]) +} + +func (csum CSum) Format(f fmt.State, verb rune) { + util.FormatByteArrayStringer(csum, csum[:], f, verb) +} + +type CSumType uint16 + +const ( + CSUM_TYPE_CRC32 = CSumType(iota) + CSUM_TYPE_XXHASH + CSUM_TYPE_SHA256 + CSUM_TYPE_BLAKE2 +) + +func (typ CSumType) String() string { + names := map[CSumType]string{ + CSUM_TYPE_CRC32: "crc32c", + CSUM_TYPE_XXHASH: "xxhash64", + CSUM_TYPE_SHA256: "sha256", + CSUM_TYPE_BLAKE2: "blake2", + } + if name, ok := names[typ]; ok { + return name + } + return fmt.Sprintf("%d", typ) +} + +func (typ CSumType) Size() int { + sizes := map[CSumType]int{ + CSUM_TYPE_CRC32: 4, + CSUM_TYPE_XXHASH: 8, + CSUM_TYPE_SHA256: 32, + CSUM_TYPE_BLAKE2: 32, + } + if size, ok := sizes[typ]; ok { + return size + } + return len(CSum{}) +} + +func (typ CSumType) Sum(data []byte) (CSum, error) { + switch typ { + case CSUM_TYPE_CRC32: + crc := crc32.Update(0, crc32.MakeTable(crc32.Castagnoli), data) + + var ret CSum + binary.LittleEndian.PutUint32(ret[:], crc) + return ret, nil + case CSUM_TYPE_XXHASH: + panic("not implemented") + case CSUM_TYPE_SHA256: + panic("not implemented") + case CSUM_TYPE_BLAKE2: + panic("not implemented") + default: + return CSum{}, fmt.Errorf("unknown checksum type: %v", typ) + } +} diff --git a/pkg/btrfs/csum_test.go b/pkg/btrfs/csum_test.go new file mode 100644 index 0000000..335b3cc --- /dev/null +++ b/pkg/btrfs/csum_test.go @@ -0,0 +1,35 @@ +package btrfs_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "lukeshu.com/btrfs-tools/pkg/btrfs" +) + +func TestCSumFormat(t *testing.T) { + t.Parallel() + type TestCase struct { + InputSum btrfs.CSum + InputFmt string + Output string + } + csum := btrfs.CSum{0xbd, 0x7b, 0x41, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} + testcases := map[string]TestCase{ + "s": TestCase{InputSum: csum, InputFmt: "%s", Output: "bd7b41f400000000000000000000000000000000000000000000000000000000"}, + "x": TestCase{InputSum: csum, InputFmt: "%x", Output: "bd7b41f400000000000000000000000000000000000000000000000000000000"}, + "v": TestCase{InputSum: csum, InputFmt: "%v", Output: "bd7b41f400000000000000000000000000000000000000000000000000000000"}, + "70s": TestCase{InputSum: csum, InputFmt: "|% 70s", Output: "| bd7b41f400000000000000000000000000000000000000000000000000000000"}, + "#180v": TestCase{InputSum: csum, InputFmt: "%#180v", Output: " btrfs.CSum{0xbd, 0x7b, 0x41, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}"}, + } + for tcName, tc := range testcases { + tc := tc + t.Run(tcName, func(t *testing.T) { + t.Parallel() + actual := fmt.Sprintf(tc.InputFmt, tc.InputSum) + assert.Equal(t, tc.Output, actual) + }) + } +} diff --git a/pkg/btrfs/types_btree.go b/pkg/btrfs/types_btree.go index 99371d2..32c063e 100644 --- a/pkg/btrfs/types_btree.go +++ b/pkg/btrfs/types_btree.go @@ -54,7 +54,8 @@ func (f NodeFlags) String() string { return util.BitfieldString(f, nodeF type Node struct { // Some context from the parent filesystem - Size uint32 // superblock.NodeSize + Size uint32 // superblock.NodeSize + ChecksumType CSumType // superblock.ChecksumType // The node's header (always present) Head NodeHeader @@ -97,7 +98,7 @@ func (node Node) CalculateChecksum() (CSum, error) { if err != nil { return CSum{}, err } - return CRC32c(data[binstruct.StaticSize(CSum{}):]), nil + return node.ChecksumType.Sum(data[binstruct.StaticSize(CSum{}):]) } func (node Node) ValidateChecksum() error { @@ -115,7 +116,8 @@ func (node Node) ValidateChecksum() error { func (node *Node) UnmarshalBinary(nodeBuf []byte) (int, error) { *node = Node{ - Size: uint32(len(nodeBuf)), + Size: uint32(len(nodeBuf)), + ChecksumType: node.ChecksumType, } n, err := binstruct.Unmarshal(nodeBuf, &node.Head) if err != nil { @@ -334,6 +336,10 @@ func ReadNode[Addr ~int64](fs util.File[Addr], sb Superblock, addr Addr, laddrCB nodeRef := &util.Ref[Addr, Node]{ File: fs, Addr: addr, + Data: Node{ + Size: sb.NodeSize, + ChecksumType: sb.ChecksumType, + }, } if _, err := binstruct.Unmarshal(nodeBuf, &nodeRef.Data.Head); err != nil { return nodeRef, fmt.Errorf("btrfs.ReadNode: node@%v: %w", addr, err) @@ -346,7 +352,10 @@ func ReadNode[Addr ~int64](fs util.File[Addr], sb Superblock, addr Addr, laddrCB } stored := nodeRef.Data.Head.Checksum - calced := CRC32c(nodeBuf[binstruct.StaticSize(CSum{}):]) + calced, err := nodeRef.Data.ChecksumType.Sum(nodeBuf[binstruct.StaticSize(CSum{}):]) + if err != nil { + return nodeRef, fmt.Errorf("btrfs.ReadNode: node@%v: %w", addr, err) + } if stored != calced { return nodeRef, fmt.Errorf("btrfs.ReadNode: node@%v: looks like a node but is corrupt: checksum mismatch: stored=%v calculated=%v", addr, stored, calced) diff --git a/pkg/btrfs/types_superblock.go b/pkg/btrfs/types_superblock.go index 0f63389..3ed1055 100644 --- a/pkg/btrfs/types_superblock.go +++ b/pkg/btrfs/types_superblock.go @@ -37,7 +37,7 @@ type Superblock struct { CompatFlags uint64 `bin:"off=0xac, siz=0x8"` // compat_flags CompatROFlags uint64 `bin:"off=0xb4, siz=0x8"` // compat_ro_flags - only implementations that support the flags can write to the filesystem IncompatFlags IncompatFlags `bin:"off=0xbc, siz=0x8"` // incompat_flags - only implementations that support the flags can use the filesystem - ChecksumType uint16 `bin:"off=0xc4, siz=0x2"` // csum_type - Btrfs currently uses the CRC32c little-endian hash function with seed -1. + ChecksumType CSumType `bin:"off=0xc4, siz=0x2"` RootLevel uint8 `bin:"off=0xc6, siz=0x1"` // root_level ChunkLevel uint8 `bin:"off=0xc7, siz=0x1"` // chunk_root_level @@ -74,7 +74,7 @@ func (sb Superblock) CalculateChecksum() (CSum, error) { if err != nil { return CSum{}, err } - return CRC32c(data[binstruct.StaticSize(CSum{}):]), nil + return sb.ChecksumType.Sum(data[binstruct.StaticSize(CSum{}):]) } func (sb Superblock) ValidateChecksum() error { diff --git a/pkg/btrfsmisc/print_tree.go b/pkg/btrfsmisc/print_tree.go index fa864a5..580a0ca 100644 --- a/pkg/btrfsmisc/print_tree.go +++ b/pkg/btrfsmisc/print_tree.go @@ -273,11 +273,11 @@ func printHeaderInfo(node btrfs.Node) { node.Head.Flags, node.Head.BackrefRev) - fmt.Printf("checksum stored %v\n", node.Head.Checksum) + fmt.Printf("checksum stored %v\n", node.Head.Checksum.Fmt(node.ChecksumType)) if calcSum, err := node.CalculateChecksum(); err != nil { fmt.Printf("checksum calced %v\n", err) } else { - fmt.Printf("checksum calced %v\n", calcSum) + fmt.Printf("checksum calced %v\n", calcSum.Fmt(node.ChecksumType)) } fmt.Printf("fs uuid %v\n", node.Head.MetadataUUID) -- cgit v1.2.3-2-g168b