summaryrefslogtreecommitdiff
path: root/lib/btrfs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/btrfs')
-rw-r--r--lib/btrfs/btrfsitem/item_uuid.go4
-rw-r--r--lib/btrfs/btrfsitem/items.go4
-rw-r--r--lib/btrfs/btrfsitem/statmode.go4
-rw-r--r--lib/btrfs/btrfsprim/misc.go6
-rw-r--r--lib/btrfs/btrfsprim/objid.go3
-rw-r--r--lib/btrfs/btrfsprim/uuid.go3
-rw-r--r--lib/btrfs/btrfssum/csum.go3
-rw-r--r--lib/btrfs/btrfssum/csum_test.go4
-rw-r--r--lib/btrfs/btrfssum/shortsum.go13
-rw-r--r--lib/btrfs/btrfstree/ops.go16
-rw-r--r--lib/btrfs/btrfstree/root.go23
-rw-r--r--lib/btrfs/btrfstree/types_node.go33
-rw-r--r--lib/btrfs/btrfstree/types_node_test.go5
-rw-r--r--lib/btrfs/btrfsvol/lvm.go11
-rw-r--r--lib/btrfs/csums.go16
-rw-r--r--lib/btrfs/io2_lv.go19
-rw-r--r--lib/btrfs/io3_btree.go3
-rw-r--r--lib/btrfs/io4_fs.go240
18 files changed, 247 insertions, 163 deletions
diff --git a/lib/btrfs/btrfsitem/item_uuid.go b/lib/btrfs/btrfsitem/item_uuid.go
index e1a6cf9..5f5f357 100644
--- a/lib/btrfs/btrfsitem/item_uuid.go
+++ b/lib/btrfs/btrfsitem/item_uuid.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -12,7 +12,7 @@ import (
)
// The Key for this item is a UUID, and the item is a subvolume IDs
-// that that UUID maps to.
+// that UUID maps to.
//
// key.objectid = first half of UUID
// key.offset = second half of UUID
diff --git a/lib/btrfs/btrfsitem/items.go b/lib/btrfs/btrfsitem/items.go
index 29b3cb0..67f96fa 100644
--- a/lib/btrfs/btrfsitem/items.go
+++ b/lib/btrfs/btrfsitem/items.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -70,7 +70,6 @@ func UnmarshalItem(key btrfsprim.Key, csumType btrfssum.CSumType, dat []byte) It
Dat: dat,
Err: fmt.Errorf("btrfsitem.UnmarshalItem({ItemType:%v}, dat): %w", key.ItemType, err),
}
-
}
if n < len(dat) {
return Error{
@@ -79,5 +78,6 @@ func UnmarshalItem(key btrfsprim.Key, csumType btrfssum.CSumType, dat []byte) It
key.ItemType, len(dat), n),
}
}
+ //nolint:forcetypeassert // items_gen.go has all types in keytype2gotype implement the Item interface.
return retPtr.Elem().Interface().(Item)
}
diff --git a/lib/btrfs/btrfsitem/statmode.go b/lib/btrfs/btrfsitem/statmode.go
index a1302ee..557b688 100644
--- a/lib/btrfs/btrfsitem/statmode.go
+++ b/lib/btrfs/btrfsitem/statmode.go
@@ -1,5 +1,5 @@
// Copyright (C) 2020-2021 Ambassador Labs
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: Apache-2.0
//
@@ -9,7 +9,6 @@ package btrfsitem
type StatMode uint32
-//nolint:deadcode,varcheck // not all of these modes will be used
const (
// 16 bits = 5⅓ octal characters
@@ -73,6 +72,7 @@ func (mode StatMode) IsRegular() bool {
// 's' (GNU `ls` behavior; though POSIX notes that many
// implementations use '=' for sockets).
func (mode StatMode) String() string {
+ //nolint:gomnd // Magic numbers is all this is.
buf := [10]byte{
// type: This string is easy; it directly pairs with
// the above ModeFmtXXX list above; the character in
diff --git a/lib/btrfs/btrfsprim/misc.go b/lib/btrfs/btrfsprim/misc.go
index 9b3a6f8..22939bf 100644
--- a/lib/btrfs/btrfsprim/misc.go
+++ b/lib/btrfs/btrfsprim/misc.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -22,8 +22,8 @@ type Key struct {
binstruct.End `bin:"off=0x11"`
}
-func (k Key) String() string {
- return fmt.Sprintf("{%v %v %v}", k.ObjectID, k.ItemType, k.Offset)
+func (key Key) String() string {
+ return fmt.Sprintf("{%v %v %v}", key.ObjectID, key.ItemType, key.Offset)
}
var MaxKey = Key{
diff --git a/lib/btrfs/btrfsprim/objid.go b/lib/btrfs/btrfsprim/objid.go
index 17a0eeb..5ba213d 100644
--- a/lib/btrfs/btrfsprim/objid.go
+++ b/lib/btrfs/btrfsprim/objid.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -68,6 +68,7 @@ func (id ObjID) Format(typ ItemType) string {
case DEV_EXTENT_KEY:
return fmt.Sprintf("%d", int64(id))
case QGROUP_RELATION_KEY:
+ //nolint:gomnd // TODO: I'm not sure what the 48/16 bit split means.
return fmt.Sprintf("%d/%d",
uint64(id)>>48,
uint64(id)&((1<<48)-1))
diff --git a/lib/btrfs/btrfsprim/uuid.go b/lib/btrfs/btrfsprim/uuid.go
index 4e3fd6b..0103ee4 100644
--- a/lib/btrfs/btrfsprim/uuid.go
+++ b/lib/btrfs/btrfsprim/uuid.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -56,6 +56,7 @@ func (a UUID) Cmp(b UUID) int {
return 0
}
+//nolint:gomnd // This is all magic numbers.
func ParseUUID(str string) (UUID, error) {
var ret UUID
j := 0
diff --git a/lib/btrfs/btrfssum/csum.go b/lib/btrfs/btrfssum/csum.go
index 770f6ea..6df9efd 100644
--- a/lib/btrfs/btrfssum/csum.go
+++ b/lib/btrfs/btrfssum/csum.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -70,6 +70,7 @@ func (typ CSumType) String() string {
}
func (typ CSumType) Size() int {
+ //nolint:gomnd // This is where we define the magic numbers.
sizes := map[CSumType]int{
TYPE_CRC32: 4,
TYPE_XXHASH: 8,
diff --git a/lib/btrfs/btrfssum/csum_test.go b/lib/btrfs/btrfssum/csum_test.go
index d8ccbfc..0a4aef6 100644
--- a/lib/btrfs/btrfssum/csum_test.go
+++ b/lib/btrfs/btrfssum/csum_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -26,7 +26,7 @@ func TestCSumFormat(t *testing.T) {
"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}"},
+ "#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}"}, //nolint:dupword
}
for tcName, tc := range testcases {
tc := tc
diff --git a/lib/btrfs/btrfssum/shortsum.go b/lib/btrfs/btrfssum/shortsum.go
index 6fd0c68..c3d6f8b 100644
--- a/lib/btrfs/btrfssum/shortsum.go
+++ b/lib/btrfs/btrfssum/shortsum.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -56,10 +56,11 @@ func (sum ShortSum) EncodeJSON(w io.Writer) error {
}
func deHex(r rune) (byte, bool) {
- if r > 0xff {
+ if r > math.MaxUint8 {
return 0, false
}
c := byte(r)
+ //nolint:gomnd // Hex conversion.
switch {
case '0' <= c && c <= '9':
return c - '0', true
@@ -130,7 +131,7 @@ func (run SumRun[Addr]) Get(sumIdx int64) (ShortSum, error) {
return "", io.EOF
}
off := int(sumIdx) * run.ChecksumSize
- return ShortSum(run.Sums[off : off+run.ChecksumSize]), nil
+ return run.Sums[off : off+run.ChecksumSize], nil
}
func (run SumRun[Addr]) SumForAddr(addr Addr) (ShortSum, bool) {
@@ -138,7 +139,7 @@ func (run SumRun[Addr]) SumForAddr(addr Addr) (ShortSum, bool) {
return "", false
}
off := int((addr-run.Addr)/BlockSize) * run.ChecksumSize
- return ShortSum(run.Sums[off : off+run.ChecksumSize]), true
+ return run.Sums[off : off+run.ChecksumSize], true
}
func (run SumRun[Addr]) Walk(ctx context.Context, fn func(Addr, ShortSum) error) error {
@@ -146,7 +147,7 @@ func (run SumRun[Addr]) Walk(ctx context.Context, fn func(Addr, ShortSum) error)
if err := ctx.Err(); err != nil {
return err
}
- if err := fn(addr, ShortSum(run.Sums[off:off+run.ChecksumSize])); err != nil {
+ if err := fn(addr, run.Sums[off:off+run.ChecksumSize]); err != nil {
return err
}
}
@@ -207,7 +208,7 @@ func (sg SumRunWithGaps[Addr]) SumForAddr(addr Addr) (ShortSum, error) {
continue
}
off := int((addr-run.Addr)/BlockSize) * run.ChecksumSize
- return ShortSum(run.Sums[off : off+run.ChecksumSize]), nil
+ return run.Sums[off : off+run.ChecksumSize], nil
}
return "", diskio.ErrWildcard
}
diff --git a/lib/btrfs/btrfstree/ops.go b/lib/btrfs/btrfstree/ops.go
index f2eb6f0..537773a 100644
--- a/lib/btrfs/btrfstree/ops.go
+++ b/lib/btrfs/btrfstree/ops.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -156,14 +156,12 @@ func (fs TreeOperatorImpl) treeWalk(ctx context.Context, path TreePath, errHandl
}
if err != nil {
errHandle(&TreeError{Path: path, Err: err})
- } else {
- if cbs.Node != nil {
- if err := cbs.Node(path, node); err != nil {
- if errors.Is(err, iofs.SkipDir) {
- return
- }
- errHandle(&TreeError{Path: path, Err: err})
+ } else if cbs.Node != nil {
+ if err := cbs.Node(path, node); err != nil {
+ if errors.Is(err, iofs.SkipDir) {
+ return
}
+ errHandle(&TreeError{Path: path, Err: err})
}
}
if ctx.Err() != nil {
@@ -506,7 +504,7 @@ func (fs TreeOperatorImpl) TreeSearchAll(treeID btrfsprim.ObjID, fn func(btrfspr
}
middleItem := middleNode.Data.BodyLeaf[middlePath.Node(-1).FromItemIdx]
- var ret = []Item{middleItem}
+ ret := []Item{middleItem}
var errs derror.MultiError
for prevPath, prevNode := middlePath, middleNode; true; {
prevPath, prevNode, err = fs.prev(prevPath, prevNode)
diff --git a/lib/btrfs/btrfstree/root.go b/lib/btrfs/btrfstree/root.go
index 12df0b2..6ec45b5 100644
--- a/lib/btrfs/btrfstree/root.go
+++ b/lib/btrfs/btrfstree/root.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -71,15 +71,18 @@ func LookupTreeRoot(fs TreeOperator, sb Superblock, treeID btrfsprim.ObjID) (*Tr
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)
+ switch rootItemBody := rootItem.Body.(type) {
+ case btrfsitem.Root:
+ return &TreeRoot{
+ TreeID: treeID,
+ RootNode: rootItemBody.ByteNr,
+ Level: rootItemBody.Level,
+ Generation: rootItemBody.Generation,
+ }, nil
+ 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))
}
- return &TreeRoot{
- TreeID: treeID,
- RootNode: rootItemBody.ByteNr,
- Level: rootItemBody.Level,
- Generation: rootItemBody.Generation,
- }, nil
}
}
diff --git a/lib/btrfs/btrfstree/types_node.go b/lib/btrfs/btrfstree/types_node.go
index 299c433..a26215b 100644
--- a/lib/btrfs/btrfstree/types_node.go
+++ b/lib/btrfs/btrfstree/types_node.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -23,19 +23,23 @@ import (
type NodeFlags uint64
+const sizeofNodeFlags = 7
+
func (NodeFlags) BinaryStaticSize() int {
- return 7
+ return sizeofNodeFlags
}
+
func (f NodeFlags) MarshalBinary() ([]byte, error) {
var bs [8]byte
binary.LittleEndian.PutUint64(bs[:], uint64(f))
- return bs[:7], nil
+ return bs[:sizeofNodeFlags], nil
}
+
func (f *NodeFlags) UnmarshalBinary(dat []byte) (int, error) {
var bs [8]byte
- copy(bs[:7], dat[:7])
+ copy(bs[:sizeofNodeFlags], dat[:sizeofNodeFlags])
*f = NodeFlags(binary.LittleEndian.Uint64(bs[:]))
- return 7, nil
+ return sizeofNodeFlags, nil
}
var (
@@ -212,10 +216,11 @@ func (node Node) MarshalBinary() ([]byte, error) {
if bs, err := binstruct.Marshal(node.Head); err != nil {
return buf, err
- } else if len(bs) != binstruct.StaticSize(NodeHeader{}) {
- return nil, fmt.Errorf("header is %v bytes but expected %v",
- len(bs), binstruct.StaticSize(NodeHeader{}))
} else {
+ if len(bs) != binstruct.StaticSize(NodeHeader{}) {
+ return nil, fmt.Errorf("header is %v bytes but expected %v",
+ len(bs), binstruct.StaticSize(NodeHeader{}))
+ }
copy(buf, bs)
}
@@ -417,9 +422,11 @@ func (e *IOError) Unwrap() error { return e.Err }
// NodeError are ErrNotANode and *IOError.
func ReadNode[Addr ~int64](fs diskio.File[Addr], sb Superblock, addr Addr, exp NodeExpectations) (*diskio.Ref[Addr, Node], error) {
if int(sb.NodeSize) < binstruct.StaticSize(NodeHeader{}) {
- return nil, &NodeError[Addr]{Op: "btrfstree.ReadNode", NodeAddr: addr,
+ return nil, &NodeError[Addr]{
+ Op: "btrfstree.ReadNode", NodeAddr: addr,
Err: fmt.Errorf("superblock.NodeSize=%v is too small to contain even a node header (%v bytes)",
- sb.NodeSize, binstruct.StaticSize(NodeHeader{}))}
+ sb.NodeSize, binstruct.StaticSize(NodeHeader{})),
+ }
}
nodeBuf := make([]byte, sb.NodeSize)
if _, err := fs.ReadAt(nodeBuf, addr); err != nil {
@@ -455,9 +462,11 @@ func ReadNode[Addr ~int64](fs diskio.File[Addr], sb Superblock, addr Addr, exp N
return nodeRef, &NodeError[Addr]{Op: "btrfstree.ReadNode", NodeAddr: addr, Err: err}
}
if stored != calced {
- return nodeRef, &NodeError[Addr]{Op: "btrfstree.ReadNode", NodeAddr: addr,
+ return nodeRef, &NodeError[Addr]{
+ Op: "btrfstree.ReadNode", NodeAddr: addr,
Err: fmt.Errorf("looks like a node but is corrupt: checksum mismatch: stored=%v calculated=%v",
- stored, calced)}
+ stored, calced),
+ }
}
// parse (main)
diff --git a/lib/btrfs/btrfstree/types_node_test.go b/lib/btrfs/btrfstree/types_node_test.go
index 040b90c..80855d8 100644
--- a/lib/btrfs/btrfstree/types_node_test.go
+++ b/lib/btrfs/btrfstree/types_node_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -23,13 +23,12 @@ func FuzzRoundTripNode(f *testing.F) {
n, err := binstruct.Unmarshal(inDat, &node)
if err != nil {
t.Logf("err=%v", err)
- //require.Equal(t, 0, n)
} else {
require.Equal(t, len(inDat), n)
outDat, err := binstruct.Marshal(node)
require.NoError(t, err)
- require.Equal(t, inDat[:], outDat)
+ require.Equal(t, inDat, outDat)
}
})
}
diff --git a/lib/btrfs/btrfsvol/lvm.go b/lib/btrfs/btrfsvol/lvm.go
index c7551fc..1cb1ded 100644
--- a/lib/btrfs/btrfsvol/lvm.go
+++ b/lib/btrfs/btrfsvol/lvm.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -82,6 +82,7 @@ func (lv *LogicalVolume[PhysicalVolume]) Close() error {
}
return nil
}
+
func (lv *LogicalVolume[PhysicalVolume]) AddPhysicalVolume(id DeviceID, dev PhysicalVolume) error {
lv.init()
if other, exists := lv.id2pv[id]; exists {
@@ -121,9 +122,11 @@ type Mapping struct {
func (lv *LogicalVolume[PhysicalVolume]) CouldAddMapping(m Mapping) bool {
return lv.addMapping(m, true) == nil
}
+
func (lv *LogicalVolume[PhysicalVolume]) AddMapping(m Mapping) error {
return lv.addMapping(m, false)
}
+
func (lv *LogicalVolume[PhysicalVolume]) addMapping(m Mapping, dryRun bool) error {
lv.init()
// sanity check
@@ -335,10 +338,8 @@ func (lv *LogicalVolume[PhysicalVolume]) maybeShortReadAt(dat []byte, laddr Logi
}
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))
- }
+ } else if !bytes.Equal(dat, buf) {
+ return 0, fmt.Errorf("inconsistent stripes at laddr=%v len=%v", laddr, len(dat))
}
}
return len(dat), nil
diff --git a/lib/btrfs/csums.go b/lib/btrfs/csums.go
index bbd19bd..a32f090 100644
--- a/lib/btrfs/csums.go
+++ b/lib/btrfs/csums.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -56,9 +56,15 @@ func LookupCSum(fs btrfstree.TreeOperator, alg btrfssum.CSumType, laddr btrfsvol
if err != nil {
return btrfssum.SumRun[btrfsvol.LogicalAddr]{}, err
}
- body, ok := item.Body.(btrfsitem.ExtentCSum)
- if !ok {
- return btrfssum.SumRun[btrfsvol.LogicalAddr]{}, fmt.Errorf("item body is %T not ExtentCSum", item.Body)
+ if item.Key.ItemType != btrfsitem.EXTENT_CSUM_KEY {
+ 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:
+ return body.SumRun, nil
+ 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))
}
- return body.SumRun, nil
}
diff --git a/lib/btrfs/io2_lv.go b/lib/btrfs/io2_lv.go
index 5580285..ac7ea70 100644
--- a/lib/btrfs/io2_lv.go
+++ b/lib/btrfs/io2_lv.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -70,6 +70,7 @@ func (fs *FS) Size() btrfsvol.LogicalAddr {
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)
}
@@ -171,10 +172,20 @@ func (fs *FS) initDev(ctx context.Context, sb btrfstree.Superblock) error {
if item.Key.ItemType != btrfsitem.CHUNK_ITEM_KEY {
return nil
}
- for _, mapping := range item.Body.(btrfsitem.Chunk).Mappings(item.Key) {
- if err := fs.LV.AddMapping(mapping); err != nil {
- return err
+ switch itemBody := item.Body.(type) {
+ case btrfsitem.Chunk:
+ for _, mapping := range itemBody.Mappings(item.Key) {
+ if err := fs.LV.AddMapping(mapping); err != nil {
+ return err
+ }
}
+ case btrfsitem.Error:
+ // do nothing
+ default:
+ // This is a panic because the item decoder should not emit CHUNK_ITEM items as
+ // anything but btrfsitem.Chunk or btrfsitem.Error without this code also being
+ // updated.
+ panic(fmt.Errorf("should not happen: CHUNK_ITEM has unexpected item type: %T", itemBody))
}
return nil
},
diff --git a/lib/btrfs/io3_btree.go b/lib/btrfs/io3_btree.go
index b8c1a6d..8ec4b41 100644
--- a/lib/btrfs/io3_btree.go
+++ b/lib/btrfs/io3_btree.go
@@ -17,12 +17,15 @@ import (
func (fs *FS) TreeWalk(ctx context.Context, treeID btrfsprim.ObjID, errHandle func(*btrfstree.TreeError), cbs btrfstree.TreeWalkHandler) {
btrfstree.TreeOperatorImpl{NodeSource: fs}.TreeWalk(ctx, treeID, errHandle, cbs)
}
+
func (fs *FS) TreeLookup(treeID btrfsprim.ObjID, key btrfsprim.Key) (btrfstree.Item, error) {
return btrfstree.TreeOperatorImpl{NodeSource: fs}.TreeLookup(treeID, key)
}
+
func (fs *FS) TreeSearch(treeID btrfsprim.ObjID, fn func(key btrfsprim.Key, size uint32) int) (btrfstree.Item, error) {
return btrfstree.TreeOperatorImpl{NodeSource: fs}.TreeSearch(treeID, fn)
}
+
func (fs *FS) TreeSearchAll(treeID btrfsprim.ObjID, fn func(key btrfsprim.Key, size uint32) int) ([]btrfstree.Item, error) {
return btrfstree.TreeOperatorImpl{NodeSource: fs}.TreeSearchAll(treeID, fn)
}
diff --git a/lib/btrfs/io4_fs.go b/lib/btrfs/io4_fs.go
index ea81bc2..799c865 100644
--- a/lib/btrfs/io4_fs.go
+++ b/lib/btrfs/io4_fs.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -22,6 +22,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/containers"
"git.lukeshu.com/btrfs-progs-ng/lib/maps"
"git.lukeshu.com/btrfs-progs-ng/lib/slices"
+ "git.lukeshu.com/btrfs-progs-ng/lib/textui"
)
type BareInode struct {
@@ -69,31 +70,37 @@ type Subvolume struct {
TreeID btrfsprim.ObjID
NoChecksums bool
- rootOnce sync.Once
- rootVal btrfsitem.Root
- rootErr error
+ initOnce sync.Once
- bareInodeCache containers.LRUCache[btrfsprim.ObjID, *BareInode]
- fullInodeCache containers.LRUCache[btrfsprim.ObjID, *FullInode]
- dirCache containers.LRUCache[btrfsprim.ObjID, *Dir]
- fileCache containers.LRUCache[btrfsprim.ObjID, *File]
+ rootVal btrfsitem.Root
+ rootErr error
+
+ bareInodeCache *containers.LRUCache[btrfsprim.ObjID, *BareInode]
+ fullInodeCache *containers.LRUCache[btrfsprim.ObjID, *FullInode]
+ dirCache *containers.LRUCache[btrfsprim.ObjID, *Dir]
+ fileCache *containers.LRUCache[btrfsprim.ObjID, *File]
}
func (sv *Subvolume) init() {
- sv.rootOnce.Do(func() {
+ sv.initOnce.Do(func() {
root, err := sv.FS.TreeSearch(btrfsprim.ROOT_TREE_OBJECTID, btrfstree.RootItemSearchFn(sv.TreeID))
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
+ } else {
+ switch rootBody := root.Body.(type) {
+ 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))
+ }
}
- sv.rootVal = rootBody
+ sv.bareInodeCache = containers.NewLRUCache[btrfsprim.ObjID, *BareInode](textui.Tunable(128))
+ sv.fullInodeCache = containers.NewLRUCache[btrfsprim.ObjID, *FullInode](textui.Tunable(128))
+ sv.dirCache = containers.NewLRUCache[btrfsprim.ObjID, *Dir](textui.Tunable(128))
+ sv.fileCache = containers.NewLRUCache[btrfsprim.ObjID, *File](textui.Tunable(128))
})
}
@@ -103,6 +110,7 @@ func (sv *Subvolume) GetRootInode() (btrfsprim.ObjID, error) {
}
func (sv *Subvolume) LoadBareInode(inode btrfsprim.ObjID) (*BareInode, error) {
+ sv.init()
val := sv.bareInodeCache.GetOrElse(inode, func() (val *BareInode) {
val = &BareInode{
Inode: inode,
@@ -117,12 +125,14 @@ func (sv *Subvolume) LoadBareInode(inode btrfsprim.ObjID) (*BareInode, error) {
return
}
- itemBody, ok := item.Body.(btrfsitem.Inode)
- if !ok {
- val.Errs = append(val.Errs, fmt.Errorf("malformed inode"))
- return
+ switch itemBody := item.Body.(type) {
+ case btrfsitem.Inode:
+ val.InodeItem = &itemBody
+ 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))
}
- val.InodeItem = &itemBody
return
})
@@ -133,6 +143,7 @@ func (sv *Subvolume) LoadBareInode(inode btrfsprim.ObjID) (*BareInode, error) {
}
func (sv *Subvolume) LoadFullInode(inode btrfsprim.ObjID) (*FullInode, error) {
+ sv.init()
val := sv.fullInodeCache.GetOrElse(inode, func() (val *FullInode) {
val = &FullInode{
BareInode: BareInode{
@@ -152,17 +163,29 @@ func (sv *Subvolume) LoadFullInode(inode btrfsprim.ObjID) (*FullInode, error) {
for _, item := range items {
switch item.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"))
+ switch itemBody := item.Body.(type) {
+ case btrfsitem.Inode:
+ if val.InodeItem != nil {
+ if !reflect.DeepEqual(itemBody, *val.InodeItem) {
+ val.Errs = append(val.Errs, fmt.Errorf("multiple inodes"))
+ }
+ continue
}
- continue
+ val.InodeItem = &itemBody
+ 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))
}
- val.InodeItem = &itemBody
case btrfsitem.XATTR_ITEM_KEY:
- itemBody := item.Body.(btrfsitem.DirEntry)
- val.XAttrs[string(itemBody.Name)] = string(itemBody.Data)
+ switch itemBody := item.Body.(type) {
+ case btrfsitem.DirEntry:
+ val.XAttrs[string(itemBody.Name)] = string(itemBody.Data)
+ 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))
+ }
default:
val.OtherItems = append(val.OtherItems, item)
}
@@ -176,6 +199,7 @@ func (sv *Subvolume) LoadFullInode(inode btrfsprim.ObjID) (*FullInode, error) {
}
func (sv *Subvolume) LoadDir(inode btrfsprim.ObjID) (*Dir, error) {
+ sv.init()
val := sv.dirCache.GetOrElse(inode, func() (val *Dir) {
val = new(Dir)
fullInode, err := sv.LoadFullInode(inode)
@@ -194,77 +218,95 @@ func (sv *Subvolume) LoadDir(inode btrfsprim.ObjID) (*Dir, error) {
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 {
+func (dir *Dir) populate() {
+ dir.ChildrenByName = make(map[string]btrfsitem.DirEntry)
+ dir.ChildrenByIndex = make(map[uint64]btrfsitem.DirEntry)
+ for _, item := range dir.OtherItems {
switch item.Key.ItemType {
case btrfsitem.INODE_REF_KEY:
- body := item.Body.(btrfsitem.InodeRefs)
- if len(body) != 1 {
- ret.Errs = append(ret.Errs, fmt.Errorf("INODE_REF item with %d entries on a directory",
- len(body)))
- continue
- }
- ref := InodeRef{
- Inode: btrfsprim.ObjID(item.Key.Offset),
- InodeRef: body[0],
- }
- if ret.DotDot != nil {
- if !reflect.DeepEqual(ref, *ret.DotDot) {
- ret.Errs = append(ret.Errs, fmt.Errorf("multiple INODE_REF items on a directory"))
+ switch body := item.Body.(type) {
+ case btrfsitem.InodeRefs:
+ if len(body) != 1 {
+ dir.Errs = append(dir.Errs, fmt.Errorf("INODE_REF item with %d entries on a directory",
+ len(body)))
+ continue
}
- continue
+ ref := InodeRef{
+ Inode: btrfsprim.ObjID(item.Key.Offset),
+ InodeRef: body[0],
+ }
+ if dir.DotDot != nil {
+ if !reflect.DeepEqual(ref, *dir.DotDot) {
+ dir.Errs = append(dir.Errs, fmt.Errorf("multiple INODE_REF items on a directory"))
+ }
+ continue
+ }
+ dir.DotDot = &ref
+ 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))
}
- ret.DotDot = &ref
case btrfsitem.DIR_ITEM_KEY:
- entry := item.Body.(btrfsitem.DirEntry)
- namehash := btrfsitem.NameHash(entry.Name)
- if namehash != item.Key.Offset {
- ret.Errs = append(ret.Errs, fmt.Errorf("direntry crc32c mismatch: key=%#x crc32c(%q)=%#x",
- item.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))
+ switch entry := item.Body.(type) {
+ 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",
+ item.Key.Offset, entry.Name, namehash))
+ continue
+ }
+ if other, exists := dir.ChildrenByName[string(entry.Name)]; exists {
+ if !reflect.DeepEqual(entry, other) {
+ dir.Errs = append(dir.Errs, fmt.Errorf("multiple instances of direntry name %q", entry.Name))
+ }
+ continue
}
- continue
+ 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))
}
- ret.ChildrenByName[string(entry.Name)] = entry
case btrfsitem.DIR_INDEX_KEY:
index := item.Key.Offset
- entry := item.Body.(btrfsitem.DirEntry)
- 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))
+ switch entry := item.Body.(type) {
+ 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
}
- continue
+ 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))
}
- ret.ChildrenByIndex[index] = entry
default:
panic(fmt.Errorf("TODO: handle item type %v", item.Key.ItemType))
}
}
entriesWithIndexes := make(containers.Set[string])
nextIndex := uint64(2)
- for index, entry := range ret.ChildrenByIndex {
+ for index, entry := range dir.ChildrenByIndex {
if index+1 > nextIndex {
nextIndex = index + 1
}
entriesWithIndexes.Insert(string(entry.Name))
- 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
+ if other, exists := dir.ChildrenByName[string(entry.Name)]; !exists {
+ dir.Errs = append(dir.Errs, fmt.Errorf("missing by-name direntry for %q", entry.Name))
+ dir.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
+ dir.Errs = append(dir.Errs, fmt.Errorf("direntry index %v and direntry name %q disagree", index, entry.Name))
+ dir.ChildrenByName[string(entry.Name)] = entry
}
}
- for _, name := range maps.SortedKeys(ret.ChildrenByName) {
+ for _, name := range maps.SortedKeys(dir.ChildrenByName) {
if !entriesWithIndexes.Has(name) {
- ret.Errs = append(ret.Errs, fmt.Errorf("missing by-index direntry for %q", name))
- ret.ChildrenByIndex[nextIndex] = ret.ChildrenByName[name]
+ dir.Errs = append(dir.Errs, fmt.Errorf("missing by-index direntry for %q", name))
+ dir.ChildrenByIndex[nextIndex] = dir.ChildrenByName[name]
nextIndex++
}
}
@@ -293,6 +335,7 @@ func (dir *Dir) AbsPath() (string, error) {
}
func (sv *Subvolume) LoadFile(inode btrfsprim.ObjID) (*File, error) {
+ sv.init()
val := sv.fileCache.GetOrElse(inode, func() (val *File) {
val = new(File)
fullInode, err := sv.LoadFullInode(inode)
@@ -311,16 +354,23 @@ func (sv *Subvolume) LoadFile(inode btrfsprim.ObjID) (*File, error) {
return val, nil
}
-func (ret *File) populate() {
- for _, item := range ret.OtherItems {
+func (file *File) populate() {
+ for _, item := range file.OtherItems {
switch item.Key.ItemType {
case btrfsitem.INODE_REF_KEY:
// TODO
case btrfsitem.EXTENT_DATA_KEY:
- ret.Extents = append(ret.Extents, FileExtent{
- OffsetWithinFile: int64(item.Key.Offset),
- FileExtent: item.Body.(btrfsitem.FileExtent),
- })
+ switch itemBody := item.Body.(type) {
+ case btrfsitem.FileExtent:
+ file.Extents = append(file.Extents, FileExtent{
+ OffsetWithinFile: int64(item.Key.Offset),
+ FileExtent: itemBody,
+ })
+ 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))
+ }
default:
panic(fmt.Errorf("TODO: handle item type %v", item.Key.ItemType))
}
@@ -329,34 +379,34 @@ func (ret *File) populate() {
// 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
+ sort.Slice(file.Extents, func(i, j int) bool {
+ return file.Extents[i].OffsetWithinFile < file.Extents[j].OffsetWithinFile
})
pos := int64(0)
- for _, extent := range ret.Extents {
+ for _, extent := range file.Extents {
if extent.OffsetWithinFile != pos {
if extent.OffsetWithinFile > pos {
- ret.Errs = append(ret.Errs, fmt.Errorf("extent gap from %v to %v",
+ file.Errs = append(file.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",
+ file.Errs = append(file.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))
+ file.Errs = append(file.Errs, fmt.Errorf("extent %v: %w", extent.OffsetWithinFile, err))
}
pos += size
}
- if ret.InodeItem != nil && pos != ret.InodeItem.NumBytes {
- if ret.InodeItem.NumBytes > pos {
- ret.Errs = append(ret.Errs, fmt.Errorf("extent gap from %v to %v",
- pos, ret.InodeItem.NumBytes))
+ if file.InodeItem != nil && pos != file.InodeItem.NumBytes {
+ if file.InodeItem.NumBytes > pos {
+ file.Errs = append(file.Errs, fmt.Errorf("extent gap from %v to %v",
+ pos, file.InodeItem.NumBytes))
} else {
- ret.Errs = append(ret.Errs, fmt.Errorf("extent mapped past end of file from %v to %v",
- ret.InodeItem.NumBytes, pos))
+ file.Errs = append(file.Errs, fmt.Errorf("extent mapped past end of file from %v to %v",
+ file.InodeItem.NumBytes, pos))
}
}
}
@@ -401,7 +451,7 @@ func (file *File) maybeShortReadAt(dat []byte, off int64) (int, error) {
if err != nil {
return 0, err
}
- var beg btrfsvol.LogicalAddr = extent.BodyExtent.DiskByteNr.
+ beg := extent.BodyExtent.DiskByteNr.
Add(extent.BodyExtent.Offset).
Add(btrfsvol.AddrDelta(offsetWithinExt))
var block [btrfssum.BlockSize]byte