From 9ba37f3836965e5a28df48567c0b7666d959915d Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 9 Jan 2023 15:26:07 -0700 Subject: btrfsitem: Pre-allocate the stripes when unmarshaling a chunk --- lib/btrfs/btrfsitem/item_chunk.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/btrfs/btrfsitem') diff --git a/lib/btrfs/btrfsitem/item_chunk.go b/lib/btrfs/btrfsitem/item_chunk.go index 1f1d577..e0776ad 100644 --- a/lib/btrfs/btrfsitem/item_chunk.go +++ b/lib/btrfs/btrfsitem/item_chunk.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -65,7 +65,7 @@ func (chunk *Chunk) UnmarshalBinary(dat []byte) (int, error) { if err != nil { return n, err } - chunk.Stripes = nil + chunk.Stripes = make([]ChunkStripe, 0, chunk.Head.NumStripes) for i := 0; i < int(chunk.Head.NumStripes); i++ { var stripe ChunkStripe _n, err := binstruct.Unmarshal(dat[n:], &stripe) -- cgit v1.2.3-54-g00ecf From 677755c799c1b6b942349c7d9de836335c7bbf55 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Fri, 3 Feb 2023 11:32:52 -0700 Subject: btrfsitem: Have all Item implementations be pointers to structs --- lib/btrfs/Makefile | 2 +- lib/btrfs/btrfsitem/item_extent.go | 10 ++-- lib/btrfs/btrfsitem/item_freespacebitmap.go | 10 ++-- lib/btrfs/btrfsitem/item_inoderef.go | 12 +++-- lib/btrfs/btrfsitem/items.go | 18 ++++---- lib/btrfs/btrfsitem/items_gen.go | 48 +++++++++---------- lib/btrfs/btrfstree/ops.go | 2 +- lib/btrfs/btrfstree/root.go | 4 +- lib/btrfs/csums.go | 4 +- lib/btrfs/io2_lv.go | 4 +- lib/btrfs/io3_btree.go | 4 +- lib/btrfs/io4_fs.go | 52 +++++++++++---------- lib/btrfsprogs/btrfsinspect/print_tree.go | 52 ++++++++++----------- .../btrfsinspect/rebuildnodes/graph/graph.go | 6 +-- .../btrfsinspect/rebuildnodes/keyio/keyio.go | 10 ++-- .../btrfsinspect/rebuildnodes/rebuild.go | 10 ++-- .../btrfsinspect/rebuildnodes/rebuild_graph.go | 54 +++++++++++----------- lib/btrfsprogs/btrfsinspect/scandevices.go | 24 +++++----- 18 files changed, 166 insertions(+), 160 deletions(-) (limited to 'lib/btrfs/btrfsitem') diff --git a/lib/btrfs/Makefile b/lib/btrfs/Makefile index cbcaf9e..00c22e7 100644 --- a/lib/btrfs/Makefile +++ b/lib/btrfs/Makefile @@ -35,7 +35,7 @@ btrfsitem/items_gen.go: btrfsitem/items.txt $(MAKEFILE_LIST) echo 'var untypedObjID2gotype = map[btrfsprim.ObjID]reflect.Type{'; \ sed -En 's|UNTYPED=0:(.*) (.*)|btrfsprim.\1: reflect.TypeOf(\2{}),|p' $<; \ echo '}'; \ - sed -En 's,(.*)=(.*) (.+),\3,p' $< | LC_COLLATE=C sort -u | sed 's,.*,func (&) isItem() {},'; \ + sed -En 's,(.*)=(.*) (.+),\3,p' $< | LC_COLLATE=C sort -u | sed 's,.*,func (*&) isItem() {},'; \ } | gofmt >$@ files += btrfsitem/items_gen.go diff --git a/lib/btrfs/btrfsitem/item_extent.go b/lib/btrfs/btrfsitem/item_extent.go index 66aae1d..72371a9 100644 --- a/lib/btrfs/btrfsitem/item_extent.go +++ b/lib/btrfs/btrfsitem/item_extent.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -114,8 +114,8 @@ func (o *ExtentInlineRef) UnmarshalBinary(dat []byte) (int, error) { return n, err } case EXTENT_DATA_REF_KEY: - var dref ExtentDataRef - _n, err := binstruct.Unmarshal(dat[n:], &dref) + dref := new(ExtentDataRef) + _n, err := binstruct.Unmarshal(dat[n:], dref) n += _n o.Body = dref if err != nil { @@ -127,8 +127,8 @@ func (o *ExtentInlineRef) UnmarshalBinary(dat []byte) (int, error) { if err != nil { return n, err } - var sref SharedDataRef - _n, err = binstruct.Unmarshal(dat[n:], &sref) + sref := new(SharedDataRef) + _n, err = binstruct.Unmarshal(dat[n:], sref) n += _n o.Body = sref if err != nil { diff --git a/lib/btrfs/btrfsitem/item_freespacebitmap.go b/lib/btrfs/btrfsitem/item_freespacebitmap.go index ad46204..742d126 100644 --- a/lib/btrfs/btrfsitem/item_freespacebitmap.go +++ b/lib/btrfs/btrfsitem/item_freespacebitmap.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,13 +6,15 @@ package btrfsitem // key.objectid = object ID of the FreeSpaceInfo (logical_addr) // key.offset = offset of the FreeSpaceInfo (size) -type FreeSpaceBitmap []byte // FREE_SPACE_BITMAP=200 +type FreeSpaceBitmap struct { // FREE_SPACE_BITMAP=200 + Bitmap []byte +} func (o *FreeSpaceBitmap) UnmarshalBinary(dat []byte) (int, error) { - *o = dat + o.Bitmap = dat return len(dat), nil } func (o FreeSpaceBitmap) MarshalBinary() ([]byte, error) { - return []byte(o), nil + return o.Bitmap, nil } diff --git a/lib/btrfs/btrfsitem/item_inoderef.go b/lib/btrfs/btrfsitem/item_inoderef.go index 083f19e..c90fe44 100644 --- a/lib/btrfs/btrfsitem/item_inoderef.go +++ b/lib/btrfs/btrfsitem/item_inoderef.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -16,10 +16,12 @@ import ( // // Might have multiple entries if the same file has multiple hardlinks // in the same directory. -type InodeRefs []InodeRef // INODE_REF=12 +type InodeRefs struct { // INODE_REF=12 + Refs []InodeRef +} func (o *InodeRefs) UnmarshalBinary(dat []byte) (int, error) { - *o = nil + o.Refs = nil n := 0 for n < len(dat) { var ref InodeRef @@ -28,14 +30,14 @@ func (o *InodeRefs) UnmarshalBinary(dat []byte) (int, error) { if err != nil { return n, err } - *o = append(*o, ref) + o.Refs = append(o.Refs, ref) } return n, nil } func (o InodeRefs) MarshalBinary() ([]byte, error) { var dat []byte - for _, ref := range o { + for _, ref := range o.Refs { _dat, err := binstruct.Marshal(ref) dat = append(dat, _dat...) if err != nil { diff --git a/lib/btrfs/btrfsitem/items.go b/lib/btrfs/btrfsitem/items.go index 67f96fa..9964e2d 100644 --- a/lib/btrfs/btrfsitem/items.go +++ b/lib/btrfs/btrfsitem/items.go @@ -25,7 +25,7 @@ type Error struct { Err error } -func (Error) isItem() {} +func (*Error) isItem() {} func (o Error) MarshalBinary() ([]byte, error) { return o.Dat, nil @@ -43,7 +43,7 @@ func UnmarshalItem(key btrfsprim.Key, csumType btrfssum.CSumType, dat []byte) It var ok bool gotyp, ok = untypedObjID2gotype[key.ObjectID] if !ok { - return Error{ + return &Error{ Dat: dat, Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v, ObjectID:%v}, dat): unknown object ID for untyped item", key.ItemType, key.ObjectID), @@ -53,31 +53,31 @@ func UnmarshalItem(key btrfsprim.Key, csumType btrfssum.CSumType, dat []byte) It var ok bool gotyp, ok = keytype2gotype[key.ItemType] if !ok { - return Error{ + 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 { + ptr := reflect.New(gotyp) + if csums, ok := ptr.Interface().(*ExtentCSum); ok { csums.ChecksumSize = csumType.Size() csums.Addr = btrfsvol.LogicalAddr(key.Offset) } - n, err := binstruct.Unmarshal(dat, retPtr.Interface()) + n, err := binstruct.Unmarshal(dat, ptr.Interface()) if err != nil { - return Error{ + return &Error{ Dat: dat, Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v}, dat): %w", key.ItemType, err), } } if n < len(dat) { - return Error{ + 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), } } //nolint:forcetypeassert // items_gen.go has all types in keytype2gotype implement the Item interface. - return retPtr.Elem().Interface().(Item) + return ptr.Interface().(Item) } diff --git a/lib/btrfs/btrfsitem/items_gen.go b/lib/btrfs/btrfsitem/items_gen.go index 9daef81..21c6795 100644 --- a/lib/btrfs/btrfsitem/items_gen.go +++ b/lib/btrfs/btrfsitem/items_gen.go @@ -80,27 +80,27 @@ var untypedObjID2gotype = map[btrfsprim.ObjID]reflect.Type{ btrfsprim.FREE_SPACE_OBJECTID: reflect.TypeOf(FreeSpaceHeader{}), } -func (BlockGroup) isItem() {} -func (Chunk) isItem() {} -func (Dev) isItem() {} -func (DevExtent) isItem() {} -func (DevStats) isItem() {} -func (DirEntry) 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 (InodeRefs) isItem() {} -func (Metadata) isItem() {} -func (QGroupInfo) isItem() {} -func (QGroupLimit) isItem() {} -func (QGroupStatus) isItem() {} -func (Root) isItem() {} -func (RootRef) isItem() {} -func (SharedDataRef) isItem() {} -func (UUIDMap) isItem() {} +func (*BlockGroup) isItem() {} +func (*Chunk) isItem() {} +func (*Dev) isItem() {} +func (*DevExtent) isItem() {} +func (*DevStats) isItem() {} +func (*DirEntry) 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 (*InodeRefs) isItem() {} +func (*Metadata) isItem() {} +func (*QGroupInfo) isItem() {} +func (*QGroupLimit) isItem() {} +func (*QGroupStatus) isItem() {} +func (*Root) isItem() {} +func (*RootRef) isItem() {} +func (*SharedDataRef) isItem() {} +func (*UUIDMap) isItem() {} diff --git a/lib/btrfs/btrfstree/ops.go b/lib/btrfs/btrfstree/ops.go index cdacef9..bda4ac8 100644 --- a/lib/btrfs/btrfstree/ops.go +++ b/lib/btrfs/btrfstree/ops.go @@ -207,7 +207,7 @@ func (fs TreeOperatorImpl) treeWalk(ctx context.Context, path TreePath, errHandl ToKey: item.Key, ToMaxKey: item.Key, }) - if errBody, isErr := item.Body.(btrfsitem.Error); isErr { + if errBody, isErr := item.Body.(*btrfsitem.Error); isErr { if cbs.BadItem == nil { errHandle(&TreeError{Path: itemPath, Err: errBody.Err}) } else { diff --git a/lib/btrfs/btrfstree/root.go b/lib/btrfs/btrfstree/root.go index 319904b..ace2b49 100644 --- a/lib/btrfs/btrfstree/root.go +++ b/lib/btrfs/btrfstree/root.go @@ -72,14 +72,14 @@ func LookupTreeRoot(fs TreeOperator, sb Superblock, treeID btrfsprim.ObjID) (*Tr return nil, err } switch rootItemBody := rootItem.Body.(type) { - case btrfsitem.Root: + case *btrfsitem.Root: return &TreeRoot{ TreeID: treeID, RootNode: rootItemBody.ByteNr, Level: rootItemBody.Level, Generation: rootItemBody.Generation, }, nil - case btrfsitem.Error: + case *btrfsitem.Error: return nil, fmt.Errorf("malformed ROOT_ITEM for tree %v: %w", treeID, rootItemBody.Err) default: panic(fmt.Errorf("should not happen: ROOT_ITEM has unexpected item type: %T", rootItemBody)) diff --git a/lib/btrfs/csums.go b/lib/btrfs/csums.go index a32f090..2bfa588 100644 --- a/lib/btrfs/csums.go +++ b/lib/btrfs/csums.go @@ -60,9 +60,9 @@ func LookupCSum(fs btrfstree.TreeOperator, alg btrfssum.CSumType, laddr btrfsvol return btrfssum.SumRun[btrfsvol.LogicalAddr]{}, fmt.Errorf("item type is %v, not EXTENT_CSUM", item.Key.ItemType) } switch body := item.Body.(type) { - case btrfsitem.ExtentCSum: + case *btrfsitem.ExtentCSum: return body.SumRun, nil - case btrfsitem.Error: + case *btrfsitem.Error: return btrfssum.SumRun[btrfsvol.LogicalAddr]{}, body.Err default: panic(fmt.Errorf("should not happen: EXTENT_CSUM has unexpected item type: %T", body)) diff --git a/lib/btrfs/io2_lv.go b/lib/btrfs/io2_lv.go index ac7ea70..856ac20 100644 --- a/lib/btrfs/io2_lv.go +++ b/lib/btrfs/io2_lv.go @@ -173,13 +173,13 @@ func (fs *FS) initDev(ctx context.Context, sb btrfstree.Superblock) error { return nil } switch itemBody := item.Body.(type) { - case btrfsitem.Chunk: + case *btrfsitem.Chunk: for _, mapping := range itemBody.Mappings(item.Key) { if err := fs.LV.AddMapping(mapping); err != nil { return err } } - case btrfsitem.Error: + case *btrfsitem.Error: // do nothing default: // This is a panic because the item decoder should not emit CHUNK_ITEM items as diff --git a/lib/btrfs/io3_btree.go b/lib/btrfs/io3_btree.go index 8ec4b41..18df98e 100644 --- a/lib/btrfs/io3_btree.go +++ b/lib/btrfs/io3_btree.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -43,7 +43,7 @@ func (fs *FS) populateTreeUUIDs(ctx context.Context) { }, btrfstree.TreeWalkHandler{ Item: func(_ btrfstree.TreePath, item btrfstree.Item) error { - itemBody, ok := item.Body.(btrfsitem.Root) + itemBody, ok := item.Body.(*btrfsitem.Root) if !ok { return nil } diff --git a/lib/btrfs/io4_fs.go b/lib/btrfs/io4_fs.go index adc0928..c21cafc 100644 --- a/lib/btrfs/io4_fs.go +++ b/lib/btrfs/io4_fs.go @@ -88,9 +88,9 @@ func (sv *Subvolume) init() { sv.rootErr = err } else { switch rootBody := root.Body.(type) { - case btrfsitem.Root: - sv.rootVal = rootBody - case btrfsitem.Error: + case *btrfsitem.Root: + sv.rootVal = *rootBody + case *btrfsitem.Error: sv.rootErr = fmt.Errorf("FS_TREE ROOT_ITEM has malformed body: %w", rootBody.Err) default: panic(fmt.Errorf("should not happen: ROOT_ITEM has unexpected item type: %T", rootBody)) @@ -126,9 +126,10 @@ func (sv *Subvolume) LoadBareInode(inode btrfsprim.ObjID) (*BareInode, error) { } switch itemBody := item.Body.(type) { - case btrfsitem.Inode: - val.InodeItem = &itemBody - case btrfsitem.Error: + case *btrfsitem.Inode: + bodyCopy := *itemBody + val.InodeItem = &bodyCopy + case *btrfsitem.Error: val.Errs = append(val.Errs, fmt.Errorf("malformed inode: %w", itemBody.Err)) default: panic(fmt.Errorf("should not happen: INODE_ITEM has unexpected item type: %T", itemBody)) @@ -164,24 +165,25 @@ func (sv *Subvolume) LoadFullInode(inode btrfsprim.ObjID) (*FullInode, error) { switch item.Key.ItemType { case btrfsitem.INODE_ITEM_KEY: switch itemBody := item.Body.(type) { - case btrfsitem.Inode: + case *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 - case btrfsitem.Error: + bodyCopy := *itemBody + val.InodeItem = &bodyCopy + case *btrfsitem.Error: val.Errs = append(val.Errs, fmt.Errorf("malformed INODE_ITEM: %w", itemBody.Err)) default: panic(fmt.Errorf("should not happen: INODE_ITEM has unexpected item type: %T", itemBody)) } case btrfsitem.XATTR_ITEM_KEY: switch itemBody := item.Body.(type) { - case btrfsitem.DirEntry: + case *btrfsitem.DirEntry: val.XAttrs[string(itemBody.Name)] = string(itemBody.Data) - case btrfsitem.Error: + case *btrfsitem.Error: val.Errs = append(val.Errs, fmt.Errorf("malformed XATTR_ITEM: %w", itemBody.Err)) default: panic(fmt.Errorf("should not happen: XATTR_ITEM has unexpected item type: %T", itemBody)) @@ -225,15 +227,15 @@ func (dir *Dir) populate() { switch item.Key.ItemType { case btrfsitem.INODE_REF_KEY: switch body := item.Body.(type) { - case btrfsitem.InodeRefs: - if len(body) != 1 { + case *btrfsitem.InodeRefs: + if len(body.Refs) != 1 { dir.Errs = append(dir.Errs, fmt.Errorf("INODE_REF item with %d entries on a directory", - len(body))) + len(body.Refs))) continue } ref := InodeRef{ Inode: btrfsprim.ObjID(item.Key.Offset), - InodeRef: body[0], + InodeRef: body.Refs[0], } if dir.DotDot != nil { if !reflect.DeepEqual(ref, *dir.DotDot) { @@ -242,14 +244,14 @@ func (dir *Dir) populate() { continue } dir.DotDot = &ref - case btrfsitem.Error: + case *btrfsitem.Error: dir.Errs = append(dir.Errs, fmt.Errorf("malformed INODE_REF: %w", body.Err)) default: panic(fmt.Errorf("should not happen: INODE_REF has unexpected item type: %T", body)) } case btrfsitem.DIR_ITEM_KEY: switch entry := item.Body.(type) { - case btrfsitem.DirEntry: + case *btrfsitem.DirEntry: namehash := btrfsitem.NameHash(entry.Name) if namehash != item.Key.Offset { dir.Errs = append(dir.Errs, fmt.Errorf("direntry crc32c mismatch: key=%#x crc32c(%q)=%#x", @@ -262,8 +264,8 @@ func (dir *Dir) populate() { } continue } - dir.ChildrenByName[string(entry.Name)] = entry - case btrfsitem.Error: + dir.ChildrenByName[string(entry.Name)] = *entry + case *btrfsitem.Error: dir.Errs = append(dir.Errs, fmt.Errorf("malformed DIR_ITEM: %w", entry.Err)) default: panic(fmt.Errorf("should not happen: DIR_ITEM has unexpected item type: %T", entry)) @@ -271,15 +273,15 @@ func (dir *Dir) populate() { case btrfsitem.DIR_INDEX_KEY: index := item.Key.Offset switch entry := item.Body.(type) { - case btrfsitem.DirEntry: + case *btrfsitem.DirEntry: if other, exists := dir.ChildrenByIndex[index]; exists { if !reflect.DeepEqual(entry, other) { dir.Errs = append(dir.Errs, fmt.Errorf("multiple instances of direntry index %v", index)) } continue } - dir.ChildrenByIndex[index] = entry - case btrfsitem.Error: + dir.ChildrenByIndex[index] = *entry + case *btrfsitem.Error: dir.Errs = append(dir.Errs, fmt.Errorf("malformed DIR_INDEX: %w", entry.Err)) default: panic(fmt.Errorf("should not happen: DIR_INDEX has unexpected item type: %T", entry)) @@ -361,12 +363,12 @@ func (file *File) populate() { // TODO case btrfsitem.EXTENT_DATA_KEY: switch itemBody := item.Body.(type) { - case btrfsitem.FileExtent: + case *btrfsitem.FileExtent: file.Extents = append(file.Extents, FileExtent{ OffsetWithinFile: int64(item.Key.Offset), - FileExtent: itemBody, + FileExtent: *itemBody, }) - case btrfsitem.Error: + case *btrfsitem.Error: file.Errs = append(file.Errs, fmt.Errorf("malformed EXTENT_DATA: %w", itemBody.Err)) default: panic(fmt.Errorf("should not happen: EXTENT_DATA has unexpected item type: %T", itemBody)) diff --git a/lib/btrfsprogs/btrfsinspect/print_tree.go b/lib/btrfsprogs/btrfsinspect/print_tree.go index 3807df5..409ce0c 100644 --- a/lib/btrfsprogs/btrfsinspect/print_tree.go +++ b/lib/btrfsprogs/btrfsinspect/print_tree.go @@ -121,11 +121,11 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri itemOffset, itemSize) switch body := item.Body.(type) { - case btrfsitem.FreeSpaceHeader: + case *btrfsitem.FreeSpaceHeader: textui.Fprintf(out, "\t\tlocation %v\n", fmtKey(body.Location)) textui.Fprintf(out, "\t\tcache generation %v entries %v bitmaps %v\n", body.Generation, body.NumEntries, body.NumBitmaps) - case btrfsitem.Inode: + case *btrfsitem.Inode: textui.Fprintf(out, ""+ "\t\tgeneration %v transid %v size %v nbytes %v\n"+ "\t\tblock group %v mode %o links %v uid %v gid %v rdev %v\n"+ @@ -137,14 +137,14 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri textui.Fprintf(out, "\t\tctime %v\n", fmtTime(body.CTime)) textui.Fprintf(out, "\t\tmtime %v\n", fmtTime(body.MTime)) textui.Fprintf(out, "\t\totime %v\n", fmtTime(body.OTime)) - case btrfsitem.InodeRefs: - for _, ref := range body { + case *btrfsitem.InodeRefs: + for _, ref := range body.Refs { textui.Fprintf(out, "\t\tindex %v namelen %v name: %s\n", ref.Index, ref.NameLen, ref.Name) } // case btrfsitem.INODE_EXTREF_KEY: // // TODO - case btrfsitem.DirEntry: + case *btrfsitem.DirEntry: textui.Fprintf(out, "\t\tlocation %v type %v\n", fmtKey(body.Location), body.Type) textui.Fprintf(out, "\t\ttransid %v data_len %v name_len %v\n", @@ -155,7 +155,7 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri } // case btrfsitem.DIR_LOG_INDEX_KEY, btrfsitem.DIR_LOG_ITEM_KEY: // // TODO - case btrfsitem.Root: + case *btrfsitem.Root: textui.Fprintf(out, "\t\tgeneration %v root_dirid %v bytenr %d byte_limit %v bytes_used %v\n", body.Generation, body.RootDirID, body.ByteNr, body.ByteLimit, body.BytesUsed) textui.Fprintf(out, "\t\tlast_snapshot %v flags %v refs %v\n", @@ -175,7 +175,7 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri textui.Fprintf(out, "\t\tstime %v\n", fmtTime(body.STime)) textui.Fprintf(out, "\t\trtime %v\n", fmtTime(body.RTime)) } - case btrfsitem.RootRef: + case *btrfsitem.RootRef: var tag string switch item.Key.ItemType { case btrfsitem.ROOT_REF_KEY: @@ -187,7 +187,7 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri } textui.Fprintf(out, "\t\troot %v key dirid %v sequence %v name %s\n", tag, body.DirID, body.Sequence, body.Name) - case btrfsitem.Extent: + case *btrfsitem.Extent: textui.Fprintf(out, "\t\trefs %v gen %v flags %v\n", body.Head.Refs, body.Head.Generation, body.Head.Flags) if body.Head.Flags.Has(btrfsitem.EXTENT_FLAG_TREE_BLOCK) { @@ -195,7 +195,7 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri fmtKey(body.Info.Key), body.Info.Level) } printExtentInlineRefs(out, body.Refs) - case btrfsitem.Metadata: + case *btrfsitem.Metadata: textui.Fprintf(out, "\t\trefs %v gen %v flags %v\n", body.Head.Refs, body.Head.Generation, body.Head.Flags) textui.Fprintf(out, "\t\ttree block skinny level %v\n", item.Key.Offset) @@ -204,7 +204,7 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri // // TODO // case btrfsitem.SHARED_DATA_REF_KEY: // // TODO - case btrfsitem.ExtentCSum: + case *btrfsitem.ExtentCSum: start := btrfsvol.LogicalAddr(item.Key.Offset) textui.Fprintf(out, "\t\trange start %d end %d length %d", start, start.Add(body.Size()), body.Size()) @@ -222,7 +222,7 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri return nil }) textui.Fprintf(out, "\n") - case btrfsitem.FileExtent: + case *btrfsitem.FileExtent: textui.Fprintf(out, "\t\tgeneration %v type %v\n", body.Generation, body.Type) switch body.Type { @@ -249,15 +249,15 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri default: textui.Fprintf(out, "\t\t(error) unknown file extent type %v", body.Type) } - case btrfsitem.BlockGroup: + case *btrfsitem.BlockGroup: textui.Fprintf(out, "\t\tblock group used %v chunk_objectid %v flags %v\n", body.Used, body.ChunkObjectID, body.Flags) - case btrfsitem.FreeSpaceInfo: + case *btrfsitem.FreeSpaceInfo: textui.Fprintf(out, "\t\tfree space info extent count %v flags %d\n", body.ExtentCount, body.Flags) - case btrfsitem.FreeSpaceBitmap: + case *btrfsitem.FreeSpaceBitmap: textui.Fprintf(out, "\t\tfree space bitmap\n") - case btrfsitem.Chunk: + case *btrfsitem.Chunk: textui.Fprintf(out, "\t\tlength %d owner %d stripe_len %v type %v\n", body.Head.Size, body.Head.Owner, body.Head.StripeLen, body.Head.Type) textui.Fprintf(out, "\t\tio_align %v io_width %v sector_size %v\n", @@ -270,7 +270,7 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri textui.Fprintf(out, "\t\t\tdev_uuid %v\n", stripe.DeviceUUID) } - case btrfsitem.Dev: + case *btrfsitem.Dev: textui.Fprintf(out, ""+ "\t\tdevid %d total_bytes %v bytes_used %v\n"+ "\t\tio_align %v io_width %v sector_size %v type %v\n"+ @@ -284,18 +284,18 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri body.SeekSpeed, body.Bandwidth, body.DevUUID, body.FSUUID) - case btrfsitem.DevExtent: + case *btrfsitem.DevExtent: textui.Fprintf(out, ""+ "\t\tdev extent chunk_tree %d\n"+ "\t\tchunk_objectid %v chunk_offset %d length %d\n"+ "\t\tchunk_tree_uuid %v\n", body.ChunkTree, body.ChunkObjectID, body.ChunkOffset, body.Length, body.ChunkTreeUUID) - case btrfsitem.QGroupStatus: + case *btrfsitem.QGroupStatus: textui.Fprintf(out, ""+ "\t\tversion %v generation %v flags %v scan %d\n", body.Version, body.Generation, body.Flags, body.RescanProgress) - case btrfsitem.QGroupInfo: + case *btrfsitem.QGroupInfo: textui.Fprintf(out, ""+ "\t\tgeneration %v\n"+ "\t\treferenced %d referenced_compressed %d\n"+ @@ -303,7 +303,7 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri body.Generation, body.ReferencedBytes, body.ReferencedBytesCompressed, body.ExclusiveBytes, body.ExclusiveBytesCompressed) - case btrfsitem.QGroupLimit: + case *btrfsitem.QGroupLimit: textui.Fprintf(out, ""+ "\t\tflags %x\n"+ "\t\tmax_referenced %d max_exclusive %d\n"+ @@ -311,11 +311,11 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri uint64(body.Flags), body.MaxReferenced, body.MaxExclusive, body.RsvReferenced, body.RsvExclusive) - case btrfsitem.UUIDMap: + case *btrfsitem.UUIDMap: textui.Fprintf(out, "\t\tsubvol_id %d\n", body.ObjID) // case btrfsitem.STRING_ITEM_KEY: // // TODO - case btrfsitem.DevStats: + case *btrfsitem.DevStats: textui.Fprintf(out, "\t\tpersistent item objectid %v offset %v\n", item.Key.ObjectID.Format(item.Key.ItemType), item.Key.Offset) switch item.Key.ObjectID { @@ -332,7 +332,7 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri } // case btrfsitem.TEMPORARY_ITEM_KEY: // // TODO - case btrfsitem.Empty: + case *btrfsitem.Empty: switch item.Key.ItemType { case btrfsitem.ORPHAN_ITEM_KEY: // 48 textui.Fprintf(out, "\t\torphan item\n") @@ -351,7 +351,7 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri default: textui.Fprintf(out, "\t\t(error) unhandled empty item type: %v\n", item.Key.ItemType) } - case btrfsitem.Error: + case *btrfsitem.Error: textui.Fprintf(out, "\t\t(error) error item: %v\n", body.Err) default: textui.Fprintf(out, "\t\t(error) unhandled item type: %T\n", body) @@ -423,10 +423,10 @@ func printExtentInlineRefs(out io.Writer, refs []btrfsitem.ExtentInlineRef) { default: textui.Fprintf(out, "\t\t(error) unexpected empty sub-item type: %v\n", ref.Type) } - case btrfsitem.ExtentDataRef: + case *btrfsitem.ExtentDataRef: textui.Fprintf(out, "\t\textent data backref root %v objectid %v offset %v count %v\n", subitem.Root, subitem.ObjectID, subitem.Offset, subitem.Count) - case btrfsitem.SharedDataRef: + case *btrfsitem.SharedDataRef: textui.Fprintf(out, "\t\tshared data backref parent %v count %v\n", ref.Offset, subitem.Count) default: diff --git a/lib/btrfsprogs/btrfsinspect/rebuildnodes/graph/graph.go b/lib/btrfsprogs/btrfsinspect/rebuildnodes/graph/graph.go index cf86d74..c04fec0 100644 --- a/lib/btrfsprogs/btrfsinspect/rebuildnodes/graph/graph.go +++ b/lib/btrfsprogs/btrfsinspect/rebuildnodes/graph/graph.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -151,7 +151,7 @@ func (g Graph) InsertNode(nodeRef *diskio.Ref[btrfsvol.LogicalAddr, btrfstree.No if nodeRef.Data.Head.Level == 0 { cnt := 0 for _, item := range nodeRef.Data.BodyLeaf { - if _, ok := item.Body.(btrfsitem.Root); ok { + if _, ok := item.Body.(*btrfsitem.Root); ok { cnt++ } } @@ -161,7 +161,7 @@ func (g Graph) InsertNode(nodeRef *diskio.Ref[btrfsvol.LogicalAddr, btrfstree.No g.Nodes[nodeRef.Addr] = nodeData for i, item := range nodeRef.Data.BodyLeaf { keys[i] = item.Key - if itemBody, ok := item.Body.(btrfsitem.Root); ok { + if itemBody, ok := item.Body.(*btrfsitem.Root); ok { kps = append(kps, Edge{ FromRoot: nodeRef.Addr, FromItem: i, diff --git a/lib/btrfsprogs/btrfsinspect/rebuildnodes/keyio/keyio.go b/lib/btrfsprogs/btrfsinspect/rebuildnodes/keyio/keyio.go index a85b78e..149706d 100644 --- a/lib/btrfsprogs/btrfsinspect/rebuildnodes/keyio/keyio.go +++ b/lib/btrfsprogs/btrfsinspect/rebuildnodes/keyio/keyio.go @@ -73,27 +73,27 @@ func (o *Handle) InsertNode(nodeRef *diskio.Ref[btrfsvol.LogicalAddr, btrfstree. Idx: i, } switch itemBody := item.Body.(type) { - case btrfsitem.Inode: + case *btrfsitem.Inode: o.Flags[ptr] = FlagsAndErr{ NoDataSum: itemBody.Flags.Has(btrfsitem.INODE_NODATASUM), Err: nil, } - case btrfsitem.DirEntry: + case *btrfsitem.DirEntry: if item.Key.ItemType == btrfsprim.DIR_INDEX_KEY { o.Names[ptr] = append([]byte(nil), itemBody.Name...) } - case btrfsitem.ExtentCSum: + case *btrfsitem.ExtentCSum: o.Sizes[ptr] = SizeAndErr{ Size: uint64(itemBody.Size()), Err: nil, } - case btrfsitem.FileExtent: + case *btrfsitem.FileExtent: size, err := itemBody.Size() o.Sizes[ptr] = SizeAndErr{ Size: uint64(size), Err: err, } - case btrfsitem.Error: + case *btrfsitem.Error: switch item.Key.ItemType { case btrfsprim.INODE_ITEM_KEY: o.Flags[ptr] = FlagsAndErr{ diff --git a/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild.go b/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild.go index bd29278..624441f 100644 --- a/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild.go +++ b/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild.go @@ -290,9 +290,9 @@ func (o *rebuilder) cbLookupRoot(ctx context.Context, tree btrfsprim.ObjID) (off o.ioErr(ctx, fmt.Errorf("could not read previously read item: %v", key)) } switch itemBody := itemBody.(type) { - case btrfsitem.Root: - return btrfsprim.Generation(key.Offset), itemBody, true - case btrfsitem.Error: + case *btrfsitem.Root: + return btrfsprim.Generation(key.Offset), *itemBody, true + case *btrfsitem.Error: o.fsErr(ctx, fmt.Errorf("error decoding item: %v: %w", key, itemBody.Err)) return 0, btrfsitem.Root{}, false default: @@ -315,9 +315,9 @@ func (o *rebuilder) cbLookupUUID(ctx context.Context, uuid btrfsprim.UUID) (id b o.ioErr(ctx, fmt.Errorf("could not read previously read item: %v", key)) } switch itemBody := itemBody.(type) { - case btrfsitem.UUIDMap: + case *btrfsitem.UUIDMap: return itemBody.ObjID, true - case btrfsitem.Error: + case *btrfsitem.Error: o.fsErr(ctx, fmt.Errorf("error decoding item: %v: %w", key, itemBody.Err)) return 0, false default: diff --git a/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild_graph.go b/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild_graph.go index 9e40465..710030c 100644 --- a/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild_graph.go +++ b/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild_graph.go @@ -50,7 +50,7 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, // https://btrfs.wiki.kernel.org/index.php/File:References.png (from the page // https://btrfs.wiki.kernel.org/index.php/Data_Structures ) switch body := item.Body.(type) { - case btrfsitem.BlockGroup: + case *btrfsitem.BlockGroup: o.want(ctx, "Chunk", btrfsprim.CHUNK_TREE_OBJECTID, body.ChunkObjectID, @@ -60,22 +60,22 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, item.Key.ObjectID, btrfsitem.FREE_SPACE_INFO_KEY, item.Key.Offset) - case btrfsitem.Chunk: + case *btrfsitem.Chunk: o.want(ctx, "owning Root", btrfsprim.ROOT_TREE_OBJECTID, body.Head.Owner, btrfsitem.ROOT_ITEM_KEY) - case btrfsitem.Dev: + case *btrfsitem.Dev: // nothing - case btrfsitem.DevExtent: + case *btrfsitem.DevExtent: o.wantOff(ctx, "Chunk", body.ChunkTree, body.ChunkObjectID, btrfsitem.CHUNK_ITEM_KEY, uint64(body.ChunkOffset)) - case btrfsitem.DevStats: + case *btrfsitem.DevStats: // nothing - case btrfsitem.DirEntry: + case *btrfsitem.DirEntry: // containing-directory o.wantOff(ctx, "containing dir inode", treeID, @@ -126,9 +126,9 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, o.fsErr(ctx, fmt.Errorf("DirEntry: unexpected .Location.ItemType=%v", body.Location.ItemType)) } } - case btrfsitem.Empty: + case *btrfsitem.Empty: // nothing - case btrfsitem.Extent: + case *btrfsitem.Extent: // if body.Head.Flags.Has(btrfsitem.EXTENT_FLAG_TREE_BLOCK) { // // Supposedly this flag indicates that // // body.Info.Key identifies a node by the @@ -140,7 +140,7 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, switch refBody := ref.Body.(type) { case nil: // nothing - case btrfsitem.ExtentDataRef: + case *btrfsitem.ExtentDataRef: o.wantOff(ctx, "referencing Inode", refBody.Root, refBody.ObjectID, @@ -151,7 +151,7 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, refBody.ObjectID, btrfsitem.EXTENT_DATA_KEY, uint64(refBody.Offset)) - case btrfsitem.SharedDataRef: + case *btrfsitem.SharedDataRef: // nothing default: // This is a panic because the item decoder should not emit a new @@ -159,9 +159,9 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, panic(fmt.Errorf("should not happen: Extent: unexpected .Refs[%d].Body type %T", i, refBody)) } } - case btrfsitem.ExtentCSum: + case *btrfsitem.ExtentCSum: // nothing - case btrfsitem.ExtentDataRef: + case *btrfsitem.ExtentDataRef: o.want(ctx, "Extent being referenced", btrfsprim.EXTENT_TREE_OBJECTID, item.Key.ObjectID, @@ -176,7 +176,7 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, body.ObjectID, btrfsitem.EXTENT_DATA_KEY, uint64(body.Offset)) - case btrfsitem.FileExtent: + case *btrfsitem.FileExtent: o.wantOff(ctx, "containing Inode", treeID, item.Key.ObjectID, @@ -194,19 +194,19 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, default: o.fsErr(ctx, fmt.Errorf("FileExtent: unexpected body.Type=%v", body.Type)) } - case btrfsitem.FreeSpaceBitmap: + case *btrfsitem.FreeSpaceBitmap: o.wantOff(ctx, "FreeSpaceInfo", treeID, item.Key.ObjectID, btrfsitem.FREE_SPACE_INFO_KEY, item.Key.Offset) - case btrfsitem.FreeSpaceHeader: + case *btrfsitem.FreeSpaceHeader: o.wantOff(ctx, ".Location", treeID, body.Location.ObjectID, body.Location.ItemType, body.Location.Offset) - case btrfsitem.FreeSpaceInfo: + case *btrfsitem.FreeSpaceInfo: if body.Flags.Has(btrfsitem.FREE_SPACE_USING_BITMAPS) { o.wantOff(ctx, "FreeSpaceBitmap", treeID, @@ -214,7 +214,7 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, btrfsitem.FREE_SPACE_BITMAP_KEY, item.Key.Offset) } - case btrfsitem.Inode: + case *btrfsitem.Inode: o.want(ctx, "backrefs", treeID, // TODO: validate the number of these against body.NLink item.Key.ObjectID, @@ -227,7 +227,7 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, body.BlockGroup, btrfsitem.BLOCK_GROUP_ITEM_KEY) } - case btrfsitem.InodeRefs: + case *btrfsitem.InodeRefs: o.wantOff(ctx, "child Inode", treeID, item.Key.ObjectID, @@ -238,7 +238,7 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, btrfsprim.ObjID(item.Key.Offset), btrfsitem.INODE_ITEM_KEY, 0) - for _, ref := range body { + for _, ref := range body.Refs { o.wantOff(ctx, "DIR_ITEM", treeID, btrfsprim.ObjID(item.Key.Offset), @@ -250,12 +250,12 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, btrfsitem.DIR_INDEX_KEY, uint64(ref.Index)) } - case btrfsitem.Metadata: + case *btrfsitem.Metadata: for i, ref := range body.Refs { switch refBody := ref.Body.(type) { case nil: // nothing - case btrfsitem.ExtentDataRef: + case *btrfsitem.ExtentDataRef: o.wantOff(ctx, "referencing INode", refBody.Root, refBody.ObjectID, @@ -266,7 +266,7 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, refBody.ObjectID, btrfsitem.EXTENT_DATA_KEY, uint64(refBody.Offset)) - case btrfsitem.SharedDataRef: + case *btrfsitem.SharedDataRef: // nothing default: // This is a panic because the item decoder should not emit a new @@ -274,7 +274,7 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, panic(fmt.Errorf("should not happen: Metadata: unexpected .Refs[%d].Body type %T", i, refBody)) } } - case btrfsitem.Root: + case *btrfsitem.Root: if body.RootDirID != 0 { o.wantOff(ctx, "root directory", item.Key.ObjectID, @@ -298,7 +298,7 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, key.ItemType, key.Offset) } - case btrfsitem.RootRef: + case *btrfsitem.RootRef: var otherType btrfsprim.ItemType var parent, child btrfsprim.ObjID switch item.Key.ItemType { @@ -347,17 +347,17 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID, treeID, child, btrfsitem.ROOT_ITEM_KEY) - case btrfsitem.SharedDataRef: + case *btrfsitem.SharedDataRef: o.want(ctx, "Extent", btrfsprim.EXTENT_TREE_OBJECTID, item.Key.ObjectID, btrfsitem.EXTENT_ITEM_KEY) - case btrfsitem.UUIDMap: + case *btrfsitem.UUIDMap: o.want(ctx, "subvolume Root", btrfsprim.ROOT_TREE_OBJECTID, body.ObjID, btrfsitem.ROOT_ITEM_KEY) - case btrfsitem.Error: + case *btrfsitem.Error: o.fsErr(ctx, fmt.Errorf("error decoding item: %w", body.Err)) default: // This is a panic because the item decoder should not emit new types without this diff --git a/lib/btrfsprogs/btrfsinspect/scandevices.go b/lib/btrfsprogs/btrfsinspect/scandevices.go index 9b8360c..91b5136 100644 --- a/lib/btrfsprogs/btrfsinspect/scandevices.go +++ b/lib/btrfsprogs/btrfsinspect/scandevices.go @@ -183,14 +183,14 @@ func ScanOneDevice(ctx context.Context, dev *btrfs.Device, sb btrfstree.Superblo switch item.Key.ItemType { case btrfsitem.CHUNK_ITEM_KEY: switch itemBody := item.Body.(type) { - case btrfsitem.Chunk: + case *btrfsitem.Chunk: dlog.Tracef(ctx, "node@%v: item %v: found chunk", nodeRef.Addr, i) result.FoundChunks = append(result.FoundChunks, btrfstree.SysChunk{ Key: item.Key, - Chunk: itemBody, + Chunk: *itemBody, }) - case btrfsitem.Error: + case *btrfsitem.Error: dlog.Errorf(ctx, "node@%v: item %v: error: malformed CHUNK_ITEM: %v", nodeRef.Addr, i, itemBody.Err) default: @@ -198,14 +198,14 @@ func ScanOneDevice(ctx context.Context, dev *btrfs.Device, sb btrfstree.Superblo } case btrfsitem.BLOCK_GROUP_ITEM_KEY: switch itemBody := item.Body.(type) { - case btrfsitem.BlockGroup: + case *btrfsitem.BlockGroup: dlog.Tracef(ctx, "node@%v: item %v: found block group", nodeRef.Addr, i) result.FoundBlockGroups = append(result.FoundBlockGroups, SysBlockGroup{ Key: item.Key, - BG: itemBody, + BG: *itemBody, }) - case btrfsitem.Error: + case *btrfsitem.Error: dlog.Errorf(ctx, "node@%v: item %v: error: malformed BLOCK_GROUP_ITEM: %v", nodeRef.Addr, i, itemBody.Err) default: @@ -213,14 +213,14 @@ func ScanOneDevice(ctx context.Context, dev *btrfs.Device, sb btrfstree.Superblo } case btrfsitem.DEV_EXTENT_KEY: switch itemBody := item.Body.(type) { - case btrfsitem.DevExtent: + case *btrfsitem.DevExtent: dlog.Tracef(ctx, "node@%v: item %v: found dev extent", nodeRef.Addr, i) result.FoundDevExtents = append(result.FoundDevExtents, SysDevExtent{ Key: item.Key, - DevExt: itemBody, + DevExt: *itemBody, }) - case btrfsitem.Error: + case *btrfsitem.Error: dlog.Errorf(ctx, "node@%v: item %v: error: malformed DEV_EXTENT: %v", nodeRef.Addr, i, itemBody.Err) default: @@ -228,14 +228,14 @@ func ScanOneDevice(ctx context.Context, dev *btrfs.Device, sb btrfstree.Superblo } case btrfsitem.EXTENT_CSUM_KEY: switch itemBody := item.Body.(type) { - case btrfsitem.ExtentCSum: + case *btrfsitem.ExtentCSum: dlog.Tracef(ctx, "node@%v: item %v: found csums", nodeRef.Addr, i) result.FoundExtentCSums = append(result.FoundExtentCSums, SysExtentCSum{ Generation: nodeRef.Data.Head.Generation, - Sums: itemBody, + Sums: *itemBody, }) - case btrfsitem.Error: + case *btrfsitem.Error: dlog.Errorf(ctx, "node@%v: item %v: error: malformed is EXTENT_CSUM: %v", nodeRef.Addr, i, itemBody.Err) default: -- cgit v1.2.3-54-g00ecf From 11ac4fae146e5f599f34a5fafa27e20fecf713a9 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Fri, 3 Feb 2023 14:29:33 -0700 Subject: btrfsitem: Clean up items_gen.go --- lib/btrfs/Makefile | 20 +++++-- lib/btrfs/btrfsitem/items_gen.go | 123 +++++++++++++++++++++++++++++---------- 2 files changed, 106 insertions(+), 37 deletions(-) (limited to 'lib/btrfs/btrfsitem') diff --git a/lib/btrfs/Makefile b/lib/btrfs/Makefile index 00c22e7..ac39425 100644 --- a/lib/btrfs/Makefile +++ b/lib/btrfs/Makefile @@ -27,16 +27,26 @@ btrfsitem/items_gen.go: btrfsitem/items.txt $(MAKEFILE_LIST) echo '"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim"'; \ echo ')'; \ echo 'const ('; \ - sed -E 's,(.*)=(.*) (.*),\1_KEY=btrfsprim.\1_KEY,' $<; \ + sed -E 's/(.*)=(.*) (.*)/\1_KEY=btrfsprim.\1_KEY/' $<; \ echo ')'; \ + echo 'var ('; \ + sed -E 's/(.*)=(.*) (.*)/\3/p' $< | LC_COLLATE=C sort -u | sed 's/.*/\l&Type = reflect.TypeOf(&{})/'; \ + echo ')'; \ + echo '// keytype2gotype is used by UnmarshalItem.'; \ echo 'var keytype2gotype = map[Type]reflect.Type{'; \ - sed -En 's|(.*)=([^:]*) (.*)|\1_KEY: reflect.TypeOf(\3{}),|p' $<; \ + sed -En 's/(.*)=([^:]*) (.*)/\1_KEY: \l\3Type,/p' $<; \ echo '}'; \ + echo '// untypedObjID2gotype is used by UnmarshalItem.'; \ echo 'var untypedObjID2gotype = map[btrfsprim.ObjID]reflect.Type{'; \ - sed -En 's|UNTYPED=0:(.*) (.*)|btrfsprim.\1: reflect.TypeOf(\2{}),|p' $<; \ + sed -En 's/UNTYPED=0:(.*) (.*)/btrfsprim.\1: \l\2Type,/p' $<; \ echo '}'; \ - sed -En 's,(.*)=(.*) (.+),\3,p' $< | LC_COLLATE=C sort -u | sed 's,.*,func (*&) isItem() {},'; \ - } | gofmt >$@ + echo '// isItem implements Item.'; \ + sed -En 's/(.*)=(.*) (.+)/\3/p' $< | LC_COLLATE=C sort -u | sed 's/.*/func (*&) isItem() {}/'; \ + echo '// Type assertions.'; \ + echo 'var ('; \ + sed -En 's/(.*)=(.*) (.+)/\3/p' $< | LC_COLLATE=C sort -u | sed 's/.*/_ Item = (*&)(nil)/'; \ + echo ')'; \ + } | sed 's/uUID/uuid/g' | gofmt >$@ files += btrfsitem/items_gen.go btrfsprim/itemtype.go: btrfsitem/items.txt $(MAKEFILE_LIST) diff --git a/lib/btrfs/btrfsitem/items_gen.go b/lib/btrfs/btrfsitem/items_gen.go index 21c6795..8074610 100644 --- a/lib/btrfs/btrfsitem/items_gen.go +++ b/lib/btrfs/btrfsitem/items_gen.go @@ -43,43 +43,74 @@ const ( XATTR_ITEM_KEY = btrfsprim.XATTR_ITEM_KEY ) +var ( + blockGroupType = reflect.TypeOf(BlockGroup{}) + chunkType = reflect.TypeOf(Chunk{}) + devType = reflect.TypeOf(Dev{}) + devExtentType = reflect.TypeOf(DevExtent{}) + devStatsType = reflect.TypeOf(DevStats{}) + dirEntryType = reflect.TypeOf(DirEntry{}) + emptyType = reflect.TypeOf(Empty{}) + extentType = reflect.TypeOf(Extent{}) + extentCSumType = reflect.TypeOf(ExtentCSum{}) + extentDataRefType = reflect.TypeOf(ExtentDataRef{}) + fileExtentType = reflect.TypeOf(FileExtent{}) + freeSpaceBitmapType = reflect.TypeOf(FreeSpaceBitmap{}) + freeSpaceHeaderType = reflect.TypeOf(FreeSpaceHeader{}) + freeSpaceInfoType = reflect.TypeOf(FreeSpaceInfo{}) + inodeType = reflect.TypeOf(Inode{}) + inodeRefsType = reflect.TypeOf(InodeRefs{}) + metadataType = reflect.TypeOf(Metadata{}) + qGroupInfoType = reflect.TypeOf(QGroupInfo{}) + qGroupLimitType = reflect.TypeOf(QGroupLimit{}) + qGroupStatusType = reflect.TypeOf(QGroupStatus{}) + rootType = reflect.TypeOf(Root{}) + rootRefType = reflect.TypeOf(RootRef{}) + sharedDataRefType = reflect.TypeOf(SharedDataRef{}) + uuidMapType = reflect.TypeOf(UUIDMap{}) +) + +// keytype2gotype is used by UnmarshalItem. 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(DirEntry{}), - DIR_ITEM_KEY: reflect.TypeOf(DirEntry{}), - 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(InodeRefs{}), - METADATA_ITEM_KEY: reflect.TypeOf(Metadata{}), - ORPHAN_ITEM_KEY: reflect.TypeOf(Empty{}), - PERSISTENT_ITEM_KEY: reflect.TypeOf(DevStats{}), - QGROUP_INFO_KEY: reflect.TypeOf(QGroupInfo{}), - QGROUP_LIMIT_KEY: reflect.TypeOf(QGroupLimit{}), - QGROUP_RELATION_KEY: reflect.TypeOf(Empty{}), - QGROUP_STATUS_KEY: reflect.TypeOf(QGroupStatus{}), - 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(DirEntry{}), + BLOCK_GROUP_ITEM_KEY: blockGroupType, + CHUNK_ITEM_KEY: chunkType, + DEV_EXTENT_KEY: devExtentType, + DEV_ITEM_KEY: devType, + DIR_INDEX_KEY: dirEntryType, + DIR_ITEM_KEY: dirEntryType, + EXTENT_CSUM_KEY: extentCSumType, + EXTENT_DATA_KEY: fileExtentType, + EXTENT_DATA_REF_KEY: extentDataRefType, + EXTENT_ITEM_KEY: extentType, + FREE_SPACE_BITMAP_KEY: freeSpaceBitmapType, + FREE_SPACE_EXTENT_KEY: emptyType, + FREE_SPACE_INFO_KEY: freeSpaceInfoType, + INODE_ITEM_KEY: inodeType, + INODE_REF_KEY: inodeRefsType, + METADATA_ITEM_KEY: metadataType, + ORPHAN_ITEM_KEY: emptyType, + PERSISTENT_ITEM_KEY: devStatsType, + QGROUP_INFO_KEY: qGroupInfoType, + QGROUP_LIMIT_KEY: qGroupLimitType, + QGROUP_RELATION_KEY: emptyType, + QGROUP_STATUS_KEY: qGroupStatusType, + ROOT_BACKREF_KEY: rootRefType, + ROOT_ITEM_KEY: rootType, + ROOT_REF_KEY: rootRefType, + SHARED_BLOCK_REF_KEY: emptyType, + SHARED_DATA_REF_KEY: sharedDataRefType, + TREE_BLOCK_REF_KEY: emptyType, + UUID_RECEIVED_SUBVOL_KEY: uuidMapType, + UUID_SUBVOL_KEY: uuidMapType, + XATTR_ITEM_KEY: dirEntryType, } + +// untypedObjID2gotype is used by UnmarshalItem. var untypedObjID2gotype = map[btrfsprim.ObjID]reflect.Type{ - btrfsprim.FREE_SPACE_OBJECTID: reflect.TypeOf(FreeSpaceHeader{}), + btrfsprim.FREE_SPACE_OBJECTID: freeSpaceHeaderType, } +// isItem implements Item. func (*BlockGroup) isItem() {} func (*Chunk) isItem() {} func (*Dev) isItem() {} @@ -104,3 +135,31 @@ func (*Root) isItem() {} func (*RootRef) isItem() {} func (*SharedDataRef) isItem() {} func (*UUIDMap) isItem() {} + +// Type assertions. +var ( + _ Item = (*BlockGroup)(nil) + _ Item = (*Chunk)(nil) + _ Item = (*Dev)(nil) + _ Item = (*DevExtent)(nil) + _ Item = (*DevStats)(nil) + _ Item = (*DirEntry)(nil) + _ Item = (*Empty)(nil) + _ Item = (*Extent)(nil) + _ Item = (*ExtentCSum)(nil) + _ Item = (*ExtentDataRef)(nil) + _ Item = (*FileExtent)(nil) + _ Item = (*FreeSpaceBitmap)(nil) + _ Item = (*FreeSpaceHeader)(nil) + _ Item = (*FreeSpaceInfo)(nil) + _ Item = (*Inode)(nil) + _ Item = (*InodeRefs)(nil) + _ Item = (*Metadata)(nil) + _ Item = (*QGroupInfo)(nil) + _ Item = (*QGroupLimit)(nil) + _ Item = (*QGroupStatus)(nil) + _ Item = (*Root)(nil) + _ Item = (*RootRef)(nil) + _ Item = (*SharedDataRef)(nil) + _ Item = (*UUIDMap)(nil) +) -- cgit v1.2.3-54-g00ecf From 21e92e5dea4d8efc65403eeaee91b32856b86cb6 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Fri, 3 Feb 2023 13:04:17 -0700 Subject: btrfsitem: Add `Free` and `CloneItem` methods to Items --- lib/btrfs/Makefile | 42 ++++-- lib/btrfs/btrfsitem/item_blockgroup.go | 4 +- lib/btrfs/btrfsitem/item_chunk.go | 28 +++- lib/btrfs/btrfsitem/item_dev.go | 4 +- lib/btrfs/btrfsitem/item_devextent.go | 4 +- lib/btrfs/btrfsitem/item_dir.go | 21 ++- lib/btrfs/btrfsitem/item_empty.go | 4 +- lib/btrfs/btrfsitem/item_extent.go | 36 ++++- lib/btrfs/btrfsitem/item_extentcsum.go | 4 +- lib/btrfs/btrfsitem/item_extentdataref.go | 4 +- lib/btrfs/btrfsitem/item_fileextent.go | 17 ++- lib/btrfs/btrfsitem/item_freespacebitmap.go | 17 ++- lib/btrfs/btrfsitem/item_freespaceinfo.go | 4 +- lib/btrfs/btrfsitem/item_inode.go | 4 +- lib/btrfs/btrfsitem/item_inoderef.go | 30 +++- lib/btrfs/btrfsitem/item_metadata.go | 31 +++- lib/btrfs/btrfsitem/item_persistent.go | 4 +- lib/btrfs/btrfsitem/item_qgroupinfo.go | 2 +- lib/btrfs/btrfsitem/item_qgrouplimit.go | 2 +- lib/btrfs/btrfsitem/item_qgroupstatus.go | 2 +- lib/btrfs/btrfsitem/item_root.go | 4 +- lib/btrfs/btrfsitem/item_rootref.go | 17 ++- lib/btrfs/btrfsitem/item_shareddataref.go | 4 +- lib/btrfs/btrfsitem/item_untyped.go | 4 +- lib/btrfs/btrfsitem/item_uuid.go | 2 +- lib/btrfs/btrfsitem/items.go | 55 +++++-- lib/btrfs/btrfsitem/items_gen.go | 226 +++++++++++++++++++++++++++- lib/btrfs/io4_fs.go | 10 +- lib/containers/slicepool.go | 35 +++++ 29 files changed, 539 insertions(+), 82 deletions(-) create mode 100644 lib/containers/slicepool.go (limited to 'lib/btrfs/btrfsitem') diff --git a/lib/btrfs/Makefile b/lib/btrfs/Makefile index ac39425..a1fe747 100644 --- a/lib/btrfs/Makefile +++ b/lib/btrfs/Makefile @@ -8,9 +8,9 @@ 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 \ + sed -En 's,^type (\S+) .* // (trivial|complex) (.*=.*),\1 \2 \3,p' $(filter btrfsitem/item_%.go,$^) | while read -r typ class keys; do \ for key in $$keys; do \ - echo "$$key" "$$typ"; \ + echo "$$key" "$$class" "$$typ"; \ done; \ done; \ } | LC_COLLATE=C sort >$@ @@ -24,27 +24,47 @@ btrfsitem/items_gen.go: btrfsitem/items.txt $(MAKEFILE_LIST) echo 'import ('; \ echo '"reflect"'; \ echo; \ + echo '"git.lukeshu.com/go/typedsync"'; \ + echo; \ echo '"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim"'; \ echo ')'; \ echo 'const ('; \ - sed -E 's/(.*)=(.*) (.*)/\1_KEY=btrfsprim.\1_KEY/' $<; \ + sed -E 's/(.*)=(.*) (trivial|complex) (.*)/\1_KEY=btrfsprim.\1_KEY/' $<; \ echo ')'; \ echo 'var ('; \ - sed -E 's/(.*)=(.*) (.*)/\3/p' $< | LC_COLLATE=C sort -u | sed 's/.*/\l&Type = reflect.TypeOf(&{})/'; \ + sed -E 's/(.*)=(.*) (trivial|complex) (.*)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/\l&Type = reflect.TypeOf(&{})/'; \ echo ')'; \ echo '// keytype2gotype is used by UnmarshalItem.'; \ echo 'var keytype2gotype = map[Type]reflect.Type{'; \ - sed -En 's/(.*)=([^:]*) (.*)/\1_KEY: \l\3Type,/p' $<; \ + sed -En 's/(.*)=([^:]*) (trivial|complex) (.*)/\1_KEY: \l\4Type,/p' $<; \ echo '}'; \ echo '// untypedObjID2gotype is used by UnmarshalItem.'; \ echo 'var untypedObjID2gotype = map[btrfsprim.ObjID]reflect.Type{'; \ - sed -En 's/UNTYPED=0:(.*) (.*)/btrfsprim.\1: \l\2Type,/p' $<; \ + sed -En 's/UNTYPED=0:(.*) (trivial|complex) (.*)/btrfsprim.\1: \l\3Type,/p' $<; \ + echo '}'; \ + echo '// Pools.'; \ + echo 'var ('; \ + sed -E 's/(.*)=(.*) (trivial|complex) (.*)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/\l&Pool = typedsync.Pool[Item]{New: func() Item { return new(&) }}/'; \ + echo ')'; \ + echo '// gotype2pool is used by UnmarshalItem.'; \ + echo 'var gotype2pool = map[reflect.Type]*typedsync.Pool[Item]{'; \ + sed -E 's/(.*)=(.*) (trivial|complex) (.*)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/\l&Type: \&\l&Pool,/'; \ echo '}'; \ echo '// isItem implements Item.'; \ - sed -En 's/(.*)=(.*) (.+)/\3/p' $< | LC_COLLATE=C sort -u | sed 's/.*/func (*&) isItem() {}/'; \ - echo '// Type assertions.'; \ + sed -En 's/(.*)=(.*) (trivial|complex) (.+)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/func (*&) isItem() {}/'; \ + echo '// Free implements Item.'; \ + sed -En 's/(.*)=(.*) (trivial) (.+)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/func (o *&) Free() {*o = &{}; \l&Pool.Put(o)}/'; \ + echo '// Clone is a handy method.'; \ + sed -En 's/(.*)=(.*) (trivial) (.+)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/func (o &) Clone() & { return o }/'; \ + echo '// CloneItem implements Item.'; \ + sed -En 's/(.*)=(.*) (trivial|complex) (.+)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/func (o *&) CloneItem() Item { ret, _ := \l&Pool.Get(); *(ret.(*&)) = o.Clone(); return ret }/'; \ + echo '// Item type assertions.'; \ + echo 'var ('; \ + sed -En 's/(.*)=(.*) (trivial|complex) (.+)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/_ Item = (*&)(nil)/'; \ + echo ')'; \ + echo '// Clone type assertions.'; \ echo 'var ('; \ - sed -En 's/(.*)=(.*) (.+)/\3/p' $< | LC_COLLATE=C sort -u | sed 's/.*/_ Item = (*&)(nil)/'; \ + sed -En 's/(.*)=(.*) (trivial|complex) (.+)/\4/p' $< | LC_COLLATE=C sort -u | sed 's/.*/_ interface{ Clone() & } = &{}/'; \ echo ')'; \ } | sed 's/uUID/uuid/g' | gofmt >$@ files += btrfsitem/items_gen.go @@ -57,11 +77,11 @@ btrfsprim/itemtype.go: btrfsitem/items.txt $(MAKEFILE_LIST) echo 'import "fmt"'; \ echo 'type ItemType uint8'; \ echo 'const ('; \ - sed -E 's,(.*)=([^:]*)(:.*)? (.*),\1_KEY=ItemType(\2),' $< | uniq; \ + sed -E 's,(.*)=([^:]*)(:.*)? (trivial|complex) (.*),\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_/'; \ + sed -E 's@(.*)=(.*) (trivial|complex) (.*)@\1_KEY: "\1",@' $< | sed 's/"UUID_/&KEY_/'; \ echo ' }'; \ echo ' if name, ok := names[t]; ok {'; \ echo ' return name'; \ diff --git a/lib/btrfs/btrfsitem/item_blockgroup.go b/lib/btrfs/btrfsitem/item_blockgroup.go index 6fc09ac..ae0ca12 100644 --- a/lib/btrfs/btrfsitem/item_blockgroup.go +++ b/lib/btrfs/btrfsitem/item_blockgroup.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,7 +12,7 @@ import ( // key.objectid = logical_addr // key.offset = size of chunk -type BlockGroup struct { // BLOCK_GROUP_ITEM=192 +type BlockGroup struct { // trivial BLOCK_GROUP_ITEM=192 Used int64 `bin:"off=0, siz=8"` ChunkObjectID btrfsprim.ObjID `bin:"off=8, siz=8"` // always FIRST_CHUNK_TREE_OBJECTID Flags btrfsvol.BlockGroupFlags `bin:"off=16, siz=8"` diff --git a/lib/btrfs/btrfsitem/item_chunk.go b/lib/btrfs/btrfsitem/item_chunk.go index e0776ad..2280a0b 100644 --- a/lib/btrfs/btrfsitem/item_chunk.go +++ b/lib/btrfs/btrfsitem/item_chunk.go @@ -15,7 +15,7 @@ import ( // // key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID // key.offset = logical_addr -type Chunk struct { // CHUNK_ITEM=228 +type Chunk struct { // complex CHUNK_ITEM=228 Head ChunkHeader Stripes []ChunkStripe } @@ -60,20 +60,36 @@ func (chunk Chunk) Mappings(key btrfsprim.Key) []btrfsvol.Mapping { return ret } +var chunkStripePool containers.SlicePool[ChunkStripe] + +func (chunk *Chunk) Free() { + for i := range chunk.Stripes { + chunk.Stripes[i] = ChunkStripe{} + } + chunkStripePool.Put(chunk.Stripes) + *chunk = Chunk{} + chunkPool.Put(chunk) +} + +func (chunk Chunk) Clone() Chunk { + ret := chunk + ret.Stripes = chunkStripePool.Get(len(chunk.Stripes)) + copy(ret.Stripes, chunk.Stripes) + 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 = make([]ChunkStripe, 0, chunk.Head.NumStripes) - for i := 0; i < int(chunk.Head.NumStripes); i++ { - var stripe ChunkStripe - _n, err := binstruct.Unmarshal(dat[n:], &stripe) + chunk.Stripes = chunkStripePool.Get(int(chunk.Head.NumStripes)) + for i := range chunk.Stripes { + _n, err := binstruct.Unmarshal(dat[n:], &chunk.Stripes[i]) n += _n if err != nil { return n, err } - chunk.Stripes = append(chunk.Stripes, stripe) } return n, nil } diff --git a/lib/btrfs/btrfsitem/item_dev.go b/lib/btrfs/btrfsitem/item_dev.go index fd7f458..188711e 100644 --- a/lib/btrfs/btrfsitem/item_dev.go +++ b/lib/btrfs/btrfsitem/item_dev.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,7 +12,7 @@ import ( // key.objectid = BTRFS_DEV_ITEMS_OBJECTID // key.offset = device_id (starting at 1) -type Dev struct { // DEV_ITEM=216 +type Dev struct { // trivial DEV_ITEM=216 DevID btrfsvol.DeviceID `bin:"off=0x0, siz=0x8"` NumBytes uint64 `bin:"off=0x8, siz=0x8"` diff --git a/lib/btrfs/btrfsitem/item_devextent.go b/lib/btrfs/btrfsitem/item_devextent.go index 47bdbcf..cade165 100644 --- a/lib/btrfs/btrfsitem/item_devextent.go +++ b/lib/btrfs/btrfsitem/item_devextent.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,7 +12,7 @@ import ( // key.objectid = device_id // key.offset = physical_addr -type DevExtent struct { // DEV_EXTENT=204 +type DevExtent struct { // trivial DEV_EXTENT=204 ChunkTree btrfsprim.ObjID `bin:"off=0, siz=8"` // always CHUNK_TREE_OBJECTID ChunkObjectID btrfsprim.ObjID `bin:"off=8, siz=8"` // which chunk within .ChunkTree owns this extent, always FIRST_CHUNK_TREE_OBJECTID ChunkOffset btrfsvol.LogicalAddr `bin:"off=16, siz=8"` // offset of the CHUNK_ITEM that owns this extent, within the .ChunkObjectID diff --git a/lib/btrfs/btrfsitem/item_dir.go b/lib/btrfs/btrfsitem/item_dir.go index 584e44d..0049072 100644 --- a/lib/btrfs/btrfsitem/item_dir.go +++ b/lib/btrfs/btrfsitem/item_dir.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -23,7 +23,7 @@ func NameHash(dat []byte) uint64 { // key.offset = // - for DIR_ITEM and XATTR_ITEM = NameHash(name) // - for DIR_INDEX = index id in the directory (starting at 2, because "." and "..") -type DirEntry struct { // DIR_ITEM=84 DIR_INDEX=96 XATTR_ITEM=24 +type DirEntry struct { // complex DIR_ITEM=84 DIR_INDEX=96 XATTR_ITEM=24 Location btrfsprim.Key `bin:"off=0x0, siz=0x11"` TransID int64 `bin:"off=0x11, siz=8"` DataLen uint16 `bin:"off=0x19, siz=2"` // [ignored-when-writing] @@ -34,6 +34,19 @@ type DirEntry struct { // DIR_ITEM=84 DIR_INDEX=96 XATTR_ITEM=24 Name []byte `bin:"-"` } +func (o *DirEntry) Free() { + bytePool.Put(o.Data) + bytePool.Put(o.Name) + *o = DirEntry{} + dirEntryPool.Put(o) +} + +func (o DirEntry) Clone() DirEntry { + o.Data = cloneBytes(o.Data) + o.Name = cloneBytes(o.Name) + return o +} + func (o *DirEntry) UnmarshalBinary(dat []byte) (int, error) { if err := binutil.NeedNBytes(dat, 0x1e); err != nil { return 0, err @@ -49,9 +62,9 @@ func (o *DirEntry) UnmarshalBinary(dat []byte) (int, error) { if err := binutil.NeedNBytes(dat, 0x1e+int(o.DataLen)+int(o.NameLen)); err != nil { return 0, err } - o.Name = dat[n : n+int(o.NameLen)] + o.Name = cloneBytes(dat[n : n+int(o.NameLen)]) n += int(o.NameLen) - o.Data = dat[n : n+int(o.DataLen)] + o.Data = cloneBytes(dat[n : n+int(o.DataLen)]) n += int(o.DataLen) return n, nil } diff --git a/lib/btrfs/btrfsitem/item_empty.go b/lib/btrfs/btrfsitem/item_empty.go index 47b4a15..7343c8f 100644 --- a/lib/btrfs/btrfsitem/item_empty.go +++ b/lib/btrfs/btrfsitem/item_empty.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,6 +8,6 @@ import ( "git.lukeshu.com/btrfs-progs-ng/lib/binstruct" ) -type Empty struct { // ORPHAN_ITEM=48 TREE_BLOCK_REF=176 SHARED_BLOCK_REF=182 FREE_SPACE_EXTENT=199 QGROUP_RELATION=246 +type Empty struct { // trivial 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/lib/btrfs/btrfsitem/item_extent.go b/lib/btrfs/btrfsitem/item_extent.go index 72371a9..3789cfe 100644 --- a/lib/btrfs/btrfsitem/item_extent.go +++ b/lib/btrfs/btrfsitem/item_extent.go @@ -9,18 +9,44 @@ import ( "git.lukeshu.com/btrfs-progs-ng/lib/binstruct" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim" + "git.lukeshu.com/btrfs-progs-ng/lib/containers" "git.lukeshu.com/btrfs-progs-ng/lib/fmtutil" ) // key.objectid = laddr of the extent // key.offset = length of the extent -type Extent struct { // EXTENT_ITEM=168 +type Extent struct { // complex EXTENT_ITEM=168 Head ExtentHeader Info TreeBlockInfo // only if .Head.Flags.Has(EXTENT_FLAG_TREE_BLOCK) Refs []ExtentInlineRef } +var extentInlineRefPool containers.SlicePool[ExtentInlineRef] + +func (o *Extent) Free() { + for i := range o.Refs { + if o.Refs[i].Body != nil { + o.Refs[i].Body.Free() + } + o.Refs[i] = ExtentInlineRef{} + } + extentInlineRefPool.Put(o.Refs) + *o = Extent{} + extentPool.Put(o) +} + +func (o Extent) Clone() Extent { + ret := o + ret.Refs = extentInlineRefPool.Get(len(o.Refs)) + copy(ret.Refs, o.Refs) + for i := range ret.Refs { + ret.Refs[i].Body = o.Refs[i].Body.CloneItem() + } + return ret +} + func (o *Extent) UnmarshalBinary(dat []byte) (int, error) { + *o = Extent{} n, err := binstruct.Unmarshal(dat, &o.Head) if err != nil { return n, err @@ -32,7 +58,9 @@ func (o *Extent) UnmarshalBinary(dat []byte) (int, error) { return n, err } } - o.Refs = nil + if n < len(dat) { + o.Refs = extentInlineRefPool.Get(1)[:0] + } for n < len(dat) { var ref ExtentInlineRef _n, err := binstruct.Unmarshal(dat[n:], &ref) @@ -114,7 +142,7 @@ func (o *ExtentInlineRef) UnmarshalBinary(dat []byte) (int, error) { return n, err } case EXTENT_DATA_REF_KEY: - dref := new(ExtentDataRef) + dref, _ := extentDataRefPool.Get() _n, err := binstruct.Unmarshal(dat[n:], dref) n += _n o.Body = dref @@ -127,7 +155,7 @@ func (o *ExtentInlineRef) UnmarshalBinary(dat []byte) (int, error) { if err != nil { return n, err } - sref := new(SharedDataRef) + sref, _ := sharedDataRefPool.Get() _n, err = binstruct.Unmarshal(dat[n:], sref) n += _n o.Body = sref diff --git a/lib/btrfs/btrfsitem/item_extentcsum.go b/lib/btrfs/btrfsitem/item_extentcsum.go index bcfe334..dfa166d 100644 --- a/lib/btrfs/btrfsitem/item_extentcsum.go +++ b/lib/btrfs/btrfsitem/item_extentcsum.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -13,7 +13,7 @@ import ( // key.objectid = BTRFS_EXTENT_CSUM_OBJECTID // key.offset = laddr of checksummed region -type ExtentCSum struct { // EXTENT_CSUM=128 +type ExtentCSum struct { // trivial EXTENT_CSUM=128 // Checksum of each sector starting at key.offset btrfssum.SumRun[btrfsvol.LogicalAddr] } diff --git a/lib/btrfs/btrfsitem/item_extentdataref.go b/lib/btrfs/btrfsitem/item_extentdataref.go index 8c856e2..6f2257b 100644 --- a/lib/btrfs/btrfsitem/item_extentdataref.go +++ b/lib/btrfs/btrfsitem/item_extentdataref.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,7 +11,7 @@ import ( // key.objectid = laddr of the extent being referenced // key.offset = crc32c([root,objectid,offset]) -type ExtentDataRef struct { // EXTENT_DATA_REF=178 +type ExtentDataRef struct { // trivial EXTENT_DATA_REF=178 Root btrfsprim.ObjID `bin:"off=0, siz=8"` // subvolume tree ID that references this extent ObjectID btrfsprim.ObjID `bin:"off=8, siz=8"` // inode number that references this extent within the .Root subvolume Offset int64 `bin:"off=16, siz=8"` // byte offset for the extent within the file diff --git a/lib/btrfs/btrfsitem/item_fileextent.go b/lib/btrfs/btrfsitem/item_fileextent.go index 83e5d34..30a14ef 100644 --- a/lib/btrfs/btrfsitem/item_fileextent.go +++ b/lib/btrfs/btrfsitem/item_fileextent.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,7 +14,7 @@ import ( // key.objectid = inode // key.offset = offset within file -type FileExtent struct { // EXTENT_DATA=108 +type FileExtent struct { // complex EXTENT_DATA=108 Generation btrfsprim.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 @@ -46,6 +46,17 @@ type FileExtentExtent struct { binstruct.End `bin:"off=0x20"` } +func (o *FileExtent) Free() { + bytePool.Put(o.BodyInline) + *o = FileExtent{} + fileExtentPool.Put(o) +} + +func (o FileExtent) Clone() FileExtent { + o.BodyInline = cloneBytes(o.BodyInline) + return o +} + func (o *FileExtent) UnmarshalBinary(dat []byte) (int, error) { n, err := binstruct.UnmarshalWithoutInterface(dat, o) if err != nil { @@ -53,7 +64,7 @@ func (o *FileExtent) UnmarshalBinary(dat []byte) (int, error) { } switch o.Type { case FILE_EXTENT_INLINE: - o.BodyInline = dat[n:] + o.BodyInline = cloneBytes(dat[n:]) n += len(o.BodyInline) case FILE_EXTENT_REG, FILE_EXTENT_PREALLOC: _n, err := binstruct.Unmarshal(dat[n:], &o.BodyExtent) diff --git a/lib/btrfs/btrfsitem/item_freespacebitmap.go b/lib/btrfs/btrfsitem/item_freespacebitmap.go index 742d126..ebc00e4 100644 --- a/lib/btrfs/btrfsitem/item_freespacebitmap.go +++ b/lib/btrfs/btrfsitem/item_freespacebitmap.go @@ -6,15 +6,26 @@ package btrfsitem // key.objectid = object ID of the FreeSpaceInfo (logical_addr) // key.offset = offset of the FreeSpaceInfo (size) -type FreeSpaceBitmap struct { // FREE_SPACE_BITMAP=200 +type FreeSpaceBitmap struct { // complex FREE_SPACE_BITMAP=200 Bitmap []byte } +func (o *FreeSpaceBitmap) Free() { + bytePool.Put(o.Bitmap) + *o = FreeSpaceBitmap{} + freeSpaceBitmapPool.Put(o) +} + +func (o FreeSpaceBitmap) Clone() FreeSpaceBitmap { + o.Bitmap = cloneBytes(o.Bitmap) + return o +} + func (o *FreeSpaceBitmap) UnmarshalBinary(dat []byte) (int, error) { - o.Bitmap = dat + o.Bitmap = cloneBytes(dat) return len(dat), nil } func (o FreeSpaceBitmap) MarshalBinary() ([]byte, error) { - return o.Bitmap, nil + return append([]byte(nil), o.Bitmap...), nil } diff --git a/lib/btrfs/btrfsitem/item_freespaceinfo.go b/lib/btrfs/btrfsitem/item_freespaceinfo.go index b38da20..0699367 100644 --- a/lib/btrfs/btrfsitem/item_freespaceinfo.go +++ b/lib/btrfs/btrfsitem/item_freespaceinfo.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,7 +11,7 @@ import ( // key.objectid = object ID of the BlockGroup (logical_addr) // key.offset = offset of the BlockGroup (size) -type FreeSpaceInfo struct { // FREE_SPACE_INFO=198 +type FreeSpaceInfo struct { // trivial FREE_SPACE_INFO=198 ExtentCount int32 `bin:"off=0, siz=4"` Flags FreeSpaceFlags `bin:"off=4, siz=4"` binstruct.End `bin:"off=8"` diff --git a/lib/btrfs/btrfsitem/item_inode.go b/lib/btrfs/btrfsitem/item_inode.go index 704b56a..69f8445 100644 --- a/lib/btrfs/btrfsitem/item_inode.go +++ b/lib/btrfs/btrfsitem/item_inode.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,7 +12,7 @@ import ( // key.objectid = inode number // key.offset = 0 -type Inode struct { // INODE_ITEM=1 +type Inode struct { // trivial INODE_ITEM=1 Generation btrfsprim.Generation `bin:"off=0x00, siz=0x08"` TransID int64 `bin:"off=0x08, siz=0x08"` Size int64 `bin:"off=0x10, siz=0x08"` // stat diff --git a/lib/btrfs/btrfsitem/item_inoderef.go b/lib/btrfs/btrfsitem/item_inoderef.go index c90fe44..074b26d 100644 --- a/lib/btrfs/btrfsitem/item_inoderef.go +++ b/lib/btrfs/btrfsitem/item_inoderef.go @@ -9,6 +9,7 @@ import ( "git.lukeshu.com/btrfs-progs-ng/lib/binstruct" "git.lukeshu.com/btrfs-progs-ng/lib/binstruct/binutil" + "git.lukeshu.com/btrfs-progs-ng/lib/containers" ) // key.objectid = inode number of the file @@ -16,12 +17,37 @@ import ( // // Might have multiple entries if the same file has multiple hardlinks // in the same directory. -type InodeRefs struct { // INODE_REF=12 +type InodeRefs struct { // complex INODE_REF=12 Refs []InodeRef } +var inodeRefPool containers.SlicePool[InodeRef] + +func (o *InodeRefs) Free() { + for i := range o.Refs { + bytePool.Put(o.Refs[i].Name) + o.Refs[i] = InodeRef{} + } + inodeRefPool.Put(o.Refs) + *o = InodeRefs{} + inodeRefsPool.Put(o) +} + +func (o InodeRefs) Clone() InodeRefs { + var ret InodeRefs + ret.Refs = inodeRefPool.Get(len(o.Refs)) + copy(ret.Refs, o.Refs) + for i := range ret.Refs { + ret.Refs[i].Name = cloneBytes(o.Refs[i].Name) + } + return ret +} + func (o *InodeRefs) UnmarshalBinary(dat []byte) (int, error) { o.Refs = nil + if len(dat) > 0 { + o.Refs = inodeRefPool.Get(1)[:0] + } n := 0 for n < len(dat) { var ref InodeRef @@ -70,7 +96,7 @@ func (o *InodeRef) UnmarshalBinary(dat []byte) (int, error) { return 0, err } dat = dat[n:] - o.Name = dat[:o.NameLen] + o.Name = cloneBytes(dat[:o.NameLen]) n += int(o.NameLen) return n, nil } diff --git a/lib/btrfs/btrfsitem/item_metadata.go b/lib/btrfs/btrfsitem/item_metadata.go index e90af8d..db2315e 100644 --- a/lib/btrfs/btrfsitem/item_metadata.go +++ b/lib/btrfs/btrfsitem/item_metadata.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,17 +9,42 @@ import ( ) // Metadata is like Extent, but doesn't have .Info. -type Metadata struct { // METADATA_ITEM=169 +type Metadata struct { // complex METADATA_ITEM=169 Head ExtentHeader Refs []ExtentInlineRef } +func (o *Metadata) Free() { + for i := range o.Refs { + if o.Refs[i].Body != nil { + o.Refs[i].Body.Free() + } + o.Refs[i] = ExtentInlineRef{} + } + extentInlineRefPool.Put(o.Refs) + *o = Metadata{} + metadataPool.Put(o) +} + +func (o Metadata) Clone() Metadata { + ret := o + ret.Refs = extentInlineRefPool.Get(len(o.Refs)) + copy(ret.Refs, o.Refs) + for i := range ret.Refs { + ret.Refs[i].Body = o.Refs[i].Body.CloneItem() + } + return ret +} + func (o *Metadata) UnmarshalBinary(dat []byte) (int, error) { + *o = Metadata{} n, err := binstruct.Unmarshal(dat, &o.Head) if err != nil { return n, err } - o.Refs = nil + if n < len(dat) { + o.Refs = extentInlineRefPool.Get(1)[:0] + } for n < len(dat) { var ref ExtentInlineRef _n, err := binstruct.Unmarshal(dat[n:], &ref) diff --git a/lib/btrfs/btrfsitem/item_persistent.go b/lib/btrfs/btrfsitem/item_persistent.go index a827074..4655aee 100644 --- a/lib/btrfs/btrfsitem/item_persistent.go +++ b/lib/btrfs/btrfsitem/item_persistent.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -17,7 +17,7 @@ const ( DEV_STAT_VALUES_MAX ) -type DevStats struct { // PERSISTENT_ITEM=249 +type DevStats struct { // trivial PERSISTENT_ITEM=249 Values [DEV_STAT_VALUES_MAX]int64 `bin:"off=0, siz=40"` binstruct.End `bin:"off=40"` } diff --git a/lib/btrfs/btrfsitem/item_qgroupinfo.go b/lib/btrfs/btrfsitem/item_qgroupinfo.go index 8cceb0b..6699030 100644 --- a/lib/btrfs/btrfsitem/item_qgroupinfo.go +++ b/lib/btrfs/btrfsitem/item_qgroupinfo.go @@ -11,7 +11,7 @@ import ( // key.objectid = 0 // key.offset = ID of the qgroup -type QGroupInfo struct { // QGROUP_INFO=242 +type QGroupInfo struct { // trivial QGROUP_INFO=242 Generation btrfsprim.Generation `bin:"off=0, siz=8"` ReferencedBytes uint64 `bin:"off=8, siz=8"` ReferencedBytesCompressed uint64 `bin:"off=16, siz=8"` diff --git a/lib/btrfs/btrfsitem/item_qgrouplimit.go b/lib/btrfs/btrfsitem/item_qgrouplimit.go index 9d1e05f..47f7eca 100644 --- a/lib/btrfs/btrfsitem/item_qgrouplimit.go +++ b/lib/btrfs/btrfsitem/item_qgrouplimit.go @@ -36,7 +36,7 @@ func (f QGroupLimitFlags) String() string { // key.objectid = 0 // key.offset = ID of the qgroup -type QGroupLimit struct { // QGROUP_LIMIT=244 +type QGroupLimit struct { // trivial QGROUP_LIMIT=244 Flags QGroupLimitFlags `bin:"off=0, siz=8"` MaxReferenced uint64 `bin:"off=8, siz=8"` MaxExclusive uint64 `bin:"off=16, siz=8"` diff --git a/lib/btrfs/btrfsitem/item_qgroupstatus.go b/lib/btrfs/btrfsitem/item_qgroupstatus.go index e7bd62c..346c913 100644 --- a/lib/btrfs/btrfsitem/item_qgroupstatus.go +++ b/lib/btrfs/btrfsitem/item_qgroupstatus.go @@ -34,7 +34,7 @@ const QGroupStatusVersion uint64 = 1 // key.objectid = 0 // key.offset = 0 -type QGroupStatus struct { // QGROUP_STATUS=240 +type QGroupStatus struct { // trivial QGROUP_STATUS=240 Version uint64 `bin:"off=0, siz=8"` Generation btrfsprim.Generation `bin:"off=8, siz=8"` Flags QGroupStatusFlags `bin:"off=16, siz=8"` diff --git a/lib/btrfs/btrfsitem/item_root.go b/lib/btrfs/btrfsitem/item_root.go index ffbbf4d..c0db900 100644 --- a/lib/btrfs/btrfsitem/item_root.go +++ b/lib/btrfs/btrfsitem/item_root.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,7 +15,7 @@ import ( // key.offset = either // - 0 if objectid is one of the BTRFS_*_TREE_OBJECTID defines or a non-snapshot volume; or // - transaction_id of when this snapshot was created -type Root struct { // ROOT_ITEM=132 +type Root struct { // trivial ROOT_ITEM=132 Inode Inode `bin:"off=0x000, siz=0xa0"` // ??? Generation btrfsprim.Generation `bin:"off=0x0a0, siz=0x08"` RootDirID btrfsprim.ObjID `bin:"off=0x0a8, siz=0x08"` // inode number of the root inode diff --git a/lib/btrfs/btrfsitem/item_rootref.go b/lib/btrfs/btrfsitem/item_rootref.go index b33883d..4179890 100644 --- a/lib/btrfs/btrfsitem/item_rootref.go +++ b/lib/btrfs/btrfsitem/item_rootref.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -19,7 +19,7 @@ import ( // ROOT_REF | ROOT_BACKREF // key.objectid = ID of the parent subvolume | ID of the child subvolume // key.offset = ID of the child subvolume | ID of the parent subvolume -type RootRef struct { // ROOT_REF=156 ROOT_BACKREF=144 +type RootRef struct { // complex ROOT_REF=156 ROOT_BACKREF=144 DirID btrfsprim.ObjID `bin:"off=0x00, siz=0x8"` // inode of the parent directory of the dir entry Sequence int64 `bin:"off=0x08, siz=0x8"` // index of that dir entry within the parent NameLen uint16 `bin:"off=0x10, siz=0x2"` // [ignored-when-writing] @@ -27,6 +27,17 @@ type RootRef struct { // ROOT_REF=156 ROOT_BACKREF=144 Name []byte `bin:"-"` } +func (o *RootRef) Free() { + bytePool.Put(o.Name) + *o = RootRef{} + rootRefPool.Put(o) +} + +func (o RootRef) Clone() RootRef { + o.Name = cloneBytes(o.Name) + return o +} + func (o *RootRef) UnmarshalBinary(dat []byte) (int, error) { if err := binutil.NeedNBytes(dat, 0x12); err != nil { return 0, err @@ -42,7 +53,7 @@ func (o *RootRef) UnmarshalBinary(dat []byte) (int, error) { if err := binutil.NeedNBytes(dat, 0x12+int(o.NameLen)); err != nil { return 0, err } - o.Name = dat[n : n+int(o.NameLen)] + o.Name = cloneBytes(dat[n : n+int(o.NameLen)]) n += int(o.NameLen) return n, nil } diff --git a/lib/btrfs/btrfsitem/item_shareddataref.go b/lib/btrfs/btrfsitem/item_shareddataref.go index d7765af..6143a5c 100644 --- a/lib/btrfs/btrfsitem/item_shareddataref.go +++ b/lib/btrfs/btrfsitem/item_shareddataref.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,7 +12,7 @@ import ( // // key.offset = laddr of the leaf node containing the FileExtent // (EXTENT_DATA_KEY) for this reference. -type SharedDataRef struct { // SHARED_DATA_REF=184 +type SharedDataRef struct { // trivial SHARED_DATA_REF=184 Count int32 `bin:"off=0, siz=4"` // reference count binstruct.End `bin:"off=4"` } diff --git a/lib/btrfs/btrfsitem/item_untyped.go b/lib/btrfs/btrfsitem/item_untyped.go index acf4ebe..9bda094 100644 --- a/lib/btrfs/btrfsitem/item_untyped.go +++ b/lib/btrfs/btrfsitem/item_untyped.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,7 +9,7 @@ import ( "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim" ) -type FreeSpaceHeader struct { // UNTYPED=0:FREE_SPACE_OBJECTID +type FreeSpaceHeader struct { // trivial UNTYPED=0:FREE_SPACE_OBJECTID Location btrfsprim.Key `bin:"off=0x00, siz=0x11"` Generation btrfsprim.Generation `bin:"off=0x11, siz=0x8"` NumEntries int64 `bin:"off=0x19, siz=0x8"` diff --git a/lib/btrfs/btrfsitem/item_uuid.go b/lib/btrfs/btrfsitem/item_uuid.go index 5f5f357..fca409d 100644 --- a/lib/btrfs/btrfsitem/item_uuid.go +++ b/lib/btrfs/btrfsitem/item_uuid.go @@ -16,7 +16,7 @@ import ( // // key.objectid = first half of UUID // key.offset = second half of UUID -type UUIDMap struct { // UUID_SUBVOL=251 UUID_RECEIVED_SUBVOL=252 +type UUIDMap struct { // trivial UUID_SUBVOL=251 UUID_RECEIVED_SUBVOL=252 ObjID btrfsprim.ObjID `bin:"off=0, siz=8"` binstruct.End `bin:"off=8"` } diff --git a/lib/btrfs/btrfsitem/items.go b/lib/btrfs/btrfsitem/items.go index 9964e2d..49d421f 100644 --- a/lib/btrfs/btrfsitem/items.go +++ b/lib/btrfs/btrfsitem/items.go @@ -8,16 +8,21 @@ import ( "fmt" "reflect" + "git.lukeshu.com/go/typedsync" + "git.lukeshu.com/btrfs-progs-ng/lib/binstruct" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfssum" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" + "git.lukeshu.com/btrfs-progs-ng/lib/containers" ) type Type = btrfsprim.ItemType type Item interface { isItem() + Free() + CloneItem() Item } type Error struct { @@ -25,8 +30,23 @@ type Error struct { Err error } +var errorPool = &typedsync.Pool[*Error]{New: func() *Error { return new(Error) }} + func (*Error) isItem() {} +func (o *Error) Free() { + *o = Error{} + errorPool.Put(o) +} + +func (o Error) Clone() Error { return o } + +func (o *Error) CloneItem() Item { + ret, _ := errorPool.Get() + *ret = *o + return ret +} + func (o Error) MarshalBinary() ([]byte, error) { return o.Dat, nil } @@ -43,41 +63,58 @@ func UnmarshalItem(key btrfsprim.Key, csumType btrfssum.CSumType, dat []byte) It var ok bool gotyp, ok = untypedObjID2gotype[key.ObjectID] if !ok { - return &Error{ + ret, _ := errorPool.Get() + *ret = Error{ Dat: dat, Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v, ObjectID:%v}, dat): unknown object ID for untyped item", key.ItemType, key.ObjectID), } + return ret } } else { var ok bool gotyp, ok = keytype2gotype[key.ItemType] if !ok { - return &Error{ + ret, _ := errorPool.Get() + *ret = Error{ Dat: dat, Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v}, dat): unknown item type", key.ItemType), } + return ret } } - ptr := reflect.New(gotyp) - if csums, ok := ptr.Interface().(*ExtentCSum); ok { + ptr, _ := gotype2pool[gotyp].Get() + if csums, ok := ptr.(*ExtentCSum); ok { csums.ChecksumSize = csumType.Size() csums.Addr = btrfsvol.LogicalAddr(key.Offset) } - n, err := binstruct.Unmarshal(dat, ptr.Interface()) + n, err := binstruct.Unmarshal(dat, ptr) if err != nil { - return &Error{ + ptr.Free() + ret, _ := errorPool.Get() + *ret = Error{ Dat: dat, Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v}, dat): %w", key.ItemType, err), } + return ret } if n < len(dat) { - return &Error{ + ptr.Free() + ret, _ := errorPool.Get() + *ret = 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 ret } - //nolint:forcetypeassert // items_gen.go has all types in keytype2gotype implement the Item interface. - return ptr.Interface().(Item) + return ptr +} + +var bytePool containers.SlicePool[byte] + +func cloneBytes(in []byte) []byte { + out := bytePool.Get(len(in)) + copy(out, in) + return out } diff --git a/lib/btrfs/btrfsitem/items_gen.go b/lib/btrfs/btrfsitem/items_gen.go index 8074610..31d2a76 100644 --- a/lib/btrfs/btrfsitem/items_gen.go +++ b/lib/btrfs/btrfsitem/items_gen.go @@ -5,6 +5,8 @@ package btrfsitem import ( "reflect" + "git.lukeshu.com/go/typedsync" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim" ) @@ -110,6 +112,62 @@ var untypedObjID2gotype = map[btrfsprim.ObjID]reflect.Type{ btrfsprim.FREE_SPACE_OBJECTID: freeSpaceHeaderType, } +// Pools. +var ( + blockGroupPool = typedsync.Pool[Item]{New: func() Item { return new(BlockGroup) }} + chunkPool = typedsync.Pool[Item]{New: func() Item { return new(Chunk) }} + devPool = typedsync.Pool[Item]{New: func() Item { return new(Dev) }} + devExtentPool = typedsync.Pool[Item]{New: func() Item { return new(DevExtent) }} + devStatsPool = typedsync.Pool[Item]{New: func() Item { return new(DevStats) }} + dirEntryPool = typedsync.Pool[Item]{New: func() Item { return new(DirEntry) }} + emptyPool = typedsync.Pool[Item]{New: func() Item { return new(Empty) }} + extentPool = typedsync.Pool[Item]{New: func() Item { return new(Extent) }} + extentCSumPool = typedsync.Pool[Item]{New: func() Item { return new(ExtentCSum) }} + extentDataRefPool = typedsync.Pool[Item]{New: func() Item { return new(ExtentDataRef) }} + fileExtentPool = typedsync.Pool[Item]{New: func() Item { return new(FileExtent) }} + freeSpaceBitmapPool = typedsync.Pool[Item]{New: func() Item { return new(FreeSpaceBitmap) }} + freeSpaceHeaderPool = typedsync.Pool[Item]{New: func() Item { return new(FreeSpaceHeader) }} + freeSpaceInfoPool = typedsync.Pool[Item]{New: func() Item { return new(FreeSpaceInfo) }} + inodePool = typedsync.Pool[Item]{New: func() Item { return new(Inode) }} + inodeRefsPool = typedsync.Pool[Item]{New: func() Item { return new(InodeRefs) }} + metadataPool = typedsync.Pool[Item]{New: func() Item { return new(Metadata) }} + qGroupInfoPool = typedsync.Pool[Item]{New: func() Item { return new(QGroupInfo) }} + qGroupLimitPool = typedsync.Pool[Item]{New: func() Item { return new(QGroupLimit) }} + qGroupStatusPool = typedsync.Pool[Item]{New: func() Item { return new(QGroupStatus) }} + rootPool = typedsync.Pool[Item]{New: func() Item { return new(Root) }} + rootRefPool = typedsync.Pool[Item]{New: func() Item { return new(RootRef) }} + sharedDataRefPool = typedsync.Pool[Item]{New: func() Item { return new(SharedDataRef) }} + uuidMapPool = typedsync.Pool[Item]{New: func() Item { return new(UUIDMap) }} +) + +// gotype2pool is used by UnmarshalItem. +var gotype2pool = map[reflect.Type]*typedsync.Pool[Item]{ + blockGroupType: &blockGroupPool, + chunkType: &chunkPool, + devType: &devPool, + devExtentType: &devExtentPool, + devStatsType: &devStatsPool, + dirEntryType: &dirEntryPool, + emptyType: &emptyPool, + extentType: &extentPool, + extentCSumType: &extentCSumPool, + extentDataRefType: &extentDataRefPool, + fileExtentType: &fileExtentPool, + freeSpaceBitmapType: &freeSpaceBitmapPool, + freeSpaceHeaderType: &freeSpaceHeaderPool, + freeSpaceInfoType: &freeSpaceInfoPool, + inodeType: &inodePool, + inodeRefsType: &inodeRefsPool, + metadataType: &metadataPool, + qGroupInfoType: &qGroupInfoPool, + qGroupLimitType: &qGroupLimitPool, + qGroupStatusType: &qGroupStatusPool, + rootType: &rootPool, + rootRefType: &rootRefPool, + sharedDataRefType: &sharedDataRefPool, + uuidMapType: &uuidMapPool, +} + // isItem implements Item. func (*BlockGroup) isItem() {} func (*Chunk) isItem() {} @@ -136,7 +194,145 @@ func (*RootRef) isItem() {} func (*SharedDataRef) isItem() {} func (*UUIDMap) isItem() {} -// Type assertions. +// Free implements Item. +func (o *BlockGroup) Free() { *o = BlockGroup{}; blockGroupPool.Put(o) } +func (o *Dev) Free() { *o = Dev{}; devPool.Put(o) } +func (o *DevExtent) Free() { *o = DevExtent{}; devExtentPool.Put(o) } +func (o *DevStats) Free() { *o = DevStats{}; devStatsPool.Put(o) } +func (o *Empty) Free() { *o = Empty{}; emptyPool.Put(o) } +func (o *ExtentCSum) Free() { *o = ExtentCSum{}; extentCSumPool.Put(o) } +func (o *ExtentDataRef) Free() { *o = ExtentDataRef{}; extentDataRefPool.Put(o) } +func (o *FreeSpaceHeader) Free() { *o = FreeSpaceHeader{}; freeSpaceHeaderPool.Put(o) } +func (o *FreeSpaceInfo) Free() { *o = FreeSpaceInfo{}; freeSpaceInfoPool.Put(o) } +func (o *Inode) Free() { *o = Inode{}; inodePool.Put(o) } +func (o *QGroupInfo) Free() { *o = QGroupInfo{}; qGroupInfoPool.Put(o) } +func (o *QGroupLimit) Free() { *o = QGroupLimit{}; qGroupLimitPool.Put(o) } +func (o *QGroupStatus) Free() { *o = QGroupStatus{}; qGroupStatusPool.Put(o) } +func (o *Root) Free() { *o = Root{}; rootPool.Put(o) } +func (o *SharedDataRef) Free() { *o = SharedDataRef{}; sharedDataRefPool.Put(o) } +func (o *UUIDMap) Free() { *o = UUIDMap{}; uuidMapPool.Put(o) } + +// Clone is a handy method. +func (o BlockGroup) Clone() BlockGroup { return o } +func (o Dev) Clone() Dev { return o } +func (o DevExtent) Clone() DevExtent { return o } +func (o DevStats) Clone() DevStats { return o } +func (o Empty) Clone() Empty { return o } +func (o ExtentCSum) Clone() ExtentCSum { return o } +func (o ExtentDataRef) Clone() ExtentDataRef { return o } +func (o FreeSpaceHeader) Clone() FreeSpaceHeader { return o } +func (o FreeSpaceInfo) Clone() FreeSpaceInfo { return o } +func (o Inode) Clone() Inode { return o } +func (o QGroupInfo) Clone() QGroupInfo { return o } +func (o QGroupLimit) Clone() QGroupLimit { return o } +func (o QGroupStatus) Clone() QGroupStatus { return o } +func (o Root) Clone() Root { return o } +func (o SharedDataRef) Clone() SharedDataRef { return o } +func (o UUIDMap) Clone() UUIDMap { return o } + +// CloneItem implements Item. +func (o *BlockGroup) CloneItem() Item { + ret, _ := blockGroupPool.Get() + *(ret.(*BlockGroup)) = o.Clone() + return ret +} +func (o *Chunk) CloneItem() Item { ret, _ := chunkPool.Get(); *(ret.(*Chunk)) = o.Clone(); return ret } +func (o *Dev) CloneItem() Item { ret, _ := devPool.Get(); *(ret.(*Dev)) = o.Clone(); return ret } +func (o *DevExtent) CloneItem() Item { + ret, _ := devExtentPool.Get() + *(ret.(*DevExtent)) = o.Clone() + return ret +} +func (o *DevStats) CloneItem() Item { + ret, _ := devStatsPool.Get() + *(ret.(*DevStats)) = o.Clone() + return ret +} +func (o *DirEntry) CloneItem() Item { + ret, _ := dirEntryPool.Get() + *(ret.(*DirEntry)) = o.Clone() + return ret +} +func (o *Empty) CloneItem() Item { ret, _ := emptyPool.Get(); *(ret.(*Empty)) = o.Clone(); return ret } +func (o *Extent) CloneItem() Item { + ret, _ := extentPool.Get() + *(ret.(*Extent)) = o.Clone() + return ret +} +func (o *ExtentCSum) CloneItem() Item { + ret, _ := extentCSumPool.Get() + *(ret.(*ExtentCSum)) = o.Clone() + return ret +} +func (o *ExtentDataRef) CloneItem() Item { + ret, _ := extentDataRefPool.Get() + *(ret.(*ExtentDataRef)) = o.Clone() + return ret +} +func (o *FileExtent) CloneItem() Item { + ret, _ := fileExtentPool.Get() + *(ret.(*FileExtent)) = o.Clone() + return ret +} +func (o *FreeSpaceBitmap) CloneItem() Item { + ret, _ := freeSpaceBitmapPool.Get() + *(ret.(*FreeSpaceBitmap)) = o.Clone() + return ret +} +func (o *FreeSpaceHeader) CloneItem() Item { + ret, _ := freeSpaceHeaderPool.Get() + *(ret.(*FreeSpaceHeader)) = o.Clone() + return ret +} +func (o *FreeSpaceInfo) CloneItem() Item { + ret, _ := freeSpaceInfoPool.Get() + *(ret.(*FreeSpaceInfo)) = o.Clone() + return ret +} +func (o *Inode) CloneItem() Item { ret, _ := inodePool.Get(); *(ret.(*Inode)) = o.Clone(); return ret } +func (o *InodeRefs) CloneItem() Item { + ret, _ := inodeRefsPool.Get() + *(ret.(*InodeRefs)) = o.Clone() + return ret +} +func (o *Metadata) CloneItem() Item { + ret, _ := metadataPool.Get() + *(ret.(*Metadata)) = o.Clone() + return ret +} +func (o *QGroupInfo) CloneItem() Item { + ret, _ := qGroupInfoPool.Get() + *(ret.(*QGroupInfo)) = o.Clone() + return ret +} +func (o *QGroupLimit) CloneItem() Item { + ret, _ := qGroupLimitPool.Get() + *(ret.(*QGroupLimit)) = o.Clone() + return ret +} +func (o *QGroupStatus) CloneItem() Item { + ret, _ := qGroupStatusPool.Get() + *(ret.(*QGroupStatus)) = o.Clone() + return ret +} +func (o *Root) CloneItem() Item { ret, _ := rootPool.Get(); *(ret.(*Root)) = o.Clone(); return ret } +func (o *RootRef) CloneItem() Item { + ret, _ := rootRefPool.Get() + *(ret.(*RootRef)) = o.Clone() + return ret +} +func (o *SharedDataRef) CloneItem() Item { + ret, _ := sharedDataRefPool.Get() + *(ret.(*SharedDataRef)) = o.Clone() + return ret +} +func (o *UUIDMap) CloneItem() Item { + ret, _ := uuidMapPool.Get() + *(ret.(*UUIDMap)) = o.Clone() + return ret +} + +// Item type assertions. var ( _ Item = (*BlockGroup)(nil) _ Item = (*Chunk)(nil) @@ -163,3 +359,31 @@ var ( _ Item = (*SharedDataRef)(nil) _ Item = (*UUIDMap)(nil) ) + +// Clone type assertions. +var ( + _ interface{ Clone() BlockGroup } = BlockGroup{} + _ interface{ Clone() Chunk } = Chunk{} + _ interface{ Clone() Dev } = Dev{} + _ interface{ Clone() DevExtent } = DevExtent{} + _ interface{ Clone() DevStats } = DevStats{} + _ interface{ Clone() DirEntry } = DirEntry{} + _ interface{ Clone() Empty } = Empty{} + _ interface{ Clone() Extent } = Extent{} + _ interface{ Clone() ExtentCSum } = ExtentCSum{} + _ interface{ Clone() ExtentDataRef } = ExtentDataRef{} + _ interface{ Clone() FileExtent } = FileExtent{} + _ interface{ Clone() FreeSpaceBitmap } = FreeSpaceBitmap{} + _ interface{ Clone() FreeSpaceHeader } = FreeSpaceHeader{} + _ interface{ Clone() FreeSpaceInfo } = FreeSpaceInfo{} + _ interface{ Clone() Inode } = Inode{} + _ interface{ Clone() InodeRefs } = InodeRefs{} + _ interface{ Clone() Metadata } = Metadata{} + _ interface{ Clone() QGroupInfo } = QGroupInfo{} + _ interface{ Clone() QGroupLimit } = QGroupLimit{} + _ interface{ Clone() QGroupStatus } = QGroupStatus{} + _ interface{ Clone() Root } = Root{} + _ interface{ Clone() RootRef } = RootRef{} + _ interface{ Clone() SharedDataRef } = SharedDataRef{} + _ interface{ Clone() UUIDMap } = UUIDMap{} +) diff --git a/lib/btrfs/io4_fs.go b/lib/btrfs/io4_fs.go index c21cafc..56cf266 100644 --- a/lib/btrfs/io4_fs.go +++ b/lib/btrfs/io4_fs.go @@ -89,7 +89,7 @@ func (sv *Subvolume) init() { } else { switch rootBody := root.Body.(type) { case *btrfsitem.Root: - sv.rootVal = *rootBody + sv.rootVal = rootBody.Clone() case *btrfsitem.Error: sv.rootErr = fmt.Errorf("FS_TREE ROOT_ITEM has malformed body: %w", rootBody.Err) default: @@ -127,7 +127,7 @@ func (sv *Subvolume) LoadBareInode(inode btrfsprim.ObjID) (*BareInode, error) { switch itemBody := item.Body.(type) { case *btrfsitem.Inode: - bodyCopy := *itemBody + bodyCopy := itemBody.Clone() val.InodeItem = &bodyCopy case *btrfsitem.Error: val.Errs = append(val.Errs, fmt.Errorf("malformed inode: %w", itemBody.Err)) @@ -172,7 +172,7 @@ func (sv *Subvolume) LoadFullInode(inode btrfsprim.ObjID) (*FullInode, error) { } continue } - bodyCopy := *itemBody + bodyCopy := itemBody.Clone() val.InodeItem = &bodyCopy case *btrfsitem.Error: val.Errs = append(val.Errs, fmt.Errorf("malformed INODE_ITEM: %w", itemBody.Err)) @@ -264,7 +264,7 @@ func (dir *Dir) populate() { } continue } - dir.ChildrenByName[string(entry.Name)] = *entry + dir.ChildrenByName[string(entry.Name)] = entry.Clone() case *btrfsitem.Error: dir.Errs = append(dir.Errs, fmt.Errorf("malformed DIR_ITEM: %w", entry.Err)) default: @@ -280,7 +280,7 @@ func (dir *Dir) populate() { } continue } - dir.ChildrenByIndex[index] = *entry + dir.ChildrenByIndex[index] = entry.Clone() case *btrfsitem.Error: dir.Errs = append(dir.Errs, fmt.Errorf("malformed DIR_INDEX: %w", entry.Err)) default: diff --git a/lib/containers/slicepool.go b/lib/containers/slicepool.go new file mode 100644 index 0000000..861691a --- /dev/null +++ b/lib/containers/slicepool.go @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package containers + +import ( + "git.lukeshu.com/go/typedsync" +) + +type SlicePool[T any] struct { + // TODO(lukeshu): Consider bucketing slices by size, to + // increase odds that the `cap(ret) >= size` check passes. + inner typedsync.Pool[[]T] +} + +func (p *SlicePool[T]) Get(size int) []T { + if size == 0 { + return nil + } + ret, ok := p.inner.Get() + if ok && cap(ret) >= size { + ret = ret[:size] + } else { + ret = make([]T, size) + } + return ret +} + +func (p *SlicePool[T]) Put(slice []T) { + if slice == nil { + return + } + p.inner.Put(slice) +} -- cgit v1.2.3-54-g00ecf