diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2022-07-10 13:18:30 -0600 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2022-07-10 13:35:20 -0600 |
commit | 27401b6ea459921a6152ab1744da1618358465f4 (patch) | |
tree | 2c4f9c096f1a593e65d7f824901e815ca48bfaf0 /pkg/btrfs | |
parent | 42f6f78e0a32ba0eda707154f8e1ffb4579604ee (diff) |
Rename the module, mv pkg lib
Diffstat (limited to 'pkg/btrfs')
44 files changed, 0 insertions, 4236 deletions
diff --git a/pkg/btrfs/Makefile b/pkg/btrfs/Makefile deleted file mode 100644 index a770356..0000000 --- a/pkg/btrfs/Makefile +++ /dev/null @@ -1,79 +0,0 @@ -.DEFAULT_GOAL = all -.SECONDARY: -.DELETE_ON_ERROR: - -btrfsitem/items.txt: btrfsitem $(wildcard btrfsitem/item_*.go) $(MAKEFILE_LIST) - { \ - sed -En 's,^type (\S+) .* // (.*=.*),\1 \2,p' $(filter btrfsitem/item_%.go,$^) | while read -r typ keys; do \ - for key in $$keys; do \ - echo "$$key" "$$typ"; \ - done; \ - done; \ - } | LC_COLLATE=C sort >$@ -files += btrfsitem/items.txt - -btrfsitem/items_gen.go: btrfsitem/items.txt $(MAKEFILE_LIST) - { \ - echo '// Code generated by Make. DO NOT EDIT.'; \ - echo; \ - echo 'package $(@D)'; \ - echo 'import ('; \ - echo '"reflect"'; \ - echo; \ - echo '"lukeshu.com/btrfs-tools/pkg/btrfs/internal"'; \ - echo ')'; \ - echo 'const ('; \ - sed -E 's,(.*)=(.*) (.*),\1_KEY=internal.\1_KEY,' $<; \ - echo ')'; \ - echo 'var keytype2gotype = map[Type]reflect.Type{'; \ - sed -En 's|(.*)=([^:]*) (.*)|\1_KEY: reflect.TypeOf(\3{}),|p' $<; \ - echo '}'; \ - echo 'var untypedObjID2gotype = map[internal.ObjID]reflect.Type{'; \ - sed -En 's|UNTYPED=0:(.*) (.*)|internal.\1: reflect.TypeOf(\2{}),|p' $<; \ - echo '}'; \ - sed -En 's,(.*)=(.*) (.+),\3,p' $< | LC_COLLATE=C sort -u | sed 's,.*,func (&) isItem() {},'; \ - } | gofmt >$@ -files += btrfsitem/items_gen.go - -internal/itemtype.go: btrfsitem/items.txt $(MAKEFILE_LIST) - { \ - echo '// Code generated by Make. DO NOT EDIT.'; \ - echo; \ - echo 'package $(@D)'; \ - echo 'import "fmt"'; \ - echo 'type ItemType uint8'; \ - echo 'const ('; \ - sed -E 's,(.*)=([^:]*)(:.*)? (.*),\1_KEY=ItemType(\2),' $< | uniq; \ - echo ')'; \ - echo 'func (t ItemType) String() string {'; \ - echo ' names := map[ItemType]string{'; \ - sed -E 's@(.*)=(.*) (.*)@\1_KEY: "\1",@' $< | sed 's/"UUID_/&KEY_/'; \ - echo ' }'; \ - echo ' if name, ok := names[t]; ok {'; \ - echo ' return name'; \ - echo ' }'; \ - echo ' return fmt.Sprintf("%d", t)'; \ - echo '}'; \ - } | gofmt >$@ -files += internal/itemtype.go - -aliases_objid.go: internal/objid.go $(MAKEFILE_LIST) - { \ - echo '// Code generated by Make. DO NOT EDIT.'; \ - echo; \ - echo 'package btrfs'; \ - echo 'import ('; \ - echo '"lukeshu.com/btrfs-tools/pkg/btrfs/internal"'; \ - echo ')'; \ - echo 'const('; \ - sed -En 's/^\s*(\S*_OBJECTIDS?)\s*=.*/\1 = internal.\1/p' <$<; \ - echo ')'; \ - } | gofmt >$@ -files += aliases_objid.go - -all: $(files) -.PHONY: all - -clean: - rm -f -- $(files) -.PHONY: all diff --git a/pkg/btrfs/aliases.go b/pkg/btrfs/aliases.go deleted file mode 100644 index 1a18ae6..0000000 --- a/pkg/btrfs/aliases.go +++ /dev/null @@ -1,19 +0,0 @@ -package btrfs - -import ( - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" - "lukeshu.com/btrfs-tools/pkg/util" -) - -type ( - // (u)int64 types - - Generation = internal.Generation - ObjID = internal.ObjID - - // complex types - - Key = internal.Key - Time = internal.Time - UUID = util.UUID -) diff --git a/pkg/btrfs/aliases_objid.go b/pkg/btrfs/aliases_objid.go deleted file mode 100644 index 12a58a2..0000000 --- a/pkg/btrfs/aliases_objid.go +++ /dev/null @@ -1,37 +0,0 @@ -// Code generated by Make. DO NOT EDIT. - -package btrfs - -import ( - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" -) - -const ( - ROOT_TREE_OBJECTID = internal.ROOT_TREE_OBJECTID - EXTENT_TREE_OBJECTID = internal.EXTENT_TREE_OBJECTID - CHUNK_TREE_OBJECTID = internal.CHUNK_TREE_OBJECTID - DEV_TREE_OBJECTID = internal.DEV_TREE_OBJECTID - FS_TREE_OBJECTID = internal.FS_TREE_OBJECTID - ROOT_TREE_DIR_OBJECTID = internal.ROOT_TREE_DIR_OBJECTID - CSUM_TREE_OBJECTID = internal.CSUM_TREE_OBJECTID - QUOTA_TREE_OBJECTID = internal.QUOTA_TREE_OBJECTID - UUID_TREE_OBJECTID = internal.UUID_TREE_OBJECTID - FREE_SPACE_TREE_OBJECTID = internal.FREE_SPACE_TREE_OBJECTID - BLOCK_GROUP_TREE_OBJECTID = internal.BLOCK_GROUP_TREE_OBJECTID - DEV_STATS_OBJECTID = internal.DEV_STATS_OBJECTID - BALANCE_OBJECTID = internal.BALANCE_OBJECTID - ORPHAN_OBJECTID = internal.ORPHAN_OBJECTID - TREE_LOG_OBJECTID = internal.TREE_LOG_OBJECTID - TREE_LOG_FIXUP_OBJECTID = internal.TREE_LOG_FIXUP_OBJECTID - TREE_RELOC_OBJECTID = internal.TREE_RELOC_OBJECTID - DATA_RELOC_TREE_OBJECTID = internal.DATA_RELOC_TREE_OBJECTID - EXTENT_CSUM_OBJECTID = internal.EXTENT_CSUM_OBJECTID - FREE_SPACE_OBJECTID = internal.FREE_SPACE_OBJECTID - FREE_INO_OBJECTID = internal.FREE_INO_OBJECTID - MULTIPLE_OBJECTIDS = internal.MULTIPLE_OBJECTIDS - FIRST_FREE_OBJECTID = internal.FIRST_FREE_OBJECTID - LAST_FREE_OBJECTID = internal.LAST_FREE_OBJECTID - FIRST_CHUNK_TREE_OBJECTID = internal.FIRST_CHUNK_TREE_OBJECTID - DEV_ITEMS_OBJECTID = internal.DEV_ITEMS_OBJECTID - EMPTY_SUBVOL_DIR_OBJECTID = internal.EMPTY_SUBVOL_DIR_OBJECTID -) diff --git a/pkg/btrfs/btrfsitem/item_blockgroup.go b/pkg/btrfs/btrfsitem/item_blockgroup.go deleted file mode 100644 index 63cbcbb..0000000 --- a/pkg/btrfs/btrfsitem/item_blockgroup.go +++ /dev/null @@ -1,16 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" -) - -// key.objectid = logical_addr -// key.offset = size of chunk -type BlockGroup struct { // BLOCK_GROUP_ITEM=192 - Used int64 `bin:"off=0, siz=8"` - ChunkObjectID internal.ObjID `bin:"off=8, siz=8"` // always BTRFS_FIRST_CHUNK_TREE_OBJECTID - Flags btrfsvol.BlockGroupFlags `bin:"off=16, siz=8"` - binstruct.End `bin:"off=24"` -} diff --git a/pkg/btrfs/btrfsitem/item_chunk.go b/pkg/btrfs/btrfsitem/item_chunk.go deleted file mode 100644 index f567506..0000000 --- a/pkg/btrfs/btrfsitem/item_chunk.go +++ /dev/null @@ -1,90 +0,0 @@ -package btrfsitem - -import ( - "fmt" - - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" - "lukeshu.com/btrfs-tools/pkg/util" -) - -// Maps logical address to physical. -// -// key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID -// key.offset = logical_addr -type Chunk struct { // CHUNK_ITEM=228 - Head ChunkHeader - Stripes []ChunkStripe -} - -type ChunkHeader struct { - Size btrfsvol.AddrDelta `bin:"off=0x0, siz=0x8"` - Owner internal.ObjID `bin:"off=0x8, siz=0x8"` // root referencing this chunk (always EXTENT_TREE_OBJECTID=2) - StripeLen uint64 `bin:"off=0x10, siz=0x8"` // ??? - Type btrfsvol.BlockGroupFlags `bin:"off=0x18, siz=0x8"` - IOOptimalAlign uint32 `bin:"off=0x20, siz=0x4"` - IOOptimalWidth uint32 `bin:"off=0x24, siz=0x4"` - IOMinSize uint32 `bin:"off=0x28, siz=0x4"` // sector size - NumStripes uint16 `bin:"off=0x2c, siz=0x2"` // [ignored-when-writing] - SubStripes uint16 `bin:"off=0x2e, siz=0x2"` // ??? - binstruct.End `bin:"off=0x30"` -} - -type ChunkStripe struct { - DeviceID btrfsvol.DeviceID `bin:"off=0x0, siz=0x8"` - Offset btrfsvol.PhysicalAddr `bin:"off=0x8, siz=0x8"` - DeviceUUID util.UUID `bin:"off=0x10, siz=0x10"` - binstruct.End `bin:"off=0x20"` -} - -func (chunk Chunk) Mappings(key internal.Key) []btrfsvol.Mapping { - ret := make([]btrfsvol.Mapping, 0, len(chunk.Stripes)) - for _, stripe := range chunk.Stripes { - ret = append(ret, btrfsvol.Mapping{ - LAddr: btrfsvol.LogicalAddr(key.Offset), - PAddr: btrfsvol.QualifiedPhysicalAddr{ - Dev: stripe.DeviceID, - Addr: stripe.Offset, - }, - Size: chunk.Head.Size, - SizeLocked: true, - Flags: &chunk.Head.Type, - }) - } - return ret -} - -func (chunk *Chunk) UnmarshalBinary(dat []byte) (int, error) { - n, err := binstruct.Unmarshal(dat, &chunk.Head) - if err != nil { - return n, err - } - chunk.Stripes = nil - for i := 0; i < int(chunk.Head.NumStripes); i++ { - var stripe ChunkStripe - _n, err := binstruct.Unmarshal(dat[n:], &stripe) - n += _n - if err != nil { - return n, fmt.Errorf("%T.UnmarshalBinary: %w", *chunk, err) - } - chunk.Stripes = append(chunk.Stripes, stripe) - } - return n, nil -} - -func (chunk Chunk) MarshalBinary() ([]byte, error) { - chunk.Head.NumStripes = uint16(len(chunk.Stripes)) - ret, err := binstruct.Marshal(chunk.Head) - if err != nil { - return ret, err - } - for _, stripe := range chunk.Stripes { - _ret, err := binstruct.Marshal(stripe) - ret = append(ret, _ret...) - if err != nil { - return ret, fmt.Errorf("%T.MarshalBinary: %w", chunk, err) - } - } - return ret, nil -} diff --git a/pkg/btrfs/btrfsitem/item_dev.go b/pkg/btrfs/btrfsitem/item_dev.go deleted file mode 100644 index e7b214a..0000000 --- a/pkg/btrfs/btrfsitem/item_dev.go +++ /dev/null @@ -1,33 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" - "lukeshu.com/btrfs-tools/pkg/util" -) - -// key.objectid = BTRFS_DEV_ITEMS_OBJECTID -// key.offset = device_id (starting at 1) -type Dev struct { // DEV_ITEM=216 - DevID btrfsvol.DeviceID `bin:"off=0x0, siz=0x8"` - - NumBytes uint64 `bin:"off=0x8, siz=0x8"` - NumBytesUsed uint64 `bin:"off=0x10, siz=0x8"` - - IOOptimalAlign uint32 `bin:"off=0x18, siz=0x4"` - IOOptimalWidth uint32 `bin:"off=0x1c, siz=0x4"` - IOMinSize uint32 `bin:"off=0x20, siz=0x4"` // sector size - - Type uint64 `bin:"off=0x24, siz=0x8"` - Generation internal.Generation `bin:"off=0x2c, siz=0x8"` - StartOffset uint64 `bin:"off=0x34, siz=0x8"` - DevGroup uint32 `bin:"off=0x3c, siz=0x4"` - SeekSpeed uint8 `bin:"off=0x40, siz=0x1"` - Bandwidth uint8 `bin:"off=0x41, siz=0x1"` - - DevUUID util.UUID `bin:"off=0x42, siz=0x10"` - FSUUID util.UUID `bin:"off=0x52, siz=0x10"` - - binstruct.End `bin:"off=0x62"` -} diff --git a/pkg/btrfs/btrfsitem/item_devextent.go b/pkg/btrfs/btrfsitem/item_devextent.go deleted file mode 100644 index 56fba27..0000000 --- a/pkg/btrfs/btrfsitem/item_devextent.go +++ /dev/null @@ -1,32 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" - "lukeshu.com/btrfs-tools/pkg/util" -) - -// key.objectid = device_id -// key.offset = physical_addr -type DevExtent struct { // DEV_EXTENT=204 - ChunkTree int64 `bin:"off=0, siz=8"` - ChunkObjectID internal.ObjID `bin:"off=8, siz=8"` - ChunkOffset btrfsvol.LogicalAddr `bin:"off=16, siz=8"` - Length btrfsvol.AddrDelta `bin:"off=24, siz=8"` - ChunkTreeUUID util.UUID `bin:"off=32, siz=16"` - binstruct.End `bin:"off=48"` -} - -func (devext DevExtent) Mapping(key internal.Key) btrfsvol.Mapping { - return btrfsvol.Mapping{ - LAddr: devext.ChunkOffset, - PAddr: btrfsvol.QualifiedPhysicalAddr{ - Dev: btrfsvol.DeviceID(key.ObjectID), - Addr: btrfsvol.PhysicalAddr(key.Offset), - }, - Size: devext.Length, - SizeLocked: true, - Flags: nil, - } -} diff --git a/pkg/btrfs/btrfsitem/item_dir.go b/pkg/btrfs/btrfsitem/item_dir.go deleted file mode 100644 index 2c4c75b..0000000 --- a/pkg/btrfs/btrfsitem/item_dir.go +++ /dev/null @@ -1,115 +0,0 @@ -package btrfsitem - -import ( - "fmt" - "hash/crc32" - - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" -) - -// key.objectid = inode of directory containing this entry -// key.offset = -// for DIR_ITEM and XATTR_ITEM = NameHash(name) -// for DIR_INDEX = index id in the directory (starting at 2, because "." and "..") -type DirEntries []DirEntry // DIR_ITEM=84 DIR_INDEX=96 XATTR_ITEM=24 - -func NameHash(dat []byte) uint64 { - return uint64(^crc32.Update(1, crc32.MakeTable(crc32.Castagnoli), dat)) -} - -func (o *DirEntries) UnmarshalBinary(dat []byte) (int, error) { - *o = nil - n := 0 - for n < len(dat) { - var ref DirEntry - _n, err := binstruct.Unmarshal(dat, &ref) - n += _n - if err != nil { - return n, err - } - *o = append(*o, ref) - } - return n, nil -} - -func (o DirEntries) MarshalBinary() ([]byte, error) { - var ret []byte - for _, ref := range o { - bs, err := binstruct.Marshal(ref) - ret = append(ret, bs...) - if err != nil { - return ret, err - } - } - return ret, nil -} - -type DirEntry struct { - Location internal.Key `bin:"off=0x0, siz=0x11"` - TransID int64 `bin:"off=0x11, siz=8"` - DataLen uint16 `bin:"off=0x19, siz=2"` // [ignored-when-writing] - NameLen uint16 `bin:"off=0x1b, siz=2"` // [ignored-when-writing] - Type FileType `bin:"off=0x1d, siz=1"` - binstruct.End `bin:"off=0x1e"` - Data []byte `bin:"-"` - Name []byte `bin:"-"` -} - -func (o *DirEntry) UnmarshalBinary(dat []byte) (int, error) { - n, err := binstruct.UnmarshalWithoutInterface(dat, o) - if err != nil { - return n, err - } - o.Data = dat[n : n+int(o.DataLen)] - n += int(o.DataLen) - o.Name = dat[n : n+int(o.NameLen)] - n += int(o.NameLen) - return n, nil -} - -func (o DirEntry) MarshalBinary() ([]byte, error) { - o.DataLen = uint16(len(o.Data)) - o.NameLen = uint16(len(o.Name)) - dat, err := binstruct.MarshalWithoutInterface(o) - if err != nil { - return dat, err - } - dat = append(dat, o.Data...) - dat = append(dat, o.Name...) - return dat, nil -} - -type FileType uint8 - -const ( - FT_UNKNOWN = FileType(0) - FT_REG_FILE = FileType(1) - FT_DIR = FileType(2) - FT_CHRDEV = FileType(3) - FT_BLKDEV = FileType(4) - FT_FIFO = FileType(5) - FT_SOCK = FileType(6) - FT_SYMLINK = FileType(7) - FT_XATTR = FileType(8) - - FT_MAX = FileType(9) -) - -func (ft FileType) String() string { - names := map[FileType]string{ - FT_UNKNOWN: "UNKNOWN", - FT_REG_FILE: "FILE", // XXX - FT_DIR: "DIR", - FT_CHRDEV: "CHRDEV", - FT_BLKDEV: "BLKDEV", - FT_FIFO: "FIFO", - FT_SOCK: "SOCK", - FT_SYMLINK: "SYMLINK", - FT_XATTR: "XATTR", - } - if name, ok := names[ft]; ok { - return name - } - return fmt.Sprintf("DIR_ITEM.%d", uint8(ft)) -} diff --git a/pkg/btrfs/btrfsitem/item_empty.go b/pkg/btrfs/btrfsitem/item_empty.go deleted file mode 100644 index 93e2f9e..0000000 --- a/pkg/btrfs/btrfsitem/item_empty.go +++ /dev/null @@ -1,9 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" -) - -type Empty struct { // ORPHAN_ITEM=48 TREE_BLOCK_REF=176 SHARED_BLOCK_REF=182 FREE_SPACE_EXTENT=199 QGROUP_RELATION=246 - binstruct.End `bin:"off=0"` -} diff --git a/pkg/btrfs/btrfsitem/item_extent.go b/pkg/btrfs/btrfsitem/item_extent.go deleted file mode 100644 index ab8a4c6..0000000 --- a/pkg/btrfs/btrfsitem/item_extent.go +++ /dev/null @@ -1,165 +0,0 @@ -package btrfsitem - -import ( - "fmt" - - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" - "lukeshu.com/btrfs-tools/pkg/util" -) - -type Extent struct { // EXTENT_ITEM=168 - Head ExtentHeader - Info TreeBlockInfo // only if .Head.Flags.Has(EXTENT_FLAG_TREE_BLOCK) - Refs []ExtentInlineRef -} - -func (o *Extent) UnmarshalBinary(dat []byte) (int, error) { - n, err := binstruct.Unmarshal(dat, &o.Head) - if err != nil { - return n, err - } - if o.Head.Flags.Has(EXTENT_FLAG_TREE_BLOCK) { - _n, err := binstruct.Unmarshal(dat[n:], &o.Info) - n += _n - if err != nil { - return n, err - } - } - o.Refs = nil - for n < len(dat) { - var ref ExtentInlineRef - _n, err := binstruct.Unmarshal(dat[n:], &ref) - n += _n - o.Refs = append(o.Refs, ref) - if err != nil { - return n, err - } - } - return n, nil -} - -func (o Extent) MarshalBinary() ([]byte, error) { - dat, err := binstruct.Marshal(o.Head) - if err != nil { - return dat, err - } - if o.Head.Flags.Has(EXTENT_FLAG_TREE_BLOCK) { - bs, err := binstruct.Marshal(o.Info) - dat = append(dat, bs...) - if err != nil { - return dat, err - } - } - for _, ref := range o.Refs { - bs, err := binstruct.Marshal(ref) - dat = append(dat, bs...) - if err != nil { - return dat, err - } - } - return dat, nil -} - -type ExtentHeader struct { - Refs int64 `bin:"off=0, siz=8"` - Generation internal.Generation `bin:"off=8, siz=8"` - Flags ExtentFlags `bin:"off=16, siz=8"` - binstruct.End `bin:"off=24"` -} - -type TreeBlockInfo struct { - Key internal.Key `bin:"off=0, siz=0x11"` - Level uint8 `bin:"off=0x11, siz=0x8"` - binstruct.End `bin:"off=0x19"` -} - -type ExtentFlags uint64 - -const ( - EXTENT_FLAG_DATA = ExtentFlags(1 << iota) - EXTENT_FLAG_TREE_BLOCK -) - -var extentFlagNames = []string{ - "DATA", - "TREE_BLOCK", -} - -func (f ExtentFlags) Has(req ExtentFlags) bool { return f&req == req } -func (f ExtentFlags) String() string { return util.BitfieldString(f, extentFlagNames, util.HexNone) } - -type ExtentInlineRef struct { - Type Type // only 4 valid values: {TREE,SHARED}_BLOCK_REF_KEY, {EXTENT,SHARED}_DATA_REF_KEY - Offset uint64 // only when Type != EXTENT_DATA_REF_KEY - Body Item // only when Type == *_DATA_REF_KEY -} - -func (o *ExtentInlineRef) UnmarshalBinary(dat []byte) (int, error) { - o.Type = Type(dat[0]) - n := 1 - switch o.Type { - case TREE_BLOCK_REF_KEY, SHARED_BLOCK_REF_KEY: - _n, err := binstruct.Unmarshal(dat[n:], &o.Offset) - n += _n - if err != nil { - return n, err - } - case EXTENT_DATA_REF_KEY: - var dref ExtentDataRef - _n, err := binstruct.Unmarshal(dat[n:], &dref) - n += _n - o.Body = dref - if err != nil { - return n, err - } - case SHARED_DATA_REF_KEY: - _n, err := binstruct.Unmarshal(dat[n:], &o.Offset) - n += _n - if err != nil { - return n, err - } - var sref SharedDataRef - _n, err = binstruct.Unmarshal(dat[n:], &sref) - n += _n - o.Body = sref - if err != nil { - return n, err - } - default: - return n, fmt.Errorf("btrfsitem.ExtentInlineRef.UnmarshalBinary: unexpected item type %v", o.Type) - } - return n, nil -} - -func (o ExtentInlineRef) MarshalBinary() ([]byte, error) { - dat := []byte{byte(o.Type)} - switch o.Type { - case TREE_BLOCK_REF_KEY, SHARED_BLOCK_REF_KEY: - _dat, err := binstruct.Marshal(o.Offset) - dat = append(dat, _dat...) - if err != nil { - return dat, err - } - case EXTENT_DATA_REF_KEY: - _dat, err := binstruct.Marshal(o.Body) - dat = append(dat, _dat...) - if err != nil { - return dat, err - } - case SHARED_DATA_REF_KEY: - _dat, err := binstruct.Marshal(o.Offset) - dat = append(dat, _dat...) - if err != nil { - return dat, err - } - _dat, err = binstruct.Marshal(o.Body) - dat = append(dat, _dat...) - if err != nil { - return dat, err - } - default: - return dat, fmt.Errorf("btrfsitem.ExtentInlineRef.MarshalBinary: unexpected item type %v", o.Type) - } - return dat, nil -} diff --git a/pkg/btrfs/btrfsitem/item_extentcsum.go b/pkg/btrfs/btrfsitem/item_extentcsum.go deleted file mode 100644 index 27c7c9b..0000000 --- a/pkg/btrfs/btrfsitem/item_extentcsum.go +++ /dev/null @@ -1,39 +0,0 @@ -package btrfsitem - -import ( - "fmt" - - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfssum" -) - -// key.objectid = BTRFS_EXTENT_CSUM_OBJECTID -// key.offset = laddr of checksummed region -type ExtentCSum struct { // EXTENT_CSUM=128 - ChecksumSize int - // Checksum of each sector starting at key.offset - Sums []btrfssum.CSum -} - -func (o *ExtentCSum) UnmarshalBinary(dat []byte) (int, error) { - if o.ChecksumSize == 0 { - return 0, fmt.Errorf("btrfs.ExtentCSum.UnmarshalBinary: .ChecksumSize must be set") - } - for len(dat) >= o.ChecksumSize { - var csum btrfssum.CSum - copy(csum[:], dat[:o.ChecksumSize]) - dat = dat[o.ChecksumSize:] - o.Sums = append(o.Sums, csum) - } - return len(o.Sums) * o.ChecksumSize, nil -} - -func (o ExtentCSum) MarshalBinary() ([]byte, error) { - if o.ChecksumSize == 0 { - return nil, fmt.Errorf("btrfs.ExtentCSum.MarshalBinary: .ChecksumSize must be set") - } - var dat []byte - for _, csum := range o.Sums { - dat = append(dat, csum[:o.ChecksumSize]...) - } - return dat, nil -} diff --git a/pkg/btrfs/btrfsitem/item_extentdataref.go b/pkg/btrfs/btrfsitem/item_extentdataref.go deleted file mode 100644 index 93585be..0000000 --- a/pkg/btrfs/btrfsitem/item_extentdataref.go +++ /dev/null @@ -1,14 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" -) - -type ExtentDataRef struct { // EXTENT_DATA_REF=178 - Root internal.ObjID `bin:"off=0, siz=8"` - ObjectID internal.ObjID `bin:"off=8, siz=8"` - Offset int64 `bin:"off=16, siz=8"` - Count int32 `bin:"off=24, siz=4"` - binstruct.End `bin:"off=28"` -} diff --git a/pkg/btrfs/btrfsitem/item_fileextent.go b/pkg/btrfs/btrfsitem/item_fileextent.go deleted file mode 100644 index f480207..0000000 --- a/pkg/btrfs/btrfsitem/item_fileextent.go +++ /dev/null @@ -1,137 +0,0 @@ -package btrfsitem - -import ( - "fmt" - - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" -) - -// key.objectid = inode -// key.offset = offset within file -type FileExtent struct { // EXTENT_DATA=108 - Generation internal.Generation `bin:"off=0x0, siz=0x8"` // transaction ID that created this extent - RAMBytes int64 `bin:"off=0x8, siz=0x8"` // upper bound of what compressed data will decompress to - - // 32 bits describing the data encoding - Compression CompressionType `bin:"off=0x10, siz=0x1"` - Encryption uint8 `bin:"off=0x11, siz=0x1"` - OtherEncoding uint16 `bin:"off=0x12, siz=0x2"` // reserved for later use - - Type FileExtentType `bin:"off=0x14, siz=0x1"` // inline data or real extent - - binstruct.End `bin:"off=0x15"` - - // only one of these, depending on .Type - BodyInline []byte `bin:"-"` // .Type == FILE_EXTENT_INLINE - BodyExtent struct { // .Type == FILE_EXTENT_REG or FILE_EXTENT_PREALLOC - // Position and size of extent within the device - DiskByteNr btrfsvol.LogicalAddr `bin:"off=0x0, siz=0x8"` - DiskNumBytes btrfsvol.AddrDelta `bin:"off=0x8, siz=0x8"` - - // Position of data within the extent - Offset btrfsvol.AddrDelta `bin:"off=0x10, siz=0x8"` - - // Decompressed/unencrypted size - NumBytes int64 `bin:"off=0x18, siz=0x8"` - - binstruct.End `bin:"off=0x20"` - } `bin:"-"` -} - -func (o *FileExtent) UnmarshalBinary(dat []byte) (int, error) { - n, err := binstruct.UnmarshalWithoutInterface(dat, o) - if err != nil { - return n, err - } - switch o.Type { - case FILE_EXTENT_INLINE: - o.BodyInline = dat[n:] - n += len(o.BodyInline) - case FILE_EXTENT_REG, FILE_EXTENT_PREALLOC: - _n, err := binstruct.Unmarshal(dat[n:], &o.BodyExtent) - n += _n - if err != nil { - return n, err - } - default: - return n, fmt.Errorf("unknown file extent type %v", o.Type) - } - return n, nil -} - -func (o FileExtent) MarshalBinary() ([]byte, error) { - dat, err := binstruct.MarshalWithoutInterface(o) - if err != nil { - return dat, err - } - switch o.Type { - case FILE_EXTENT_INLINE: - dat = append(dat, o.BodyInline...) - case FILE_EXTENT_REG, FILE_EXTENT_PREALLOC: - bs, err := binstruct.Marshal(o.BodyExtent) - dat = append(dat, bs...) - if err != nil { - return dat, err - } - default: - return dat, fmt.Errorf("unknown file extent type %v", o.Type) - } - return dat, nil -} - -type FileExtentType uint8 - -const ( - FILE_EXTENT_INLINE = FileExtentType(iota) - FILE_EXTENT_REG - FILE_EXTENT_PREALLOC -) - -func (o FileExtent) Size() (int64, error) { - switch o.Type { - case FILE_EXTENT_INLINE: - return int64(len(o.BodyInline)), nil - case FILE_EXTENT_REG, FILE_EXTENT_PREALLOC: - return o.BodyExtent.NumBytes, nil - default: - return 0, fmt.Errorf("unknown file extent type %v", o.Type) - } -} - -func (fet FileExtentType) String() string { - names := map[FileExtentType]string{ - FILE_EXTENT_INLINE: "inline", - FILE_EXTENT_REG: "regular", - FILE_EXTENT_PREALLOC: "prealloc", - } - name, ok := names[fet] - if !ok { - name = "unknown" - } - return fmt.Sprintf("%d (%s)", fet, name) -} - -type CompressionType uint8 - -const ( - COMPRESS_NONE = CompressionType(iota) - COMPRESS_ZLIB - COMPRESS_LZO - COMPRESS_ZSTD -) - -func (ct CompressionType) String() string { - names := map[CompressionType]string{ - COMPRESS_NONE: "none", - COMPRESS_ZLIB: "zlib", - COMPRESS_LZO: "lzo", - COMPRESS_ZSTD: "zstd", - } - name, ok := names[ct] - if !ok { - name = "unknown" - } - return fmt.Sprintf("%d (%s)", ct, name) -} diff --git a/pkg/btrfs/btrfsitem/item_freespacebitmap.go b/pkg/btrfs/btrfsitem/item_freespacebitmap.go deleted file mode 100644 index 6158eb0..0000000 --- a/pkg/btrfs/btrfsitem/item_freespacebitmap.go +++ /dev/null @@ -1,12 +0,0 @@ -package btrfsitem - -type FreeSpaceBitmap []byte // FREE_SPACE_BITMAP=200 - -func (o *FreeSpaceBitmap) UnmarshalBinary(dat []byte) (int, error) { - *o = dat - return len(dat), nil -} - -func (o FreeSpaceBitmap) MarshalBinary() ([]byte, error) { - return []byte(o), nil -} diff --git a/pkg/btrfs/btrfsitem/item_freespaceinfo.go b/pkg/btrfs/btrfsitem/item_freespaceinfo.go deleted file mode 100644 index 4931844..0000000 --- a/pkg/btrfs/btrfsitem/item_freespaceinfo.go +++ /dev/null @@ -1,11 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" -) - -type FreeSpaceInfo struct { // FREE_SPACE_INFO=198 - ExtentCount int32 `bin:"off=0, siz=4"` - Flags uint32 `bin:"off=4, siz=4"` - binstruct.End `bin:"off=8"` -} diff --git a/pkg/btrfs/btrfsitem/item_inode.go b/pkg/btrfs/btrfsitem/item_inode.go deleted file mode 100644 index b41f288..0000000 --- a/pkg/btrfs/btrfsitem/item_inode.go +++ /dev/null @@ -1,64 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" - "lukeshu.com/btrfs-tools/pkg/linux" - "lukeshu.com/btrfs-tools/pkg/util" -) - -type Inode struct { // INODE_ITEM=1 - Generation internal.Generation `bin:"off=0x00, siz=0x08"` - TransID int64 `bin:"off=0x08, siz=0x08"` - Size int64 `bin:"off=0x10, siz=0x08"` // stat - NumBytes int64 `bin:"off=0x18, siz=0x08"` - BlockGroup int64 `bin:"off=0x20, siz=0x08"` - NLink int32 `bin:"off=0x28, siz=0x04"` // stat - UID int32 `bin:"off=0x2C, siz=0x04"` // stat - GID int32 `bin:"off=0x30, siz=0x04"` // stat - Mode linux.StatMode `bin:"off=0x34, siz=0x04"` // stat - RDev int64 `bin:"off=0x38, siz=0x08"` // stat - Flags InodeFlags `bin:"off=0x40, siz=0x08"` // statx.stx_attributes, sorta - Sequence int64 `bin:"off=0x48, siz=0x08"` // NFS - Reserved [4]int64 `bin:"off=0x50, siz=0x20"` - ATime internal.Time `bin:"off=0x70, siz=0x0c"` // stat - CTime internal.Time `bin:"off=0x7c, siz=0x0c"` // stat - MTime internal.Time `bin:"off=0x88, siz=0x0c"` // stat - OTime internal.Time `bin:"off=0x94, siz=0x0c"` // statx.stx_btime (why is this called "otime" instead of "btime"?) - binstruct.End `bin:"off=0xa0"` -} - -type InodeFlags uint64 - -const ( - INODE_NODATASUM = InodeFlags(1 << iota) - INODE_NODATACOW - INODE_READONLY - INODE_NOCOMPRESS - INODE_PREALLOC - INODE_SYNC - INODE_IMMUTABLE - INODE_APPEND - INODE_NODUMP - INODE_NOATIME - INODE_DIRSYNC - INODE_COMPRESS -) - -var inodeFlagNames = []string{ - "NODATASUM", - "NODATACOW", - "READONLY", - "NOCOMPRESS", - "PREALLOC", - "SYNC", - "IMMUTABLE", - "APPEND", - "NODUMP", - "NOATIME", - "DIRSYNC", - "COMPRESS", -} - -func (f InodeFlags) Has(req InodeFlags) bool { return f&req == req } -func (f InodeFlags) String() string { return util.BitfieldString(f, inodeFlagNames, util.HexLower) } diff --git a/pkg/btrfs/btrfsitem/item_inoderef.go b/pkg/btrfs/btrfsitem/item_inoderef.go deleted file mode 100644 index 2c26df6..0000000 --- a/pkg/btrfs/btrfsitem/item_inoderef.go +++ /dev/null @@ -1,35 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" -) - -// key.objectid = inode number of the file -// key.offset = inode number of the parent file -type InodeRef struct { // INODE_REF=12 - Index int64 `bin:"off=0x0, siz=0x8"` - NameLen uint16 `bin:"off=0x8, siz=0x2"` // [ignored-when-writing] - binstruct.End `bin:"off=0xa"` - Name []byte `bin:"-"` -} - -func (o *InodeRef) UnmarshalBinary(dat []byte) (int, error) { - n, err := binstruct.UnmarshalWithoutInterface(dat, o) - if err != nil { - return n, err - } - dat = dat[n:] - o.Name = dat[:o.NameLen] - n += int(o.NameLen) - return n, nil -} - -func (o InodeRef) MarshalBinary() ([]byte, error) { - o.NameLen = uint16(len(o.Name)) - dat, err := binstruct.MarshalWithoutInterface(o) - if err != nil { - return dat, err - } - dat = append(dat, o.Name...) - return dat, nil -} diff --git a/pkg/btrfs/btrfsitem/item_metadata.go b/pkg/btrfs/btrfsitem/item_metadata.go deleted file mode 100644 index 7bfe677..0000000 --- a/pkg/btrfs/btrfsitem/item_metadata.go +++ /dev/null @@ -1,44 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" -) - -// Metadata is like Extent, but doesn't have .Info. -type Metadata struct { // METADATA_ITEM=169 - Head ExtentHeader - Refs []ExtentInlineRef -} - -func (o *Metadata) UnmarshalBinary(dat []byte) (int, error) { - n, err := binstruct.Unmarshal(dat, &o.Head) - if err != nil { - return n, err - } - o.Refs = nil - for n < len(dat) { - var ref ExtentInlineRef - _n, err := binstruct.Unmarshal(dat[n:], &ref) - n += _n - o.Refs = append(o.Refs, ref) - if err != nil { - return n, err - } - } - return n, nil -} - -func (o Metadata) MarshalBinary() ([]byte, error) { - dat, err := binstruct.Marshal(o.Head) - if err != nil { - return dat, err - } - for _, ref := range o.Refs { - bs, err := binstruct.Marshal(ref) - dat = append(dat, bs...) - if err != nil { - return dat, err - } - } - return dat, nil -} diff --git a/pkg/btrfs/btrfsitem/item_persistent.go b/pkg/btrfs/btrfsitem/item_persistent.go deleted file mode 100644 index fd69c3d..0000000 --- a/pkg/btrfs/btrfsitem/item_persistent.go +++ /dev/null @@ -1,19 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" -) - -const ( - DEV_STAT_WRITE_ERRS = iota - DEV_STAT_READ_ERRS - DEV_STAT_FLUSH_ERRS - DEV_STAT_CORRUPTION_ERRS - DEV_STAT_GENERATION_ERRS - DEV_STAT_VALUES_MAX -) - -type DevStats struct { // PERSISTENT_ITEM=249 - Values [DEV_STAT_VALUES_MAX]int64 `bin:"off=0, siz=40"` - binstruct.End `bin:"off=40"` -} diff --git a/pkg/btrfs/btrfsitem/item_root.go b/pkg/btrfs/btrfsitem/item_root.go deleted file mode 100644 index de0a070..0000000 --- a/pkg/btrfs/btrfsitem/item_root.go +++ /dev/null @@ -1,51 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" - "lukeshu.com/btrfs-tools/pkg/util" -) - -type Root struct { // ROOT_ITEM=132 - Inode Inode `bin:"off=0x000, siz=0xa0"` - Generation internal.Generation `bin:"off=0x0a0, siz=0x08"` - RootDirID internal.ObjID `bin:"off=0x0a8, siz=0x08"` - ByteNr btrfsvol.LogicalAddr `bin:"off=0x0b0, siz=0x08"` - ByteLimit int64 `bin:"off=0x0b8, siz=0x08"` - BytesUsed int64 `bin:"off=0x0c0, siz=0x08"` - LastSnapshot int64 `bin:"off=0x0c8, siz=0x08"` - Flags RootFlags `bin:"off=0x0d0, siz=0x08"` - Refs int32 `bin:"off=0x0d8, siz=0x04"` - DropProgress internal.Key `bin:"off=0x0dc, siz=0x11"` - DropLevel uint8 `bin:"off=0x0ed, siz=0x01"` - Level uint8 `bin:"off=0x0ee, siz=0x01"` - GenerationV2 internal.Generation `bin:"off=0x0ef, siz=0x08"` - UUID util.UUID `bin:"off=0x0f7, siz=0x10"` - ParentUUID util.UUID `bin:"off=0x107, siz=0x10"` - ReceivedUUID util.UUID `bin:"off=0x117, siz=0x10"` - CTransID int64 `bin:"off=0x127, siz=0x08"` - OTransID int64 `bin:"off=0x12f, siz=0x08"` - STransID int64 `bin:"off=0x137, siz=0x08"` - RTransID int64 `bin:"off=0x13f, siz=0x08"` - CTime internal.Time `bin:"off=0x147, siz=0x0c"` - OTime internal.Time `bin:"off=0x153, siz=0x0c"` - STime internal.Time `bin:"off=0x15f, siz=0x0c"` - RTime internal.Time `bin:"off=0x16b, siz=0x0c"` - GlobalTreeID internal.ObjID `bin:"off=0x177, siz=0x08"` - Reserved [7]int64 `bin:"off=0x17f, siz=0x38"` - binstruct.End `bin:"off=0x1b7"` -} - -type RootFlags uint64 - -const ( - ROOT_SUBVOL_RDONLY = RootFlags(1 << iota) -) - -var rootFlagNames = []string{ - "SUBVOL_RDONLY", -} - -func (f RootFlags) Has(req RootFlags) bool { return f&req == req } -func (f RootFlags) String() string { return util.BitfieldString(f, rootFlagNames, util.HexLower) } diff --git a/pkg/btrfs/btrfsitem/item_rootref.go b/pkg/btrfs/btrfsitem/item_rootref.go deleted file mode 100644 index 567e9c7..0000000 --- a/pkg/btrfs/btrfsitem/item_rootref.go +++ /dev/null @@ -1,34 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" -) - -type RootRef struct { // ROOT_REF=156 ROOT_BACKREF=144 - DirID internal.ObjID `bin:"off=0x00, siz=0x8"` - Sequence int64 `bin:"off=0x08, siz=0x8"` - NameLen uint16 `bin:"off=0x10, siz=0x2"` // [ignored-when-writing] - binstruct.End `bin:"off=0x12"` - Name []byte `bin:"-"` -} - -func (o *RootRef) UnmarshalBinary(dat []byte) (int, error) { - n, err := binstruct.UnmarshalWithoutInterface(dat, o) - if err != nil { - return n, err - } - o.Name = dat[n : n+int(o.NameLen)] - n += int(o.NameLen) - return n, nil -} - -func (o RootRef) MarshalBinary() ([]byte, error) { - o.NameLen = uint16(len(o.Name)) - dat, err := binstruct.MarshalWithoutInterface(o) - if err != nil { - return dat, err - } - dat = append(dat, o.Name...) - return dat, nil -} diff --git a/pkg/btrfs/btrfsitem/item_shareddataref.go b/pkg/btrfs/btrfsitem/item_shareddataref.go deleted file mode 100644 index 0d73231..0000000 --- a/pkg/btrfs/btrfsitem/item_shareddataref.go +++ /dev/null @@ -1,10 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" -) - -type SharedDataRef struct { // SHARED_DATA_REF=184 - Count int32 `bin:"off=0, siz=4"` - binstruct.End `bin:"off=4"` -} diff --git a/pkg/btrfs/btrfsitem/item_untyped.go b/pkg/btrfs/btrfsitem/item_untyped.go deleted file mode 100644 index f5348e1..0000000 --- a/pkg/btrfs/btrfsitem/item_untyped.go +++ /dev/null @@ -1,14 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" -) - -type FreeSpaceHeader struct { // UNTYPED=0:FREE_SPACE_OBJECTID - Location internal.Key `bin:"off=0x00, siz=0x11"` - Generation internal.Generation `bin:"off=0x11, siz=0x8"` - NumEntries int64 `bin:"off=0x19, siz=0x8"` - NumBitmaps int64 `bin:"off=0x21, siz=0x8"` - binstruct.End `bin:"off=0x29"` -} diff --git a/pkg/btrfs/btrfsitem/item_uuid.go b/pkg/btrfs/btrfsitem/item_uuid.go deleted file mode 100644 index 45543b3..0000000 --- a/pkg/btrfs/btrfsitem/item_uuid.go +++ /dev/null @@ -1,16 +0,0 @@ -package btrfsitem - -import ( - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" -) - -// The Key for this item is a UUID, and the item is a subvolume IDs -// that that UUID maps to. -// -// key.objectid = first half of UUID -// key.offset = second half of UUID -type UUIDMap struct { // UUID_SUBVOL=251 UUID_RECEIVED_SUBVOL=252 - ObjID internal.ObjID `bin:"off=0, siz=8"` - binstruct.End `bin:"off=8"` -} diff --git a/pkg/btrfs/btrfsitem/items.go b/pkg/btrfs/btrfsitem/items.go deleted file mode 100644 index 5ad3471..0000000 --- a/pkg/btrfs/btrfsitem/items.go +++ /dev/null @@ -1,77 +0,0 @@ -package btrfsitem - -import ( - "fmt" - "reflect" - - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfssum" - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" -) - -type Type = internal.ItemType - -type Item interface { - isItem() -} - -type Error struct { - Dat []byte - Err error -} - -func (Error) isItem() {} - -func (o Error) MarshalBinary() ([]byte, error) { - return o.Dat, nil -} - -func (o *Error) UnmarshalBinary(dat []byte) (int, error) { - o.Dat = dat - return len(dat), nil -} - -// Rather than returning a separate error value, return an Error item. -func UnmarshalItem(key internal.Key, csumType btrfssum.CSumType, dat []byte) Item { - var gotyp reflect.Type - if key.ItemType == UNTYPED_KEY { - var ok bool - gotyp, ok = untypedObjID2gotype[key.ObjectID] - if !ok { - return Error{ - Dat: dat, - Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v, ObjectID:%v}, dat): unknown object ID for untyped item", - key.ItemType, key.ObjectID), - } - } - } else { - var ok bool - gotyp, ok = keytype2gotype[key.ItemType] - if !ok { - return Error{ - Dat: dat, - Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v}, dat): unknown item type", key.ItemType), - } - } - } - retPtr := reflect.New(gotyp) - if csums, ok := retPtr.Interface().(*ExtentCSum); ok { - csums.ChecksumSize = csumType.Size() - } - n, err := binstruct.Unmarshal(dat, retPtr.Interface()) - if err != nil { - return Error{ - Dat: dat, - Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v}, dat): %w", key.ItemType, err), - } - - } - if n < len(dat) { - return Error{ - Dat: dat, - Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v}, dat): left over data: got %v bytes but only consumed %v", - key.ItemType, len(dat), n), - } - } - return retPtr.Elem().Interface().(Item) -} diff --git a/pkg/btrfs/btrfsitem/items.txt b/pkg/btrfs/btrfsitem/items.txt deleted file mode 100644 index 7898775..0000000 --- a/pkg/btrfs/btrfsitem/items.txt +++ /dev/null @@ -1,29 +0,0 @@ -BLOCK_GROUP_ITEM=192 BlockGroup -CHUNK_ITEM=228 Chunk -DEV_EXTENT=204 DevExtent -DEV_ITEM=216 Dev -DIR_INDEX=96 DirEntries -DIR_ITEM=84 DirEntries -EXTENT_CSUM=128 ExtentCSum -EXTENT_DATA=108 FileExtent -EXTENT_DATA_REF=178 ExtentDataRef -EXTENT_ITEM=168 Extent -FREE_SPACE_BITMAP=200 FreeSpaceBitmap -FREE_SPACE_EXTENT=199 Empty -FREE_SPACE_INFO=198 FreeSpaceInfo -INODE_ITEM=1 Inode -INODE_REF=12 InodeRef -METADATA_ITEM=169 Metadata -ORPHAN_ITEM=48 Empty -PERSISTENT_ITEM=249 DevStats -QGROUP_RELATION=246 Empty -ROOT_BACKREF=144 RootRef -ROOT_ITEM=132 Root -ROOT_REF=156 RootRef -SHARED_BLOCK_REF=182 Empty -SHARED_DATA_REF=184 SharedDataRef -TREE_BLOCK_REF=176 Empty -UNTYPED=0:FREE_SPACE_OBJECTID FreeSpaceHeader -UUID_RECEIVED_SUBVOL=252 UUIDMap -UUID_SUBVOL=251 UUIDMap -XATTR_ITEM=24 DirEntries diff --git a/pkg/btrfs/btrfsitem/items_gen.go b/pkg/btrfs/btrfsitem/items_gen.go deleted file mode 100644 index b5b64bd..0000000 --- a/pkg/btrfs/btrfsitem/items_gen.go +++ /dev/null @@ -1,97 +0,0 @@ -// Code generated by Make. DO NOT EDIT. - -package btrfsitem - -import ( - "reflect" - - "lukeshu.com/btrfs-tools/pkg/btrfs/internal" -) - -const ( - BLOCK_GROUP_ITEM_KEY = internal.BLOCK_GROUP_ITEM_KEY - CHUNK_ITEM_KEY = internal.CHUNK_ITEM_KEY - DEV_EXTENT_KEY = internal.DEV_EXTENT_KEY - DEV_ITEM_KEY = internal.DEV_ITEM_KEY - DIR_INDEX_KEY = internal.DIR_INDEX_KEY - DIR_ITEM_KEY = internal.DIR_ITEM_KEY - EXTENT_CSUM_KEY = internal.EXTENT_CSUM_KEY - EXTENT_DATA_KEY = internal.EXTENT_DATA_KEY - EXTENT_DATA_REF_KEY = internal.EXTENT_DATA_REF_KEY - EXTENT_ITEM_KEY = internal.EXTENT_ITEM_KEY - FREE_SPACE_BITMAP_KEY = internal.FREE_SPACE_BITMAP_KEY - FREE_SPACE_EXTENT_KEY = internal.FREE_SPACE_EXTENT_KEY - FREE_SPACE_INFO_KEY = internal.FREE_SPACE_INFO_KEY - INODE_ITEM_KEY = internal.INODE_ITEM_KEY - INODE_REF_KEY = internal.INODE_REF_KEY - METADATA_ITEM_KEY = internal.METADATA_ITEM_KEY - ORPHAN_ITEM_KEY = internal.ORPHAN_ITEM_KEY - PERSISTENT_ITEM_KEY = internal.PERSISTENT_ITEM_KEY - QGROUP_RELATION_KEY = internal.QGROUP_RELATION_KEY - ROOT_BACKREF_KEY = internal.ROOT_BACKREF_KEY - ROOT_ITEM_KEY = internal.ROOT_ITEM_KEY - ROOT_REF_KEY = internal.ROOT_REF_KEY - SHARED_BLOCK_REF_KEY = internal.SHARED_BLOCK_REF_KEY - SHARED_DATA_REF_KEY = internal.SHARED_DATA_REF_KEY - TREE_BLOCK_REF_KEY = internal.TREE_BLOCK_REF_KEY - UNTYPED_KEY = internal.UNTYPED_KEY - UUID_RECEIVED_SUBVOL_KEY = internal.UUID_RECEIVED_SUBVOL_KEY - UUID_SUBVOL_KEY = internal.UUID_SUBVOL_KEY - XATTR_ITEM_KEY = internal.XATTR_ITEM_KEY -) - -var keytype2gotype = map[Type]reflect.Type{ - BLOCK_GROUP_ITEM_KEY: reflect.TypeOf(BlockGroup{}), - CHUNK_ITEM_KEY: reflect.TypeOf(Chunk{}), - DEV_EXTENT_KEY: reflect.TypeOf(DevExtent{}), - DEV_ITEM_KEY: reflect.TypeOf(Dev{}), - DIR_INDEX_KEY: reflect.TypeOf(DirEntries{}), - DIR_ITEM_KEY: reflect.TypeOf(DirEntries{}), - EXTENT_CSUM_KEY: reflect.TypeOf(ExtentCSum{}), - EXTENT_DATA_KEY: reflect.TypeOf(FileExtent{}), - EXTENT_DATA_REF_KEY: reflect.TypeOf(ExtentDataRef{}), - EXTENT_ITEM_KEY: reflect.TypeOf(Extent{}), - FREE_SPACE_BITMAP_KEY: reflect.TypeOf(FreeSpaceBitmap{}), - FREE_SPACE_EXTENT_KEY: reflect.TypeOf(Empty{}), - FREE_SPACE_INFO_KEY: reflect.TypeOf(FreeSpaceInfo{}), - INODE_ITEM_KEY: reflect.TypeOf(Inode{}), - INODE_REF_KEY: reflect.TypeOf(InodeRef{}), - METADATA_ITEM_KEY: reflect.TypeOf(Metadata{}), - ORPHAN_ITEM_KEY: reflect.TypeOf(Empty{}), - PERSISTENT_ITEM_KEY: reflect.TypeOf(DevStats{}), - QGROUP_RELATION_KEY: reflect.TypeOf(Empty{}), - ROOT_BACKREF_KEY: reflect.TypeOf(RootRef{}), - ROOT_ITEM_KEY: reflect.TypeOf(Root{}), - ROOT_REF_KEY: reflect.TypeOf(RootRef{}), - SHARED_BLOCK_REF_KEY: reflect.TypeOf(Empty{}), - SHARED_DATA_REF_KEY: reflect.TypeOf(SharedDataRef{}), - TREE_BLOCK_REF_KEY: reflect.TypeOf(Empty{}), - UUID_RECEIVED_SUBVOL_KEY: reflect.TypeOf(UUIDMap{}), - UUID_SUBVOL_KEY: reflect.TypeOf(UUIDMap{}), - XATTR_ITEM_KEY: reflect.TypeOf(DirEntries{}), -} -var untypedObjID2gotype = map[internal.ObjID]reflect.Type{ - internal.FREE_SPACE_OBJECTID: reflect.TypeOf(FreeSpaceHeader{}), -} - -func (BlockGroup) isItem() {} -func (Chunk) isItem() {} -func (Dev) isItem() {} -func (DevExtent) isItem() {} -func (DevStats) isItem() {} -func (DirEntries) isItem() {} -func (Empty) isItem() {} -func (Extent) isItem() {} -func (ExtentCSum) isItem() {} -func (ExtentDataRef) isItem() {} -func (FileExtent) isItem() {} -func (FreeSpaceBitmap) isItem() {} -func (FreeSpaceHeader) isItem() {} -func (FreeSpaceInfo) isItem() {} -func (Inode) isItem() {} -func (InodeRef) isItem() {} -func (Metadata) isItem() {} -func (Root) isItem() {} -func (RootRef) isItem() {} -func (SharedDataRef) isItem() {} -func (UUIDMap) isItem() {} diff --git a/pkg/btrfs/btrfssum/csum.go b/pkg/btrfs/btrfssum/csum.go deleted file mode 100644 index 22d8149..0000000 --- a/pkg/btrfs/btrfssum/csum.go +++ /dev/null @@ -1,78 +0,0 @@ -package btrfssum - -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 ( - TYPE_CRC32 = CSumType(iota) - TYPE_XXHASH - TYPE_SHA256 - TYPE_BLAKE2 -) - -func (typ CSumType) String() string { - names := map[CSumType]string{ - TYPE_CRC32: "crc32c", - TYPE_XXHASH: "xxhash64", - TYPE_SHA256: "sha256", - 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{ - TYPE_CRC32: 4, - TYPE_XXHASH: 8, - TYPE_SHA256: 32, - 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 TYPE_CRC32: - crc := crc32.Update(0, crc32.MakeTable(crc32.Castagnoli), data) - - var ret CSum - binary.LittleEndian.PutUint32(ret[:], crc) - return ret, nil - case TYPE_XXHASH: - panic("not implemented") - case TYPE_SHA256: - panic("not implemented") - case TYPE_BLAKE2: - panic("not implemented") - default: - return CSum{}, fmt.Errorf("unknown checksum type: %v", typ) - } -} diff --git a/pkg/btrfs/btrfssum/csum_test.go b/pkg/btrfs/btrfssum/csum_test.go deleted file mode 100644 index c47409c..0000000 --- a/pkg/btrfs/btrfssum/csum_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package btrfssum_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfssum" -) - -func TestCSumFormat(t *testing.T) { - t.Parallel() - type TestCase struct { - InputSum btrfssum.CSum - InputFmt string - Output string - } - csum := btrfssum.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": {InputSum: csum, InputFmt: "%s", Output: "bd7b41f400000000000000000000000000000000000000000000000000000000"}, - "x": {InputSum: csum, InputFmt: "%x", Output: "bd7b41f400000000000000000000000000000000000000000000000000000000"}, - "v": {InputSum: csum, InputFmt: "%v", Output: "bd7b41f400000000000000000000000000000000000000000000000000000000"}, - "70s": {InputSum: csum, InputFmt: "|% 70s", Output: "| bd7b41f400000000000000000000000000000000000000000000000000000000"}, - "#180v": {InputSum: csum, InputFmt: "%#180v", Output: " btrfssum.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/btrfsvol/addr.go b/pkg/btrfs/btrfsvol/addr.go deleted file mode 100644 index b426611..0000000 --- a/pkg/btrfs/btrfsvol/addr.go +++ /dev/null @@ -1,47 +0,0 @@ -package btrfsvol - -import ( - "fmt" - - "lukeshu.com/btrfs-tools/pkg/util" -) - -type ( - PhysicalAddr int64 - LogicalAddr int64 - AddrDelta int64 -) - -func formatAddr(addr int64, f fmt.State, verb rune) { - switch verb { - case 'v', 's', 'q': - str := fmt.Sprintf("%#016x", addr) - fmt.Fprintf(f, util.FmtStateString(f, verb), str) - default: - fmt.Fprintf(f, util.FmtStateString(f, verb), addr) - } -} - -func (a PhysicalAddr) Format(f fmt.State, verb rune) { formatAddr(int64(a), f, verb) } -func (a LogicalAddr) Format(f fmt.State, verb rune) { formatAddr(int64(a), f, verb) } -func (d AddrDelta) Format(f fmt.State, verb rune) { formatAddr(int64(d), f, verb) } - -func (a PhysicalAddr) Sub(b PhysicalAddr) AddrDelta { return AddrDelta(a - b) } -func (a LogicalAddr) Sub(b LogicalAddr) AddrDelta { return AddrDelta(a - b) } - -func (a PhysicalAddr) Add(b AddrDelta) PhysicalAddr { return a + PhysicalAddr(b) } -func (a LogicalAddr) Add(b AddrDelta) LogicalAddr { return a + LogicalAddr(b) } - -type DeviceID uint64 - -type QualifiedPhysicalAddr struct { - Dev DeviceID - Addr PhysicalAddr -} - -func (a QualifiedPhysicalAddr) Cmp(b QualifiedPhysicalAddr) int { - if d := int(a.Dev - b.Dev); d != 0 { - return d - } - return int(a.Addr - b.Addr) -} diff --git a/pkg/btrfs/btrfsvol/addr_test.go b/pkg/btrfs/btrfsvol/addr_test.go deleted file mode 100644 index cfc5053..0000000 --- a/pkg/btrfs/btrfsvol/addr_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package btrfsvol_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" -) - -func TestAddrFormat(t *testing.T) { - t.Parallel() - type TestCase struct { - InputAddr btrfsvol.LogicalAddr - InputFmt string - Output string - } - addr := btrfsvol.LogicalAddr(0x3a41678000) - testcases := map[string]TestCase{ - "v": {InputAddr: addr, InputFmt: "%v", Output: "0x0000003a41678000"}, - "s": {InputAddr: addr, InputFmt: "%s", Output: "0x0000003a41678000"}, - "q": {InputAddr: addr, InputFmt: "%q", Output: `"0x0000003a41678000"`}, - "x": {InputAddr: addr, InputFmt: "%x", Output: "3a41678000"}, - "d": {InputAddr: addr, InputFmt: "%d", Output: "250205405184"}, - "neg": {InputAddr: -1, InputFmt: "%v", Output: "-0x000000000000001"}, - } - for tcName, tc := range testcases { - tc := tc - t.Run(tcName, func(t *testing.T) { - t.Parallel() - actual := fmt.Sprintf(tc.InputFmt, tc.InputAddr) - assert.Equal(t, tc.Output, actual) - }) - } -} diff --git a/pkg/btrfs/btrfsvol/blockgroupflags.go b/pkg/btrfs/btrfsvol/blockgroupflags.go deleted file mode 100644 index ba1610b..0000000 --- a/pkg/btrfs/btrfsvol/blockgroupflags.go +++ /dev/null @@ -1,51 +0,0 @@ -package btrfsvol - -import ( - "lukeshu.com/btrfs-tools/pkg/util" -) - -type BlockGroupFlags uint64 - -const ( - BLOCK_GROUP_DATA = BlockGroupFlags(1 << iota) - BLOCK_GROUP_SYSTEM - BLOCK_GROUP_METADATA - BLOCK_GROUP_RAID0 - BLOCK_GROUP_RAID1 - BLOCK_GROUP_DUP - BLOCK_GROUP_RAID10 - BLOCK_GROUP_RAID5 - BLOCK_GROUP_RAID6 - BLOCK_GROUP_RAID1C3 - BLOCK_GROUP_RAID1C4 - - BLOCK_GROUP_RAID_MASK = (BLOCK_GROUP_RAID1 | BLOCK_GROUP_DUP | BLOCK_GROUP_RAID10 | BLOCK_GROUP_RAID5 | BLOCK_GROUP_RAID6 | BLOCK_GROUP_RAID1C3 | BLOCK_GROUP_RAID1C4) -) - -var blockGroupFlagNames = []string{ - "DATA", - "SYSTEM", - "METADATA", - - "RAID0", - "RAID1", - "DUP", - "RAID10", - "RAID5", - "RAID6", - "RAID1C3", - "RAID1C4", -} - -func (f BlockGroupFlags) Has(req BlockGroupFlags) bool { return f&req == req } -func (f BlockGroupFlags) String() string { - ret := util.BitfieldString(f, blockGroupFlagNames, util.HexNone) - if f&BLOCK_GROUP_RAID_MASK == 0 { - if ret == "" { - ret = "single" - } else { - ret += "|single" - } - } - return ret -} diff --git a/pkg/btrfs/btrfsvol/chunk.go b/pkg/btrfs/btrfsvol/chunk.go deleted file mode 100644 index 8e5c4db..0000000 --- a/pkg/btrfs/btrfsvol/chunk.go +++ /dev/null @@ -1,96 +0,0 @@ -package btrfsvol - -import ( - "fmt" - "sort" - - "lukeshu.com/btrfs-tools/pkg/util" -) - -// logical => []physical -type chunkMapping struct { - LAddr LogicalAddr - PAddrs []QualifiedPhysicalAddr - Size AddrDelta - SizeLocked bool - Flags *BlockGroupFlags -} - -type ChunkMapping = chunkMapping - -// return -1 if 'a' is wholly to the left of 'b' -// return 0 if there is some overlap between 'a' and 'b' -// return 1 if 'a is wholly to the right of 'b' -func (a chunkMapping) cmpRange(b chunkMapping) int { - switch { - case a.LAddr.Add(a.Size) <= b.LAddr: - // 'a' is wholly to the left of 'b'. - return -1 - case b.LAddr.Add(b.Size) <= a.LAddr: - // 'a' is wholly to the right of 'b'. - return 1 - default: - // There is some overlap. - return 0 - } -} - -func (a chunkMapping) union(rest ...chunkMapping) (chunkMapping, error) { - // sanity check - for _, chunk := range rest { - if a.cmpRange(chunk) != 0 { - return chunkMapping{}, fmt.Errorf("chunks don't overlap") - } - } - chunks := append([]chunkMapping{a}, rest...) - // figure out the logical range (.LAddr and .Size) - beg := chunks[0].LAddr - end := chunks[0].LAddr.Add(chunks[0].Size) - for _, chunk := range chunks { - beg = util.Min(beg, chunk.LAddr) - end = util.Max(end, chunk.LAddr.Add(chunk.Size)) - } - ret := chunkMapping{ - LAddr: beg, - Size: end.Sub(beg), - } - for _, chunk := range chunks { - if chunk.SizeLocked { - ret.SizeLocked = true - if ret.Size != chunk.Size { - return chunkMapping{}, fmt.Errorf("member chunk has locked size=%v, but union would have size=%v", - chunk.Size, ret.Size) - } - } - } - // figure out the physical stripes (.PAddrs) - paddrs := make(map[QualifiedPhysicalAddr]struct{}) - for _, chunk := range chunks { - offsetWithinRet := chunk.LAddr.Sub(ret.LAddr) - for _, stripe := range chunk.PAddrs { - paddrs[QualifiedPhysicalAddr{ - Dev: stripe.Dev, - Addr: stripe.Addr.Add(-offsetWithinRet), - }] = struct{}{} - } - } - ret.PAddrs = util.MapKeys(paddrs) - sort.Slice(ret.PAddrs, func(i, j int) bool { - return ret.PAddrs[i].Cmp(ret.PAddrs[j]) < 0 - }) - // figure out the flags (.Flags) - for _, chunk := range chunks { - if chunk.Flags == nil { - continue - } - if ret.Flags == nil { - val := *chunk.Flags - ret.Flags = &val - } - if *ret.Flags != *chunk.Flags { - return ret, fmt.Errorf("mismatch flags: %v != %v", *ret.Flags, *chunk.Flags) - } - } - // done - return ret, nil -} diff --git a/pkg/btrfs/btrfsvol/devext.go b/pkg/btrfs/btrfsvol/devext.go deleted file mode 100644 index 8a69c7e..0000000 --- a/pkg/btrfs/btrfsvol/devext.go +++ /dev/null @@ -1,89 +0,0 @@ -package btrfsvol - -import ( - "fmt" - - "lukeshu.com/btrfs-tools/pkg/util" -) - -// physical => logical -type devextMapping struct { - PAddr PhysicalAddr - LAddr LogicalAddr - Size AddrDelta - SizeLocked bool - Flags *BlockGroupFlags -} - -// return -1 if 'a' is wholly to the left of 'b' -// return 0 if there is some overlap between 'a' and 'b' -// return 1 if 'a is wholly to the right of 'b' -func (a devextMapping) cmpRange(b devextMapping) int { - switch { - case a.PAddr.Add(a.Size) <= b.PAddr: - // 'a' is wholly to the left of 'b'. - return -1 - case b.PAddr.Add(b.Size) <= a.PAddr: - // 'a' is wholly to the right of 'b'. - return 1 - default: - // There is some overlap. - return 0 - } -} - -func (a devextMapping) union(rest ...devextMapping) (devextMapping, error) { - // sanity check - for _, ext := range rest { - if a.cmpRange(ext) != 0 { - return devextMapping{}, fmt.Errorf("devexts don't overlap") - } - } - exts := append([]devextMapping{a}, rest...) - // figure out the physical range (.PAddr and .Size) - beg := exts[0].PAddr - end := beg.Add(exts[0].Size) - for _, ext := range exts { - beg = util.Min(beg, ext.PAddr) - end = util.Max(end, ext.PAddr.Add(ext.Size)) - } - ret := devextMapping{ - PAddr: beg, - Size: end.Sub(beg), - } - for _, ext := range exts { - if ext.SizeLocked { - ret.SizeLocked = true - if ret.Size != ext.Size { - return devextMapping{}, fmt.Errorf("member devext has locked size=%v, but union would have size=%v", - ext.Size, ret.Size) - } - } - } - // figure out the logical range (.LAddr) - first := true - for _, ext := range exts { - offsetWithinRet := ext.PAddr.Sub(ret.PAddr) - laddr := ext.LAddr.Add(-offsetWithinRet) - if first { - ret.LAddr = laddr - } else if laddr != ret.LAddr { - return ret, fmt.Errorf("devexts don't agree on laddr: %v != %v", ret.LAddr, laddr) - } - } - // figure out the flags (.Flags) - for _, ext := range exts { - if ext.Flags == nil { - continue - } - if ret.Flags == nil { - val := *ext.Flags - ret.Flags = &val - } - if *ret.Flags != *ext.Flags { - return ret, fmt.Errorf("mismatch flags: %v != %v", *ret.Flags, *ext.Flags) - } - } - // done - return ret, nil -} diff --git a/pkg/btrfs/btrfsvol/lvm.go b/pkg/btrfs/btrfsvol/lvm.go deleted file mode 100644 index c05b9dd..0000000 --- a/pkg/btrfs/btrfsvol/lvm.go +++ /dev/null @@ -1,355 +0,0 @@ -package btrfsvol - -import ( - "bytes" - "fmt" - "os" - "reflect" - - "lukeshu.com/btrfs-tools/pkg/rbtree" - "lukeshu.com/btrfs-tools/pkg/util" -) - -type LogicalVolume[PhysicalVolume util.File[PhysicalAddr]] struct { - name string - - id2pv map[DeviceID]PhysicalVolume - - logical2physical *rbtree.Tree[LogicalAddr, chunkMapping] - physical2logical map[DeviceID]*rbtree.Tree[PhysicalAddr, devextMapping] -} - -var _ util.File[LogicalAddr] = (*LogicalVolume[util.File[PhysicalAddr]])(nil) - -func (lv *LogicalVolume[PhysicalVolume]) init() { - if lv.id2pv == nil { - lv.id2pv = make(map[DeviceID]PhysicalVolume) - } - if lv.logical2physical == nil { - lv.logical2physical = &rbtree.Tree[LogicalAddr, chunkMapping]{ - KeyFn: func(chunk chunkMapping) LogicalAddr { - return chunk.LAddr - }, - } - } - if lv.physical2logical == nil { - lv.physical2logical = make(map[DeviceID]*rbtree.Tree[PhysicalAddr, devextMapping], len(lv.id2pv)) - } - for devid := range lv.id2pv { - if _, ok := lv.physical2logical[devid]; !ok { - lv.physical2logical[devid] = &rbtree.Tree[PhysicalAddr, devextMapping]{ - KeyFn: func(ext devextMapping) PhysicalAddr { - return ext.PAddr - }, - } - } - } -} - -func (lv *LogicalVolume[PhysicalVolume]) SetName(name string) { - lv.name = name -} - -func (lv *LogicalVolume[PhysicalVolume]) Name() string { - return lv.name -} - -func (lv *LogicalVolume[PhysicalVolume]) Size() (LogicalAddr, error) { - lv.init() - lastChunk := lv.logical2physical.Max() - if lastChunk == nil { - return 0, nil - } - return lastChunk.Value.LAddr.Add(lastChunk.Value.Size), nil -} - -func (lv *LogicalVolume[PhysicalVolume]) AddPhysicalVolume(id DeviceID, dev PhysicalVolume) error { - lv.init() - if other, exists := lv.id2pv[id]; exists { - return fmt.Errorf("(%p).AddPhysicalVolume: cannot add physical volume %q: already have physical volume %q with id=%v", - lv, dev.Name(), other.Name(), id) - } - lv.id2pv[id] = dev - lv.physical2logical[id] = &rbtree.Tree[PhysicalAddr, devextMapping]{ - KeyFn: func(ext devextMapping) PhysicalAddr { - return ext.PAddr - }, - } - return nil -} - -func (lv *LogicalVolume[PhysicalVolume]) PhysicalVolumes() map[DeviceID]PhysicalVolume { - dup := make(map[DeviceID]PhysicalVolume, len(lv.id2pv)) - for k, v := range lv.id2pv { - dup[k] = v - } - return dup -} - -func (lv *LogicalVolume[PhysicalVolume]) ClearMappings() { - lv.logical2physical = nil - lv.physical2logical = nil -} - -type Mapping struct { - LAddr LogicalAddr - PAddr QualifiedPhysicalAddr - Size AddrDelta - SizeLocked bool - Flags *BlockGroupFlags -} - -func (lv *LogicalVolume[PhysicalVolume]) AddMapping(m Mapping) error { - lv.init() - // sanity check - if _, haveDev := lv.id2pv[m.PAddr.Dev]; !haveDev { - return fmt.Errorf("(%p).AddMapping: do not have a physical volume with id=%v", - lv, m.PAddr.Dev) - } - - // logical2physical - newChunk := chunkMapping{ - LAddr: m.LAddr, - PAddrs: []QualifiedPhysicalAddr{m.PAddr}, - Size: m.Size, - SizeLocked: m.SizeLocked, - Flags: m.Flags, - } - logicalOverlaps := lv.logical2physical.SearchRange(newChunk.cmpRange) - var err error - newChunk, err = newChunk.union(logicalOverlaps...) - if err != nil { - return fmt.Errorf("(%p).AddMapping: %w", lv, err) - } - - // physical2logical - newExt := devextMapping{ - PAddr: m.PAddr.Addr, - LAddr: m.LAddr, - Size: m.Size, - SizeLocked: m.SizeLocked, - Flags: m.Flags, - } - physicalOverlaps := lv.physical2logical[m.PAddr.Dev].SearchRange(newExt.cmpRange) - newExt, err = newExt.union(physicalOverlaps...) - if err != nil { - return fmt.Errorf("(%p).AddMapping: %w", lv, err) - } - - // optimize - if len(logicalOverlaps) == 1 && reflect.DeepEqual(newChunk, logicalOverlaps[0]) && - len(physicalOverlaps) == 1 && reflect.DeepEqual(newExt, physicalOverlaps[0]) { - return nil - } - - // logical2physical - for _, chunk := range logicalOverlaps { - lv.logical2physical.Delete(chunk.LAddr) - } - lv.logical2physical.Insert(newChunk) - - // physical2logical - for _, ext := range physicalOverlaps { - lv.physical2logical[m.PAddr.Dev].Delete(ext.PAddr) - } - lv.physical2logical[m.PAddr.Dev].Insert(newExt) - - // sanity check - // - // This is in-theory unnescessary, but that assumes that I - // made no mistakes in my algorithm above. - if os.Getenv("PARANOID") != "" { - if err := lv.fsck(); err != nil { - return err - } - } - - // done - return nil -} - -func (lv *LogicalVolume[PhysicalVolume]) fsck() error { - physical2logical := make(map[DeviceID]*rbtree.Tree[PhysicalAddr, devextMapping]) - if err := lv.logical2physical.Walk(func(node *rbtree.Node[chunkMapping]) error { - chunk := node.Value - for _, stripe := range chunk.PAddrs { - if _, devOK := lv.id2pv[stripe.Dev]; !devOK { - return fmt.Errorf("(%p).fsck: chunk references physical volume %v which does not exist", - lv, stripe.Dev) - } - if _, exists := physical2logical[stripe.Dev]; !exists { - physical2logical[stripe.Dev] = &rbtree.Tree[PhysicalAddr, devextMapping]{ - KeyFn: func(ext devextMapping) PhysicalAddr { - return ext.PAddr - }, - } - } - physical2logical[stripe.Dev].Insert(devextMapping{ - PAddr: stripe.Addr, - LAddr: chunk.LAddr, - Size: chunk.Size, - Flags: chunk.Flags, - }) - } - return nil - }); err != nil { - return err - } - - if len(lv.physical2logical) != len(physical2logical) { - return fmt.Errorf("(%p).fsck: skew between chunk tree and devext tree", - lv) - } - for devid := range lv.physical2logical { - if !lv.physical2logical[devid].Equal(physical2logical[devid]) { - return fmt.Errorf("(%p).fsck: skew between chunk tree and devext tree", - lv) - } - } - - return nil -} - -func (lv *LogicalVolume[PhysicalVolume]) Mappings() []Mapping { - var ret []Mapping - _ = lv.logical2physical.Walk(func(node *rbtree.Node[chunkMapping]) error { - chunk := node.Value - var flags *BlockGroupFlags - if chunk.Flags != nil { - val := *chunk.Flags - flags = &val - } - for _, slice := range chunk.PAddrs { - ret = append(ret, Mapping{ - LAddr: chunk.LAddr, - PAddr: slice, - Size: chunk.Size, - Flags: flags, - }) - } - return nil - }) - return ret -} - -func (lv *LogicalVolume[PhysicalVolume]) Resolve(laddr LogicalAddr) (paddrs map[QualifiedPhysicalAddr]struct{}, maxlen AddrDelta) { - node := lv.logical2physical.Search(func(chunk chunkMapping) int { - return chunkMapping{LAddr: laddr, Size: 1}.cmpRange(chunk) - }) - if node == nil { - return nil, 0 - } - - chunk := node.Value - - offsetWithinChunk := laddr.Sub(chunk.LAddr) - paddrs = make(map[QualifiedPhysicalAddr]struct{}) - maxlen = chunk.Size - offsetWithinChunk - for _, stripe := range chunk.PAddrs { - paddrs[QualifiedPhysicalAddr{ - Dev: stripe.Dev, - Addr: stripe.Addr.Add(offsetWithinChunk), - }] = struct{}{} - } - - return paddrs, maxlen -} - -func (lv *LogicalVolume[PhysicalVolume]) ResolveAny(laddr LogicalAddr, size AddrDelta) (LogicalAddr, QualifiedPhysicalAddr) { - node := lv.logical2physical.Search(func(chunk chunkMapping) int { - return chunkMapping{LAddr: laddr, Size: size}.cmpRange(chunk) - }) - if node == nil { - return -1, QualifiedPhysicalAddr{0, -1} - } - return node.Value.LAddr, node.Value.PAddrs[0] -} - -func (lv *LogicalVolume[PhysicalVolume]) UnResolve(paddr QualifiedPhysicalAddr) LogicalAddr { - node := lv.physical2logical[paddr.Dev].Search(func(ext devextMapping) int { - return devextMapping{PAddr: paddr.Addr, Size: 1}.cmpRange(ext) - }) - if node == nil { - return -1 - } - - ext := node.Value - - offsetWithinExt := paddr.Addr.Sub(ext.PAddr) - return ext.LAddr.Add(offsetWithinExt) -} - -func (lv *LogicalVolume[PhysicalVolume]) ReadAt(dat []byte, laddr LogicalAddr) (int, error) { - done := 0 - for done < len(dat) { - n, err := lv.maybeShortReadAt(dat[done:], laddr+LogicalAddr(done)) - done += n - if err != nil { - return done, err - } - } - return done, nil -} - -func (lv *LogicalVolume[PhysicalVolume]) maybeShortReadAt(dat []byte, laddr LogicalAddr) (int, error) { - paddrs, maxlen := lv.Resolve(laddr) - if len(paddrs) == 0 { - return 0, fmt.Errorf("read: could not map logical address %v", laddr) - } - if AddrDelta(len(dat)) > maxlen { - dat = dat[:maxlen] - } - - buf := make([]byte, len(dat)) - first := true - for paddr := range paddrs { - dev, ok := lv.id2pv[paddr.Dev] - if !ok { - return 0, fmt.Errorf("device=%v does not exist", paddr.Dev) - } - if _, err := dev.ReadAt(buf, paddr.Addr); err != nil { - return 0, fmt.Errorf("read device=%v paddr=%v: %w", paddr.Dev, paddr.Addr, err) - } - if first { - copy(dat, buf) - } else { - if !bytes.Equal(dat, buf) { - return 0, fmt.Errorf("inconsistent stripes at laddr=%v len=%v", laddr, len(dat)) - } - } - } - return len(dat), nil -} - -func (lv *LogicalVolume[PhysicalVolume]) WriteAt(dat []byte, laddr LogicalAddr) (int, error) { - done := 0 - for done < len(dat) { - n, err := lv.maybeShortWriteAt(dat[done:], laddr+LogicalAddr(done)) - done += n - if err != nil { - return done, err - } - } - return done, nil -} - -func (lv *LogicalVolume[PhysicalVolume]) maybeShortWriteAt(dat []byte, laddr LogicalAddr) (int, error) { - paddrs, maxlen := lv.Resolve(laddr) - if len(paddrs) == 0 { - return 0, fmt.Errorf("write: could not map logical address %v", laddr) - } - if AddrDelta(len(dat)) > maxlen { - dat = dat[:maxlen] - } - - for paddr := range paddrs { - dev, ok := lv.id2pv[paddr.Dev] - if !ok { - return 0, fmt.Errorf("device=%v does not exist", paddr.Dev) - } - if _, err := dev.WriteAt(dat, paddr.Addr); err != nil { - return 0, fmt.Errorf("write device=%v paddr=%v: %w", paddr.Dev, paddr.Addr, err) - } - } - return len(dat), nil -} diff --git a/pkg/btrfs/internal/itemtype.go b/pkg/btrfs/internal/itemtype.go deleted file mode 100644 index 60731aa..0000000 --- a/pkg/btrfs/internal/itemtype.go +++ /dev/null @@ -1,77 +0,0 @@ -// Code generated by Make. DO NOT EDIT. - -package internal - -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/pkg/btrfs/internal/misc.go b/pkg/btrfs/internal/misc.go deleted file mode 100644 index 3dc77d6..0000000 --- a/pkg/btrfs/internal/misc.go +++ /dev/null @@ -1,37 +0,0 @@ -package internal - -import ( - "time" - - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/util" -) - -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 (a Key) Cmp(b Key) int { - if d := util.CmpUint(a.ObjectID, b.ObjectID); d != 0 { - return d - } - if d := util.CmpUint(a.ItemType, b.ItemType); d != 0 { - return d - } - return util.CmpUint(a.Offset, b.Offset) -} - -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/pkg/btrfs/internal/objid.go b/pkg/btrfs/internal/objid.go deleted file mode 100644 index b3f09f6..0000000 --- a/pkg/btrfs/internal/objid.go +++ /dev/null @@ -1,144 +0,0 @@ -package internal - -import ( - "fmt" - - "lukeshu.com/btrfs-tools/pkg/util" -) - -type ObjID uint64 - -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(util.MaxUint64pp - 4) // for storing balance parameters in the root tree - ORPHAN_OBJECTID = ObjID(util.MaxUint64pp - 5) // orphan objectid for tracking unlinked/truncated files - TREE_LOG_OBJECTID = ObjID(util.MaxUint64pp - 6) // does write ahead logging to speed up fsyncs - TREE_LOG_FIXUP_OBJECTID = ObjID(util.MaxUint64pp - 7) - TREE_RELOC_OBJECTID = ObjID(util.MaxUint64pp - 8) // space balancing - DATA_RELOC_TREE_OBJECTID = ObjID(util.MaxUint64pp - 9) - EXTENT_CSUM_OBJECTID = ObjID(util.MaxUint64pp - 10) // extent checksums all have this objectid - FREE_SPACE_OBJECTID = ObjID(util.MaxUint64pp - 11) // For storing free space cache - FREE_INO_OBJECTID = ObjID(util.MaxUint64pp - 12) // stores the inode number for the free-ino cache - - MULTIPLE_OBJECTIDS = ObjID(util.MaxUint64pp - 255) // dummy objectid represents multiple objectids - - // All files have objectids in this range. - FIRST_FREE_OBJECTID = ObjID(256) - LAST_FREE_OBJECTID = ObjID(util.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/pkg/btrfs/io1_pv.go b/pkg/btrfs/io1_pv.go deleted file mode 100644 index b9f7ea4..0000000 --- a/pkg/btrfs/io1_pv.go +++ /dev/null @@ -1,96 +0,0 @@ -package btrfs - -import ( - "fmt" - "os" - - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" - "lukeshu.com/btrfs-tools/pkg/util" -) - -type Device struct { - *os.File - - cacheSuperblocks []*util.Ref[btrfsvol.PhysicalAddr, Superblock] - cacheSuperblock *util.Ref[btrfsvol.PhysicalAddr, Superblock] -} - -var _ util.File[btrfsvol.PhysicalAddr] = (*Device)(nil) - -func (dev Device) Size() (btrfsvol.PhysicalAddr, error) { - fi, err := dev.Stat() - if err != nil { - return 0, err - } - return btrfsvol.PhysicalAddr(fi.Size()), nil -} - -func (dev *Device) ReadAt(dat []byte, paddr btrfsvol.PhysicalAddr) (int, error) { - return dev.File.ReadAt(dat, int64(paddr)) -} - -func (dev *Device) WriteAt(dat []byte, paddr btrfsvol.PhysicalAddr) (int, error) { - return dev.File.WriteAt(dat, int64(paddr)) -} - -var SuperblockAddrs = []btrfsvol.PhysicalAddr{ - 0x00_0001_0000, // 64KiB - 0x00_0400_0000, // 64MiB - 0x40_0000_0000, // 256GiB -} - -func (dev *Device) Superblocks() ([]*util.Ref[btrfsvol.PhysicalAddr, Superblock], error) { - if dev.cacheSuperblocks != nil { - return dev.cacheSuperblocks, nil - } - superblockSize := btrfsvol.PhysicalAddr(binstruct.StaticSize(Superblock{})) - - sz, err := dev.Size() - if err != nil { - return nil, err - } - - var ret []*util.Ref[btrfsvol.PhysicalAddr, Superblock] - for i, addr := range SuperblockAddrs { - if addr+superblockSize <= sz { - superblock := &util.Ref[btrfsvol.PhysicalAddr, Superblock]{ - File: dev, - Addr: addr, - } - if err := superblock.Read(); err != nil { - return nil, fmt.Errorf("superblock %v: %w", i, err) - } - ret = append(ret, superblock) - } - } - if len(ret) == 0 { - return nil, fmt.Errorf("no superblocks") - } - dev.cacheSuperblocks = ret - return ret, nil -} - -func (dev *Device) Superblock() (*util.Ref[btrfsvol.PhysicalAddr, Superblock], error) { - if dev.cacheSuperblock != nil { - return dev.cacheSuperblock, nil - } - sbs, err := dev.Superblocks() - if err != nil { - return nil, err - } - - for i, sb := range sbs { - if err := sb.Data.ValidateChecksum(); err != nil { - return nil, fmt.Errorf("superblock %v: %w", i, err) - } - if i > 0 { - if !sb.Data.Equal(sbs[0].Data) { - return nil, fmt.Errorf("superblock %v and superblock %v disagree", 0, i) - } - } - } - - dev.cacheSuperblock = sbs[0] - return sbs[0], nil -} diff --git a/pkg/btrfs/io2_lv.go b/pkg/btrfs/io2_lv.go deleted file mode 100644 index 486a4ae..0000000 --- a/pkg/btrfs/io2_lv.go +++ /dev/null @@ -1,187 +0,0 @@ -package btrfs - -import ( - "fmt" - "io" - - "github.com/datawire/dlib/derror" - - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsitem" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" - "lukeshu.com/btrfs-tools/pkg/util" -) - -type FS struct { - // You should probably not access .LV directly, except when - // implementing special things like fsck. - LV btrfsvol.LogicalVolume[*Device] - - cacheSuperblocks []*util.Ref[btrfsvol.PhysicalAddr, Superblock] - cacheSuperblock *util.Ref[btrfsvol.PhysicalAddr, Superblock] -} - -var _ util.File[btrfsvol.LogicalAddr] = (*FS)(nil) - -func (fs *FS) AddDevice(dev *Device) error { - sb, err := dev.Superblock() - if err != nil { - return err - } - if err := fs.LV.AddPhysicalVolume(sb.Data.DevItem.DevID, dev); err != nil { - return err - } - fs.cacheSuperblocks = nil - fs.cacheSuperblock = nil - if err := fs.initDev(sb); err != nil { - return err - } - return nil -} - -func (fs *FS) Name() string { - if name := fs.LV.Name(); name != "" { - return name - } - sb, err := fs.Superblock() - if err != nil { - return fmt.Sprintf("fs_uuid=%v", "(unreadable)") - } - name := fmt.Sprintf("fs_uuid=%v", sb.Data.FSUUID) - fs.LV.SetName(name) - return name -} - -func (fs *FS) Size() (btrfsvol.LogicalAddr, error) { - return fs.LV.Size() -} - -func (fs *FS) ReadAt(p []byte, off btrfsvol.LogicalAddr) (int, error) { - return fs.LV.ReadAt(p, off) -} -func (fs *FS) WriteAt(p []byte, off btrfsvol.LogicalAddr) (int, error) { - return fs.LV.WriteAt(p, off) -} - -func (fs *FS) Resolve(laddr btrfsvol.LogicalAddr) (paddrs map[btrfsvol.QualifiedPhysicalAddr]struct{}, maxlen btrfsvol.AddrDelta) { - return fs.LV.Resolve(laddr) -} - -func (fs *FS) Superblocks() ([]*util.Ref[btrfsvol.PhysicalAddr, Superblock], error) { - if fs.cacheSuperblocks != nil { - return fs.cacheSuperblocks, nil - } - var ret []*util.Ref[btrfsvol.PhysicalAddr, Superblock] - devs := fs.LV.PhysicalVolumes() - if len(devs) == 0 { - return nil, fmt.Errorf("no devices") - } - for _, dev := range devs { - sbs, err := dev.Superblocks() - if err != nil { - return nil, fmt.Errorf("file %q: %w", dev.Name(), err) - } - ret = append(ret, sbs...) - } - fs.cacheSuperblocks = ret - return ret, nil -} - -func (fs *FS) Superblock() (*util.Ref[btrfsvol.PhysicalAddr, Superblock], error) { - if fs.cacheSuperblock != nil { - return fs.cacheSuperblock, nil - } - sbs, err := fs.Superblocks() - if err != nil { - return nil, err - } - if len(sbs) == 0 { - return nil, fmt.Errorf("no superblocks") - } - - fname := "" - sbi := 0 - for i, sb := range sbs { - if sb.File.Name() != fname { - fname = sb.File.Name() - sbi = 0 - } else { - sbi++ - } - - if err := sb.Data.ValidateChecksum(); err != nil { - return nil, fmt.Errorf("file %q superblock %v: %w", sb.File.Name(), sbi, err) - } - if i > 0 { - // FIXME(lukeshu): This is probably wrong, but - // lots of my multi-device code is probably - // wrong. - if !sb.Data.Equal(sbs[0].Data) { - return nil, fmt.Errorf("file %q superblock %v and file %q superblock %v disagree", - sbs[0].File.Name(), 0, - sb.File.Name(), sbi) - } - } - } - - fs.cacheSuperblock = sbs[0] - return sbs[0], nil -} - -func (fs *FS) ReInit() error { - fs.LV.ClearMappings() - for _, dev := range fs.LV.PhysicalVolumes() { - sb, err := dev.Superblock() - if err != nil { - return fmt.Errorf("file %q: %w", dev.Name(), err) - } - if err := fs.initDev(sb); err != nil { - return fmt.Errorf("file %q: %w", dev.Name(), err) - } - } - return nil -} - -func (fs *FS) initDev(sb *util.Ref[btrfsvol.PhysicalAddr, Superblock]) error { - syschunks, err := sb.Data.ParseSysChunkArray() - if err != nil { - return err - } - for _, chunk := range syschunks { - for _, mapping := range chunk.Chunk.Mappings(chunk.Key) { - if err := fs.LV.AddMapping(mapping); err != nil { - return err - } - } - } - if err := fs.TreeWalk(CHUNK_TREE_OBJECTID, TreeWalkHandler{ - Item: func(_ TreePath, item Item) error { - if item.Head.Key.ItemType != btrfsitem.CHUNK_ITEM_KEY { - return nil - } - for _, mapping := range item.Body.(btrfsitem.Chunk).Mappings(item.Head.Key) { - if err := fs.LV.AddMapping(mapping); err != nil { - return err - } - } - return nil - }, - }); err != nil { - return err - } - return nil -} - -func (fs *FS) Close() error { - var errs derror.MultiError - for _, dev := range fs.LV.PhysicalVolumes() { - if err := dev.Close(); err != nil && err == nil { - errs = append(errs, err) - } - } - if errs != nil { - return errs - } - return nil -} - -var _ io.Closer = (*FS)(nil) diff --git a/pkg/btrfs/io3_btree.go b/pkg/btrfs/io3_btree.go deleted file mode 100644 index be46d21..0000000 --- a/pkg/btrfs/io3_btree.go +++ /dev/null @@ -1,562 +0,0 @@ -package btrfs - -import ( - "errors" - "fmt" - "io" - iofs "io/fs" - "strings" - - "github.com/datawire/dlib/derror" - - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsitem" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" - "lukeshu.com/btrfs-tools/pkg/util" -) - -// - The first element will always have an ItemIdx of -1. -// -// - For .Item() callbacks, the last element will always have a -// NodeAddr of 0. -// -// For example, given the tree structure -// -// [superblock] -// | -// | <------------------------------------------ pathElem={idx:-1, addr:0x01, lvl:3} -// | -// +[0x01]-----------+ -// | lvl=3 | -// +-+-+-+-+-+-+-+-+-+ -// |1|2|3|4|5|6|7|8|9| -// +---+---+---+---+-+ -// | -// | <------------------------------ pathElem={idx:8, addr:0x02, lvl:2} -// | -// +[0x02]-----------+ -// | lvl=2 | -// +-+-+-+-+-+-+-+-+-+ -// |1|2|3|4|5|6|7|8|9| -// +---+---+---+---+-+ -// | -// | <-------------------- pathElem={idx:7, addr:0x03, lvl:1} -// | -// +[0x03]-----------+ -// | lvl=1 | -// +-+-+-+-+-+-+-+-+-+ -// |1|2|3|4|5|6|7|8|9| -// +---+---+---+---+-+ -// | -// | <---------------- pathElem={idx:4, addr:0x04, lvl:0} -// | -// +[0x04]-----------+ -// | lvl=0 | -// +-+-+-+-+-+-+-+-+-+ -// |1|2|3|4|5|6|7|8|9| -// +---+---+---+---+-+ -// | -// | <--------------- pathElem={idx:5, addr:0, lvl:0} -// | -// [item] -// -// the path would be -// -// {-1, 0x01, 3}→{8, 0x02, 2}→{7, 0x03, 1}→{4, 0x04, 0}→{2, 0, 0} -type TreePath []TreePathElem - -// A TreePathElem essentially represents a KeyPointer. -type TreePathElem struct { - // ItemIdx is the index of this KeyPointer in the parent Node; - // or -1 if this is the root and there is no KeyPointer. - ItemIdx int - // NodeAddr is the address of the node that the KeyPointer - // points at, or 0 if this is a leaf item and nothing is - // being pointed at. - NodeAddr btrfsvol.LogicalAddr - // NodeLevel is the expected or actual level of the node at - // NodeAddr. - NodeLevel uint8 -} - -func (elem TreePathElem) writeNodeTo(w io.Writer) { - fmt.Fprintf(w, "node:%d@%v", elem.NodeLevel, elem.NodeAddr) -} - -func (path TreePath) String() string { - if len(path) == 0 { - return "(empty-path)" - } - var ret strings.Builder - path[0].writeNodeTo(&ret) - for _, elem := range path[1:] { - fmt.Fprintf(&ret, "[%v]", elem.ItemIdx) - if elem.NodeAddr != 0 { - ret.WriteString("->") - elem.writeNodeTo(&ret) - } - } - return ret.String() -} - -// A treeRoot is more-or-less a btrfsitem.Root, but simpler and generalized for -type treeRoot struct { - TreeID ObjID - RootNode btrfsvol.LogicalAddr - Level uint8 - Generation Generation -} - -func (fs *FS) lookupTree(treeID ObjID) (*treeRoot, error) { - sb, err := fs.Superblock() - if err != nil { - return nil, err - } - switch treeID { - case ROOT_TREE_OBJECTID: - return &treeRoot{ - TreeID: treeID, - RootNode: sb.Data.RootTree, - Level: sb.Data.RootLevel, - Generation: sb.Data.Generation, // XXX: same generation as LOG_TREE? - }, nil - case CHUNK_TREE_OBJECTID: - return &treeRoot{ - TreeID: treeID, - RootNode: sb.Data.ChunkTree, - Level: sb.Data.ChunkLevel, - Generation: sb.Data.ChunkRootGeneration, - }, nil - case TREE_LOG_OBJECTID: - return &treeRoot{ - TreeID: treeID, - RootNode: sb.Data.LogTree, - Level: sb.Data.LogLevel, - Generation: sb.Data.Generation, // XXX: same generation as ROOT_TREE? - }, nil - case BLOCK_GROUP_TREE_OBJECTID: - return &treeRoot{ - TreeID: treeID, - RootNode: sb.Data.BlockGroupRoot, - Level: sb.Data.BlockGroupRootLevel, - Generation: sb.Data.BlockGroupRootGeneration, - }, nil - default: - rootItem, err := fs.TreeSearch(ROOT_TREE_OBJECTID, func(key Key) int { - if key.ObjectID == treeID && key.ItemType == btrfsitem.ROOT_ITEM_KEY { - return 0 - } - return Key{ - ObjectID: treeID, - ItemType: btrfsitem.ROOT_ITEM_KEY, - Offset: 0, - }.Cmp(key) - }) - if err != nil { - return nil, err - } - rootItemBody, ok := rootItem.Body.(btrfsitem.Root) - if !ok { - return nil, fmt.Errorf("malformed ROOT_ITEM for tree %v", treeID) - } - return &treeRoot{ - TreeID: treeID, - RootNode: rootItemBody.ByteNr, - Level: rootItemBody.Level, - Generation: rootItemBody.Generation, - }, nil - } -} - -type TreeWalkHandler struct { - // Callbacks for entire nodes - PreNode func(TreePath) error - Node func(TreePath, *util.Ref[btrfsvol.LogicalAddr, Node], error) error - PostNode func(TreePath, *util.Ref[btrfsvol.LogicalAddr, Node]) error - // Callbacks for items on internal nodes - PreKeyPointer func(TreePath, KeyPointer) error - PostKeyPointer func(TreePath, KeyPointer) error - // Callbacks for items on leaf nodes - Item func(TreePath, Item) error -} - -// The lifecycle of callbacks is: -// -// 001 .PreNode() -// 002 (read node) -// 003 .Node() -// for item in node.items: -// if internal: -// 004 .PreKeyPointer() -// 005 (recurse) -// 006 .PostKeyPointer() -// else: -// 004 .Item() -// 007 .PostNode() -func (fs *FS) TreeWalk(treeID ObjID, cbs TreeWalkHandler) error { - rootInfo, err := fs.lookupTree(treeID) - if err != nil { - return err - } - path := TreePath{ - TreePathElem{ - ItemIdx: -1, - NodeAddr: rootInfo.RootNode, - NodeLevel: rootInfo.Level, - }, - } - return fs.treeWalk(path, cbs) -} - -func (fs *FS) treeWalk(path TreePath, cbs TreeWalkHandler) error { - if path[len(path)-1].NodeAddr == 0 { - return nil - } - - if cbs.PreNode != nil { - if err := cbs.PreNode(path); err != nil { - if errors.Is(err, iofs.SkipDir) { - return nil - } - return err - } - } - node, err := fs.readNodeAtLevel(path[len(path)-1].NodeAddr, path[len(path)-1].NodeLevel) - if node != nil && err == nil { - path[len(path)-1].NodeLevel = node.Data.Head.Level - } - if cbs.Node != nil { - err = cbs.Node(path, node, err) - } - if err != nil { - if errors.Is(err, iofs.SkipDir) { - return nil - } - return fmt.Errorf("btrfs.FS.TreeWalk: %w", err) - } - if node != nil { - for i, item := range node.Data.BodyInternal { - itemPath := append(path, TreePathElem{ - ItemIdx: i, - NodeAddr: item.BlockPtr, - NodeLevel: node.Data.Head.Level - 1, - }) - if cbs.PreKeyPointer != nil { - if err := cbs.PreKeyPointer(itemPath, item); err != nil { - if errors.Is(err, iofs.SkipDir) { - continue - } - return err - } - } - if err := fs.treeWalk(itemPath, cbs); err != nil { - return err - } - if cbs.PostKeyPointer != nil { - if err := cbs.PostKeyPointer(itemPath, item); err != nil { - if errors.Is(err, iofs.SkipDir) { - continue - } - return err - } - } - } - for i, item := range node.Data.BodyLeaf { - if cbs.Item != nil { - itemPath := append(path, TreePathElem{ - ItemIdx: i, - }) - if err := cbs.Item(itemPath, item); err != nil { - if errors.Is(err, iofs.SkipDir) { - continue - } - return fmt.Errorf("btrfs.FS.TreeWalk: callback: %w", err) - } - } - } - } - if cbs.PostNode != nil { - if err := cbs.PostNode(path, node); err != nil { - if errors.Is(err, iofs.SkipDir) { - return nil - } - return err - } - } - return nil -} - -func (fs *FS) treeSearch(treeRoot treeRoot, fn func(Key) int) (TreePath, *util.Ref[btrfsvol.LogicalAddr, Node], error) { - path := TreePath{ - TreePathElem{ - ItemIdx: -1, - NodeAddr: treeRoot.RootNode, - NodeLevel: treeRoot.Level, - }, - } - for { - if path[len(path)-1].NodeAddr == 0 { - return nil, nil, iofs.ErrNotExist - } - node, err := fs.readNodeAtLevel(path[len(path)-1].NodeAddr, path[len(path)-1].NodeLevel) - if err != nil { - return nil, nil, err - } - path[len(path)-1].NodeLevel = node.Data.Head.Level - - if node.Data.Head.Level > 0 { - // internal node - - // Search for the right-most node.Data.BodyInternal item for which - // `fn(item.Key) >= 0`. - // - // + + + + 0 - - - - - // - // There may or may not be a value that returns '0'. - // - // Implement this search as a binary search. - lastGood := -1 - firstBad := len(node.Data.BodyInternal) - for firstBad > lastGood+1 { - midpoint := (lastGood + firstBad) / 2 - direction := fn(node.Data.BodyInternal[midpoint].Key) - if direction < 0 { - firstBad = midpoint - } else { - lastGood = midpoint - } - } - if lastGood < 0 { - return nil, nil, iofs.ErrNotExist - } - path = append(path, TreePathElem{ - ItemIdx: lastGood, - NodeAddr: node.Data.BodyInternal[lastGood].BlockPtr, - NodeLevel: node.Data.Head.Level - 1, - }) - } else { - // leaf node - - // Search for a member of node.Data.BodyLeaf for which - // `fn(item.Head.Key) == 0`. - // - // + + + + 0 - - - - - // - // Such an item might not exist; in this case, return nil/ErrNotExist. - // Multiple such items might exist; in this case, it does not matter which - // is returned. - // - // Implement this search as a binary search. - beg := 0 - end := len(node.Data.BodyLeaf) - for beg < end { - midpoint := (beg + end) / 2 - direction := fn(node.Data.BodyLeaf[midpoint].Head.Key) - switch { - case direction < 0: - end = midpoint - case direction > 0: - beg = midpoint + 1 - case direction == 0: - path = append(path, TreePathElem{ - ItemIdx: midpoint, - }) - return path, node, nil - } - } - return nil, nil, iofs.ErrNotExist - } - } -} - -func (fs *FS) prev(path TreePath, node *util.Ref[btrfsvol.LogicalAddr, Node]) (TreePath, *util.Ref[btrfsvol.LogicalAddr, Node], error) { - var err error - path = append(TreePath(nil), path...) - - // go up - for path[len(path)-1].ItemIdx < 1 { - path = path[:len(path)-1] - if len(path) == 0 { - return nil, nil, nil - } - } - // go left - path[len(path)-1].ItemIdx-- - if path[len(path)-1].NodeAddr != 0 { - if node.Addr != path[len(path)-2].NodeAddr { - node, err = fs.readNodeAtLevel(path[len(path)-2].NodeAddr, path[len(path)-2].NodeLevel) - if err != nil { - return nil, nil, err - } - path[len(path)-1].NodeAddr = node.Data.BodyInternal[path[len(path)-1].ItemIdx].BlockPtr - } - } - // go down - for path[len(path)-1].NodeAddr != 0 { - if node.Addr != path[len(path)-1].NodeAddr { - node, err = fs.readNodeAtLevel(path[len(path)-1].NodeAddr, path[len(path)-1].NodeLevel) - if err != nil { - return nil, nil, err - } - } - if node.Data.Head.Level > 0 { - path = append(path, TreePathElem{ - ItemIdx: len(node.Data.BodyInternal) - 1, - NodeAddr: node.Data.BodyInternal[len(node.Data.BodyInternal)-1].BlockPtr, - NodeLevel: node.Data.Head.Level - 1, - }) - } else { - path = append(path, TreePathElem{ - ItemIdx: len(node.Data.BodyLeaf) - 1, - }) - } - } - // return - if node.Addr != path[len(path)-2].NodeAddr { - node, err = fs.readNodeAtLevel(path[len(path)-2].NodeAddr, path[len(path)-2].NodeLevel) - if err != nil { - return nil, nil, err - } - } - return path, node, nil -} - -func (fs *FS) next(path TreePath, node *util.Ref[btrfsvol.LogicalAddr, Node]) (TreePath, *util.Ref[btrfsvol.LogicalAddr, Node], error) { - var err error - path = append(TreePath(nil), path...) - - // go up - if node.Addr != path[len(path)-2].NodeAddr { - node, err = fs.readNodeAtLevel(path[len(path)-2].NodeAddr, path[len(path)-2].NodeLevel) - if err != nil { - return nil, nil, err - } - path[len(path)-2].NodeLevel = node.Data.Head.Level - } - for path[len(path)-1].ItemIdx+1 >= int(node.Data.Head.NumItems) { - path = path[:len(path)-1] - if len(path) == 1 { - return nil, nil, nil - } - if node.Addr != path[len(path)-2].NodeAddr { - node, err = fs.readNodeAtLevel(path[len(path)-2].NodeAddr, path[len(path)-2].NodeLevel) - if err != nil { - return nil, nil, err - } - path[len(path)-2].NodeLevel = node.Data.Head.Level - } - } - // go left - path[len(path)-1].ItemIdx++ - if path[len(path)-1].NodeAddr != 0 { - if node.Addr != path[len(path)-2].NodeAddr { - node, err = fs.readNodeAtLevel(path[len(path)-2].NodeAddr, path[len(path)-2].NodeLevel) - if err != nil { - return nil, nil, err - } - path[len(path)-1].NodeAddr = node.Data.BodyInternal[path[len(path)-1].ItemIdx].BlockPtr - } - } - // go down - for path[len(path)-1].NodeAddr != 0 { - if node.Addr != path[len(path)-1].NodeAddr { - node, err = fs.readNodeAtLevel(path[len(path)-1].NodeAddr, path[len(path)-1].NodeLevel) - if err != nil { - return nil, nil, err - } - path[len(path)-1].NodeLevel = node.Data.Head.Level - } - if node.Data.Head.Level > 0 { - path = append(path, TreePathElem{ - ItemIdx: 0, - NodeAddr: node.Data.BodyInternal[len(node.Data.BodyInternal)-1].BlockPtr, - NodeLevel: node.Data.Head.Level - 1, - }) - } else { - path = append(path, TreePathElem{ - ItemIdx: 0, - }) - } - } - // return - if node.Addr != path[len(path)-2].NodeAddr { - node, err = fs.readNodeAtLevel(path[len(path)-2].NodeAddr, path[len(path)-2].NodeLevel) - if err != nil { - return nil, nil, err - } - } - return path, node, nil -} - -func (fs *FS) TreeSearch(treeID ObjID, fn func(Key) int) (Item, error) { - rootInfo, err := fs.lookupTree(treeID) - if err != nil { - return Item{}, err - } - path, node, err := fs.treeSearch(*rootInfo, fn) - if err != nil { - return Item{}, err - } - return node.Data.BodyLeaf[path[len(path)-1].ItemIdx], nil -} - -func (fs *FS) TreeLookup(treeID ObjID, key Key) (Item, error) { - item, err := fs.TreeSearch(treeID, key.Cmp) - if err != nil { - err = fmt.Errorf("item with key=%v: %w", key, err) - } - return item, err -} - -// If some items are able to be read, but there is an error reading the full set, then it might -// return *both* a list of items and an error. -// -// If no such item is found, an error that is io/fs.ErrNotExist is returned. -func (fs *FS) TreeSearchAll(treeID ObjID, fn func(Key) int) ([]Item, error) { - rootInfo, err := fs.lookupTree(treeID) - if err != nil { - return nil, err - } - middlePath, middleNode, err := fs.treeSearch(*rootInfo, fn) - if err != nil { - return nil, err - } - middleItem := middleNode.Data.BodyLeaf[middlePath[len(middlePath)-1].ItemIdx] - - var ret = []Item{middleItem} - var errs derror.MultiError - for prevPath, prevNode := middlePath, middleNode; true; { - prevPath, prevNode, err = fs.prev(prevPath, prevNode) - if err != nil { - errs = append(errs, err) - break - } - if prevPath == nil { - break - } - prevItem := prevNode.Data.BodyLeaf[prevPath[len(prevPath)-1].ItemIdx] - if fn(prevItem.Head.Key) != 0 { - break - } - ret = append(ret, prevItem) - } - util.ReverseSlice(ret) - for nextPath, nextNode := middlePath, middleNode; true; { - nextPath, nextNode, err = fs.next(nextPath, nextNode) - if err != nil { - errs = append(errs, err) - break - } - if nextPath == nil { - break - } - nextItem := nextNode.Data.BodyLeaf[nextPath[len(nextPath)-1].ItemIdx] - if fn(nextItem.Head.Key) != 0 { - break - } - ret = append(ret, nextItem) - } - if errs != nil { - err = errs - } - return ret, err -} diff --git a/pkg/btrfs/io4_fs.go b/pkg/btrfs/io4_fs.go deleted file mode 100644 index 18ae42a..0000000 --- a/pkg/btrfs/io4_fs.go +++ /dev/null @@ -1,404 +0,0 @@ -package btrfs - -import ( - "fmt" - "io" - "path/filepath" - "reflect" - "sort" - "sync" - - "github.com/datawire/dlib/derror" - - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsitem" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" - "lukeshu.com/btrfs-tools/pkg/util" -) - -type BareInode struct { - Inode ObjID - InodeItem *btrfsitem.Inode - Errs derror.MultiError -} - -type FullInode struct { - BareInode - OtherItems []Item -} - -type InodeRef struct { - Inode ObjID - btrfsitem.InodeRef -} - -type Dir struct { - FullInode - DotDot *InodeRef - ChildrenByName map[string]btrfsitem.DirEntry - ChildrenByIndex map[uint64]btrfsitem.DirEntry - SV *Subvolume -} - -type FileExtent struct { - OffsetWithinFile int64 - btrfsitem.FileExtent -} - -type File struct { - FullInode - Extents []FileExtent - SV *Subvolume -} - -type Subvolume struct { - FS *FS - TreeID ObjID - - rootOnce sync.Once - rootVal btrfsitem.Root - rootErr error - - bareInodeCache util.LRUCache[ObjID, *BareInode] - fullInodeCache util.LRUCache[ObjID, *FullInode] - dirCache util.LRUCache[ObjID, *Dir] - fileCache util.LRUCache[ObjID, *File] -} - -func (sv *Subvolume) init() { - sv.rootOnce.Do(func() { - root, err := sv.FS.TreeLookup(ROOT_TREE_OBJECTID, Key{ - ObjectID: sv.TreeID, - ItemType: btrfsitem.ROOT_ITEM_KEY, - Offset: 0, - }) - if err != nil { - sv.rootErr = err - return - } - - rootBody, ok := root.Body.(btrfsitem.Root) - if !ok { - sv.rootErr = fmt.Errorf("FS_TREE ROOT_ITEM has malformed body") - return - } - - sv.rootVal = rootBody - }) -} - -func (sv *Subvolume) GetRootInode() (ObjID, error) { - sv.init() - return sv.rootVal.RootDirID, sv.rootErr -} - -func (sv *Subvolume) LoadBareInode(inode ObjID) (*BareInode, error) { - val := sv.bareInodeCache.GetOrElse(inode, func() (val *BareInode) { - val = &BareInode{ - Inode: inode, - } - item, err := sv.FS.TreeLookup(sv.TreeID, Key{ - ObjectID: inode, - ItemType: btrfsitem.INODE_ITEM_KEY, - Offset: 0, - }) - if err != nil { - val.Errs = append(val.Errs, err) - return - } - - itemBody, ok := item.Body.(btrfsitem.Inode) - if !ok { - val.Errs = append(val.Errs, fmt.Errorf("malformed inode")) - return - } - val.InodeItem = &itemBody - - return - }) - if val.InodeItem == nil { - return nil, val.Errs - } - return val, nil -} - -func (sv *Subvolume) LoadFullInode(inode ObjID) (*FullInode, error) { - val := sv.fullInodeCache.GetOrElse(inode, func() (val *FullInode) { - val = &FullInode{ - BareInode: BareInode{ - Inode: inode, - }, - } - items, err := sv.FS.TreeSearchAll(sv.TreeID, func(key Key) int { - return util.CmpUint(inode, key.ObjectID) - }) - if err != nil { - val.Errs = append(val.Errs, err) - if len(items) == 0 { - return - } - } - for _, item := range items { - switch item.Head.Key.ItemType { - case btrfsitem.INODE_ITEM_KEY: - itemBody := item.Body.(btrfsitem.Inode) - if val.InodeItem != nil { - if !reflect.DeepEqual(itemBody, *val.InodeItem) { - val.Errs = append(val.Errs, fmt.Errorf("multiple inodes")) - } - continue - } - val.InodeItem = &itemBody - default: - val.OtherItems = append(val.OtherItems, item) - } - } - return - }) - if val.InodeItem == nil && val.OtherItems == nil { - return nil, val.Errs - } - return val, nil -} - -func (sv *Subvolume) LoadDir(inode ObjID) (*Dir, error) { - val := sv.dirCache.GetOrElse(inode, func() (val *Dir) { - val = new(Dir) - fullInode, err := sv.LoadFullInode(inode) - if err != nil { - val.Errs = append(val.Errs, err) - return - } - val.FullInode = *fullInode - val.SV = sv - val.populate() - return - }) - if val.Inode == 0 { - return nil, val.Errs - } - return val, nil -} - -func (ret *Dir) populate() { - ret.ChildrenByName = make(map[string]btrfsitem.DirEntry) - ret.ChildrenByIndex = make(map[uint64]btrfsitem.DirEntry) - for _, item := range ret.OtherItems { - switch item.Head.Key.ItemType { - case btrfsitem.INODE_REF_KEY: - ref := InodeRef{ - Inode: ObjID(item.Head.Key.Offset), - InodeRef: item.Body.(btrfsitem.InodeRef), - } - if ret.DotDot != nil { - if !reflect.DeepEqual(ref, *ret.DotDot) { - ret.Errs = append(ret.Errs, fmt.Errorf("multiple INODE_REF items on a directory")) - } - continue - } - ret.DotDot = &ref - case btrfsitem.DIR_ITEM_KEY: - body := item.Body.(btrfsitem.DirEntries) - if len(body) != 1 { - ret.Errs = append(ret.Errs, fmt.Errorf("multiple direntries in single DIR_ITEM?")) - continue - } - for _, entry := range body { - namehash := btrfsitem.NameHash(entry.Name) - if namehash != item.Head.Key.Offset { - ret.Errs = append(ret.Errs, fmt.Errorf("direntry crc32c mismatch: key=%#x crc32c(%q)=%#x", - item.Head.Key.Offset, entry.Name, namehash)) - continue - } - if other, exists := ret.ChildrenByName[string(entry.Name)]; exists { - if !reflect.DeepEqual(entry, other) { - ret.Errs = append(ret.Errs, fmt.Errorf("multiple instances of direntry name %q", entry.Name)) - } - continue - } - ret.ChildrenByName[string(entry.Name)] = entry - } - case btrfsitem.DIR_INDEX_KEY: - index := item.Head.Key.Offset - body := item.Body.(btrfsitem.DirEntries) - if len(body) != 1 { - ret.Errs = append(ret.Errs, fmt.Errorf("multiple direntries in single DIR_INDEX?")) - continue - } - for _, entry := range body { - if other, exists := ret.ChildrenByIndex[index]; exists { - if !reflect.DeepEqual(entry, other) { - ret.Errs = append(ret.Errs, fmt.Errorf("multiple instances of direntry index %v", index)) - } - continue - } - ret.ChildrenByIndex[index] = entry - } - //case btrfsitem.XATTR_ITEM_KEY: - default: - panic(fmt.Errorf("TODO: handle item type %v", item.Head.Key.ItemType)) - } - } - entriesWithIndexes := make(map[string]struct{}) - nextIndex := uint64(2) - for index, entry := range ret.ChildrenByIndex { - if index+1 > nextIndex { - nextIndex = index + 1 - } - entriesWithIndexes[string(entry.Name)] = struct{}{} - if other, exists := ret.ChildrenByName[string(entry.Name)]; !exists { - ret.Errs = append(ret.Errs, fmt.Errorf("missing by-name direntry for %q", entry.Name)) - ret.ChildrenByName[string(entry.Name)] = entry - } else if !reflect.DeepEqual(entry, other) { - ret.Errs = append(ret.Errs, fmt.Errorf("direntry index %v and direntry name %q disagree", index, entry.Name)) - ret.ChildrenByName[string(entry.Name)] = entry - } - } - for _, name := range util.SortedMapKeys(ret.ChildrenByName) { - if _, exists := entriesWithIndexes[name]; !exists { - ret.Errs = append(ret.Errs, fmt.Errorf("missing by-index direntry for %q", name)) - ret.ChildrenByIndex[nextIndex] = ret.ChildrenByName[name] - nextIndex++ - } - } -} - -func (dir *Dir) AbsPath() (string, error) { - rootInode, err := dir.SV.GetRootInode() - if err != nil { - return "", err - } - if rootInode == dir.Inode { - return "/", nil - } - if dir.DotDot == nil { - return "", fmt.Errorf("missing .. entry in dir inode %v", dir.Inode) - } - parent, err := dir.SV.LoadDir(dir.DotDot.Inode) - if err != nil { - return "", err - } - parentName, err := parent.AbsPath() - if err != nil { - return "", err - } - return filepath.Join(parentName, string(dir.DotDot.Name)), nil -} - -func (sv *Subvolume) LoadFile(inode ObjID) (*File, error) { - val := sv.fileCache.GetOrElse(inode, func() (val *File) { - val = new(File) - fullInode, err := sv.LoadFullInode(inode) - if err != nil { - val.Errs = append(val.Errs, err) - return - } - val.FullInode = *fullInode - val.SV = sv - val.populate() - return - }) - if val.Inode == 0 { - return nil, val.Errs - } - return val, nil -} - -func (ret *File) populate() { - for _, item := range ret.OtherItems { - switch item.Head.Key.ItemType { - case btrfsitem.INODE_REF_KEY: - // TODO - case btrfsitem.EXTENT_DATA_KEY: - ret.Extents = append(ret.Extents, FileExtent{ - OffsetWithinFile: int64(item.Head.Key.Offset), - FileExtent: item.Body.(btrfsitem.FileExtent), - }) - default: - panic(fmt.Errorf("TODO: handle item type %v", item.Head.Key.ItemType)) - } - } - - // These should already be sorted, because of the nature of - // the btree; but this is a recovery tool for corrupt - // filesystems, so go ahead and ensure that it's sorted. - sort.Slice(ret.Extents, func(i, j int) bool { - return ret.Extents[i].OffsetWithinFile < ret.Extents[j].OffsetWithinFile - }) - - pos := int64(0) - for _, extent := range ret.Extents { - if extent.OffsetWithinFile != pos { - if extent.OffsetWithinFile > pos { - ret.Errs = append(ret.Errs, fmt.Errorf("extent gap from %v to %v", - pos, extent.OffsetWithinFile)) - } else { - ret.Errs = append(ret.Errs, fmt.Errorf("extent overlap from %v to %v", - extent.OffsetWithinFile, pos)) - } - } - size, err := extent.Size() - if err != nil { - ret.Errs = append(ret.Errs, fmt.Errorf("extent %v: %w", extent.OffsetWithinFile, err)) - } - pos += size - } - if ret.InodeItem != nil && pos != ret.InodeItem.Size { - if ret.InodeItem.Size > pos { - ret.Errs = append(ret.Errs, fmt.Errorf("extent gap from %v to %v", - pos, ret.InodeItem.Size)) - } else { - ret.Errs = append(ret.Errs, fmt.Errorf("extent mapped past end of file from %v to %v", - ret.InodeItem.Size, pos)) - } - } -} - -func (file *File) ReadAt(dat []byte, off int64) (int, error) { - // These stateles maybe-short-reads each do an O(n) extent - // lookup, so reading a file is O(n^2), but we expect n to be - // small, so whatev. Turn file.Extents it in to an rbtree if - // it becomes a problem. - done := 0 - for done < len(dat) { - n, err := file.maybeShortReadAt(dat[done:], off+int64(done)) - done += n - if err != nil { - return done, err - } - } - return done, nil -} - -func (file *File) maybeShortReadAt(dat []byte, off int64) (int, error) { - for _, extent := range file.Extents { - extBeg := extent.OffsetWithinFile - if extBeg > off { - break - } - extLen, err := extent.Size() - if err != nil { - continue - } - extEnd := extBeg + extLen - if extEnd <= off { - continue - } - offsetWithinExt := off - extent.OffsetWithinFile - readSize := util.Min(int64(len(dat)), extLen-offsetWithinExt) - switch extent.Type { - case btrfsitem.FILE_EXTENT_INLINE: - return copy(dat, extent.BodyInline[offsetWithinExt:offsetWithinExt+readSize]), nil - case btrfsitem.FILE_EXTENT_REG, btrfsitem.FILE_EXTENT_PREALLOC: - return file.SV.FS.ReadAt(dat[:readSize], - extent.BodyExtent.DiskByteNr. - Add(extent.BodyExtent.Offset). - Add(btrfsvol.AddrDelta(offsetWithinExt))) - } - } - if file.InodeItem != nil && off >= file.InodeItem.Size { - return 0, io.EOF - } - return 0, fmt.Errorf("read: could not map position %v", off) -} - -var _ io.ReaderAt = (*File)(nil) diff --git a/pkg/btrfs/types_node.go b/pkg/btrfs/types_node.go deleted file mode 100644 index 4382a91..0000000 --- a/pkg/btrfs/types_node.go +++ /dev/null @@ -1,411 +0,0 @@ -package btrfs - -import ( - "encoding/binary" - "errors" - "fmt" - - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsitem" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfssum" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" - "lukeshu.com/btrfs-tools/pkg/util" -) - -type NodeFlags uint64 - -func (NodeFlags) BinaryStaticSize() int { - return 7 -} -func (f NodeFlags) MarshalBinary() ([]byte, error) { - var bs [8]byte - binary.LittleEndian.PutUint64(bs[:], uint64(f)) - return bs[:7], nil -} -func (f *NodeFlags) UnmarshalBinary(dat []byte) (int, error) { - var bs [8]byte - copy(bs[:7], dat[:7]) - *f = NodeFlags(binary.LittleEndian.Uint64(bs[:])) - return 7, nil -} - -var ( - _ binstruct.StaticSizer = NodeFlags(0) - _ binstruct.Marshaler = NodeFlags(0) - _ binstruct.Unmarshaler = (*NodeFlags)(nil) -) - -const ( - NodeWritten = NodeFlags(1 << iota) - NodeReloc -) - -var nodeFlagNames = []string{ - "WRITTEN", - "RELOC", -} - -func (f NodeFlags) Has(req NodeFlags) bool { return f&req == req } -func (f NodeFlags) String() string { return util.BitfieldString(f, nodeFlagNames, util.HexLower) } - -type BackrefRev uint8 - -const ( - OldBackrefRev = BackrefRev(iota) - MixedBackrefRev = BackrefRev(iota) -) - -// Node: main ////////////////////////////////////////////////////////////////////////////////////// - -type Node struct { - // Some context from the parent filesystem - Size uint32 // superblock.NodeSize - ChecksumType btrfssum.CSumType // superblock.ChecksumType - - // The node's header (always present) - Head NodeHeader - - // The node's body (which one of these is present depends on - // the node's type, as specified in the header) - BodyInternal []KeyPointer // for internal nodes - BodyLeaf []Item // for leave nodes - - Padding []byte -} - -type NodeHeader struct { - Checksum btrfssum.CSum `bin:"off=0x0, siz=0x20"` - MetadataUUID UUID `bin:"off=0x20, siz=0x10"` - Addr btrfsvol.LogicalAddr `bin:"off=0x30, siz=0x8"` // Logical address of this node - Flags NodeFlags `bin:"off=0x38, siz=0x7"` - BackrefRev BackrefRev `bin:"off=0x3f, siz=0x1"` - ChunkTreeUUID UUID `bin:"off=0x40, siz=0x10"` - Generation Generation `bin:"off=0x50, siz=0x8"` - Owner ObjID `bin:"off=0x58, siz=0x8"` // The ID of the tree that contains this node - NumItems uint32 `bin:"off=0x60, siz=0x4"` // [ignored-when-writing] - Level uint8 `bin:"off=0x64, siz=0x1"` // 0 for leaf nodes, >=1 for internal nodes - binstruct.End `bin:"off=0x65"` -} - -// MaxItems returns the maximum possible valid value of -// .Head.NumItems. -func (node Node) MaxItems() uint32 { - bodyBytes := node.Size - uint32(binstruct.StaticSize(NodeHeader{})) - if node.Head.Level > 0 { - return bodyBytes / uint32(binstruct.StaticSize(KeyPointer{})) - } else { - return bodyBytes / uint32(binstruct.StaticSize(ItemHeader{})) - } -} - -func (node Node) CalculateChecksum() (btrfssum.CSum, error) { - data, err := binstruct.Marshal(node) - if err != nil { - return btrfssum.CSum{}, err - } - return node.ChecksumType.Sum(data[binstruct.StaticSize(btrfssum.CSum{}):]) -} - -func (node Node) ValidateChecksum() error { - stored := node.Head.Checksum - calced, err := node.CalculateChecksum() - if err != nil { - return err - } - if calced != stored { - return fmt.Errorf("node checksum mismatch: stored=%v calculated=%v", - stored, calced) - } - return nil -} - -func (node *Node) UnmarshalBinary(nodeBuf []byte) (int, error) { - *node = Node{ - Size: uint32(len(nodeBuf)), - ChecksumType: node.ChecksumType, - } - n, err := binstruct.Unmarshal(nodeBuf, &node.Head) - if err != nil { - return n, fmt.Errorf("btrfs.Node.UnmarshalBinary: %w", err) - } - if node.Head.Level > 0 { - _n, err := node.unmarshalInternal(nodeBuf[n:]) - n += _n - if err != nil { - return n, fmt.Errorf("btrfs.Node.UnmarshalBinary: internal: %w", err) - } - } else { - _n, err := node.unmarshalLeaf(nodeBuf[n:]) - n += _n - if err != nil { - return n, fmt.Errorf("btrfs.Node.UnmarshalBinary: leaf: %w", err) - } - } - if n != len(nodeBuf) { - return n, fmt.Errorf("btrfs.Node.UnmarshalBinary: left over data: got %v bytes but only consumed %v", - len(nodeBuf), n) - } - return n, nil -} - -func (node Node) MarshalBinary() ([]byte, error) { - if node.Size == 0 { - return nil, fmt.Errorf("btrfs.Node.MarshalBinary: .Size must be set") - } - if node.Size <= uint32(binstruct.StaticSize(NodeHeader{})) { - return nil, fmt.Errorf("btrfs.Node.MarshalBinary: .Size must be greater than %v", - binstruct.StaticSize(NodeHeader{})) - } - if node.Head.Level > 0 { - node.Head.NumItems = uint32(len(node.BodyInternal)) - } else { - node.Head.NumItems = uint32(len(node.BodyLeaf)) - } - - buf := make([]byte, node.Size) - - if bs, err := binstruct.Marshal(node.Head); err != nil { - return buf, err - } else if len(bs) != binstruct.StaticSize(NodeHeader{}) { - return nil, fmt.Errorf("btrfs.Node.MarshalBinary: header is %v bytes but expected %v", - len(bs), binstruct.StaticSize(NodeHeader{})) - } else { - copy(buf, bs) - } - - if node.Head.Level > 0 { - if err := node.marshalInternalTo(buf[binstruct.StaticSize(NodeHeader{}):]); err != nil { - return buf, err - } - } else { - if err := node.marshalLeafTo(buf[binstruct.StaticSize(NodeHeader{}):]); err != nil { - return buf, err - } - } - - return buf, nil -} - -// Node: "internal" //////////////////////////////////////////////////////////////////////////////// - -type KeyPointer struct { - Key Key `bin:"off=0x0, siz=0x11"` - BlockPtr btrfsvol.LogicalAddr `bin:"off=0x11, siz=0x8"` - Generation Generation `bin:"off=0x19, siz=0x8"` - binstruct.End `bin:"off=0x21"` -} - -func (node *Node) unmarshalInternal(bodyBuf []byte) (int, error) { - n := 0 - for i := uint32(0); i < node.Head.NumItems; i++ { - var item KeyPointer - _n, err := binstruct.Unmarshal(bodyBuf[n:], &item) - n += _n - if err != nil { - return n, fmt.Errorf("item %v: %w", i, err) - } - node.BodyInternal = append(node.BodyInternal, item) - } - node.Padding = bodyBuf[n:] - return len(bodyBuf), nil -} - -func (node *Node) marshalInternalTo(bodyBuf []byte) error { - n := 0 - for i, item := range node.BodyInternal { - bs, err := binstruct.Marshal(item) - if err != nil { - return fmt.Errorf("item %v: %w", i, err) - } - if copy(bodyBuf[n:], bs) < len(bs) { - return fmt.Errorf("item %v: not enough space: need at least %v+%v=%v bytes, but only have %v", - i, n, len(bs), n+len(bs), len(bodyBuf)) - } - n += len(bs) - } - if copy(bodyBuf[n:], node.Padding) < len(node.Padding) { - return fmt.Errorf("padding: not enough space: need at least %v+%v=%v bytes, but only have %v", - n, len(node.Padding), n+len(node.Padding), len(bodyBuf)) - } - return nil -} - -// Node: "leaf" //////////////////////////////////////////////////////////////////////////////////// - -type Item struct { - Head ItemHeader - Body btrfsitem.Item -} - -type ItemHeader struct { - Key Key `bin:"off=0x0, siz=0x11"` - DataOffset uint32 `bin:"off=0x11, siz=0x4"` // [ignored-when-writing] relative to the end of the header (0x65) - DataSize uint32 `bin:"off=0x15, siz=0x4"` // [ignored-when-writing] - binstruct.End `bin:"off=0x19"` -} - -func (node *Node) unmarshalLeaf(bodyBuf []byte) (int, error) { - head := 0 - tail := len(bodyBuf) - for i := uint32(0); i < node.Head.NumItems; i++ { - var item Item - - n, err := binstruct.Unmarshal(bodyBuf[head:], &item.Head) - head += n - if err != nil { - return 0, fmt.Errorf("item %v: head: %w", i, err) - } - if head > tail { - return 0, fmt.Errorf("item %v: head: end_offset=%#x is in the body section (offset>%#x)", - i, head, tail) - } - - dataOff := int(item.Head.DataOffset) - if dataOff < head { - return 0, fmt.Errorf("item %v: body: beg_offset=%#x is in the head section (offset<%#x)", - i, dataOff, head) - } - dataSize := int(item.Head.DataSize) - if dataOff+dataSize != tail { - return 0, fmt.Errorf("item %v: body: end_offset=%#x is not cur_tail=%#x)", - i, dataOff+dataSize, tail) - } - tail = dataOff - dataBuf := bodyBuf[dataOff : dataOff+dataSize] - item.Body = btrfsitem.UnmarshalItem(item.Head.Key, node.ChecksumType, dataBuf) - - node.BodyLeaf = append(node.BodyLeaf, item) - } - - node.Padding = bodyBuf[head:tail] - return len(bodyBuf), nil -} - -func (node *Node) marshalLeafTo(bodyBuf []byte) error { - head := 0 - tail := len(bodyBuf) - for i, item := range node.BodyLeaf { - itemBodyBuf, err := binstruct.Marshal(item.Body) - if err != nil { - return fmt.Errorf("item %v: body: %w", i, err) - } - item.Head.DataSize = uint32(len(itemBodyBuf)) - item.Head.DataOffset = uint32(tail - len(itemBodyBuf)) - itemHeadBuf, err := binstruct.Marshal(item.Head) - if err != nil { - return fmt.Errorf("item %v: head: %w", i, err) - } - - if tail-head < len(itemHeadBuf)+len(itemBodyBuf) { - return fmt.Errorf("item %v: not enough space: need at least (head_len:%v)+(body_len:%v)=%v free bytes, but only have %v", - i, len(itemHeadBuf), len(itemBodyBuf), len(itemHeadBuf)+len(itemBodyBuf), tail-head) - } - - copy(bodyBuf[head:], itemHeadBuf) - head += len(itemHeadBuf) - tail -= len(itemBodyBuf) - copy(bodyBuf[tail:], itemBodyBuf) - } - if copy(bodyBuf[head:tail], node.Padding) < len(node.Padding) { - return fmt.Errorf("padding: not enough space: need at least %v free bytes, but only have %v", - len(node.Padding), tail-head) - } - return nil -} - -func (node *Node) LeafFreeSpace() uint32 { - if node.Head.Level > 0 { - panic(fmt.Errorf("Node.LeafFreeSpace: not a leaf node")) - } - freeSpace := node.Size - freeSpace -= uint32(binstruct.StaticSize(NodeHeader{})) - for _, item := range node.BodyLeaf { - freeSpace -= uint32(binstruct.StaticSize(ItemHeader{})) - freeSpace -= item.Head.DataSize - } - return freeSpace -} - -// Tie Nodes in to the FS ////////////////////////////////////////////////////////////////////////// - -var ErrNotANode = errors.New("does not look like a node") - -func ReadNode[Addr ~int64](fs util.File[Addr], sb Superblock, addr Addr, laddrCB func(btrfsvol.LogicalAddr) error) (*util.Ref[Addr, Node], error) { - nodeBuf := make([]byte, sb.NodeSize) - if _, err := fs.ReadAt(nodeBuf, addr); err != nil { - return nil, err - } - - // parse (early) - - 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) - } - - // sanity checking - - if nodeRef.Data.Head.MetadataUUID != sb.EffectiveMetadataUUID() { - return nodeRef, fmt.Errorf("btrfs.ReadNode: node@%v: %w", addr, ErrNotANode) - } - - stored := nodeRef.Data.Head.Checksum - calced, err := nodeRef.Data.ChecksumType.Sum(nodeBuf[binstruct.StaticSize(btrfssum.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) - } - - if laddrCB != nil { - if err := laddrCB(nodeRef.Data.Head.Addr); err != nil { - return nodeRef, fmt.Errorf("btrfs.ReadNode: node@%v: %w", addr, err) - } - } - - // parse (main) - - if _, err := nodeRef.Data.UnmarshalBinary(nodeBuf); err != nil { - return nodeRef, fmt.Errorf("btrfs.ReadNode: node@%v: %w", addr, err) - } - - // return - - return nodeRef, nil -} - -func (fs *FS) ReadNode(addr btrfsvol.LogicalAddr) (*util.Ref[btrfsvol.LogicalAddr, Node], error) { - sb, err := fs.Superblock() - if err != nil { - return nil, fmt.Errorf("btrfs.FS.ReadNode: %w", err) - } - - return ReadNode[btrfsvol.LogicalAddr](fs, sb.Data, addr, func(claimAddr btrfsvol.LogicalAddr) error { - if claimAddr != addr { - return fmt.Errorf("read from laddr=%v but claims to be at laddr=%v", - addr, claimAddr) - } - return nil - }) -} - -func (fs *FS) readNodeAtLevel(addr btrfsvol.LogicalAddr, expLevel uint8) (*util.Ref[btrfsvol.LogicalAddr, Node], error) { - node, err := fs.ReadNode(addr) - if err != nil { - return node, err - } - if node.Data.Head.Level != expLevel { - return node, fmt.Errorf("btrfs.FS.ReadNode: node@%v: expected level %v but has level %v", - node.Addr, expLevel, node.Data.Head.Level) - } - return node, nil -} diff --git a/pkg/btrfs/types_superblock.go b/pkg/btrfs/types_superblock.go deleted file mode 100644 index 413e5da..0000000 --- a/pkg/btrfs/types_superblock.go +++ /dev/null @@ -1,233 +0,0 @@ -package btrfs - -import ( - "fmt" - "reflect" - - "lukeshu.com/btrfs-tools/pkg/binstruct" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsitem" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfssum" - "lukeshu.com/btrfs-tools/pkg/btrfs/btrfsvol" - "lukeshu.com/btrfs-tools/pkg/util" -) - -type Superblock struct { - Checksum btrfssum.CSum `bin:"off=0x0, siz=0x20"` // Checksum of everything past this field (from 0x20 to 0x1000) - FSUUID UUID `bin:"off=0x20, siz=0x10"` // FS UUID - Self btrfsvol.PhysicalAddr `bin:"off=0x30, siz=0x8"` // physical address of this block (different for mirrors) - Flags uint64 `bin:"off=0x38, siz=0x8"` // flags - Magic [8]byte `bin:"off=0x40, siz=0x8"` // magic ('_BHRfS_M') - Generation Generation `bin:"off=0x48, siz=0x8"` - - RootTree btrfsvol.LogicalAddr `bin:"off=0x50, siz=0x8"` // logical address of the root tree root - ChunkTree btrfsvol.LogicalAddr `bin:"off=0x58, siz=0x8"` // logical address of the chunk tree root - LogTree btrfsvol.LogicalAddr `bin:"off=0x60, siz=0x8"` // logical address of the log tree root - - LogRootTransID uint64 `bin:"off=0x68, siz=0x8"` // log_root_transid - TotalBytes uint64 `bin:"off=0x70, siz=0x8"` // total_bytes - BytesUsed uint64 `bin:"off=0x78, siz=0x8"` // bytes_used - RootDirObjectID ObjID `bin:"off=0x80, siz=0x8"` // root_dir_objectid (usually 6) - NumDevices uint64 `bin:"off=0x88, siz=0x8"` // num_devices - - SectorSize uint32 `bin:"off=0x90, siz=0x4"` - NodeSize uint32 `bin:"off=0x94, siz=0x4"` - LeafSize uint32 `bin:"off=0x98, siz=0x4"` // unused; must be the same as NodeSize - StripeSize uint32 `bin:"off=0x9c, siz=0x4"` - SysChunkArraySize uint32 `bin:"off=0xa0, siz=0x4"` - - ChunkRootGeneration Generation `bin:"off=0xa4, siz=0x8"` - 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 btrfssum.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 - LogLevel uint8 `bin:"off=0xc8, siz=0x1"` // log_root_level - - DevItem btrfsitem.Dev `bin:"off=0xc9, siz=0x62"` // DEV_ITEM data for this device - Label [0x100]byte `bin:"off=0x12b, siz=0x100"` // label (may not contain '/' or '\\') - CacheGeneration Generation `bin:"off=0x22b, siz=0x8"` - UUIDTreeGeneration Generation `bin:"off=0x233, siz=0x8"` - - // FeatureIncompatMetadataUUID - MetadataUUID UUID `bin:"off=0x23b, siz=0x10"` - - // FeatureIncompatExtentTreeV2 - NumGlobalRoots uint64 `bin:"off=0x24b, siz=0x8"` - - // FeatureIncompatExtentTreeV2 - BlockGroupRoot btrfsvol.LogicalAddr `bin:"off=0x253, siz=0x8"` - BlockGroupRootGeneration Generation `bin:"off=0x25b, siz=0x8"` - BlockGroupRootLevel uint8 `bin:"off=0x263, siz=0x1"` - - Reserved [199]byte `bin:"off=0x264, siz=0xc7"` // future expansion - - SysChunkArray [0x800]byte `bin:"off=0x32b, siz=0x800"` // 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. - SuperRoots [4]RootBackup `bin:"off=0xb2b, siz=0x2a0"` - - // Padded to 4096 bytes - Padding [565]byte `bin:"off=0xdcb, siz=0x235"` - binstruct.End `bin:"off=0x1000"` -} - -func (sb Superblock) CalculateChecksum() (btrfssum.CSum, error) { - data, err := binstruct.Marshal(sb) - if err != nil { - return btrfssum.CSum{}, err - } - return sb.ChecksumType.Sum(data[binstruct.StaticSize(btrfssum.CSum{}):]) -} - -func (sb Superblock) ValidateChecksum() error { - stored := sb.Checksum - calced, err := sb.CalculateChecksum() - if err != nil { - return err - } - if calced != stored { - return fmt.Errorf("superblock checksum mismatch: stored=%v calculated=%v", - stored, calced) - } - return nil -} - -func (a Superblock) Equal(b Superblock) bool { - a.Checksum = btrfssum.CSum{} - a.Self = 0 - - b.Checksum = btrfssum.CSum{} - b.Self = 0 - - return reflect.DeepEqual(a, b) -} - -func (sb Superblock) EffectiveMetadataUUID() UUID { - if !sb.IncompatFlags.Has(FeatureIncompatMetadataUUID) { - return sb.FSUUID - } - return sb.MetadataUUID -} - -type SysChunk struct { - Key Key - Chunk btrfsitem.Chunk -} - -func (sc SysChunk) MarshalBinary() ([]byte, error) { - dat, err := binstruct.Marshal(sc.Key) - if err != nil { - return dat, fmt.Errorf("%T.MarshalBinary: %w", sc, err) - } - _dat, err := binstruct.Marshal(sc.Chunk) - dat = append(dat, _dat...) - if err != nil { - return dat, fmt.Errorf("%T.MarshalBinary: %w", sc, err) - } - return dat, nil -} - -func (sc *SysChunk) UnmarshalBinary(dat []byte) (int, error) { - n, err := binstruct.Unmarshal(dat, &sc.Key) - if err != nil { - return n, fmt.Errorf("%T.UnmarshalBinary: %w", *sc, err) - } - _n, err := binstruct.Unmarshal(dat[n:], &sc.Chunk) - n += _n - if err != nil { - return n, fmt.Errorf("%T.UnmarshalBinary: %w", *sc, err) - } - return n, nil -} - -func (sb Superblock) ParseSysChunkArray() ([]SysChunk, error) { - dat := sb.SysChunkArray[:sb.SysChunkArraySize] - var ret []SysChunk - for len(dat) > 0 { - var pair SysChunk - n, err := binstruct.Unmarshal(dat, &pair) - dat = dat[n:] - if err != nil { - return nil, err - } - ret = append(ret, pair) - } - return ret, nil -} - -type RootBackup struct { - TreeRoot ObjID `bin:"off=0x0, siz=0x8"` - TreeRootGen Generation `bin:"off=0x8, siz=0x8"` - - ChunkRoot ObjID `bin:"off=0x10, siz=0x8"` - ChunkRootGen Generation `bin:"off=0x18, siz=0x8"` - - ExtentRoot ObjID `bin:"off=0x20, siz=0x8"` - ExtentRootGen Generation `bin:"off=0x28, siz=0x8"` - - FSRoot ObjID `bin:"off=0x30, siz=0x8"` - FSRootGen Generation `bin:"off=0x38, siz=0x8"` - - DevRoot ObjID `bin:"off=0x40, siz=0x8"` - DevRootGen Generation `bin:"off=0x48, siz=0x8"` - - ChecksumRoot ObjID `bin:"off=0x50, siz=0x8"` - ChecksumRootGen Generation `bin:"off=0x58, siz=0x8"` - - TotalBytes uint64 `bin:"off=0x60, siz=0x8"` - BytesUsed uint64 `bin:"off=0x68, siz=0x8"` - NumDevices uint64 `bin:"off=0x70, siz=0x8"` - - Unused [8 * 4]byte `bin:"off=0x78, siz=0x20"` - - TreeRootLevel uint8 `bin:"off=0x98, siz=0x1"` - ChunkRootLevel uint8 `bin:"off=0x99, siz=0x1"` - ExtentRootLevel uint8 `bin:"off=0x9a, siz=0x1"` - FSRootLevel uint8 `bin:"off=0x9b, siz=0x1"` - DevRootLevel uint8 `bin:"off=0x9c, siz=0x1"` - ChecksumRootLevel uint8 `bin:"off=0x9d, siz=0x1"` - - Padding [10]byte `bin:"off=0x9e, siz=0xa"` - binstruct.End `bin:"off=0xa8"` -} - -type IncompatFlags uint64 - -const ( - FeatureIncompatMixedBackref = IncompatFlags(1 << iota) - FeatureIncompatDefaultSubvol - FeatureIncompatMixedGroups - FeatureIncompatCompressLZO - FeatureIncompatCompressZSTD - FeatureIncompatBigMetadata // buggy - FeatureIncompatExtendedIRef - FeatureIncompatRAID56 - FeatureIncompatSkinnyMetadata - FeatureIncompatNoHoles - FeatureIncompatMetadataUUID - FeatureIncompatRAID1C34 - FeatureIncompatZoned - FeatureIncompatExtentTreeV2 -) - -var incompatFlagNames = []string{ - "FeatureIncompatMixedBackref", - "FeatureIncompatDefaultSubvol", - "FeatureIncompatMixedGroups", - "FeatureIncompatCompressLZO", - "FeatureIncompatCompressZSTD", - "FeatureIncompatBigMetadata ", - "FeatureIncompatExtendedIRef", - "FeatureIncompatRAID56", - "FeatureIncompatSkinnyMetadata", - "FeatureIncompatNoHoles", - "FeatureIncompatMetadataUUID", - "FeatureIncompatRAID1C34", - "FeatureIncompatZoned", - "FeatureIncompatExtentTreeV2", -} - -func (f IncompatFlags) Has(req IncompatFlags) bool { return f&req == req } -func (f IncompatFlags) String() string { - return util.BitfieldString(f, incompatFlagNames, util.HexLower) -} |