From 490cffb1f4ee99b013302cfed9ef849c0676735c Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 11 May 2022 22:40:03 -0600 Subject: scan for nodes --- .gitignore | 1 + cmd/btrfs-dbg/main.go | 4 + pkg/btrfs/bitfields.go | 68 ++++++++++++++++ pkg/btrfs/crc32c.go | 5 ++ pkg/btrfs/image.go | 57 +++++++++++-- pkg/btrfs/objid.go | 95 ++++++++++++++++++++++ pkg/btrfs/structs.go | 217 +++++++++++++++++++++++++++++++++++++++++++++++++ pkg/btrfs/types.go | 163 ------------------------------------- pkg/btrfs/util.go | 10 +++ pkg/btrfs/uuid.go | 5 ++ 10 files changed, 456 insertions(+), 169 deletions(-) create mode 100644 .gitignore create mode 100644 pkg/btrfs/bitfields.go create mode 100644 pkg/btrfs/objid.go create mode 100644 pkg/btrfs/structs.go delete mode 100644 pkg/btrfs/types.go create mode 100644 pkg/btrfs/util.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..397b4a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/cmd/btrfs-dbg/main.go b/cmd/btrfs-dbg/main.go index 9a87f10..6d60a73 100644 --- a/cmd/btrfs-dbg/main.go +++ b/cmd/btrfs-dbg/main.go @@ -55,5 +55,9 @@ func Main(imgfilename string) (err error) { } spew.Dump(syschunks) + if err := img.ScanForNodes(superblocks[0].Data); err != nil { + return err + } + return nil } diff --git a/pkg/btrfs/bitfields.go b/pkg/btrfs/bitfields.go new file mode 100644 index 0000000..ead4b0f --- /dev/null +++ b/pkg/btrfs/bitfields.go @@ -0,0 +1,68 @@ +package btrfs + +import ( + "fmt" + "strings" +) + +func bitfieldString[T ~uint8 | ~uint16 | ~uint32 | ~uint64](bitfield T, bitnames []string) string { + if bitfield == 0 { + return "0" + } + var out strings.Builder + fmt.Fprintf(&out, "(0x%0X)", uint64(bitfield)) + rest := bitfield + sep := ' ' + for i := 0; rest != 0; i++ { + if rest&(1< physical block numbering + BTRFS_DEV_TREE_OBJECTID = ObjID(4) // stores info about which areas of a given device are in use; one per device + BTRFS_FS_TREE_OBJECTID = ObjID(5) // one per subvolume, storing files and directories + BTRFS_ROOT_TREE_DIR_OBJECTID = ObjID(6) // directory objectid inside the root tree + BTRFS_CSUM_TREE_OBJECTID = ObjID(7) // holds checksums of all the data extents + BTRFS_QUOTA_TREE_OBJECTID = ObjID(8) + BTRFS_UUID_TREE_OBJECTID = ObjID(9) // for storing items that use the BTRFS_UUID_KEY* + BTRFS_FREE_SPACE_TREE_OBJECTID = ObjID(10) // tracks free space in block groups. + BTRFS_BLOCK_GROUP_TREE_OBJECTID = ObjID(11) // hold the block group items. + + // Objects in the DEV_TREE + BTRFS_DEV_STATS_OBJECTID = ObjID(0) // device stats in the device tree + + // ??? + BTRFS_BALANCE_OBJECTID = ObjID(maxUint64pp - 4) // for storing balance parameters in the root tree + BTRFS_ORPHAN_OBJECTID = ObjID(maxUint64pp - 5) // orphan objectid for tracking unlinked/truncated files + BTRFS_TREE_LOG_OBJECTID = ObjID(maxUint64pp - 6) // does write ahead logging to speed up fsyncs + BTRFS_TREE_LOG_FIXUP_OBJECTID = ObjID(maxUint64pp - 7) + BTRFS_TREE_RELOC_OBJECTID = ObjID(maxUint64pp - 8) // space balancing + BTRFS_DATA_RELOC_TREE_OBJECTID = ObjID(maxUint64pp - 9) + BTRFS_EXTENT_CSUM_OBJECTID = ObjID(maxUint64pp - 10) // extent checksums all have this objectid + BTRFS_FREE_SPACE_OBJECTID = ObjID(maxUint64pp - 11) // For storing free space cache + BTRFS_FREE_INO_OBJECTID = ObjID(maxUint64pp - 12) // stores the inode number for the free-ino cache + + BTRFS_MULTIPLE_OBJECTIDS = ObjID(maxUint64pp - 255) // dummy objectid represents multiple objectids + + // All files have objectids in this range. + BTRFS_FIRST_FREE_OBJECTID = ObjID(256) + BTRFS_LAST_FREE_OBJECTID = ObjID(maxUint64pp - 256) + BTRFS_FIRST_CHUNK_TREE_OBJECTID = ObjID(256) + + // Objects in the CHUNK_TREE + BTRFS_DEV_ITEMS_OBJECTID = ObjID(1) + + // ??? + BTRFS_EMPTY_SUBVOL_DIR_OBJECTID = ObjID(2) +) + +func (id ObjID) String() string { + if id > BTRFS_LAST_FREE_OBJECTID { + names := map[ObjID]string{ + BTRFS_BALANCE_OBJECTID: "BTRFS_BALANCE_OBJECTID", + BTRFS_ORPHAN_OBJECTID: "BTRFS_ORPHAN_OBJECTID", + BTRFS_TREE_LOG_OBJECTID: "BTRFS_TREE_LOG_OBJECTID", + BTRFS_TREE_LOG_FIXUP_OBJECTID: "BTRFS_TREE_LOG_FIXUP_OBJECTID", + BTRFS_TREE_RELOC_OBJECTID: "BTRFS_TREE_RELOC_OBJECTID", + BTRFS_DATA_RELOC_TREE_OBJECTID: "BTRFS_DATA_RELOC_TREE_OBJECTID", + BTRFS_EXTENT_CSUM_OBJECTID: "BTRFS_EXTENT_CSUM_OBJECTID", + BTRFS_FREE_SPACE_OBJECTID: "BTRFS_FREE_SPACE_OBJECTID", + BTRFS_FREE_INO_OBJECTID: "BTRFS_FREE_INO_OBJECTID", + BTRFS_MULTIPLE_OBJECTIDS: "BTRFS_MULTIPLE_OBJECTIDS", + } + if name, ok := names[id]; ok { + return name + } + return fmt.Sprintf("%d", int64(id)) + } + return fmt.Sprintf("%d", id) +} + +type TreeObjID ObjID + +func (id TreeObjID) String() string { + names := map[ObjID]string{ + BTRFS_ROOT_TREE_OBJECTID: "BTRFS_ROOT_TREE_OBJECTID", + BTRFS_EXTENT_TREE_OBJECTID: "BTRFS_EXTENT_TREE_OBJECTID", + BTRFS_CHUNK_TREE_OBJECTID: "BTRFS_CHUNK_TREE_OBJECTID", + BTRFS_DEV_TREE_OBJECTID: "BTRFS_DEV_TREE_OBJECTID", + BTRFS_FS_TREE_OBJECTID: "BTRFS_FS_TREE_OBJECTID", + BTRFS_ROOT_TREE_DIR_OBJECTID: "BTRFS_ROOT_TREE_DIR_OBJECTID", + BTRFS_CSUM_TREE_OBJECTID: "BTRFS_CSUM_TREE_OBJECTID", + BTRFS_QUOTA_TREE_OBJECTID: "BTRFS_QUOTA_TREE_OBJECTID", + BTRFS_UUID_TREE_OBJECTID: "BTRFS_UUID_TREE_OBJECTID", + BTRFS_FREE_SPACE_TREE_OBJECTID: "BTRFS_FREE_SPACE_TREE_OBJECTID", + BTRFS_BLOCK_GROUP_TREE_OBJECTID: "BTRFS_BLOCK_GROUP_TREE_OBJECTID", + } + if name, ok := names[ObjID(id)]; ok { + return name + } + return ObjID(id).String() +} diff --git a/pkg/btrfs/structs.go b/pkg/btrfs/structs.go new file mode 100644 index 0000000..92db216 --- /dev/null +++ b/pkg/btrfs/structs.go @@ -0,0 +1,217 @@ +package btrfs + +import ( + "time" + + "lukeshu.com/btrfs-tools/pkg/binstruct" +) + +type ( + PhysicalAddr int64 + LogicalAddr int64 +) + +type Key struct { + ObjectID ObjID `bin:"off=0, siz=8"` // Object ID. Each tree has its own set of Object IDs. + ItemType uint8 `bin:"off=8, siz=1"` // Item type. + Offset uint64 `bin:"off=9, siz=8"` // Offset. The meaning depends on the item type. + binstruct.End `bin:"off=11"` +} + +type Time struct { + Sec int64 `bin:"off=0, siz=8"` // Number of seconds since 1970-01-01T00:00:00Z. + NSec uint64 `bin:"off=8, siz=4"` // Number of nanoseconds since the beginning of the second. + binstruct.End `bin:"off=c"` +} + +func (t Time) ToStd() time.Time { + return time.Unix(t.Sec, int64(t.NSec)) +} + +type Superblock struct { + Checksum CSum `bin:"off=0, siz=20"` // Checksum of everything past this field (from 20 to 1000) + FSUUID UUID `bin:"off=20, siz=10"` // FS UUID + Self PhysicalAddr `bin:"off=30, siz=8"` // physical address of this block (different for mirrors) + Flags uint64 `bin:"off=38, siz=8"` // flags + Magic [8]byte `bin:"off=40, siz=8"` // magic ('_BHRfS_M') + Generation uint64 `bin:"off=48, siz=8"` // generation + + RootTree LogicalAddr `bin:"off=50, siz=8"` // logical address of the root tree root + ChunkTree LogicalAddr `bin:"off=58, siz=8"` // logical address of the chunk tree root + LogTree LogicalAddr `bin:"off=60, siz=8"` // logical address of the log tree root + + LogRootTransID uint64 `bin:"off=68, siz=8"` // log_root_transid + TotalBytes uint64 `bin:"off=70, siz=8"` // total_bytes + BytesUsed uint64 `bin:"off=78, siz=8"` // bytes_used + RootDirObjectID ObjID `bin:"off=80, siz=8"` // root_dir_objectid (usually 6) + NumDevices uint64 `bin:"off=88, siz=8"` // num_devices + + SectorSize uint32 `bin:"off=90, siz=4"` // sectorsize + NodeSize uint32 `bin:"off=94, siz=4"` // nodesize + LeafSize uint32 `bin:"off=98, siz=4"` // leafsize + StripeSize uint32 `bin:"off=9c, siz=4"` // stripesize + SysChunkArraySize uint32 `bin:"off=a0, siz=4"` // sys_chunk_array_size + + ChunkRootGeneration uint64 `bin:"off=a4, siz=8"` // chunk_root_generation + CompatFlags uint64 `bin:"off=ac, siz=8"` // compat_flags + CompatROFlags uint64 `bin:"off=b4, siz=8"` // compat_ro_flags - only implementations that support the flags can write to the filesystem + IncompatFlags IncompatFlags `bin:"off=bc, siz=8"` // incompat_flags - only implementations that support the flags can use the filesystem + ChecksumType uint16 `bin:"off=c4, siz=2"` // csum_type - Btrfs currently uses the CRC32c little-endian hash function with seed -1. + + RootLevel uint8 `bin:"off=c6, siz=1"` // root_level + ChunkLevel uint8 `bin:"off=c7, siz=1"` // chunk_root_level + LogLevel uint8 `bin:"off=c8, siz=1"` // log_root_level + + DevItem DevItem `bin:"off=c9, siz=62"` // DEV_ITEM data for this device + Label [0x100]byte `bin:"off=12b, siz=100"` // label (may not contain '/' or '\\') + CacheGeneration uint64 `bin:"off=22b, siz=8"` // cache_generation + UUIDTreeGeneration uint64 `bin:"off=233, siz=8"` // uuid_tree_generation + + // FeatureIncompatMetadataUUID + MetadataUUID UUID `bin:"off=23b, siz=10"` + + // FeatureIncompatExtentTreeV2 + NumGlobalRoots uint64 `bin:"off=24b, siz=8"` + + // FeatureIncompatExtentTreeV2 + BlockGroupRoot uint64 `bin:"off=253, siz=8"` + BlockGroupRootGeneration uint64 `bin:"off=25b, siz=8"` + BlockGroupRootLevel uint8 `bin:"off=263, siz=1"` + + Reserved [199]byte `bin:"off=264, siz=c7"` // future expansion + + SysChunkArray [0x800]byte `bin:"off=32b, siz=800"` // sys_chunk_array:(n bytes valid) Contains (KEY . CHUNK_ITEM) pairs for all SYSTEM chunks. This is needed to bootstrap the mapping from logical addresses to physical. + TODOSuperRoots [0x2a0]byte `bin:"off=b2b, siz=2a0"` // Contain super_roots (4 btrfs_root_backup) + + // Padded to 4096 bytes + Padding [565]byte `bin:"off=dcb, siz=235"` + binstruct.End `bin:"off=1000"` +} + +func (sb Superblock) CalculateChecksum() (CSum, error) { + data, err := binstruct.Marshal(sb) + if err != nil { + return CSum{}, err + } + return CRC32c(data[0x20:]), nil +} + +func (sb Superblock) EffectiveMetadataUUID() UUID { + if !sb.IncompatFlags.Has(FeatureIncompatMetadataUUID) { + return sb.FSUUID + } + return sb.MetadataUUID +} + +type SysChunk struct { + Key `bin:"off=0, siz=11"` + ChunkItem `bin:"off=11, siz=30"` + binstruct.End `bin:"off=41"` +} + +func (sb Superblock) ParseSysChunkArray() ([]SysChunk, error) { + dat := sb.SysChunkArray[:sb.SysChunkArraySize] + var ret []SysChunk + for len(dat) > 0 { + var pair SysChunk + if err := binstruct.Unmarshal(dat, &pair); err != nil { + return nil, err + } + dat = dat[0x41:] + + for i := 0; i < int(pair.ChunkItem.NumStripes); i++ { + var stripe ChunkItemStripe + if err := binstruct.Unmarshal(dat, &stripe); err != nil { + return nil, err + } + pair.ChunkItem.Stripes = append(pair.ChunkItem.Stripes, stripe) + dat = dat[0x20:] + } + + ret = append(ret, pair) + } + return ret, nil +} + +type NodeHeader struct { + Checksum CSum `bin:"off=0, siz=20"` // Checksum of everything after this field (from 20 to the end of the node) + MetadataUUID UUID `bin:"off=20, siz=10"` // FS UUID + Addr LogicalAddr `bin:"off=30, siz=8"` // Logical address of this node + Flags uint64 `bin:"off=38, siz=8"` // Flags + ChunkTreeUUID UUID `bin:"off=40, siz=10"` // Chunk tree UUID + Generation uint64 `bin:"off=50, siz=8"` // Generation + OwnerTree TreeObjID `bin:"off=58, siz=8"` // The ID of the tree that contains this node + NumItems uint32 `bin:"off=60, siz=4"` // Number of items + Level uint8 `bin:"off=64, siz=1"` // Level (0 for leaf nodes) + binstruct.End `bin:"off=65"` +} + +type InternalNode struct { + NodeHeader + Body []KeyPointer +} + +type KeyPointer struct { + Key Key `bin:"off=0, siz=11"` + BlockNumber uint64 `bin:"off=11, siz=8"` + Generation uint64 `bin:"off=19, siz=8"` + binstruct.End `bin:"off=21"` +} + +type LeafNode struct { + NodeHeader + Body []Item +} + +type Item struct { + Key Key `bin:"off=0, siz=11"` + DataOffset uint32 `bin:"off=11, siz=4"` // relative to the end of the header (0x65) + DataSize uint32 `bin:"off=15, siz=4"` + binstruct.End `bin:"off=19"` +} + +type DevItem struct { + DeviceID ObjID `bin:"off=0, siz=8"` // device ID + + NumBytes uint64 `bin:"off=8, siz=8"` // number of bytes + NumBytesUsed uint64 `bin:"off=10, siz=8"` // number of bytes used + + IOOptimalAlign uint32 `bin:"off=18, siz=4"` // optimal I/O align + IOOptimalWidth uint32 `bin:"off=1c, siz=4"` // optimal I/O width + IOMinSize uint32 `bin:"off=20, siz=4"` // minimal I/O size (sector size) + + Type uint64 `bin:"off=24, siz=8"` // type + Generation uint64 `bin:"off=2c, siz=8"` // generation + StartOffset uint64 `bin:"off=34, siz=8"` // start offset + DevGroup uint32 `bin:"off=3c, siz=4"` // dev group + SeekSpeed uint8 `bin:"off=40, siz=1"` // seek speed + Bandwidth uint8 `bin:"off=41, siz=1"` // bandwidth + + DevUUID UUID `bin:"off=42, siz=10"` // device UUID + FSUUID UUID `bin:"off=52, siz=10"` // FS UUID + + binstruct.End `bin:"off=62"` +} + +type ChunkItem struct { + // Maps logical address to physical. + Size uint64 `bin:"off=0, siz=8"` // size of chunk (bytes) + Root ObjID `bin:"off=8, siz=8"` // root referencing this chunk (2) + StripeLen uint64 `bin:"off=10, siz=8"` // stripe length + Type uint64 `bin:"off=18, siz=8"` // type (same as flags for block group?) + IOOptimalAlign uint32 `bin:"off=20, siz=4"` // optimal io alignment + IOOptimalWidth uint32 `bin:"off=24, siz=4"` // optimal io width + IoMinSize uint32 `bin:"off=28, siz=4"` // minimal io size (sector size) + NumStripes uint16 `bin:"off=2c, siz=2"` // number of stripes + SubStripes uint16 `bin:"off=2e, siz=2"` // sub stripes + binstruct.End `bin:"off=30"` + Stripes []ChunkItemStripe `bin:"-"` +} + +type ChunkItemStripe struct { + // Stripes follow (for each number of stripes): + DeviceID ObjID `bin:"off=0, siz=8"` // device ID + Offset uint64 `bin:"off=8, siz=8"` // offset + DeviceUUID UUID `bin:"off=10, siz=10"` // device UUID + binstruct.End `bin:"off=20"` +} diff --git a/pkg/btrfs/types.go b/pkg/btrfs/types.go deleted file mode 100644 index a91708e..0000000 --- a/pkg/btrfs/types.go +++ /dev/null @@ -1,163 +0,0 @@ -package btrfs - -import ( - "time" - - "lukeshu.com/btrfs-tools/pkg/binstruct" -) - -type ( - PhysicalAddr int64 - LogicalAddr int64 - ObjID int64 -) - -type Key struct { - ObjectID ObjID `bin:"off=0, siz=8"` // Object ID. Each tree has its own set of Object IDs. - ItemType uint8 `bin:"off=8, siz=1"` // Item type. - Offset uint64 `bin:"off=9, siz=8"` // Offset. The meaning depends on the item type. - binstruct.End `bin:"off=11"` -} - -type Time struct { - Sec int64 `bin:"off=0, siz=8"` // Number of seconds since 1970-01-01T00:00:00Z. - NSec uint64 `bin:"off=8, siz=4"` // Number of nanoseconds since the beginning of the second. - binstruct.End `bin:"off=c"` -} - -func (t Time) ToStd() time.Time { - return time.Unix(t.Sec, int64(t.NSec)) -} - -type Superblock struct { - Checksum CSum `bin:"off=0, siz=20"` // Checksum of everything past this field (from 20 to 1000) - FSUUID UUID `bin:"off=20, siz=10"` // FS UUID - Self PhysicalAddr `bin:"off=30, siz=8"` // physical address of this block (different for mirrors) - Flags uint64 `bin:"off=38, siz=8"` // flags - Magic [8]byte `bin:"off=40, siz=8"` // magic ('_BHRfS_M') - Generation uint64 `bin:"off=48, siz=8"` // generation - - RootTree LogicalAddr `bin:"off=50, siz=8"` // logical address of the root tree root - ChunkTree LogicalAddr `bin:"off=58, siz=8"` // logical address of the chunk tree root - LogTree LogicalAddr `bin:"off=60, siz=8"` // logical address of the log tree root - - LogRootTransID uint64 `bin:"off=68, siz=8"` // log_root_transid - TotalBytes uint64 `bin:"off=70, siz=8"` // total_bytes - BytesUsed uint64 `bin:"off=78, siz=8"` // bytes_used - RootDirObjectID uint64 `bin:"off=80, siz=8"` // root_dir_objectid (usually 6) - NumDevices uint64 `bin:"off=88, siz=8"` // num_devices - - SectorSize uint32 `bin:"off=90, siz=4"` // sectorsize - NodeSize uint32 `bin:"off=94, siz=4"` // nodesize - LeafSize uint32 `bin:"off=98, siz=4"` // leafsize - StripeSize uint32 `bin:"off=9c, siz=4"` // stripesize - SysChunkArraySize uint32 `bin:"off=a0, siz=4"` // sys_chunk_array_size - - ChunkRootGeneration uint64 `bin:"off=a4, siz=8"` // chunk_root_generation - CompatFlags uint64 `bin:"off=ac, siz=8"` // compat_flags - CompatROFlags uint64 `bin:"off=b4, siz=8"` // compat_ro_flags - only implementations that support the flags can write to the filesystem - IncompatFlags uint64 `bin:"off=bc, siz=8"` // incompat_flags - only implementations that support the flags can use the filesystem - ChecksumType uint16 `bin:"off=c4, siz=2"` // csum_type - Btrfs currently uses the CRC32c little-endian hash function with seed -1. - - RootLevel uint8 `bin:"off=c6, siz=1"` // root_level - ChunkLevel uint8 `bin:"off=c7, siz=1"` // chunk_root_level - LogLevel uint8 `bin:"off=c8, siz=1"` // log_root_level - - DevItem DevItem `bin:"off=c9, siz=62"` // DEV_ITEM data for this device - Label [0x100]byte `bin:"off=12b, siz=100"` // label (may not contain '/' or '\\') - CacheGeneration uint64 `bin:"off=22b, siz=8"` // cache_generation - UUIDTreeGeneration uint64 `bin:"off=233, siz=8"` // uuid_tree_generation - - Reserved [0xf0]byte `bin:"off=23b, siz=f0"` // reserved /* future expansion */ - - SysChunkArray [0x800]byte `bin:"off=32b, siz=800"` // sys_chunk_array:(n bytes valid) Contains (KEY . CHUNK_ITEM) pairs for all SYSTEM chunks. This is needed to bootstrap the mapping from logical addresses to physical. - TODOSuperRoots [0x2a0]byte `bin:"off=b2b, siz=2a0"` // Contain super_roots (4 btrfs_root_backup) - - Unused [0x235]byte `bin:"off=dcb, siz=235"` // current unused - - binstruct.End `bin:"off=1000"` -} - -func (sb Superblock) CalculateChecksum() (CSum, error) { - data, err := binstruct.Marshal(sb) - if err != nil { - return CSum{}, err - } - return CRC32c(data[0x20:]), nil -} - -type SysChunk struct { - Key `bin:"off=0, siz=11"` - ChunkItem `bin:"off=11, siz=30"` - binstruct.End `bin:"off=41"` -} - -func (sb Superblock) ParseSysChunkArray() ([]SysChunk, error) { - dat := sb.SysChunkArray[:sb.SysChunkArraySize] - var ret []SysChunk - for len(dat) > 0 { - var pair SysChunk - if err := binstruct.Unmarshal(dat, &pair); err != nil { - return nil, err - } - dat = dat[0x41:] - - for i := 0; i < int(pair.ChunkItem.NumStripes); i++ { - var stripe ChunkItemStripe - if err := binstruct.Unmarshal(dat, &stripe); err != nil { - return nil, err - } - pair.ChunkItem.Stripes = append(pair.ChunkItem.Stripes, stripe) - dat = dat[0x20:] - } - - ret = append(ret, pair) - } - return ret, nil -} - -type DevItem struct { - DeviceID uint64 `bin:"off=0, siz=8"` // device id - - NumBytes uint64 `bin:"off=8, siz=8"` // number of bytes - NumBytesUsed uint64 `bin:"off=10, siz=8"` // number of bytes used - - IOOptimalAlign uint32 `bin:"off=18, siz=4"` // optimal I/O align - IOOptimalWidth uint32 `bin:"off=1c, siz=4"` // optimal I/O width - IOMinSize uint32 `bin:"off=20, siz=4"` // minimal I/O size (sector size) - - Type uint64 `bin:"off=24, siz=8"` // type - Generation uint64 `bin:"off=2c, siz=8"` // generation - StartOffset uint64 `bin:"off=34, siz=8"` // start offset - DevGroup uint32 `bin:"off=3c, siz=4"` // dev group - SeekSpeed uint8 `bin:"off=40, siz=1"` // seek speed - Bandwidth uint8 `bin:"off=41, siz=1"` // bandwidth - - DevUUID UUID `bin:"off=42, siz=10"` // device UUID - FSUUID UUID `bin:"off=52, siz=10"` // FS UUID - - binstruct.End `bin:"off=62"` -} - -type ChunkItem struct { - // Maps logical address to physical. - Size uint64 `bin:"off=0, siz=8"` // size of chunk (bytes) - Root ObjID `bin:"off=8, siz=8"` // root referencing this chunk (2) - StripeLen uint64 `bin:"off=10, siz=8"` // stripe length - Type uint64 `bin:"off=18, siz=8"` // type (same as flags for block group?) - IOOptimalAlign uint32 `bin:"off=20, siz=4"` // optimal io alignment - IOOptimalWidth uint32 `bin:"off=24, siz=4"` // optimal io width - IoMinSize uint32 `bin:"off=28, siz=4"` // minimal io size (sector size) - NumStripes uint16 `bin:"off=2c, siz=2"` // number of stripes - SubStripes uint16 `bin:"off=2e, siz=2"` // sub stripes - binstruct.End `bin:"off=30"` - Stripes []ChunkItemStripe `bin:"-"` -} - -type ChunkItemStripe struct { - // Stripes follow (for each number of stripes): - DeviceID ObjID `bin:"off=0, siz=8"` // device id - Offset uint64 `bin:"off=8, siz=8"` // offset - DeviceUUID UUID `bin:"off=10, siz=10"` // device UUID - binstruct.End `bin:"off=20"` -} diff --git a/pkg/btrfs/util.go b/pkg/btrfs/util.go new file mode 100644 index 0000000..04462f8 --- /dev/null +++ b/pkg/btrfs/util.go @@ -0,0 +1,10 @@ +package btrfs + +func inSlice[T comparable](needle T, haystack []T) bool { + for _, straw := range haystack { + if needle == straw { + return true + } + } + return false +} diff --git a/pkg/btrfs/uuid.go b/pkg/btrfs/uuid.go index 5218a3f..b9e3e0c 100644 --- a/pkg/btrfs/uuid.go +++ b/pkg/btrfs/uuid.go @@ -1,6 +1,7 @@ package btrfs import ( + "bytes" "encoding/hex" "strings" ) @@ -17,3 +18,7 @@ func (uuid UUID) String() string { str[20:32], }, "-") } + +func (a UUID) Equal(b UUID) bool { + return bytes.Equal(a[:], b[:]) +} -- cgit v1.2.3-2-g168b