From e2cdb05eac6726c59fe1831876fddd8037156d67 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sun, 28 Aug 2022 17:55:36 -0600 Subject: btrfs: Split off btrfstree and btrfsprim sub-packages --- lib/btrfs/btrfsprim/itemtype.go | 79 +++++++++++++++++++++ lib/btrfs/btrfsprim/misc.go | 48 +++++++++++++ lib/btrfs/btrfsprim/objid.go | 148 +++++++++++++++++++++++++++++++++++++++ lib/btrfs/btrfsprim/uuid.go | 96 +++++++++++++++++++++++++ lib/btrfs/btrfsprim/uuid_test.go | 67 ++++++++++++++++++ 5 files changed, 438 insertions(+) create mode 100644 lib/btrfs/btrfsprim/itemtype.go create mode 100644 lib/btrfs/btrfsprim/misc.go create mode 100644 lib/btrfs/btrfsprim/objid.go create mode 100644 lib/btrfs/btrfsprim/uuid.go create mode 100644 lib/btrfs/btrfsprim/uuid_test.go (limited to 'lib/btrfs/btrfsprim') diff --git a/lib/btrfs/btrfsprim/itemtype.go b/lib/btrfs/btrfsprim/itemtype.go new file mode 100644 index 0000000..89cff21 --- /dev/null +++ b/lib/btrfs/btrfsprim/itemtype.go @@ -0,0 +1,79 @@ +// Code generated by Make. DO NOT EDIT. + +// SPDX-License-Identifier: GPL-2.0-or-later + +package btrfsprim + +import "fmt" + +type ItemType uint8 + +const ( + BLOCK_GROUP_ITEM_KEY = ItemType(192) + CHUNK_ITEM_KEY = ItemType(228) + DEV_EXTENT_KEY = ItemType(204) + DEV_ITEM_KEY = ItemType(216) + DIR_INDEX_KEY = ItemType(96) + DIR_ITEM_KEY = ItemType(84) + EXTENT_CSUM_KEY = ItemType(128) + EXTENT_DATA_KEY = ItemType(108) + EXTENT_DATA_REF_KEY = ItemType(178) + EXTENT_ITEM_KEY = ItemType(168) + FREE_SPACE_BITMAP_KEY = ItemType(200) + FREE_SPACE_EXTENT_KEY = ItemType(199) + FREE_SPACE_INFO_KEY = ItemType(198) + INODE_ITEM_KEY = ItemType(1) + INODE_REF_KEY = ItemType(12) + METADATA_ITEM_KEY = ItemType(169) + ORPHAN_ITEM_KEY = ItemType(48) + PERSISTENT_ITEM_KEY = ItemType(249) + QGROUP_RELATION_KEY = ItemType(246) + ROOT_BACKREF_KEY = ItemType(144) + ROOT_ITEM_KEY = ItemType(132) + ROOT_REF_KEY = ItemType(156) + SHARED_BLOCK_REF_KEY = ItemType(182) + SHARED_DATA_REF_KEY = ItemType(184) + TREE_BLOCK_REF_KEY = ItemType(176) + UNTYPED_KEY = ItemType(0) + UUID_RECEIVED_SUBVOL_KEY = ItemType(252) + UUID_SUBVOL_KEY = ItemType(251) + XATTR_ITEM_KEY = ItemType(24) +) + +func (t ItemType) String() string { + names := map[ItemType]string{ + BLOCK_GROUP_ITEM_KEY: "BLOCK_GROUP_ITEM", + CHUNK_ITEM_KEY: "CHUNK_ITEM", + DEV_EXTENT_KEY: "DEV_EXTENT", + DEV_ITEM_KEY: "DEV_ITEM", + DIR_INDEX_KEY: "DIR_INDEX", + DIR_ITEM_KEY: "DIR_ITEM", + EXTENT_CSUM_KEY: "EXTENT_CSUM", + EXTENT_DATA_KEY: "EXTENT_DATA", + EXTENT_DATA_REF_KEY: "EXTENT_DATA_REF", + EXTENT_ITEM_KEY: "EXTENT_ITEM", + FREE_SPACE_BITMAP_KEY: "FREE_SPACE_BITMAP", + FREE_SPACE_EXTENT_KEY: "FREE_SPACE_EXTENT", + FREE_SPACE_INFO_KEY: "FREE_SPACE_INFO", + INODE_ITEM_KEY: "INODE_ITEM", + INODE_REF_KEY: "INODE_REF", + METADATA_ITEM_KEY: "METADATA_ITEM", + ORPHAN_ITEM_KEY: "ORPHAN_ITEM", + PERSISTENT_ITEM_KEY: "PERSISTENT_ITEM", + QGROUP_RELATION_KEY: "QGROUP_RELATION", + ROOT_BACKREF_KEY: "ROOT_BACKREF", + ROOT_ITEM_KEY: "ROOT_ITEM", + ROOT_REF_KEY: "ROOT_REF", + SHARED_BLOCK_REF_KEY: "SHARED_BLOCK_REF", + SHARED_DATA_REF_KEY: "SHARED_DATA_REF", + TREE_BLOCK_REF_KEY: "TREE_BLOCK_REF", + UNTYPED_KEY: "UNTYPED", + UUID_RECEIVED_SUBVOL_KEY: "UUID_KEY_RECEIVED_SUBVOL", + UUID_SUBVOL_KEY: "UUID_KEY_SUBVOL", + XATTR_ITEM_KEY: "XATTR_ITEM", + } + if name, ok := names[t]; ok { + return name + } + return fmt.Sprintf("%d", t) +} diff --git a/lib/btrfs/btrfsprim/misc.go b/lib/btrfs/btrfsprim/misc.go new file mode 100644 index 0000000..0ebbe19 --- /dev/null +++ b/lib/btrfs/btrfsprim/misc.go @@ -0,0 +1,48 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package btrfsprim + +import ( + "fmt" + "time" + + "git.lukeshu.com/btrfs-progs-ng/lib/binstruct" + "git.lukeshu.com/btrfs-progs-ng/lib/containers" +) + +type Generation uint64 + +type Key struct { + ObjectID ObjID `bin:"off=0x0, siz=0x8"` // Each tree has its own set of Object IDs. + ItemType ItemType `bin:"off=0x8, siz=0x1"` + Offset uint64 `bin:"off=0x9, siz=0x8"` // The meaning depends on the item type. + binstruct.End `bin:"off=0x11"` +} + +func (k Key) String() string { + return fmt.Sprintf("{%v %v %v}", k.ObjectID, k.ItemType, k.Offset) +} + +func (a Key) Cmp(b Key) int { + if d := containers.NativeCmp(a.ObjectID, b.ObjectID); d != 0 { + return d + } + if d := containers.NativeCmp(a.ItemType, b.ItemType); d != 0 { + return d + } + return containers.NativeCmp(a.Offset, b.Offset) +} + +var _ containers.Ordered[Key] = Key{} + +type Time struct { + Sec int64 `bin:"off=0x0, siz=0x8"` // Number of seconds since 1970-01-01T00:00:00Z. + NSec uint32 `bin:"off=0x8, siz=0x4"` // Number of nanoseconds since the beginning of the second. + binstruct.End `bin:"off=0xc"` +} + +func (t Time) ToStd() time.Time { + return time.Unix(t.Sec, int64(t.NSec)) +} diff --git a/lib/btrfs/btrfsprim/objid.go b/lib/btrfs/btrfsprim/objid.go new file mode 100644 index 0000000..17a0eeb --- /dev/null +++ b/lib/btrfs/btrfsprim/objid.go @@ -0,0 +1,148 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package btrfsprim + +import ( + "fmt" +) + +type ObjID uint64 + +const maxUint64pp = 0x1_00000000_00000000 + +const ( + // The IDs of the various trees + ROOT_TREE_OBJECTID = ObjID(1) // holds pointers to all of the tree roots + EXTENT_TREE_OBJECTID = ObjID(2) // stores information about which extents are in use, and reference counts + CHUNK_TREE_OBJECTID = ObjID(3) // chunk tree stores translations from logical -> physical block numbering + DEV_TREE_OBJECTID = ObjID(4) // stores info about which areas of a given device are in use; one per device + FS_TREE_OBJECTID = ObjID(5) // one per subvolume, storing files and directories + ROOT_TREE_DIR_OBJECTID = ObjID(6) // directory objectid inside the root tree + CSUM_TREE_OBJECTID = ObjID(7) // holds checksums of all the data extents + QUOTA_TREE_OBJECTID = ObjID(8) + UUID_TREE_OBJECTID = ObjID(9) // for storing items that use the UUID_*_KEY + FREE_SPACE_TREE_OBJECTID = ObjID(10) // tracks free space in block groups. + BLOCK_GROUP_TREE_OBJECTID = ObjID(11) // hold the block group items. + + // Objects in the DEV_TREE + DEV_STATS_OBJECTID = ObjID(0) // device stats in the device tree + + // ??? + BALANCE_OBJECTID = ObjID(maxUint64pp - 4) // for storing balance parameters in the root tree + ORPHAN_OBJECTID = ObjID(maxUint64pp - 5) // orphan objectid for tracking unlinked/truncated files + TREE_LOG_OBJECTID = ObjID(maxUint64pp - 6) // does write ahead logging to speed up fsyncs + TREE_LOG_FIXUP_OBJECTID = ObjID(maxUint64pp - 7) + TREE_RELOC_OBJECTID = ObjID(maxUint64pp - 8) // space balancing + DATA_RELOC_TREE_OBJECTID = ObjID(maxUint64pp - 9) + EXTENT_CSUM_OBJECTID = ObjID(maxUint64pp - 10) // extent checksums all have this objectid + FREE_SPACE_OBJECTID = ObjID(maxUint64pp - 11) // For storing free space cache + FREE_INO_OBJECTID = ObjID(maxUint64pp - 12) // stores the inode number for the free-ino cache + + MULTIPLE_OBJECTIDS = ObjID(maxUint64pp - 255) // dummy objectid represents multiple objectids + + // All files have objectids in this range. + FIRST_FREE_OBJECTID = ObjID(256) + LAST_FREE_OBJECTID = ObjID(maxUint64pp - 256) + + FIRST_CHUNK_TREE_OBJECTID = ObjID(256) + + // Objects in the CHUNK_TREE + DEV_ITEMS_OBJECTID = ObjID(1) + + // ??? + EMPTY_SUBVOL_DIR_OBJECTID = ObjID(2) +) + +func (id ObjID) Format(typ ItemType) string { + switch typ { + case PERSISTENT_ITEM_KEY: + names := map[ObjID]string{ + DEV_STATS_OBJECTID: "DEV_STATS", + } + if name, ok := names[id]; ok { + return name + } + return fmt.Sprintf("%d", int64(id)) + case DEV_EXTENT_KEY: + return fmt.Sprintf("%d", int64(id)) + case QGROUP_RELATION_KEY: + return fmt.Sprintf("%d/%d", + uint64(id)>>48, + uint64(id)&((1<<48)-1)) + case UUID_SUBVOL_KEY, UUID_RECEIVED_SUBVOL_KEY: + return fmt.Sprintf("%#016x", uint64(id)) + case DEV_ITEM_KEY: + names := map[ObjID]string{ + BALANCE_OBJECTID: "BALANCE", + ORPHAN_OBJECTID: "ORPHAN", + TREE_LOG_OBJECTID: "TREE_LOG", + TREE_LOG_FIXUP_OBJECTID: "TREE_LOG_FIXUP", + TREE_RELOC_OBJECTID: "TREE_RELOC", + DATA_RELOC_TREE_OBJECTID: "DATA_RELOC_TREE", + EXTENT_CSUM_OBJECTID: "EXTENT_CSUM", + FREE_SPACE_OBJECTID: "FREE_SPACE", + FREE_INO_OBJECTID: "FREE_INO", + MULTIPLE_OBJECTIDS: "MULTIPLE", + + DEV_ITEMS_OBJECTID: "DEV_ITEMS", + } + if name, ok := names[id]; ok { + return name + } + return fmt.Sprintf("%d", int64(id)) + case CHUNK_ITEM_KEY: + names := map[ObjID]string{ + BALANCE_OBJECTID: "BALANCE", + ORPHAN_OBJECTID: "ORPHAN", + TREE_LOG_OBJECTID: "TREE_LOG", + TREE_LOG_FIXUP_OBJECTID: "TREE_LOG_FIXUP", + TREE_RELOC_OBJECTID: "TREE_RELOC", + DATA_RELOC_TREE_OBJECTID: "DATA_RELOC_TREE", + EXTENT_CSUM_OBJECTID: "EXTENT_CSUM", + FREE_SPACE_OBJECTID: "FREE_SPACE", + FREE_INO_OBJECTID: "FREE_INO", + MULTIPLE_OBJECTIDS: "MULTIPLE", + + FIRST_CHUNK_TREE_OBJECTID: "FIRST_CHUNK_TREE", + } + if name, ok := names[id]; ok { + return name + } + return fmt.Sprintf("%d", int64(id)) + default: + names := map[ObjID]string{ + BALANCE_OBJECTID: "BALANCE", + ORPHAN_OBJECTID: "ORPHAN", + TREE_LOG_OBJECTID: "TREE_LOG", + TREE_LOG_FIXUP_OBJECTID: "TREE_LOG_FIXUP", + TREE_RELOC_OBJECTID: "TREE_RELOC", + DATA_RELOC_TREE_OBJECTID: "DATA_RELOC_TREE", + EXTENT_CSUM_OBJECTID: "EXTENT_CSUM", + FREE_SPACE_OBJECTID: "FREE_SPACE", + FREE_INO_OBJECTID: "FREE_INO", + MULTIPLE_OBJECTIDS: "MULTIPLE", + + ROOT_TREE_OBJECTID: "ROOT_TREE", + EXTENT_TREE_OBJECTID: "EXTENT_TREE", + CHUNK_TREE_OBJECTID: "CHUNK_TREE", + DEV_TREE_OBJECTID: "DEV_TREE", + FS_TREE_OBJECTID: "FS_TREE", + ROOT_TREE_DIR_OBJECTID: "ROOT_TREE_DIR", + CSUM_TREE_OBJECTID: "CSUM_TREE", + QUOTA_TREE_OBJECTID: "QUOTA_TREE", + UUID_TREE_OBJECTID: "UUID_TREE", + FREE_SPACE_TREE_OBJECTID: "FREE_SPACE_TREE", + BLOCK_GROUP_TREE_OBJECTID: "BLOCK_GROUP_TREE", + } + if name, ok := names[id]; ok { + return name + } + return fmt.Sprintf("%d", int64(id)) + } +} + +func (id ObjID) String() string { + return id.Format(UNTYPED_KEY) +} diff --git a/lib/btrfs/btrfsprim/uuid.go b/lib/btrfs/btrfsprim/uuid.go new file mode 100644 index 0000000..4e3fd6b --- /dev/null +++ b/lib/btrfs/btrfsprim/uuid.go @@ -0,0 +1,96 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package btrfsprim + +import ( + "encoding" + "encoding/hex" + "fmt" + "strings" + + "git.lukeshu.com/btrfs-progs-ng/lib/fmtutil" +) + +type UUID [16]byte + +var ( + _ fmt.Stringer = UUID{} + _ fmt.Formatter = UUID{} + _ encoding.TextMarshaler = UUID{} + _ encoding.TextUnmarshaler = (*UUID)(nil) +) + +func (uuid UUID) String() string { + str := hex.EncodeToString(uuid[:]) + return strings.Join([]string{ + str[:8], + str[8:12], + str[12:16], + str[16:20], + str[20:32], + }, "-") +} + +func (uuid UUID) MarshalText() ([]byte, error) { + return []byte(uuid.String()), nil +} + +func (uuid *UUID) UnmarshalText(text []byte) error { + var err error + *uuid, err = ParseUUID(string(text)) + return err +} + +func (uuid UUID) Format(f fmt.State, verb rune) { + fmtutil.FormatByteArrayStringer(uuid, uuid[:], f, verb) +} + +func (a UUID) Cmp(b UUID) int { + for i := range a { + if d := int(a[i]) - int(b[i]); d != 0 { + return d + } + } + return 0 +} + +func ParseUUID(str string) (UUID, error) { + var ret UUID + j := 0 + for i := 0; i < len(str); i++ { + if j >= len(ret)*2 { + return UUID{}, fmt.Errorf("too long to be a UUID: %q|%q", str[:i], str[i:]) + } + c := str[i] + var v byte + switch { + case '0' <= c && c <= '9': + v = c - '0' + case 'a' <= c && c <= 'f': + v = c - 'a' + 10 + case 'A' <= c && c <= 'F': + v = c - 'A' + 10 + case c == '-': + continue + default: + return UUID{}, fmt.Errorf("illegal byte in UUID: %q|%q|%q", str[:i], str[i:i+1], str[i+1:]) + } + if j%2 == 0 { + ret[j/2] = v << 4 + } else { + ret[j/2] = (ret[j/2] & 0xf0) | (v & 0x0f) + } + j++ + } + return ret, nil +} + +func MustParseUUID(str string) UUID { + ret, err := ParseUUID(str) + if err != nil { + panic(err) + } + return ret +} diff --git a/lib/btrfs/btrfsprim/uuid_test.go b/lib/btrfs/btrfsprim/uuid_test.go new file mode 100644 index 0000000..9fff760 --- /dev/null +++ b/lib/btrfs/btrfsprim/uuid_test.go @@ -0,0 +1,67 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package btrfsprim_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim" +) + +func TestParseUUID(t *testing.T) { + t.Parallel() + type TestCase struct { + Input string + OutputVal btrfsprim.UUID + OutputErr string + } + testcases := map[string]TestCase{ + "basic": {Input: "a0dd94ed-e60c-42e8-8632-64e8d4765a43", OutputVal: btrfsprim.UUID{0xa0, 0xdd, 0x94, 0xed, 0xe6, 0x0c, 0x42, 0xe8, 0x86, 0x32, 0x64, 0xe8, 0xd4, 0x76, 0x5a, 0x43}}, + "too-long": {Input: "a0dd94ed-e60c-42e8-8632-64e8d4765a43a", OutputErr: `too long to be a UUID: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"|"a"`}, + "bad char": {Input: "a0dd94ej-e60c-42e8-8632-64e8d4765a43a", OutputErr: `illegal byte in UUID: "a0dd94e"|"j"|"-e60c-42e8-8632-64e8d4765a43a"`}, + } + for tcName, tc := range testcases { + tc := tc + t.Run(tcName, func(t *testing.T) { + t.Parallel() + val, err := btrfsprim.ParseUUID(tc.Input) + assert.Equal(t, tc.OutputVal, val) + if tc.OutputErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.OutputErr) + } + }) + } +} + +func TestUUIDFormat(t *testing.T) { + t.Parallel() + type TestCase struct { + InputUUID btrfsprim.UUID + InputFmt string + Output string + } + uuid := btrfsprim.MustParseUUID("a0dd94ed-e60c-42e8-8632-64e8d4765a43") + testcases := map[string]TestCase{ + "s": {InputUUID: uuid, InputFmt: "%s", Output: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"}, + "x": {InputUUID: uuid, InputFmt: "%x", Output: "a0dd94ede60c42e8863264e8d4765a43"}, + "X": {InputUUID: uuid, InputFmt: "%X", Output: "A0DD94EDE60C42E8863264E8D4765A43"}, + "v": {InputUUID: uuid, InputFmt: "%v", Output: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"}, + "40s": {InputUUID: uuid, InputFmt: "|% 40s", Output: "| a0dd94ed-e60c-42e8-8632-64e8d4765a43"}, + "#115v": {InputUUID: uuid, InputFmt: "|%#115v", Output: "| btrfsprim.UUID{0xa0, 0xdd, 0x94, 0xed, 0xe6, 0xc, 0x42, 0xe8, 0x86, 0x32, 0x64, 0xe8, 0xd4, 0x76, 0x5a, 0x43}"}, + } + for tcName, tc := range testcases { + tc := tc + t.Run(tcName, func(t *testing.T) { + t.Parallel() + actual := fmt.Sprintf(tc.InputFmt, tc.InputUUID) + assert.Equal(t, tc.Output, actual) + }) + } +} -- cgit v1.2.3-2-g168b