summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/binstruct/binint/builtins.go129
-rw-r--r--lib/binstruct/binstruct_test.go3
-rw-r--r--lib/binstruct/size.go18
-rw-r--r--lib/binstruct/structs.go4
-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
-rw-r--r--lib/btrfsprogs/btrfsinspect/mount.go18
-rw-r--r--lib/btrfsprogs/btrfsinspect/print_tree.go48
-rw-r--r--lib/btrfsprogs/btrfsinspect/rebuildmappings/fuzzymatchsums.go17
-rw-r--r--lib/btrfsprogs/btrfsinspect/rebuildmappings/matchsums.go7
-rw-r--r--lib/btrfsprogs/btrfsinspect/rebuildmappings/rebuildmappings.go9
-rw-r--r--lib/btrfsprogs/btrfsinspect/rebuildnodes/btrees/rebuilt_btrees.go8
-rw-r--r--lib/btrfsprogs/btrfsinspect/rebuildnodes/graph/graph.go4
-rw-r--r--lib/btrfsprogs/btrfsinspect/rebuildnodes/keyio/keyio.go3
-rw-r--r--lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild.go16
-rw-r--r--lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild_graph.go16
-rw-r--r--lib/btrfsprogs/btrfsinspect/rebuildnodes/scan.go2
-rw-r--r--lib/btrfsprogs/btrfsinspect/scandevices.go100
-rw-r--r--lib/btrfsprogs/btrfsutil/broken_btree.go6
-rw-r--r--lib/btrfsprogs/btrfsutil/open.go8
-rw-r--r--lib/btrfsprogs/btrfsutil/skinny_paths.go3
-rw-r--r--lib/containers/intervaltree.go4
-rw-r--r--lib/containers/intervaltree_test.go3
-rw-r--r--lib/containers/lru.go38
-rw-r--r--lib/containers/rbtree.go25
-rw-r--r--lib/containers/rbtree_test.go10
-rw-r--r--lib/containers/set.go7
-rw-r--r--lib/containers/syncmap.go11
-rw-r--r--lib/diskio/kmp.go8
-rw-r--r--lib/diskio/kmp_test.go5
-rw-r--r--lib/fmtutil/fmt.go3
-rw-r--r--lib/textui/log.go15
-rw-r--r--lib/textui/log_memstats.go2
-rw-r--r--lib/textui/log_test.go5
-rw-r--r--lib/textui/progress.go5
-rw-r--r--lib/textui/text.go12
-rw-r--r--lib/textui/text_test.go5
-rw-r--r--lib/textui/tunable.go13
54 files changed, 580 insertions, 420 deletions
diff --git a/lib/binstruct/binint/builtins.go b/lib/binstruct/binint/builtins.go
index 01186bc..cfd0fc2 100644
--- a/lib/binstruct/binint/builtins.go
+++ b/lib/binstruct/binint/builtins.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
@@ -10,230 +10,249 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/binstruct/binutil"
)
+const (
+ sizeof8 = 1
+ sizeof16 = 2
+ sizeof32 = 4
+ sizeof64 = 8
+)
+
// unsigned
type U8 uint8
-func (U8) BinaryStaticSize() int { return 1 }
+func (U8) BinaryStaticSize() int { return sizeof8 }
func (x U8) MarshalBinary() ([]byte, error) { return []byte{byte(x)}, nil }
func (x *U8) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 1); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof8); err != nil {
return 0, err
}
*x = U8(dat[0])
- return 1, nil
+ return sizeof8, nil
}
// unsigned little endian
type U16le uint16
-func (U16le) BinaryStaticSize() int { return 2 }
+func (U16le) BinaryStaticSize() int { return sizeof16 }
func (x U16le) MarshalBinary() ([]byte, error) {
- var buf [2]byte
+ var buf [sizeof16]byte
binary.LittleEndian.PutUint16(buf[:], uint16(x))
return buf[:], nil
}
+
func (x *U16le) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 2); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof16); err != nil {
return 0, err
}
*x = U16le(binary.LittleEndian.Uint16(dat))
- return 2, nil
+ return sizeof16, nil
}
type U32le uint32
-func (U32le) BinaryStaticSize() int { return 4 }
+func (U32le) BinaryStaticSize() int { return sizeof32 }
func (x U32le) MarshalBinary() ([]byte, error) {
- var buf [4]byte
+ var buf [sizeof32]byte
binary.LittleEndian.PutUint32(buf[:], uint32(x))
return buf[:], nil
}
+
func (x *U32le) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 4); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof32); err != nil {
return 0, err
}
*x = U32le(binary.LittleEndian.Uint32(dat))
- return 4, nil
+ return sizeof32, nil
}
type U64le uint64
-func (U64le) BinaryStaticSize() int { return 8 }
+func (U64le) BinaryStaticSize() int { return sizeof64 }
func (x U64le) MarshalBinary() ([]byte, error) {
- var buf [8]byte
+ var buf [sizeof64]byte
binary.LittleEndian.PutUint64(buf[:], uint64(x))
return buf[:], nil
}
+
func (x *U64le) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 8); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof64); err != nil {
return 0, err
}
*x = U64le(binary.LittleEndian.Uint64(dat))
- return 8, nil
+ return sizeof64, nil
}
// unsigned big endian
type U16be uint16
-func (U16be) BinaryStaticSize() int { return 2 }
+func (U16be) BinaryStaticSize() int { return sizeof16 }
func (x U16be) MarshalBinary() ([]byte, error) {
- var buf [2]byte
+ var buf [sizeof16]byte
binary.BigEndian.PutUint16(buf[:], uint16(x))
return buf[:], nil
}
+
func (x *U16be) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 2); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof16); err != nil {
return 0, err
}
*x = U16be(binary.BigEndian.Uint16(dat))
- return 2, nil
+ return sizeof16, nil
}
type U32be uint32
-func (U32be) BinaryStaticSize() int { return 4 }
+func (U32be) BinaryStaticSize() int { return sizeof32 }
func (x U32be) MarshalBinary() ([]byte, error) {
- var buf [4]byte
+ var buf [sizeof32]byte
binary.BigEndian.PutUint32(buf[:], uint32(x))
return buf[:], nil
}
+
func (x *U32be) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 4); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof32); err != nil {
return 0, err
}
*x = U32be(binary.BigEndian.Uint32(dat))
- return 4, nil
+ return sizeof32, nil
}
type U64be uint64
-func (U64be) BinaryStaticSize() int { return 8 }
+func (U64be) BinaryStaticSize() int { return sizeof64 }
func (x U64be) MarshalBinary() ([]byte, error) {
- var buf [8]byte
+ var buf [sizeof64]byte
binary.BigEndian.PutUint64(buf[:], uint64(x))
return buf[:], nil
}
+
func (x *U64be) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 8); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof64); err != nil {
return 0, err
}
*x = U64be(binary.BigEndian.Uint64(dat))
- return 8, nil
+ return sizeof64, nil
}
// signed
type I8 int8
-func (I8) BinaryStaticSize() int { return 1 }
+func (I8) BinaryStaticSize() int { return sizeof8 }
func (x I8) MarshalBinary() ([]byte, error) { return []byte{byte(x)}, nil }
func (x *I8) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 1); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof8); err != nil {
return 0, err
}
*x = I8(dat[0])
- return 1, nil
+ return sizeof8, nil
}
// signed little endian
type I16le int16
-func (I16le) BinaryStaticSize() int { return 2 }
+func (I16le) BinaryStaticSize() int { return sizeof16 }
func (x I16le) MarshalBinary() ([]byte, error) {
- var buf [2]byte
+ var buf [sizeof16]byte
binary.LittleEndian.PutUint16(buf[:], uint16(x))
return buf[:], nil
}
+
func (x *I16le) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 2); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof16); err != nil {
return 0, err
}
*x = I16le(binary.LittleEndian.Uint16(dat))
- return 2, nil
+ return sizeof16, nil
}
type I32le int32
-func (I32le) BinaryStaticSize() int { return 4 }
+func (I32le) BinaryStaticSize() int { return sizeof32 }
func (x I32le) MarshalBinary() ([]byte, error) {
- var buf [4]byte
+ var buf [sizeof32]byte
binary.LittleEndian.PutUint32(buf[:], uint32(x))
return buf[:], nil
}
+
func (x *I32le) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 4); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof32); err != nil {
return 0, err
}
*x = I32le(binary.LittleEndian.Uint32(dat))
- return 4, nil
+ return sizeof32, nil
}
type I64le int64
-func (I64le) BinaryStaticSize() int { return 8 }
+func (I64le) BinaryStaticSize() int { return sizeof64 }
func (x I64le) MarshalBinary() ([]byte, error) {
- var buf [8]byte
+ var buf [sizeof64]byte
binary.LittleEndian.PutUint64(buf[:], uint64(x))
return buf[:], nil
}
+
func (x *I64le) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 8); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof64); err != nil {
return 0, err
}
*x = I64le(binary.LittleEndian.Uint64(dat))
- return 8, nil
+ return sizeof64, nil
}
// signed big endian
type I16be int16
-func (I16be) BinaryStaticSize() int { return 2 }
+func (I16be) BinaryStaticSize() int { return sizeof16 }
func (x I16be) MarshalBinary() ([]byte, error) {
- var buf [2]byte
+ var buf [sizeof16]byte
binary.BigEndian.PutUint16(buf[:], uint16(x))
return buf[:], nil
}
+
func (x *I16be) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 2); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof16); err != nil {
return 0, err
}
*x = I16be(binary.BigEndian.Uint16(dat))
- return 2, nil
+ return sizeof16, nil
}
type I32be int32
-func (I32be) BinaryStaticSize() int { return 4 }
+func (I32be) BinaryStaticSize() int { return sizeof32 }
func (x I32be) MarshalBinary() ([]byte, error) {
- var buf [4]byte
+ var buf [sizeof32]byte
binary.BigEndian.PutUint32(buf[:], uint32(x))
return buf[:], nil
}
+
func (x *I32be) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 4); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof32); err != nil {
return 0, err
}
*x = I32be(binary.BigEndian.Uint32(dat))
- return 4, nil
+ return sizeof32, nil
}
type I64be int64
-func (I64be) BinaryStaticSize() int { return 8 }
+func (I64be) BinaryStaticSize() int { return sizeof64 }
func (x I64be) MarshalBinary() ([]byte, error) {
- var buf [8]byte
+ var buf [sizeof64]byte
binary.BigEndian.PutUint64(buf[:], uint64(x))
return buf[:], nil
}
+
func (x *I64be) UnmarshalBinary(dat []byte) (int, error) {
- if err := binutil.NeedNBytes(dat, 8); err != nil {
+ if err := binutil.NeedNBytes(dat, sizeof64); err != nil {
return 0, err
}
*x = I64be(binary.BigEndian.Uint64(dat))
- return 8, nil
+ return sizeof64, nil
}
diff --git a/lib/binstruct/binstruct_test.go b/lib/binstruct/binstruct_test.go
index 105e790..8780acc 100644
--- a/lib/binstruct/binstruct_test.go
+++ b/lib/binstruct/binstruct_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
@@ -13,6 +13,7 @@ import (
)
func TestSmoke(t *testing.T) {
+ t.Parallel()
type UUID [16]byte
type PhysicalAddr int64
type DevItem struct {
diff --git a/lib/binstruct/size.go b/lib/binstruct/size.go
index 365da85..d6d70c6 100644
--- a/lib/binstruct/size.go
+++ b/lib/binstruct/size.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
@@ -28,8 +28,16 @@ var (
unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
)
+const (
+ sizeof8 = 1
+ sizeof16 = 2
+ sizeof32 = 4
+ sizeof64 = 8
+)
+
func staticSize(typ reflect.Type) (int, error) {
if typ.Implements(staticSizerType) {
+ //nolint:forcetypeassert // Already did a type check via reflection.
return reflect.New(typ).Elem().Interface().(StaticSizer).BinaryStaticSize(), nil
}
if typ.Implements(marshalerType) || typ.Implements(unmarshalerType) {
@@ -42,13 +50,13 @@ func staticSize(typ reflect.Type) (int, error) {
}
switch typ.Kind() {
case reflect.Uint8, reflect.Int8:
- return 1, nil
+ return sizeof8, nil
case reflect.Uint16, reflect.Int16:
- return 2, nil
+ return sizeof16, nil
case reflect.Uint32, reflect.Int32:
- return 4, nil
+ return sizeof32, nil
case reflect.Uint64, reflect.Int64:
- return 8, nil
+ return sizeof64, nil
case reflect.Ptr:
return staticSize(typ.Elem())
case reflect.Array:
diff --git a/lib/binstruct/structs.go b/lib/binstruct/structs.go
index 9bc556c..7eea600 100644
--- a/lib/binstruct/structs.go
+++ b/lib/binstruct/structs.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
@@ -120,7 +120,7 @@ func genStructHandler(structInfo reflect.Type) (structHandler, error) {
var curOffset, endOffset int
for i := 0; i < structInfo.NumField(); i++ {
- var fieldInfo reflect.StructField = structInfo.Field(i)
+ fieldInfo := structInfo.Field(i)
if fieldInfo.Anonymous && fieldInfo.Type != endType {
err := fmt.Errorf("binstruct does not support embedded fields")
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
diff --git a/lib/btrfsprogs/btrfsinspect/mount.go b/lib/btrfsprogs/btrfsinspect/mount.go
index 2a0b232..ee3c0ec 100644
--- a/lib/btrfsprogs/btrfsinspect/mount.go
+++ b/lib/btrfsprogs/btrfsinspect/mount.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
@@ -144,11 +144,11 @@ func inodeItemToFUSE(itemBody btrfsitem.Inode) fuseops.InodeAttributes {
Size: uint64(itemBody.Size),
Nlink: uint32(itemBody.NLink),
Mode: uint32(itemBody.Mode),
- //RDev: itemBody.Rdev, // jacobsa/fuse doesn't expose rdev
+ // RDev: itemBody.Rdev, // jacobsa/fuse doesn't expose rdev
Atime: itemBody.ATime.ToStd(),
Mtime: itemBody.MTime.ToStd(),
Ctime: itemBody.CTime.ToStd(),
- //Crtime: itemBody.OTime,
+ // Crtime: itemBody.OTime,
Uid: uint32(itemBody.UID),
Gid: uint32(itemBody.GID),
}
@@ -168,7 +168,7 @@ func (sv *subvolume) LoadDir(inode btrfsprim.ObjID) (val *btrfs.Dir, err error)
if haveSubvolumes {
abspath, _err := val.AbsPath()
if _err != nil {
- return
+ return val, err
}
sv.subvolMu.Lock()
for _, index := range maps.SortedKeys(val.ChildrenByIndex) {
@@ -200,7 +200,7 @@ func (sv *subvolume) LoadDir(inode btrfsprim.ObjID) (val *btrfs.Dir, err error)
sv.subvolMu.Unlock()
}
}
- return
+ return val, err
}
func (sv *subvolume) StatFS(_ context.Context, op *fuseops.StatFSOp) error {
@@ -213,7 +213,7 @@ func (sv *subvolume) StatFS(_ context.Context, op *fuseops.StatFSOp) error {
op.IoSize = sb.SectorSize
op.BlockSize = sb.SectorSize
op.Blocks = sb.TotalBytes / uint64(sb.SectorSize) // TODO: adjust for RAID type
- //op.BlocksFree = TODO
+ // op.BlocksFree = TODO
// btrfs doesn't have a fixed number of inodes
op.Inodes = 0
@@ -260,7 +260,7 @@ func (sv *subvolume) LookUpInode(_ context.Context, op *fuseops.LookUpInodeOp) e
Child: 2, // an inode number that a real file will never have
Attributes: fuseops.InodeAttributes{
Nlink: 1,
- Mode: uint32(btrfsitem.ModeFmtDir | 0700),
+ Mode: uint32(btrfsitem.ModeFmtDir | 0o700), //nolint:gomnd // TODO
},
}
return nil
@@ -315,6 +315,7 @@ func (sv *subvolume) OpenDir(_ context.Context, op *fuseops.OpenDirOp) error {
op.Handle = handle
return nil
}
+
func (sv *subvolume) ReadDir(_ context.Context, op *fuseops.ReadDirOp) error {
state, ok := sv.dirHandles.Load(op.Handle)
if !ok {
@@ -348,6 +349,7 @@ func (sv *subvolume) ReadDir(_ context.Context, op *fuseops.ReadDirOp) error {
}
return nil
}
+
func (sv *subvolume) ReleaseDirHandle(_ context.Context, op *fuseops.ReleaseDirHandleOp) error {
_, ok := sv.dirHandles.LoadAndDelete(op.Handle)
if !ok {
@@ -369,6 +371,7 @@ func (sv *subvolume) OpenFile(_ context.Context, op *fuseops.OpenFileOp) error {
op.KeepPageCache = true
return nil
}
+
func (sv *subvolume) ReadFile(_ context.Context, op *fuseops.ReadFileOp) error {
state, ok := sv.fileHandles.Load(op.Handle)
if !ok {
@@ -392,6 +395,7 @@ func (sv *subvolume) ReadFile(_ context.Context, op *fuseops.ReadFileOp) error {
return err
}
+
func (sv *subvolume) ReleaseFileHandle(_ context.Context, op *fuseops.ReleaseFileHandleOp) error {
_, ok := sv.fileHandles.LoadAndDelete(op.Handle)
if !ok {
diff --git a/lib/btrfsprogs/btrfsinspect/print_tree.go b/lib/btrfsprogs/btrfsinspect/print_tree.go
index 6c31350..62d1d7b 100644
--- a/lib/btrfsprogs/btrfsinspect/print_tree.go
+++ b/lib/btrfsprogs/btrfsinspect/print_tree.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
@@ -142,8 +142,8 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri
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.INODE_EXTREF_KEY:
+ // // TODO
case btrfsitem.DirEntry:
textui.Fprintf(out, "\t\tlocation %v type %v\n",
fmtKey(body.Location), body.Type)
@@ -153,8 +153,8 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri
if len(body.Data) > 0 {
textui.Fprintf(out, "\t\tdata %v\n", body.Data)
}
- //case btrfsitem.DIR_LOG_INDEX_KEY, btrfsitem.DIR_LOG_ITEM_KEY:
- // // TODO
+ // case btrfsitem.DIR_LOG_INDEX_KEY, btrfsitem.DIR_LOG_ITEM_KEY:
+ // // TODO
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)
@@ -200,10 +200,10 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri
body.Head.Refs, body.Head.Generation, body.Head.Flags)
textui.Fprintf(out, "\t\ttree block skinny level %v\n", item.Key.Offset)
printExtentInlineRefs(out, body.Refs)
- //case btrfsitem.EXTENT_DATA_REF_KEY:
- // // TODO
- //case btrfsitem.SHARED_DATA_REF_KEY:
- // // TODO
+ // case btrfsitem.EXTENT_DATA_REF_KEY:
+ // // TODO
+ // case btrfsitem.SHARED_DATA_REF_KEY:
+ // // TODO
case btrfsitem.ExtentCSum:
start := btrfsvol.LogicalAddr(item.Key.Offset)
textui.Fprintf(out, "\t\trange start %d end %d length %d",
@@ -291,16 +291,16 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri
"\t\tchunk_tree_uuid %v\n",
body.ChunkTree, body.ChunkObjectID, body.ChunkOffset, body.Length,
body.ChunkTreeUUID)
- //case btrfsitem.QGROUP_STATUS_KEY:
- // // TODO
- //case btrfsitem.QGROUP_INFO_KEY:
- // // TODO
- //case btrfsitem.QGROUP_LIMIT_KEY:
- // // TODO
+ // case btrfsitem.QGROUP_STATUS_KEY:
+ // // TODO
+ // case btrfsitem.QGROUP_INFO_KEY:
+ // // TODO
+ // case btrfsitem.QGROUP_LIMIT_KEY:
+ // // TODO
case btrfsitem.UUIDMap:
textui.Fprintf(out, "\t\tsubvol_id %d\n", body.ObjID)
- //case btrfsitem.STRING_ITEM_KEY:
- // // TODO
+ // case btrfsitem.STRING_ITEM_KEY:
+ // // TODO
case btrfsitem.DevStats:
textui.Fprintf(out, "\t\tpersistent item objectid %v offset %v\n",
item.Key.ObjectID.Format(item.Key.ItemType), item.Key.Offset)
@@ -316,8 +316,8 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri
default:
textui.Fprintf(out, "\t\tunknown persistent item objectid %v\n", item.Key.ObjectID)
}
- //case btrfsitem.TEMPORARY_ITEM_KEY:
- // // TODO
+ // case btrfsitem.TEMPORARY_ITEM_KEY:
+ // // TODO
case btrfsitem.Empty:
switch item.Key.ItemType {
case btrfsitem.ORPHAN_ITEM_KEY: // 48
@@ -330,10 +330,10 @@ func printTree(ctx context.Context, out io.Writer, fs *btrfs.FS, treeID btrfspri
textui.Fprintf(out, "\t\tfree space extent\n")
case btrfsitem.QGROUP_RELATION_KEY: // 246
// do nothing
- //case btrfsitem.EXTENT_REF_V0_KEY:
- // textui.Fprintf(out, "\t\textent ref v0 (deprecated)\n")
- //case btrfsitem.CSUM_ITEM_KEY:
- // textui.Fprintf(out, "\t\tcsum item\n")
+ // case btrfsitem.EXTENT_REF_V0_KEY:
+ // textui.Fprintf(out, "\t\textent ref v0 (deprecated)\n")
+ // case btrfsitem.CSUM_ITEM_KEY:
+ // textui.Fprintf(out, "\t\tcsum item\n")
default:
textui.Fprintf(out, "\t\t(error) unhandled empty item type: %v\n", item.Key.ItemType)
}
@@ -426,7 +426,7 @@ func fmtKey(key btrfsprim.Key) string {
var out strings.Builder
textui.Fprintf(&out, "key (%v %v", key.ObjectID.Format(key.ItemType), key.ItemType)
switch key.ItemType {
- case btrfsitem.QGROUP_RELATION_KEY: //TODO, btrfsitem.QGROUP_INFO_KEY, btrfsitem.QGROUP_LIMIT_KEY:
+ case btrfsitem.QGROUP_RELATION_KEY: // TODO, btrfsitem.QGROUP_INFO_KEY, btrfsitem.QGROUP_LIMIT_KEY:
panic("TODO: printing qgroup items not yet implemented")
case btrfsitem.UUID_SUBVOL_KEY, btrfsitem.UUID_RECEIVED_SUBVOL_KEY:
textui.Fprintf(&out, " %#08x)", key.Offset)
diff --git a/lib/btrfsprogs/btrfsinspect/rebuildmappings/fuzzymatchsums.go b/lib/btrfsprogs/btrfsinspect/rebuildmappings/fuzzymatchsums.go
index ae83513..9e6b864 100644
--- a/lib/btrfsprogs/btrfsinspect/rebuildmappings/fuzzymatchsums.go
+++ b/lib/btrfsprogs/btrfsinspect/rebuildmappings/fuzzymatchsums.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
@@ -19,7 +19,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/textui"
)
-const minFuzzyPct = 0.5
+var minFuzzyPct = textui.Tunable(0.5)
type fuzzyRecord struct {
PAddr btrfsvol.QualifiedPhysicalAddr
@@ -78,7 +78,7 @@ func fuzzyMatchBlockGroupSums(ctx context.Context,
Dev: paddr.Dev,
Addr: paddr.Addr.Add(-off),
}
- matches[key] = matches[key] + 1
+ matches[key]++
}
return nil
}); err != nil {
@@ -112,8 +112,8 @@ func fuzzyMatchBlockGroupSums(ctx context.Context,
if apply {
lvl = dlog.LogLevelInfo
}
- dlog.Logf(ctx, lvl, "(%v/%v) blockgroup[laddr=%v] matches=[%s]; bestpossible=%v%% (based on %v runs)",
- i+1, numBlockgroups, bgLAddr, matchesStr, int(100*bgRun.PctFull()), len(bgRun.Runs))
+ dlog.Logf(ctx, lvl, "(%v/%v) blockgroup[laddr=%v] matches=[%s]; bestpossible=%v (based on %v runs)",
+ i+1, numBlockgroups, bgLAddr, matchesStr, number.Percent(bgRun.PctFull()), len(bgRun.Runs))
if !apply {
continue
}
@@ -145,11 +145,12 @@ type lowestN[T containers.Ordered[T]] struct {
}
func (l *lowestN[T]) Insert(v T) {
- if len(l.Dat) < l.N {
+ switch {
+ case len(l.Dat) < l.N:
l.Dat = append(l.Dat, v)
- } else if v.Cmp(l.Dat[0]) < 0 {
+ case v.Cmp(l.Dat[0]) < 0:
l.Dat[0] = v
- } else {
+ default:
return
}
sort.Slice(l.Dat, func(i, j int) bool {
diff --git a/lib/btrfsprogs/btrfsinspect/rebuildmappings/matchsums.go b/lib/btrfsprogs/btrfsinspect/rebuildmappings/matchsums.go
index be82f87..02c657f 100644
--- a/lib/btrfsprogs/btrfsinspect/rebuildmappings/matchsums.go
+++ b/lib/btrfsprogs/btrfsinspect/rebuildmappings/matchsums.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
@@ -8,6 +8,7 @@ import (
"context"
"github.com/datawire/dlib/dlog"
+ "golang.org/x/text/number"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfssum"
@@ -55,8 +56,8 @@ func matchBlockGroupSums(ctx context.Context,
if len(matches) == 1 {
lvl = dlog.LogLevelInfo
}
- dlog.Logf(ctx, lvl, "(%v/%v) blockgroup[laddr=%v] has %v matches based on %v%% coverage from %v runs",
- i+1, numBlockgroups, bgLAddr, len(matches), int(100*bgRun.PctFull()), len(bgRun.Runs))
+ dlog.Logf(ctx, lvl, "(%v/%v) blockgroup[laddr=%v] has %v matches based on %v coverage from %v runs",
+ i+1, numBlockgroups, bgLAddr, len(matches), number.Percent(bgRun.PctFull()), len(bgRun.Runs))
if len(matches) != 1 {
continue
}
diff --git a/lib/btrfsprogs/btrfsinspect/rebuildmappings/rebuildmappings.go b/lib/btrfsprogs/btrfsinspect/rebuildmappings/rebuildmappings.go
index 7311aca..665bc96 100644
--- a/lib/btrfsprogs/btrfsinspect/rebuildmappings/rebuildmappings.go
+++ b/lib/btrfsprogs/btrfsinspect/rebuildmappings/rebuildmappings.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
@@ -15,6 +15,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsinspect"
"git.lukeshu.com/btrfs-progs-ng/lib/containers"
"git.lukeshu.com/btrfs-progs-ng/lib/maps"
+ "git.lukeshu.com/btrfs-progs-ng/lib/textui"
)
func getNodeSize(fs *btrfs.FS) (btrfsvol.AddrDelta, error) {
@@ -189,20 +190,20 @@ func RebuildMappings(ctx context.Context, fs *btrfs.FS, scanResults btrfsinspect
unmappedPhysical += region.End.Sub(region.Beg)
}
}
- dlog.Infof(ctx, "... %d KiB of unmapped physical space (across %d regions)", int(unmappedPhysical/1024), numUnmappedPhysical)
+ dlog.Infof(ctx, "... %d of unmapped physical space (across %d regions)", textui.IEC(unmappedPhysical, "B"), numUnmappedPhysical)
unmappedLogicalRegions := ListUnmappedLogicalRegions(fs, logicalSums)
var unmappedLogical btrfsvol.AddrDelta
for _, region := range unmappedLogicalRegions {
unmappedLogical += region.Size()
}
- dlog.Infof(ctx, "... %d KiB of unmapped summed logical space (across %d regions)", int(unmappedLogical/1024), len(unmappedLogicalRegions))
+ dlog.Infof(ctx, "... %d of unmapped summed logical space (across %d regions)", textui.IEC(unmappedLogical, "B"), len(unmappedLogicalRegions))
var unmappedBlockGroups btrfsvol.AddrDelta
for _, bg := range bgs {
unmappedBlockGroups += bg.Size
}
- dlog.Infof(ctx, "... %d KiB of unmapped block groups (across %d groups)", int(unmappedBlockGroups/1024), len(bgs))
+ dlog.Infof(ctx, "... %d of unmapped block groups (across %d groups)", textui.IEC(unmappedBlockGroups, "B"), len(bgs))
dlog.Info(_ctx, "detailed report:")
for _, devID := range maps.SortedKeys(unmappedPhysicalRegions) {
diff --git a/lib/btrfsprogs/btrfsinspect/rebuildnodes/btrees/rebuilt_btrees.go b/lib/btrfsprogs/btrfsinspect/rebuildnodes/btrees/rebuilt_btrees.go
index 33eb352..b53a28e 100644
--- a/lib/btrfsprogs/btrfsinspect/rebuildnodes/btrees/rebuilt_btrees.go
+++ b/lib/btrfsprogs/btrfsinspect/rebuildnodes/btrees/rebuilt_btrees.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
@@ -191,7 +191,7 @@ func (ts *RebuiltTrees) AddRoot(ctx context.Context, treeID btrfsprim.ObjID, roo
var stats rootStats
stats.Leafs.D = len(tree.leafToRoots)
- progressWriter := textui.NewProgress[rootStats](ctx, dlog.LogLevelInfo, 1*time.Second)
+ progressWriter := textui.NewProgress[rootStats](ctx, dlog.LogLevelInfo, textui.Tunable(1*time.Second))
for i, leaf := range maps.SortedKeys(tree.leafToRoots) {
stats.Leafs.N = i
progressWriter.Set(stats)
@@ -287,7 +287,7 @@ func (ts *RebuiltTrees) addTree(ctx context.Context, treeID btrfsprim.ObjID, sta
ts.AddRoot(ctx, treeID, root)
}
- return
+ return true
}
func (tree *rebuiltTree) indexLeafs(ctx context.Context, graph pkggraph.Graph) {
@@ -297,7 +297,7 @@ func (tree *rebuiltTree) indexLeafs(ctx context.Context, graph pkggraph.Graph) {
var stats textui.Portion[int]
stats.D = len(graph.Nodes)
- progressWriter := textui.NewProgress[textui.Portion[int]](ctx, dlog.LogLevelInfo, 1*time.Second)
+ progressWriter := textui.NewProgress[textui.Portion[int]](ctx, dlog.LogLevelInfo, textui.Tunable(1*time.Second))
progress := func() {
stats.N = len(nodeToRoots)
progressWriter.Set(stats)
diff --git a/lib/btrfsprogs/btrfsinspect/rebuildnodes/graph/graph.go b/lib/btrfsprogs/btrfsinspect/rebuildnodes/graph/graph.go
index c4ed675..cf86d74 100644
--- a/lib/btrfsprogs/btrfsinspect/rebuildnodes/graph/graph.go
+++ b/lib/btrfsprogs/btrfsinspect/rebuildnodes/graph/graph.go
@@ -197,7 +197,7 @@ func (g Graph) FinalCheck(ctx context.Context, fs diskio.File[btrfsvol.LogicalAd
ctx = dlog.WithField(_ctx, "btrfsinspect.rebuild-nodes.read.substep", "check-keypointers")
dlog.Info(_ctx, "Checking keypointers for dead-ends...")
- progressWriter := textui.NewProgress[textui.Portion[int]](ctx, dlog.LogLevelInfo, 1*time.Second)
+ progressWriter := textui.NewProgress[textui.Portion[int]](ctx, dlog.LogLevelInfo, textui.Tunable(1*time.Second))
stats.D = len(g.EdgesTo)
progressWriter.Set(stats)
for laddr := range g.EdgesTo {
@@ -221,7 +221,7 @@ func (g Graph) FinalCheck(ctx context.Context, fs diskio.File[btrfsvol.LogicalAd
dlog.Info(_ctx, "Checking for btree loops...")
stats.D = len(g.Nodes)
stats.N = 0
- progressWriter = textui.NewProgress[textui.Portion[int]](ctx, dlog.LogLevelInfo, 1*time.Second)
+ progressWriter = textui.NewProgress[textui.Portion[int]](ctx, dlog.LogLevelInfo, textui.Tunable(1*time.Second))
progressWriter.Set(stats)
visited := make(containers.Set[btrfsvol.LogicalAddr], len(g.Nodes))
numLoops := 0
diff --git a/lib/btrfsprogs/btrfsinspect/rebuildnodes/keyio/keyio.go b/lib/btrfsprogs/btrfsinspect/rebuildnodes/keyio/keyio.go
index b1e68f9..24c3dcf 100644
--- a/lib/btrfsprogs/btrfsinspect/rebuildnodes/keyio/keyio.go
+++ b/lib/btrfsprogs/btrfsinspect/rebuildnodes/keyio/keyio.go
@@ -17,6 +17,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsinspect/rebuildnodes/graph"
"git.lukeshu.com/btrfs-progs-ng/lib/containers"
"git.lukeshu.com/btrfs-progs-ng/lib/diskio"
+ "git.lukeshu.com/btrfs-progs-ng/lib/textui"
)
type ItemPtr struct {
@@ -50,7 +51,7 @@ func NewHandle(file diskio.File[btrfsvol.LogicalAddr], sb btrfstree.Superblock)
Sizes: make(map[ItemPtr]SizeAndErr),
- cache: containers.NewLRUCache[btrfsvol.LogicalAddr, *diskio.Ref[btrfsvol.LogicalAddr, btrfstree.Node]](8),
+ cache: containers.NewLRUCache[btrfsvol.LogicalAddr, *diskio.Ref[btrfsvol.LogicalAddr, btrfstree.Node]](textui.Tunable(8)),
}
}
diff --git a/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild.go b/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild.go
index a7fe5c7..7e55732 100644
--- a/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild.go
+++ b/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild.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
@@ -126,7 +126,7 @@ func (o *rebuilder) rebuild(_ctx context.Context) error {
o.itemQueue = nil
var progress textui.Portion[int]
progress.D = len(itemQueue)
- progressWriter := textui.NewProgress[textui.Portion[int]](stepCtx, dlog.LogLevelInfo, 1*time.Second)
+ progressWriter := textui.NewProgress[textui.Portion[int]](stepCtx, dlog.LogLevelInfo, textui.Tunable(1*time.Second))
stepCtx = dlog.WithField(stepCtx, "btrfsinspect.rebuild-nodes.rebuild.substep.progress", &progress)
for i, key := range itemQueue {
itemCtx := dlog.WithField(stepCtx, "btrfsinspect.rebuild-nodes.rebuild.process.item", key)
@@ -160,7 +160,7 @@ func (o *rebuilder) rebuild(_ctx context.Context) error {
progress.D += len(resolvedAugments[treeID])
}
o.augmentQueue = make(map[btrfsprim.ObjID][]map[btrfsvol.LogicalAddr]int)
- progressWriter = textui.NewProgress[textui.Portion[int]](stepCtx, dlog.LogLevelInfo, 1*time.Second)
+ progressWriter = textui.NewProgress[textui.Portion[int]](stepCtx, dlog.LogLevelInfo, textui.Tunable(1*time.Second))
stepCtx = dlog.WithField(stepCtx, "btrfsinspect.rebuild-nodes.rebuild.substep.progress", &progress)
for _, treeID := range maps.SortedKeys(resolvedAugments) {
treeCtx := dlog.WithField(stepCtx, "btrfsinspect.rebuild-nodes.rebuild.augment.tree", treeID)
@@ -266,14 +266,14 @@ func (o *rebuilder) resolveTreeAugments(ctx context.Context, listsWithDistances
// > 2: [A]
// > 3: [B]
// >
- // > legal solution woudl be `[]`, `[A]` or `[B]`. It would not be legal
+ // > legal solution would be `[]`, `[A]` or `[B]`. It would not be legal
// > to return `[A, B]`.
//
// The algorithm should optimize for the following goals:
//
// - We prefer that each input list have an item in the return set.
//
- // > In Example 1, while `[]`, `[B]`, and `[C]` are permissable
+ // > In Example 1, while `[]`, `[B]`, and `[C]` are permissible
// > solutions, they are not optimal, because one or both of the input
// > lists are not represented.
// >
@@ -299,7 +299,7 @@ func (o *rebuilder) resolveTreeAugments(ctx context.Context, listsWithDistances
// - We prefer items that appear in more lists over items that appear in
// fewer lists.
//
- // The relative priority of these 4 goals is undefined; preferrably the
+ // The relative priority of these 4 goals is undefined; preferably the
// algorithm should be defined in a way that makes it easy to adjust the
// relative priorities.
@@ -317,7 +317,7 @@ func (o *rebuilder) resolveTreeAugments(ctx context.Context, listsWithDistances
counts := make(map[btrfsvol.LogicalAddr]int)
for _, list := range lists {
for item := range list {
- counts[item] = counts[item] + 1
+ counts[item]++
}
}
@@ -386,6 +386,7 @@ func (o *rebuilder) want(ctx context.Context, reason string, treeID btrfsprim.Ob
fmt.Sprintf("tree=%v key={%v %v ?}", treeID, objID, typ))
o._want(ctx, treeID, objID, typ)
}
+
func (o *rebuilder) _want(ctx context.Context, treeID btrfsprim.ObjID, objID btrfsprim.ObjID, typ btrfsprim.ItemType) (key btrfsprim.Key, ok bool) {
if !o.rebuilt.AddTree(ctx, treeID) {
o.itemQueue = append(o.itemQueue, o.curKey)
@@ -429,6 +430,7 @@ func (o *rebuilder) wantOff(ctx context.Context, reason string, treeID btrfsprim
ctx = dlog.WithField(ctx, "btrfsinspect.rebuild-nodes.rebuild.want.key", keyAndTree{TreeID: treeID, Key: key})
o._wantOff(ctx, treeID, key)
}
+
func (o *rebuilder) _wantOff(ctx context.Context, treeID btrfsprim.ObjID, tgt btrfsprim.Key) (ok bool) {
if !o.rebuilt.AddTree(ctx, treeID) {
o.itemQueue = append(o.itemQueue, o.curKey)
diff --git a/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild_graph.go b/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild_graph.go
index 45c9c97..bf4c95d 100644
--- a/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild_graph.go
+++ b/lib/btrfsprogs/btrfsinspect/rebuildnodes/rebuild_graph.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
@@ -112,13 +112,13 @@ func handleItem(o rebuildCallbacks, ctx context.Context, treeID btrfsprim.ObjID,
case btrfsitem.Empty:
// nothing
case btrfsitem.Extent:
- //if body.Head.Flags.Has(btrfsitem.EXTENT_FLAG_TREE_BLOCK) {
- // // Supposedly this flag indicates that that
- // // body.Info.Key identifies a node by the
- // // first key in the node. But nothing in the
- // // kernel ever reads this, so who knows if it
- // // always gets updated correctly?
- //}
+ // if body.Head.Flags.Has(btrfsitem.EXTENT_FLAG_TREE_BLOCK) {
+ // // Supposedly this flag indicates that
+ // // body.Info.Key identifies a node by the
+ // // first key in the node. But nothing in the
+ // // kernel ever reads this, so who knows if it
+ // // always gets updated correctly?
+ // }
for i, ref := range body.Refs {
switch refBody := ref.Body.(type) {
case nil:
diff --git a/lib/btrfsprogs/btrfsinspect/rebuildnodes/scan.go b/lib/btrfsprogs/btrfsinspect/rebuildnodes/scan.go
index 7a112b4..7e96e29 100644
--- a/lib/btrfsprogs/btrfsinspect/rebuildnodes/scan.go
+++ b/lib/btrfsprogs/btrfsinspect/rebuildnodes/scan.go
@@ -32,7 +32,7 @@ func ScanDevices(ctx context.Context, fs *btrfs.FS, scanResults btrfsinspect.Sca
stats.D = countNodes(scanResults)
progressWriter := textui.NewProgress[textui.Portion[int]](
dlog.WithField(ctx, "btrfsinspect.rebuild-nodes.read.substep", "read-nodes"),
- dlog.LogLevelInfo, 1*time.Second)
+ dlog.LogLevelInfo, textui.Tunable(1*time.Second))
nodeGraph := graph.New(*sb)
keyIO := keyio.NewHandle(fs, *sb)
diff --git a/lib/btrfsprogs/btrfsinspect/scandevices.go b/lib/btrfsprogs/btrfsinspect/scandevices.go
index 628995a..7668a83 100644
--- a/lib/btrfsprogs/btrfsinspect/scandevices.go
+++ b/lib/btrfsprogs/btrfsinspect/scandevices.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
@@ -126,7 +126,7 @@ func ScanOneDevice(ctx context.Context, dev *btrfs.Device, sb btrfstree.Superblo
var sums strings.Builder
sums.Grow(numSums * csumSize)
- progressWriter := textui.NewProgress[scanStats](ctx, dlog.LogLevelInfo, 1*time.Second)
+ progressWriter := textui.NewProgress[scanStats](ctx, dlog.LogLevelInfo, textui.Tunable(1*time.Second))
progress := func(pos btrfsvol.PhysicalAddr) {
progressWriter.Set(scanStats{
Portion: textui.Portion[btrfsvol.PhysicalAddr]{
@@ -176,57 +176,65 @@ func ScanOneDevice(ctx context.Context, dev *btrfs.Device, sb btrfstree.Superblo
for i, item := range nodeRef.Data.BodyLeaf {
switch item.Key.ItemType {
case btrfsitem.CHUNK_ITEM_KEY:
- chunk, ok := item.Body.(btrfsitem.Chunk)
- if !ok {
- dlog.Errorf(ctx, "node@%v: item %v: error: type is CHUNK_ITEM_KEY, but struct is %T",
- nodeRef.Addr, i, item.Body)
- continue
+ switch itemBody := item.Body.(type) {
+ 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,
+ })
+ case btrfsitem.Error:
+ dlog.Errorf(ctx, "node@%v: item %v: error: malformed CHUNK_ITEM: %v",
+ nodeRef.Addr, i, itemBody.Err)
+ default:
+ panic(fmt.Errorf("should not happen: CHUNK_ITEM has unexpected item type: %T", itemBody))
}
- dlog.Tracef(ctx, "node@%v: item %v: found chunk",
- nodeRef.Addr, i)
- result.FoundChunks = append(result.FoundChunks, btrfstree.SysChunk{
- Key: item.Key,
- Chunk: chunk,
- })
case btrfsitem.BLOCK_GROUP_ITEM_KEY:
- bg, ok := item.Body.(btrfsitem.BlockGroup)
- if !ok {
- dlog.Errorf(ctx, "node@%v: item %v: error: type is BLOCK_GROUP_ITEM_KEY, but struct is %T",
- nodeRef.Addr, i, item.Body)
- continue
+ switch itemBody := item.Body.(type) {
+ 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,
+ })
+ case btrfsitem.Error:
+ dlog.Errorf(ctx, "node@%v: item %v: error: malformed BLOCK_GROUP_ITEM: %v",
+ nodeRef.Addr, i, itemBody.Err)
+ default:
+ panic(fmt.Errorf("should not happen: BLOCK_GROUP_ITEM has unexpected item type: %T", itemBody))
}
- dlog.Tracef(ctx, "node@%v: item %v: found block group",
- nodeRef.Addr, i)
- result.FoundBlockGroups = append(result.FoundBlockGroups, SysBlockGroup{
- Key: item.Key,
- BG: bg,
- })
case btrfsitem.DEV_EXTENT_KEY:
- devext, ok := item.Body.(btrfsitem.DevExtent)
- if !ok {
- dlog.Errorf(ctx, "node@%v: item %v: error: type is DEV_EXTENT_KEY, but struct is %T",
- nodeRef.Addr, i, item.Body)
- continue
+ switch itemBody := item.Body.(type) {
+ 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,
+ })
+ case btrfsitem.Error:
+ dlog.Errorf(ctx, "node@%v: item %v: error: malformed DEV_EXTENT: %v",
+ nodeRef.Addr, i, itemBody.Err)
+ default:
+ panic(fmt.Errorf("should not happen: DEV_EXTENT has unexpected item type: %T", itemBody))
}
- dlog.Tracef(ctx, "node@%v: item %v: found dev extent",
- nodeRef.Addr, i)
- result.FoundDevExtents = append(result.FoundDevExtents, SysDevExtent{
- Key: item.Key,
- DevExt: devext,
- })
case btrfsitem.EXTENT_CSUM_KEY:
- sums, ok := item.Body.(btrfsitem.ExtentCSum)
- if !ok {
- dlog.Errorf(ctx, "node@%v: item %v: error: type is EXTENT_CSUM_OBJECTID, but struct is %T",
- nodeRef.Addr, i, item.Body)
- continue
+ switch itemBody := item.Body.(type) {
+ 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,
+ })
+ case btrfsitem.Error:
+ dlog.Errorf(ctx, "node@%v: item %v: error: malformed is EXTENT_CSUM: %v",
+ nodeRef.Addr, i, itemBody.Err)
+ default:
+ panic(fmt.Errorf("should not happen: EXTENT_CSUM has unexpected item type: %T", itemBody))
}
- dlog.Tracef(ctx, "node@%v: item %v: found csums",
- nodeRef.Addr, i)
- result.FoundExtentCSums = append(result.FoundExtentCSums, SysExtentCSum{
- Generation: nodeRef.Data.Head.Generation,
- Sums: sums,
- })
}
}
minNextNode = pos + btrfsvol.PhysicalAddr(sb.NodeSize)
diff --git a/lib/btrfsprogs/btrfsutil/broken_btree.go b/lib/btrfsprogs/btrfsutil/broken_btree.go
index 28e8b1d..f0b298e 100644
--- a/lib/btrfsprogs/btrfsutil/broken_btree.go
+++ b/lib/btrfsprogs/btrfsutil/broken_btree.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
@@ -57,7 +57,7 @@ func newTreeIndex(arena *SkinnyPathArena) treeIndex {
}
type brokenTrees struct {
- ctx context.Context
+ ctx context.Context //nolint:containedctx // don't have an option while keeping the same API
inner *btrfs.FS
arena *SkinnyPathArena
@@ -304,7 +304,7 @@ func (bt *brokenTrees) TreeWalk(ctx context.Context, treeID btrfsprim.ObjID, err
node, err = bt.inner.ReadNode(itemPath.Parent())
if err != nil {
errHandle(&btrfstree.TreeError{Path: itemPath, Err: err})
- return nil
+ return nil //nolint:nilerr // We already called errHandle().
}
}
item := node.Data.BodyLeaf[itemPath.Node(-1).FromItemIdx]
diff --git a/lib/btrfsprogs/btrfsutil/open.go b/lib/btrfsprogs/btrfsutil/open.go
index 9491a20..c5ee314 100644
--- a/lib/btrfsprogs/btrfsutil/open.go
+++ b/lib/btrfsprogs/btrfsutil/open.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
@@ -14,6 +14,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
"git.lukeshu.com/btrfs-progs-ng/lib/diskio"
+ "git.lukeshu.com/btrfs-progs-ng/lib/textui"
)
func Open(ctx context.Context, flag int, filenames ...string) (*btrfs.FS, error) {
@@ -30,8 +31,9 @@ func Open(ctx context.Context, flag int, filenames ...string) (*btrfs.FS, error)
}
bufFile := diskio.NewBufferedFile[btrfsvol.PhysicalAddr](
typedFile,
- 16384, // block size: 16KiB
- 1024, // number of blocks to buffer; total of 16MiB
+ //nolint:gomnd // False positive: gomnd.ignored-functions=[textui.Tunable] doesn't support type params.
+ textui.Tunable[btrfsvol.PhysicalAddr](16*1024), // block size: 16KiB
+ textui.Tunable(1024), // number of blocks to buffer; total of 16MiB
)
devFile := &btrfs.Device{
File: bufFile,
diff --git a/lib/btrfsprogs/btrfsutil/skinny_paths.go b/lib/btrfsprogs/btrfsutil/skinny_paths.go
index 2f71968..6a51739 100644
--- a/lib/btrfsprogs/btrfsutil/skinny_paths.go
+++ b/lib/btrfsprogs/btrfsutil/skinny_paths.go
@@ -11,6 +11,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
"git.lukeshu.com/btrfs-progs-ng/lib/containers"
"git.lukeshu.com/btrfs-progs-ng/lib/diskio"
+ "git.lukeshu.com/btrfs-progs-ng/lib/textui"
)
type skinnyItem struct {
@@ -42,7 +43,7 @@ func (a *SkinnyPathArena) init() {
// which is a good number for me. Then I tought it to do a
// better job of recovering trees, and so the memory grew, and I
// cut it to 64K. Then to 8K. Then grew it to 128K.
- a.fatItems = containers.NewLRUCache[skinnyItem, btrfstree.TreePathElem](128 * 1024)
+ a.fatItems = containers.NewLRUCache[skinnyItem, btrfstree.TreePathElem](textui.Tunable(128 * 1024))
}
}
diff --git a/lib/containers/intervaltree.go b/lib/containers/intervaltree.go
index 424b297..16bc916 100644
--- a/lib/containers/intervaltree.go
+++ b/lib/containers/intervaltree.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
@@ -139,4 +139,4 @@ func (t *IntervalTree[K, V]) SearchAll(fn func(K) int) []V {
return ret
}
-//func (t *IntervalTree[K, V]) Walk(fn func(*RBNode[V]) error) error
+// TODO: func (t *IntervalTree[K, V]) Walk(fn func(*RBNode[V]) error) error
diff --git a/lib/containers/intervaltree_test.go b/lib/containers/intervaltree_test.go
index 88c3003..45743a2 100644
--- a/lib/containers/intervaltree_test.go
+++ b/lib/containers/intervaltree_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
@@ -35,6 +35,7 @@ func (ival SimpleInterval) String() string {
}
func TestIntervalTree(t *testing.T) {
+ t.Parallel()
tree := IntervalTree[NativeOrdered[int], SimpleInterval]{
MinFn: func(ival SimpleInterval) NativeOrdered[int] { return NativeOrdered[int]{ival.Min} },
MaxFn: func(ival SimpleInterval) NativeOrdered[int] { return NativeOrdered[int]{ival.Max} },
diff --git a/lib/containers/lru.go b/lib/containers/lru.go
index a235a12..aa372ed 100644
--- a/lib/containers/lru.go
+++ b/lib/containers/lru.go
@@ -1,80 +1,70 @@
-// 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
package containers
import (
- "sync"
-
lru "github.com/hashicorp/golang-lru"
)
// LRUCache is a least-recently-used(ish) cache. A zero LRUCache is
-// usable and has a cache size of 128 items; use NewLRUCache to set a
-// different size.
+// not usable; it must be initialized with NewLRUCache.
type LRUCache[K comparable, V any] struct {
- initOnce sync.Once
- inner *lru.ARCCache
+ inner *lru.ARCCache
}
func NewLRUCache[K comparable, V any](size int) *LRUCache[K, V] {
c := new(LRUCache[K, V])
- c.initOnce.Do(func() {
- c.inner, _ = lru.NewARC(size)
- })
+ c.inner, _ = lru.NewARC(size)
return c
}
-func (c *LRUCache[K, V]) init() {
- c.initOnce.Do(func() {
- c.inner, _ = lru.NewARC(128)
- })
-}
-
func (c *LRUCache[K, V]) Add(key K, value V) {
- c.init()
c.inner.Add(key, value)
}
+
func (c *LRUCache[K, V]) Contains(key K) bool {
- c.init()
return c.inner.Contains(key)
}
+
func (c *LRUCache[K, V]) Get(key K) (value V, ok bool) {
- c.init()
_value, ok := c.inner.Get(key)
if ok {
+ //nolint:forcetypeassert // Typed wrapper around untyped lib.
value = _value.(V)
}
return value, ok
}
+
func (c *LRUCache[K, V]) Keys() []K {
- c.init()
untyped := c.inner.Keys()
typed := make([]K, len(untyped))
for i := range untyped {
+ //nolint:forcetypeassert // Typed wrapper around untyped lib.
typed[i] = untyped[i].(K)
}
return typed
}
+
func (c *LRUCache[K, V]) Len() int {
- c.init()
return c.inner.Len()
}
+
func (c *LRUCache[K, V]) Peek(key K) (value V, ok bool) {
- c.init()
_value, ok := c.inner.Peek(key)
if ok {
+ //nolint:forcetypeassert // Typed wrapper around untyped lib.
value = _value.(V)
}
return value, ok
}
+
func (c *LRUCache[K, V]) Purge() {
- c.init()
c.inner.Purge()
}
+
func (c *LRUCache[K, V]) Remove(key K) {
- c.init()
c.inner.Remove(key)
}
diff --git a/lib/containers/rbtree.go b/lib/containers/rbtree.go
index e2d8f8a..0430123 100644
--- a/lib/containers/rbtree.go
+++ b/lib/containers/rbtree.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
@@ -65,9 +65,9 @@ func (node *RBNode[V]) walk(fn func(*RBNode[V]) error) error {
}
// Search the tree for a value that satisfied the given callbackk
-// function. A return value of 0 means to to return this value; <0
-// means to go left on the tree (the value is too high), >0 means to
-// go right on th etree (the value is too low).
+// function. A return value of 0 means to return this value; <0 means
+// to go left on the tree (the value is too high), >0 means to go
+// right on th etree (the value is too low).
//
// +-----+
// | v=8 | == 0 : this is it
@@ -287,6 +287,8 @@ func (t *RBTree[K, V]) leftRotate(x *RBNode[V]) {
}
func (t *RBTree[K, V]) rightRotate(y *RBNode[V]) {
+ //nolint:dupword
+ //
// | |
// +---+ +---+
// | y | | x |
@@ -336,11 +338,12 @@ func (t *RBTree[K, V]) Insert(val V) {
Parent: parent,
Value: val,
}
- if parent == nil {
+ switch {
+ case parent == nil:
t.root = node
- } else if key.Cmp(t.KeyFn(parent.Value)) < 0 {
+ case key.Cmp(t.KeyFn(parent.Value)) < 0:
parent.Left = node
- } else {
+ default:
parent.Right = node
}
t.updateAttr(node)
@@ -388,10 +391,10 @@ func (t *RBTree[K, V]) Insert(val V) {
t.root.Color = Black
}
-func (t *RBTree[K, V]) transplant(old, new *RBNode[V]) {
- *t.parentChild(old) = new
- if new != nil {
- new.Parent = old.Parent
+func (t *RBTree[K, V]) transplant(oldNode, newNode *RBNode[V]) {
+ *t.parentChild(oldNode) = newNode
+ if newNode != nil {
+ newNode.Parent = oldNode.Parent
}
}
diff --git a/lib/containers/rbtree_test.go b/lib/containers/rbtree_test.go
index a487c52..e42410e 100644
--- a/lib/containers/rbtree_test.go
+++ b/lib/containers/rbtree_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
@@ -39,13 +39,7 @@ func (node *RBNode[V]) asciiArt(w io.Writer, u, m, l string) {
}
node.Right.asciiArt(w, u+" ", u+" ,--", u+" | ")
-
- if node.Color == Red {
- fmt.Fprintf(w, "%s%v\n", m, node)
- } else {
- fmt.Fprintf(w, "%s%v\n", m, node)
- }
-
+ fmt.Fprintf(w, "%s%v\n", m, node)
node.Left.asciiArt(w, l+" | ", l+" `--", l+" ")
}
diff --git a/lib/containers/set.go b/lib/containers/set.go
index 67ba7ac..4fc8aad 100644
--- a/lib/containers/set.go
+++ b/lib/containers/set.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,7 +22,10 @@ var (
_ lowmemjson.Decodable = (*Set[int])(nil)
)
-func cast[T any](x any) T { return x.(T) }
+func cast[T any](x any) T {
+ //nolint:forcetypeassert // Only called within a type switch.
+ return x.(T)
+}
func (o Set[T]) EncodeJSON(w io.Writer) error {
var less func(a, b T) bool
diff --git a/lib/containers/syncmap.go b/lib/containers/syncmap.go
index fb7f59b..74da4b3 100644
--- a/lib/containers/syncmap.go
+++ b/lib/containers/syncmap.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
@@ -15,30 +15,39 @@ type SyncMap[K comparable, V any] struct {
func (m *SyncMap[K, V]) Delete(key K) {
m.inner.Delete(key)
}
+
func (m *SyncMap[K, V]) Load(key K) (value V, ok bool) {
_value, ok := m.inner.Load(key)
if ok {
+ //nolint:forcetypeassert // Typed wrapper around untyped lib.
value = _value.(V)
}
return value, ok
}
+
func (m *SyncMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
_value, ok := m.inner.LoadAndDelete(key)
if ok {
+ //nolint:forcetypeassert // Typed wrapper around untyped lib.
value = _value.(V)
}
return value, ok
}
+
func (m *SyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
_actual, loaded := m.inner.LoadOrStore(key, value)
+ //nolint:forcetypeassert // Typed wrapper around untyped lib.
actual = _actual.(V)
return actual, loaded
}
+
func (m *SyncMap[K, V]) Range(f func(key K, value V) bool) {
m.inner.Range(func(key, value any) bool {
+ //nolint:forcetypeassert // Typed wrapper around untyped lib.
return f(key.(K), value.(V))
})
}
+
func (m *SyncMap[K, V]) Store(key K, value V) {
m.inner.Store(key, value)
}
diff --git a/lib/diskio/kmp.go b/lib/diskio/kmp.go
index 5f6e295..6949aa4 100644
--- a/lib/diskio/kmp.go
+++ b/lib/diskio/kmp.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
@@ -15,6 +15,7 @@ func kmpEq2[K ~int64, V comparable](aS Sequence[K, V], aI K, bS Sequence[K, V],
aV, aErr := aS.Get(aI)
bV, bErr := bS.Get(bI)
if aErr != nil {
+ //nolint:errorlint // The == is just a fast-path; we still fall back to errors.Is.
if aErr == ErrWildcard || errors.Is(aErr, ErrWildcard) {
aV = bV
aErr = nil
@@ -23,6 +24,7 @@ func kmpEq2[K ~int64, V comparable](aS Sequence[K, V], aI K, bS Sequence[K, V],
}
}
if bErr != nil {
+ //nolint:errorlint // The == is just a fast-path; we still fall back to errors.Is.
if bErr == ErrWildcard || errors.Is(bErr, ErrWildcard) {
bV = aV
bErr = nil
@@ -39,6 +41,7 @@ func kmpEq2[K ~int64, V comparable](aS Sequence[K, V], aI K, bS Sequence[K, V],
func kmpEq1[K ~int64, V comparable](aV V, bS Sequence[K, V], bI K) bool {
bV, bErr := bS.Get(bI)
if bErr != nil {
+ //nolint:errorlint // The == is just a fast-path; we still fall back to errors.Is.
if bErr == ErrWildcard || errors.Is(bErr, ErrWildcard) {
return true
}
@@ -53,6 +56,7 @@ func kmpEq1[K ~int64, V comparable](aV V, bS Sequence[K, V], bI K) bool {
func buildKMPTable[K ~int64, V comparable](substr Sequence[K, V]) ([]K, error) {
var substrLen K
for {
+ //nolint:errorlint // The == is just a fast-path; we still fall back to errors.Is.
if _, err := substr.Get(substrLen); err != nil && !(err == ErrWildcard || errors.Is(err, ErrWildcard)) {
if errors.Is(err, io.EOF) {
break
@@ -84,7 +88,7 @@ func buildKMPTable[K ~int64, V comparable](substr Sequence[K, V]) ([]K, error) {
}
// IndexAll returns the starting-position of all possibly-overlapping
-// occurances of 'substr' in the 'str' sequence.
+// occurrences of 'substr' in the 'str' sequence.
//
// Will hop around in 'substr', but will only get the natural sequence
// [0...) in order from 'str'. When hopping around in 'substr' it
diff --git a/lib/diskio/kmp_test.go b/lib/diskio/kmp_test.go
index 59b6224..4d4b3be 100644
--- a/lib/diskio/kmp_test.go
+++ b/lib/diskio/kmp_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
@@ -14,6 +14,7 @@ import (
)
func TestBuildKMPTable(t *testing.T) {
+ t.Parallel()
substr := SliceSequence[int64, byte]([]byte("ababaa"))
table, err := buildKMPTable[int64, byte](substr)
require.NoError(t, err)
@@ -81,6 +82,7 @@ func (re RESeq) Get(i int64) (byte, error) {
}
func TestKMPWildcard(t *testing.T) {
+ t.Parallel()
type testcase struct {
InStr string
InSubstr string
@@ -111,6 +113,7 @@ func TestKMPWildcard(t *testing.T) {
for tcName, tc := range testcases {
tc := tc
t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
matches, err := IndexAll[int64, byte](
StringSequence[int64](tc.InStr),
RESeq(tc.InSubstr))
diff --git a/lib/fmtutil/fmt.go b/lib/fmtutil/fmt.go
index 3898046..b310eb6 100644
--- a/lib/fmtutil/fmt.go
+++ b/lib/fmtutil/fmt.go
@@ -52,7 +52,8 @@ func FormatByteArrayStringer(
fmt.Formatter
},
objBytes []byte,
- f fmt.State, verb rune) {
+ f fmt.State, verb rune,
+) {
switch verb {
case 'v':
if !f.Flag('#') {
diff --git a/lib/textui/log.go b/lib/textui/log.go
index 5ddc7a8..d2373f8 100644
--- a/lib/textui/log.go
+++ b/lib/textui/log.go
@@ -1,5 +1,5 @@
-// Copyright (C) 2019-2022 Ambassador Labs
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2019-2022-2023 Ambassador Labs
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: Apache-2.0
//
@@ -134,21 +134,21 @@ func (l *logger) Log(lvl dlog.LogLevel, msg string) {
// UnformattedLog implements dlog.OptimizedLogger.
func (l *logger) UnformattedLog(lvl dlog.LogLevel, args ...any) {
l.log(lvl, func(w io.Writer) {
- printer.Fprint(w, args...)
+ _, _ = printer.Fprint(w, args...)
})
}
// UnformattedLogln implements dlog.OptimizedLogger.
func (l *logger) UnformattedLogln(lvl dlog.LogLevel, args ...any) {
l.log(lvl, func(w io.Writer) {
- printer.Fprintln(w, args...)
+ _, _ = printer.Fprintln(w, args...)
})
}
// UnformattedLogf implements dlog.OptimizedLogger.
func (l *logger) UnformattedLogf(lvl dlog.LogLevel, format string, args ...any) {
l.log(lvl, func(w io.Writer) {
- printer.Fprintf(w, format, args...)
+ _, _ = printer.Fprintf(w, format, args...)
})
}
@@ -159,6 +159,7 @@ var (
)
func init() {
+ //nolint:dogsled // I can't change the signature of the stdlib.
_, file, _, _ := runtime.Caller(0)
thisModDir = filepath.Dir(filepath.Dir(filepath.Dir(file)))
}
@@ -171,8 +172,8 @@ func (l *logger) log(lvl dlog.LogLevel, writeMsg func(io.Writer)) {
// This is optimized for mostly-single-threaded usage. If I cared more
// about multi-threaded performance, I'd trade in some
// memory-use/allocations and (1) instead of using a static `logBuf`,
- // I'd have a `logBufPool` `sync.Pool`, and (2) have the the final call
- // to `l.out.Write()` be the only thing protected by `logMu`.
+ // I'd have a `logBufPool` `sync.Pool`, and (2) have the final call to
+ // `l.out.Write()` be the only thing protected by `logMu`.
logMu.Lock()
defer logMu.Unlock()
defer logBuf.Reset()
diff --git a/lib/textui/log_memstats.go b/lib/textui/log_memstats.go
index 39733c6..6e3c3a1 100644
--- a/lib/textui/log_memstats.go
+++ b/lib/textui/log_memstats.go
@@ -19,7 +19,7 @@ type LiveMemUse struct {
var _ fmt.Stringer = (*LiveMemUse)(nil)
-const liveMemUseUpdateInterval = 1 * time.Second
+var liveMemUseUpdateInterval = Tunable(1 * time.Second)
func (o *LiveMemUse) String() string {
o.mu.Lock()
diff --git a/lib/textui/log_test.go b/lib/textui/log_test.go
index 08eb38c..514d96e 100644
--- a/lib/textui/log_test.go
+++ b/lib/textui/log_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
@@ -20,6 +20,7 @@ func logLineRegexp(inner string) string {
}
func TestLogFormat(t *testing.T) {
+ t.Parallel()
var out strings.Builder
ctx := dlog.WithLogger(context.Background(), textui.NewLogger(&out, dlog.LogLevelTrace))
dlog.Debugf(ctx, "foo %d", 12345)
@@ -29,6 +30,7 @@ func TestLogFormat(t *testing.T) {
}
func TestLogLevel(t *testing.T) {
+ t.Parallel()
var out strings.Builder
ctx := dlog.WithLogger(context.Background(), textui.NewLogger(&out, dlog.LogLevelInfo))
dlog.Error(ctx, "Error")
@@ -54,6 +56,7 @@ func TestLogLevel(t *testing.T) {
}
func TestLogField(t *testing.T) {
+ t.Parallel()
var out strings.Builder
ctx := dlog.WithLogger(context.Background(), textui.NewLogger(&out, dlog.LogLevelInfo))
ctx = dlog.WithField(ctx, "foo", 12345)
diff --git a/lib/textui/progress.go b/lib/textui/progress.go
index 7b3f63a..68d986f 100644
--- a/lib/textui/progress.go
+++ b/lib/textui/progress.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
@@ -19,7 +19,7 @@ type Stats interface {
}
type Progress[T Stats] struct {
- ctx context.Context
+ ctx context.Context //nolint:containedctx // captured for separate goroutine
lvl dlog.LogLevel
interval time.Duration
@@ -56,6 +56,7 @@ func (p *Progress[T]) Done() {
}
func (p *Progress[T]) flush(force bool) {
+ //nolint:forcetypeassert // It wasn't worth it to me (yet?) to make a typed wrapper around atomic.Value.
cur := p.cur.Load().(T)
if !force && cur == p.oldStat {
return
diff --git a/lib/textui/text.go b/lib/textui/text.go
index d6a80b3..c0a3c5e 100644
--- a/lib/textui/text.go
+++ b/lib/textui/text.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
@@ -53,7 +53,7 @@ var (
// String implements fmt.Formatter.
func (h humanized) Format(f fmt.State, verb rune) {
- printer.Fprintf(f, fmtutil.FmtStateString(f, verb), h.val)
+ _, _ = printer.Fprintf(f, fmtutil.FmtStateString(f, verb), h.val)
}
// String implements fmt.Stringer.
@@ -72,9 +72,7 @@ type Portion[T constraints.Integer] struct {
N, D T
}
-var (
- _ fmt.Stringer = Portion[int]{}
-)
+var _ fmt.Stringer = Portion[int]{}
// String implements fmt.Stringer.
func (p Portion[T]) String() string {
@@ -146,7 +144,7 @@ func (v metric[T]) Format(f fmt.State, verb rune) {
if v.Val < 0 {
y = -y
}
- printer.Fprintf(f, fmtutil.FmtStateString(f, verb)+"%s%s",
+ _, _ = printer.Fprintf(f, fmtutil.FmtStateString(f, verb)+"%s%s",
y, prefix, v.Unit)
}
@@ -194,7 +192,7 @@ func (v iec[T]) Format(f fmt.State, verb rune) {
if v.Val < 0 {
y = -y
}
- printer.Fprintf(f, fmtutil.FmtStateString(f, verb)+"%s%s",
+ _, _ = printer.Fprintf(f, fmtutil.FmtStateString(f, verb)+"%s%s",
number.Decimal(y), prefix, v.Unit)
}
diff --git a/lib/textui/text_test.go b/lib/textui/text_test.go
index c4b42f6..4b93683 100644
--- a/lib/textui/text_test.go
+++ b/lib/textui/text_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
@@ -16,12 +16,14 @@ import (
)
func TestFprintf(t *testing.T) {
+ t.Parallel()
var out strings.Builder
textui.Fprintf(&out, "%d", 12345)
assert.Equal(t, "12,345", out.String())
}
func TestHumanized(t *testing.T) {
+ t.Parallel()
assert.Equal(t, "12,345", fmt.Sprint(textui.Humanized(12345)))
assert.Equal(t, "12,345 ", fmt.Sprintf("%-8d", textui.Humanized(12345)))
@@ -32,6 +34,7 @@ func TestHumanized(t *testing.T) {
}
func TestPortion(t *testing.T) {
+ t.Parallel()
assert.Equal(t, "100% (0/0)", fmt.Sprint(textui.Portion[int]{}))
assert.Equal(t, "0% (1/12,345)", fmt.Sprint(textui.Portion[int]{N: 1, D: 12345}))
assert.Equal(t, "100% (0/0)", fmt.Sprint(textui.Portion[btrfsvol.PhysicalAddr]{}))
diff --git a/lib/textui/tunable.go b/lib/textui/tunable.go
new file mode 100644
index 0000000..7e5f311
--- /dev/null
+++ b/lib/textui/tunable.go
@@ -0,0 +1,13 @@
+// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package textui
+
+// Tunable annotates a value as something that might want to be tuned
+// as the program gets optimized.
+//
+// TODO(lukeshu): Have Tunable be runtime-configurable.
+func Tunable[T any](x T) T {
+ return x
+}