summaryrefslogtreecommitdiff
path: root/lib/btrfs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/btrfs')
-rw-r--r--lib/btrfs/btrfsitem/item_chunk.go5
-rw-r--r--lib/btrfs/btrfstree/btree.go22
-rw-r--r--lib/btrfs/btrfstree/btree_tree.go210
-rw-r--r--lib/btrfs/btrfstree/path.go22
-rw-r--r--lib/btrfs/btrfstree/readnode.go16
-rw-r--r--lib/btrfs/btrfstree/types_node.go93
-rw-r--r--lib/btrfs/io2_lv.go2
-rw-r--r--lib/btrfs/io3_btree.go6
-rw-r--r--lib/btrfs/io4_fs.go83
9 files changed, 226 insertions, 233 deletions
diff --git a/lib/btrfs/btrfsitem/item_chunk.go b/lib/btrfs/btrfsitem/item_chunk.go
index 607df75..9bdef1f 100644
--- a/lib/btrfs/btrfsitem/item_chunk.go
+++ b/lib/btrfs/btrfsitem/item_chunk.go
@@ -61,10 +61,7 @@ func (chunk Chunk) Mappings(key btrfsprim.Key) []btrfsvol.Mapping {
},
Size: chunk.Head.Size,
SizeLocked: true,
- Flags: containers.Optional[btrfsvol.BlockGroupFlags]{
- OK: true,
- Val: chunk.Head.Type,
- },
+ Flags: containers.OptionalValue(chunk.Head.Type),
})
}
return ret
diff --git a/lib/btrfs/btrfstree/btree.go b/lib/btrfs/btrfstree/btree.go
index 89d4f9d..e91fcc1 100644
--- a/lib/btrfs/btrfstree/btree.go
+++ b/lib/btrfs/btrfstree/btree.go
@@ -11,8 +11,6 @@ import (
"fmt"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
- "git.lukeshu.com/btrfs-progs-ng/lib/diskio"
)
type TreeSearcher interface {
@@ -71,20 +69,20 @@ type TreeWalkHandler struct {
// node immediately stops getting processed; if PreNode, Node,
// or BadNode return io/fs.SkipDir then key pointers and items
// within the node are not processed.
- PreNode func(TreePath) error
- Node func(TreePath, *diskio.Ref[btrfsvol.LogicalAddr, Node]) error
- BadNode func(TreePath, *diskio.Ref[btrfsvol.LogicalAddr, Node], error) error
- PostNode func(TreePath, *diskio.Ref[btrfsvol.LogicalAddr, Node]) error
+ PreNode func(Path) error
+ Node func(Path, *Node) error
+ BadNode func(Path, *Node, error) error
+ PostNode func(Path, *Node) error
// Callbacks for items on interior nodes
- PreKeyPointer func(TreePath, KeyPointer) error
- PostKeyPointer func(TreePath, KeyPointer) error
+ PreKeyPointer func(Path, KeyPointer) error
+ PostKeyPointer func(Path, KeyPointer) error
// Callbacks for items on leaf nodes
- Item func(TreePath, Item) error
- BadItem func(TreePath, Item) error
+ Item func(Path, Item) error
+ BadItem func(Path, Item) error
}
type TreeError struct {
- Path TreePath
+ Path Path
Err error
}
@@ -96,5 +94,5 @@ func (e *TreeError) Error() string {
type NodeSource interface {
Superblock() (*Superblock, error)
- ReadNode(TreePath) (*diskio.Ref[btrfsvol.LogicalAddr, Node], error)
+ ReadNode(Path) (*Node, error)
}
diff --git a/lib/btrfs/btrfstree/btree_tree.go b/lib/btrfs/btrfstree/btree_tree.go
index 1e3c789..459f481 100644
--- a/lib/btrfs/btrfstree/btree_tree.go
+++ b/lib/btrfs/btrfstree/btree_tree.go
@@ -15,8 +15,6 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
- "git.lukeshu.com/btrfs-progs-ng/lib/diskio"
"git.lukeshu.com/btrfs-progs-ng/lib/slices"
)
@@ -28,11 +26,11 @@ type TreeOperatorImpl struct {
func (fs TreeOperatorImpl) TreeWalk(ctx context.Context, treeID btrfsprim.ObjID, errHandle func(*TreeError), cbs TreeWalkHandler) {
sb, err := fs.Superblock()
if err != nil {
- errHandle(&TreeError{Path: TreePath{{FromTree: treeID, ToMaxKey: btrfsprim.MaxKey}}, Err: err})
+ errHandle(&TreeError{Path: Path{{FromTree: treeID, ToMaxKey: btrfsprim.MaxKey}}, Err: err})
}
rootInfo, err := LookupTreeRoot(fs, *sb, treeID)
if err != nil {
- errHandle(&TreeError{Path: TreePath{{FromTree: treeID, ToMaxKey: btrfsprim.MaxKey}}, Err: err})
+ errHandle(&TreeError{Path: Path{{FromTree: treeID, ToMaxKey: btrfsprim.MaxKey}}, Err: err})
return
}
fs.RawTreeWalk(ctx, *rootInfo, errHandle, cbs)
@@ -41,7 +39,7 @@ func (fs TreeOperatorImpl) TreeWalk(ctx context.Context, treeID btrfsprim.ObjID,
// RawTreeWalk is a utility method to help with implementing the
// 'TreeOperator' interface.
func (fs TreeOperatorImpl) RawTreeWalk(ctx context.Context, rootInfo TreeRoot, errHandle func(*TreeError), cbs TreeWalkHandler) {
- path := TreePath{{
+ path := Path{{
FromTree: rootInfo.TreeID,
FromItemSlot: -1,
ToNodeAddr: rootInfo.RootNode,
@@ -52,7 +50,7 @@ func (fs TreeOperatorImpl) RawTreeWalk(ctx context.Context, rootInfo TreeRoot, e
fs.treeWalk(ctx, path, errHandle, cbs)
}
-func (fs TreeOperatorImpl) treeWalk(ctx context.Context, path TreePath, errHandle func(*TreeError), cbs TreeWalkHandler) {
+func (fs TreeOperatorImpl) treeWalk(ctx context.Context, path Path, errHandle func(*TreeError), cbs TreeWalkHandler) {
if ctx.Err() != nil {
return
}
@@ -72,7 +70,7 @@ func (fs TreeOperatorImpl) treeWalk(ctx context.Context, path TreePath, errHandl
}
}
node, err := fs.ReadNode(path)
- defer FreeNodeRef(node)
+ defer node.Free()
if ctx.Err() != nil {
return
}
@@ -97,17 +95,17 @@ func (fs TreeOperatorImpl) treeWalk(ctx context.Context, path TreePath, errHandl
return
}
if node != nil {
- for i, item := range node.Data.BodyInterior {
+ for i, item := range node.BodyInterior {
toMaxKey := path.Node(-1).ToMaxKey
- if i+1 < len(node.Data.BodyInterior) {
- toMaxKey = node.Data.BodyInterior[i+1].Key.Mm()
+ if i+1 < len(node.BodyInterior) {
+ toMaxKey = node.BodyInterior[i+1].Key.Mm()
}
- itemPath := append(path, TreePathElem{
- FromTree: node.Data.Head.Owner,
+ itemPath := append(path, PathElem{
+ FromTree: node.Head.Owner,
FromItemSlot: i,
ToNodeAddr: item.BlockPtr,
ToNodeGeneration: item.Generation,
- ToNodeLevel: node.Data.Head.Level - 1,
+ ToNodeLevel: node.Head.Level - 1,
ToKey: item.Key,
ToMaxKey: toMaxKey,
})
@@ -129,9 +127,9 @@ func (fs TreeOperatorImpl) treeWalk(ctx context.Context, path TreePath, errHandl
}
}
}
- for i, item := range node.Data.BodyLeaf {
- itemPath := append(path, TreePathElem{
- FromTree: node.Data.Head.Owner,
+ for i, item := range node.BodyLeaf {
+ itemPath := append(path, PathElem{
+ FromTree: node.Head.Owner,
FromItemSlot: i,
ToKey: item.Key,
ToMaxKey: item.Key,
@@ -169,8 +167,8 @@ func (fs TreeOperatorImpl) treeWalk(ctx context.Context, path TreePath, errHandl
}
}
-func (fs TreeOperatorImpl) treeSearch(treeRoot TreeRoot, fn func(btrfsprim.Key, uint32) int) (TreePath, *diskio.Ref[btrfsvol.LogicalAddr, Node], error) {
- path := TreePath{{
+func (fs TreeOperatorImpl) treeSearch(treeRoot TreeRoot, fn func(btrfsprim.Key, uint32) int) (Path, *Node, error) {
+ path := Path{{
FromTree: treeRoot.TreeID,
FromItemSlot: -1,
ToNodeAddr: treeRoot.RootNode,
@@ -184,15 +182,15 @@ func (fs TreeOperatorImpl) treeSearch(treeRoot TreeRoot, fn func(btrfsprim.Key,
}
node, err := fs.ReadNode(path)
if err != nil {
- FreeNodeRef(node)
+ node.Free()
return nil, nil, err
}
switch {
- case node.Data.Head.Level > 0:
+ case node.Head.Level > 0:
// interior node
- // Search for the right-most node.Data.BodyInterior item for which
+ // Search for the right-most node.BodyInterior item for which
// `fn(item.Key) >= 0`.
//
// + + + + 0 - - - -
@@ -200,31 +198,31 @@ func (fs TreeOperatorImpl) treeSearch(treeRoot TreeRoot, fn func(btrfsprim.Key,
// There may or may not be a value that returns '0'.
//
// i.e. find the highest value that isn't too high.
- lastGood, ok := slices.SearchHighest(node.Data.BodyInterior, func(kp KeyPointer) int {
+ lastGood, ok := slices.SearchHighest(node.BodyInterior, func(kp KeyPointer) int {
return slices.Min(fn(kp.Key, math.MaxUint32), 0) // don't return >0; a key can't be "too low"
})
if !ok {
- FreeNodeRef(node)
+ node.Free()
return nil, nil, ErrNoItem
}
toMaxKey := path.Node(-1).ToMaxKey
- if lastGood+1 < len(node.Data.BodyInterior) {
- toMaxKey = node.Data.BodyInterior[lastGood+1].Key.Mm()
+ if lastGood+1 < len(node.BodyInterior) {
+ toMaxKey = node.BodyInterior[lastGood+1].Key.Mm()
}
- path = append(path, TreePathElem{
- FromTree: node.Data.Head.Owner,
+ path = append(path, PathElem{
+ FromTree: node.Head.Owner,
FromItemSlot: lastGood,
- ToNodeAddr: node.Data.BodyInterior[lastGood].BlockPtr,
- ToNodeGeneration: node.Data.BodyInterior[lastGood].Generation,
- ToNodeLevel: node.Data.Head.Level - 1,
- ToKey: node.Data.BodyInterior[lastGood].Key,
+ ToNodeAddr: node.BodyInterior[lastGood].BlockPtr,
+ ToNodeGeneration: node.BodyInterior[lastGood].Generation,
+ ToNodeLevel: node.Head.Level - 1,
+ ToKey: node.BodyInterior[lastGood].Key,
ToMaxKey: toMaxKey,
})
- FreeNodeRef(node)
+ node.Free()
default:
// leaf node
- // Search for a member of node.Data.BodyLeaf for which
+ // Search for a member of node.BodyLeaf for which
// `fn(item.Head.Key) == 0`.
//
// + + + + 0 - - - -
@@ -234,25 +232,25 @@ func (fs TreeOperatorImpl) treeSearch(treeRoot TreeRoot, fn func(btrfsprim.Key,
// is returned.
//
// Implement this search as a binary search.
- slot, ok := slices.Search(node.Data.BodyLeaf, func(item Item) int {
+ slot, ok := slices.Search(node.BodyLeaf, func(item Item) int {
return fn(item.Key, item.BodySize)
})
if !ok {
- FreeNodeRef(node)
+ node.Free()
return nil, nil, ErrNoItem
}
- path = append(path, TreePathElem{
- FromTree: node.Data.Head.Owner,
+ path = append(path, PathElem{
+ FromTree: node.Head.Owner,
FromItemSlot: slot,
- ToKey: node.Data.BodyLeaf[slot].Key,
- ToMaxKey: node.Data.BodyLeaf[slot].Key,
+ ToKey: node.BodyLeaf[slot].Key,
+ ToMaxKey: node.BodyLeaf[slot].Key,
})
return path, node, nil
}
}
}
-func (fs TreeOperatorImpl) prev(path TreePath, node *diskio.Ref[btrfsvol.LogicalAddr, Node]) (TreePath, *diskio.Ref[btrfsvol.LogicalAddr, Node], error) {
+func (fs TreeOperatorImpl) prev(path Path, node *Node) (Path, *Node, error) {
var err error
path = path.DeepCopy()
@@ -266,139 +264,139 @@ func (fs TreeOperatorImpl) prev(path TreePath, node *diskio.Ref[btrfsvol.Logical
// go left
path.Node(-1).FromItemSlot--
if path.Node(-1).ToNodeAddr != 0 {
- if node.Addr != path.Node(-2).ToNodeAddr {
- FreeNodeRef(node)
+ if node.Head.Addr != path.Node(-2).ToNodeAddr {
+ node.Free()
node, err = fs.ReadNode(path.Parent())
if err != nil {
- FreeNodeRef(node)
+ node.Free()
return nil, nil, err
}
- path.Node(-1).ToNodeAddr = node.Data.BodyInterior[path.Node(-1).FromItemSlot].BlockPtr
+ path.Node(-1).ToNodeAddr = node.BodyInterior[path.Node(-1).FromItemSlot].BlockPtr
}
}
// go down
for path.Node(-1).ToNodeAddr != 0 {
- if node.Addr != path.Node(-1).ToNodeAddr {
- FreeNodeRef(node)
+ if node.Head.Addr != path.Node(-1).ToNodeAddr {
+ node.Free()
node, err = fs.ReadNode(path)
if err != nil {
- FreeNodeRef(node)
+ node.Free()
return nil, nil, err
}
}
- if node.Data.Head.Level > 0 {
- path = append(path, TreePathElem{
- FromTree: node.Data.Head.Owner,
- FromItemSlot: len(node.Data.BodyInterior) - 1,
- ToNodeAddr: node.Data.BodyInterior[len(node.Data.BodyInterior)-1].BlockPtr,
- ToNodeGeneration: node.Data.BodyInterior[len(node.Data.BodyInterior)-1].Generation,
- ToNodeLevel: node.Data.Head.Level - 1,
- ToKey: node.Data.BodyInterior[len(node.Data.BodyInterior)-1].Key,
+ if node.Head.Level > 0 {
+ path = append(path, PathElem{
+ FromTree: node.Head.Owner,
+ FromItemSlot: len(node.BodyInterior) - 1,
+ ToNodeAddr: node.BodyInterior[len(node.BodyInterior)-1].BlockPtr,
+ ToNodeGeneration: node.BodyInterior[len(node.BodyInterior)-1].Generation,
+ ToNodeLevel: node.Head.Level - 1,
+ ToKey: node.BodyInterior[len(node.BodyInterior)-1].Key,
ToMaxKey: path.Node(-1).ToMaxKey,
})
} else {
- path = append(path, TreePathElem{
- FromTree: node.Data.Head.Owner,
- FromItemSlot: len(node.Data.BodyLeaf) - 1,
- ToKey: node.Data.BodyLeaf[len(node.Data.BodyLeaf)-1].Key,
- ToMaxKey: node.Data.BodyLeaf[len(node.Data.BodyLeaf)-1].Key,
+ path = append(path, PathElem{
+ FromTree: node.Head.Owner,
+ FromItemSlot: len(node.BodyLeaf) - 1,
+ ToKey: node.BodyLeaf[len(node.BodyLeaf)-1].Key,
+ ToMaxKey: node.BodyLeaf[len(node.BodyLeaf)-1].Key,
})
}
}
// return
- if node.Addr != path.Node(-2).ToNodeAddr {
- FreeNodeRef(node)
+ if node.Head.Addr != path.Node(-2).ToNodeAddr {
+ node.Free()
node, err = fs.ReadNode(path.Parent())
if err != nil {
- FreeNodeRef(node)
+ node.Free()
return nil, nil, err
}
}
return path, node, nil
}
-func (fs TreeOperatorImpl) next(path TreePath, node *diskio.Ref[btrfsvol.LogicalAddr, Node]) (TreePath, *diskio.Ref[btrfsvol.LogicalAddr, Node], error) {
+func (fs TreeOperatorImpl) next(path Path, node *Node) (Path, *Node, error) {
var err error
path = path.DeepCopy()
// go up
- if node.Addr != path.Node(-2).ToNodeAddr {
- FreeNodeRef(node)
+ if node.Head.Addr != path.Node(-2).ToNodeAddr {
+ node.Free()
node, err = fs.ReadNode(path.Parent())
if err != nil {
- FreeNodeRef(node)
+ node.Free()
return nil, nil, err
}
- path.Node(-2).ToNodeLevel = node.Data.Head.Level
+ path.Node(-2).ToNodeLevel = node.Head.Level
}
- for path.Node(-1).FromItemSlot+1 >= int(node.Data.Head.NumItems) {
+ for path.Node(-1).FromItemSlot+1 >= int(node.Head.NumItems) {
path = path.Parent()
if len(path) == 1 {
return nil, nil, nil
}
- if node.Addr != path.Node(-2).ToNodeAddr {
- FreeNodeRef(node)
+ if node.Head.Addr != path.Node(-2).ToNodeAddr {
+ node.Free()
node, err = fs.ReadNode(path.Parent())
if err != nil {
- FreeNodeRef(node)
+ node.Free()
return nil, nil, err
}
- path.Node(-2).ToNodeLevel = node.Data.Head.Level
+ path.Node(-2).ToNodeLevel = node.Head.Level
}
}
// go right
path.Node(-1).FromItemSlot++
if path.Node(-1).ToNodeAddr != 0 {
- if node.Addr != path.Node(-2).ToNodeAddr {
- FreeNodeRef(node)
+ if node.Head.Addr != path.Node(-2).ToNodeAddr {
+ node.Free()
node, err = fs.ReadNode(path.Parent())
if err != nil {
- FreeNodeRef(node)
+ node.Free()
return nil, nil, err
}
- path.Node(-1).ToNodeAddr = node.Data.BodyInterior[path.Node(-1).FromItemSlot].BlockPtr
+ path.Node(-1).ToNodeAddr = node.BodyInterior[path.Node(-1).FromItemSlot].BlockPtr
}
}
// go down
for path.Node(-1).ToNodeAddr != 0 {
- if node.Addr != path.Node(-1).ToNodeAddr {
- FreeNodeRef(node)
+ if node.Head.Addr != path.Node(-1).ToNodeAddr {
+ node.Free()
node, err = fs.ReadNode(path)
if err != nil {
- FreeNodeRef(node)
+ node.Free()
return nil, nil, err
}
- path.Node(-1).ToNodeLevel = node.Data.Head.Level
+ path.Node(-1).ToNodeLevel = node.Head.Level
}
- if node.Data.Head.Level > 0 {
+ if node.Head.Level > 0 {
toMaxKey := path.Node(-1).ToMaxKey
- if len(node.Data.BodyInterior) > 1 {
- toMaxKey = node.Data.BodyInterior[1].Key.Mm()
+ if len(node.BodyInterior) > 1 {
+ toMaxKey = node.BodyInterior[1].Key.Mm()
}
- path = append(path, TreePathElem{
- FromTree: node.Data.Head.Owner,
+ path = append(path, PathElem{
+ FromTree: node.Head.Owner,
FromItemSlot: 0,
- ToNodeAddr: node.Data.BodyInterior[0].BlockPtr,
- ToNodeGeneration: node.Data.BodyInterior[0].Generation,
- ToNodeLevel: node.Data.Head.Level - 1,
- ToKey: node.Data.BodyInterior[0].Key,
+ ToNodeAddr: node.BodyInterior[0].BlockPtr,
+ ToNodeGeneration: node.BodyInterior[0].Generation,
+ ToNodeLevel: node.Head.Level - 1,
+ ToKey: node.BodyInterior[0].Key,
ToMaxKey: toMaxKey,
})
} else {
- path = append(path, TreePathElem{
- FromTree: node.Data.Head.Owner,
+ path = append(path, PathElem{
+ FromTree: node.Head.Owner,
FromItemSlot: 0,
- ToKey: node.Data.BodyInterior[0].Key,
- ToMaxKey: node.Data.BodyInterior[0].Key,
+ ToKey: node.BodyInterior[0].Key,
+ ToMaxKey: node.BodyInterior[0].Key,
})
}
}
// return
- if node.Addr != path.Node(-2).ToNodeAddr {
- FreeNodeRef(node)
+ if node.Head.Addr != path.Node(-2).ToNodeAddr {
+ node.Free()
node, err = fs.ReadNode(path.Parent())
if err != nil {
- FreeNodeRef(node)
+ node.Free()
return nil, nil, err
}
}
@@ -419,9 +417,9 @@ func (fs TreeOperatorImpl) TreeSearch(treeID btrfsprim.ObjID, searcher TreeSearc
if err != nil {
return Item{}, fmt.Errorf("item with %s: %w", searcher, err)
}
- item := node.Data.BodyLeaf[path.Node(-1).FromItemSlot]
+ item := node.BodyLeaf[path.Node(-1).FromItemSlot]
item.Body = item.Body.CloneItem()
- FreeNodeRef(node)
+ node.Free()
return item, nil
}
@@ -444,7 +442,7 @@ func (fs TreeOperatorImpl) TreeSearchAll(treeID btrfsprim.ObjID, searcher TreeSe
if err != nil {
return nil, fmt.Errorf("items with %s: %w", searcher, err)
}
- middleItem := middleNode.Data.BodyLeaf[middlePath.Node(-1).FromItemSlot]
+ middleItem := middleNode.BodyLeaf[middlePath.Node(-1).FromItemSlot]
ret := []Item{middleItem}
var errs derror.MultiError
@@ -458,7 +456,7 @@ func (fs TreeOperatorImpl) TreeSearchAll(treeID btrfsprim.ObjID, searcher TreeSe
if len(prevPath) == 0 {
break
}
- prevItem := prevNode.Data.BodyLeaf[prevPath.Node(-1).FromItemSlot]
+ prevItem := prevNode.BodyLeaf[prevPath.Node(-1).FromItemSlot]
if searcher.Search(prevItem.Key, prevItem.BodySize) != 0 {
break
}
@@ -467,11 +465,11 @@ func (fs TreeOperatorImpl) TreeSearchAll(treeID btrfsprim.ObjID, searcher TreeSe
ret = append(ret, item)
}
slices.Reverse(ret)
- if prevNode.Addr != middlePath.Node(-1).ToNodeAddr {
- FreeNodeRef(prevNode)
+ if prevNode.Head.Addr != middlePath.Node(-1).ToNodeAddr {
+ prevNode.Free()
middleNode, err = fs.ReadNode(middlePath)
if err != nil {
- FreeNodeRef(middleNode)
+ middleNode.Free()
return nil, fmt.Errorf("items with %s: %w", searcher, err)
}
}
@@ -485,7 +483,7 @@ func (fs TreeOperatorImpl) TreeSearchAll(treeID btrfsprim.ObjID, searcher TreeSe
if len(nextPath) == 0 {
break
}
- nextItem := nextNode.Data.BodyLeaf[nextPath.Node(-1).FromItemSlot]
+ nextItem := nextNode.BodyLeaf[nextPath.Node(-1).FromItemSlot]
if searcher.Search(nextItem.Key, nextItem.BodySize) != 0 {
break
}
@@ -493,7 +491,7 @@ func (fs TreeOperatorImpl) TreeSearchAll(treeID btrfsprim.ObjID, searcher TreeSe
item.Body = item.Body.CloneItem()
ret = append(ret, item)
}
- FreeNodeRef(nextNode)
+ nextNode.Free()
if errs != nil {
err = errs
}
diff --git a/lib/btrfs/btrfstree/path.go b/lib/btrfs/btrfstree/path.go
index b9ab5bc..c07d8a0 100644
--- a/lib/btrfs/btrfstree/path.go
+++ b/lib/btrfs/btrfstree/path.go
@@ -13,7 +13,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
)
-// TreePath is a path from the superblock (i.e. the root of the btrfs
+// Path is a path from the superblock (i.e. the root of the btrfs
// system) to the a node or item within one of the btrees in the
// system.
//
@@ -61,10 +61,10 @@ import (
// | <--------------- pathElem={from_tree:A, from_slot:1,
// | to_addr:0, to_gen: 0, to_lvl:0}
// [item]
-type TreePath []TreePathElem
+type Path []PathElem
-// A TreePathElem essentially represents a KeyPointer.
-type TreePathElem struct {
+// A PathElem essentially represents a KeyPointer.
+type PathElem struct {
// FromTree is the owning tree ID of the parent node; or the
// well-known tree ID if this is the root.
FromTree btrfsprim.ObjID
@@ -94,17 +94,17 @@ type TreePathElem struct {
ToMaxKey btrfsprim.Key
}
-func (elem TreePathElem) writeNodeTo(w io.Writer) {
+func (elem PathElem) writeNodeTo(w io.Writer) {
fmt.Fprintf(w, "node:%d@%v", elem.ToNodeLevel, elem.ToNodeAddr)
}
-func (path TreePath) String() string {
+func (path Path) String() string {
if len(path) == 0 {
return "(empty-path)"
}
var ret strings.Builder
fmt.Fprintf(&ret, "%s->", path[0].FromTree.Format(btrfsprim.ROOT_TREE_OBJECTID))
- if len(path) == 1 && path[0] == (TreePathElem{FromTree: path[0].FromTree, FromItemSlot: -1}) {
+ if len(path) == 1 && path[0] == (PathElem{FromTree: path[0].FromTree, FromItemSlot: -1}) {
ret.WriteString("(empty-path)")
} else {
path[0].writeNodeTo(&ret)
@@ -119,11 +119,11 @@ func (path TreePath) String() string {
return ret.String()
}
-func (path TreePath) DeepCopy() TreePath {
- return append(TreePath(nil), path...)
+func (path Path) DeepCopy() Path {
+ return append(Path(nil), path...)
}
-func (path TreePath) Parent() TreePath {
+func (path Path) Parent() Path {
return path[:len(path)-1]
}
@@ -131,7 +131,7 @@ func (path TreePath) Parent() TreePath {
// `&path[x]`, but negative values of x move down from the end of path
// (similar to how lists work in many other languages, such as
// Python).
-func (path TreePath) Node(x int) *TreePathElem {
+func (path Path) Node(x int) *PathElem {
if x < 0 {
x += len(path)
}
diff --git a/lib/btrfs/btrfstree/readnode.go b/lib/btrfs/btrfstree/readnode.go
index 4ccc17b..c2e3b0f 100644
--- a/lib/btrfs/btrfstree/readnode.go
+++ b/lib/btrfs/btrfstree/readnode.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
@@ -33,8 +33,8 @@ type NodeFile interface {
// 'NodeSource' interface.
func FSReadNode(
fs NodeFile,
- path TreePath,
-) (*diskio.Ref[btrfsvol.LogicalAddr, Node], error) {
+ path Path,
+) (*Node, error) {
sb, err := fs.Superblock()
if err != nil {
return nil, fmt.Errorf("btrfs.FS.ReadNode: %w", err)
@@ -63,11 +63,11 @@ func FSReadNode(
}
return ReadNode[btrfsvol.LogicalAddr](fs, *sb, path.Node(-1).ToNodeAddr, NodeExpectations{
- LAddr: containers.Optional[btrfsvol.LogicalAddr]{OK: true, Val: path.Node(-1).ToNodeAddr},
- Level: containers.Optional[uint8]{OK: true, Val: path.Node(-1).ToNodeLevel},
- Generation: containers.Optional[btrfsprim.Generation]{OK: true, Val: path.Node(-1).ToNodeGeneration},
+ LAddr: containers.OptionalValue(path.Node(-1).ToNodeAddr),
+ Level: containers.OptionalValue(path.Node(-1).ToNodeLevel),
+ Generation: containers.OptionalValue(path.Node(-1).ToNodeGeneration),
Owner: checkOwner,
- MinItem: containers.Optional[btrfsprim.Key]{OK: true, Val: path.Node(-1).ToKey},
- MaxItem: containers.Optional[btrfsprim.Key]{OK: true, Val: path.Node(-1).ToMaxKey},
+ MinItem: containers.OptionalValue(path.Node(-1).ToKey),
+ MaxItem: containers.OptionalValue(path.Node(-1).ToMaxKey),
})
}
diff --git a/lib/btrfs/btrfstree/types_node.go b/lib/btrfs/btrfstree/types_node.go
index 8295ccb..622f23c 100644
--- a/lib/btrfs/btrfstree/types_node.go
+++ b/lib/btrfs/btrfstree/types_node.go
@@ -8,7 +8,6 @@ import (
"encoding/binary"
"errors"
"fmt"
- "unsafe"
"git.lukeshu.com/go/typedsync"
"github.com/datawire/dlib/derror"
@@ -308,12 +307,16 @@ type ItemHeader struct {
var itemPool containers.SlicePool[Item]
func (node *Node) Free() {
+ if node == nil {
+ return
+ }
for i := range node.BodyLeaf {
node.BodyLeaf[i].Body.Free()
node.BodyLeaf[i] = Item{}
}
itemPool.Put(node.BodyLeaf)
*node = Node{}
+ nodePool.Put(node)
}
func (node *Node) unmarshalLeaf(bodyBuf []byte) (int, error) {
@@ -440,32 +443,19 @@ func (e *IOError) Unwrap() error { return e.Err }
var bytePool containers.SlicePool[byte]
-var nodePool = typedsync.Pool[*diskio.Ref[int64, Node]]{
- New: func() *diskio.Ref[int64, Node] {
- return new(diskio.Ref[int64, Node])
+var nodePool = typedsync.Pool[*Node]{
+ New: func() *Node {
+ return new(Node)
},
}
-func FreeNodeRef[Addr ~int64](ref *diskio.Ref[Addr, Node]) {
- if ref == nil {
- return
- }
- ref.Data.Free()
- nodePool.Put((*diskio.Ref[int64, Node])(unsafe.Pointer(ref))) //nolint:gosec // I know it's unsafe.
-}
-
-func newNodeRef[Addr ~int64]() *diskio.Ref[Addr, Node] {
- ret, _ := nodePool.Get()
- return (*diskio.Ref[Addr, Node])(unsafe.Pointer(ret)) //nolint:gosec // I know it's unsafe.
-}
-
// ReadNode reads a node from the given file.
//
// It is possible that both a non-nil diskio.Ref and an error are
// returned. The error returned (if non-nil) is always of type
// *NodeError[Addr]. Notable errors that may be inside of the
// NodeError are ErrNotANode and *IOError.
-func ReadNode[Addr ~int64](fs diskio.File[Addr], sb Superblock, addr Addr, exp NodeExpectations) (*diskio.Ref[Addr, Node], error) {
+func ReadNode[Addr ~int64](fs diskio.ReaderAt[Addr], sb Superblock, addr Addr, exp NodeExpectations) (*Node, error) {
if int(sb.NodeSize) < nodeHeaderSize {
return nil, &NodeError[Addr]{
Op: "btrfstree.ReadNode", NodeAddr: addr,
@@ -481,16 +471,10 @@ func ReadNode[Addr ~int64](fs diskio.File[Addr], sb Superblock, addr Addr, exp N
// parse (early)
- nodeRef := newNodeRef[Addr]()
- *nodeRef = diskio.Ref[Addr, Node]{
- File: fs,
- Addr: addr,
- Data: Node{
- Size: sb.NodeSize,
- ChecksumType: sb.ChecksumType,
- },
- }
- if _, err := binstruct.Unmarshal(nodeBuf, &nodeRef.Data.Head); err != nil {
+ node, _ := nodePool.Get()
+ node.Size = sb.NodeSize
+ node.ChecksumType = sb.ChecksumType
+ if _, err := binstruct.Unmarshal(nodeBuf, &node.Head); err != nil {
// If there are enough bytes there (and we checked
// that above), then it shouldn't be possible for this
// unmarshal to fail.
@@ -499,20 +483,20 @@ func ReadNode[Addr ~int64](fs diskio.File[Addr], sb Superblock, addr Addr, exp N
// sanity checking (that prevents the main parse)
- if nodeRef.Data.Head.MetadataUUID != sb.EffectiveMetadataUUID() {
+ if node.Head.MetadataUUID != sb.EffectiveMetadataUUID() {
bytePool.Put(nodeBuf)
- return nodeRef, &NodeError[Addr]{Op: "btrfstree.ReadNode", NodeAddr: addr, Err: ErrNotANode}
+ return node, &NodeError[Addr]{Op: "btrfstree.ReadNode", NodeAddr: addr, Err: ErrNotANode}
}
- stored := nodeRef.Data.Head.Checksum
- calced, err := nodeRef.Data.ChecksumType.Sum(nodeBuf[csumSize:])
+ stored := node.Head.Checksum
+ calced, err := node.ChecksumType.Sum(nodeBuf[csumSize:])
if err != nil {
bytePool.Put(nodeBuf)
- return nodeRef, &NodeError[Addr]{Op: "btrfstree.ReadNode", NodeAddr: addr, Err: err}
+ return node, &NodeError[Addr]{Op: "btrfstree.ReadNode", NodeAddr: addr, Err: err}
}
if stored != calced {
bytePool.Put(nodeBuf)
- return nodeRef, &NodeError[Addr]{
+ return node, &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),
@@ -530,50 +514,57 @@ func ReadNode[Addr ~int64](fs diskio.File[Addr], sb Superblock, addr Addr, exp N
// garbage data that is was never a valid node, so parsing it
// isn't useful.
- if _, err := binstruct.Unmarshal(nodeBuf, &nodeRef.Data); err != nil {
+ if _, err := binstruct.Unmarshal(nodeBuf, node); err != nil {
bytePool.Put(nodeBuf)
- return nodeRef, &NodeError[Addr]{Op: "btrfstree.ReadNode", NodeAddr: addr, Err: err}
+ return node, &NodeError[Addr]{Op: "btrfstree.ReadNode", NodeAddr: addr, Err: err}
}
bytePool.Put(nodeBuf)
// sanity checking (that doesn't prevent parsing)
+ if err := exp.Check(node); err != nil {
+ return node, &NodeError[Addr]{Op: "btrfstree.ReadNode", NodeAddr: addr, Err: err}
+ }
+
+ // return
+
+ return node, nil
+}
+
+func (exp NodeExpectations) Check(node *Node) error {
var errs derror.MultiError
- if exp.LAddr.OK && nodeRef.Data.Head.Addr != exp.LAddr.Val {
+ if exp.LAddr.OK && node.Head.Addr != exp.LAddr.Val {
errs = append(errs, fmt.Errorf("read from laddr=%v but claims to be at laddr=%v",
- exp.LAddr.Val, nodeRef.Data.Head.Addr))
+ exp.LAddr.Val, node.Head.Addr))
}
- if exp.Level.OK && nodeRef.Data.Head.Level != exp.Level.Val {
+ if exp.Level.OK && node.Head.Level != exp.Level.Val {
errs = append(errs, fmt.Errorf("expected level=%v but claims to be level=%v",
- exp.Level.Val, nodeRef.Data.Head.Level))
+ exp.Level.Val, node.Head.Level))
}
- if exp.Generation.OK && nodeRef.Data.Head.Generation != exp.Generation.Val {
+ if exp.Generation.OK && node.Head.Generation != exp.Generation.Val {
errs = append(errs, fmt.Errorf("expected generation=%v but claims to be generation=%v",
- exp.Generation.Val, nodeRef.Data.Head.Generation))
+ exp.Generation.Val, node.Head.Generation))
}
if exp.Owner != nil {
- if err := exp.Owner(nodeRef.Data.Head.Owner); err != nil {
+ if err := exp.Owner(node.Head.Owner); err != nil {
errs = append(errs, err)
}
}
- if nodeRef.Data.Head.NumItems == 0 {
+ if node.Head.NumItems == 0 {
errs = append(errs, fmt.Errorf("has no items"))
} else {
- if minItem, _ := nodeRef.Data.MinItem(); exp.MinItem.OK && exp.MinItem.Val.Compare(minItem) > 0 {
+ if minItem, _ := node.MinItem(); exp.MinItem.OK && exp.MinItem.Val.Compare(minItem) > 0 {
errs = append(errs, fmt.Errorf("expected minItem>=%v but node has minItem=%v",
exp.MinItem, minItem))
}
- if maxItem, _ := nodeRef.Data.MaxItem(); exp.MaxItem.OK && exp.MaxItem.Val.Compare(maxItem) < 0 {
+ if maxItem, _ := node.MaxItem(); exp.MaxItem.OK && exp.MaxItem.Val.Compare(maxItem) < 0 {
errs = append(errs, fmt.Errorf("expected maxItem<=%v but node has maxItem=%v",
exp.MaxItem, maxItem))
}
}
if len(errs) > 0 {
- return nodeRef, &NodeError[Addr]{Op: "btrfstree.ReadNode", NodeAddr: addr, Err: errs}
+ return errs
}
-
- // return
-
- return nodeRef, nil
+ return nil
}
diff --git a/lib/btrfs/io2_lv.go b/lib/btrfs/io2_lv.go
index 856ac20..d05d51f 100644
--- a/lib/btrfs/io2_lv.go
+++ b/lib/btrfs/io2_lv.go
@@ -168,7 +168,7 @@ func (fs *FS) initDev(ctx context.Context, sb btrfstree.Superblock) error {
errs = append(errs, err)
},
btrfstree.TreeWalkHandler{
- Item: func(_ btrfstree.TreePath, item btrfstree.Item) error {
+ Item: func(_ btrfstree.Path, item btrfstree.Item) error {
if item.Key.ItemType != btrfsitem.CHUNK_ITEM_KEY {
return nil
}
diff --git a/lib/btrfs/io3_btree.go b/lib/btrfs/io3_btree.go
index 8d35269..80ab10f 100644
--- a/lib/btrfs/io3_btree.go
+++ b/lib/btrfs/io3_btree.go
@@ -10,8 +10,6 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfstree"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
- "git.lukeshu.com/btrfs-progs-ng/lib/diskio"
)
// This file is ordered from low-level to high-level.
@@ -19,7 +17,7 @@ import (
// btrfstree.NodeSource ////////////////////////////////////////////////////////
// ReadNode implements btrfstree.NodeSource.
-func (fs *FS) ReadNode(path btrfstree.TreePath) (*diskio.Ref[btrfsvol.LogicalAddr, btrfstree.Node], error) {
+func (fs *FS) ReadNode(path btrfstree.Path) (*btrfstree.Node, error) {
return btrfstree.FSReadNode(fs, path)
}
@@ -39,7 +37,7 @@ func (fs *FS) populateTreeUUIDs(ctx context.Context) {
// do nothing
},
btrfstree.TreeWalkHandler{
- Item: func(_ btrfstree.TreePath, item btrfstree.Item) error {
+ Item: func(_ btrfstree.Path, item btrfstree.Item) error {
itemBody, ok := item.Body.(*btrfsitem.Root)
if !ok {
return nil
diff --git a/lib/btrfs/io4_fs.go b/lib/btrfs/io4_fs.go
index b1a1232..0445441 100644
--- a/lib/btrfs/io4_fs.go
+++ b/lib/btrfs/io4_fs.go
@@ -10,7 +10,6 @@ import (
"path/filepath"
"reflect"
"sort"
- "sync"
"github.com/datawire/dlib/derror"
@@ -20,6 +19,7 @@ import (
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfstree"
"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/maps"
"git.lukeshu.com/btrfs-progs-ng/lib/slices"
"git.lukeshu.com/btrfs-progs-ng/lib/textui"
@@ -62,15 +62,13 @@ type File struct {
}
type Subvolume struct {
- FS interface {
+ fs interface {
btrfstree.TreeOperator
Superblock() (*btrfstree.Superblock, error)
- ReadAt(p []byte, off btrfsvol.LogicalAddr) (int, error)
+ diskio.ReaderAt[btrfsvol.LogicalAddr]
}
TreeID btrfsprim.ObjID
- NoChecksums bool
-
- initOnce sync.Once
+ noChecksums bool
rootVal btrfsitem.Root
rootErr error
@@ -81,41 +79,57 @@ type Subvolume struct {
fileCache containers.ARCache[btrfsprim.ObjID, *File]
}
-func (sv *Subvolume) init() {
- sv.initOnce.Do(func() {
- root, err := sv.FS.TreeSearch(btrfsprim.ROOT_TREE_OBJECTID, btrfstree.SearchRootItem(sv.TreeID))
- if err != nil {
- sv.rootErr = err
- } else {
- switch rootBody := root.Body.(type) {
- case *btrfsitem.Root:
- sv.rootVal = rootBody.Clone()
- 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))
- }
+func NewSubvolume(
+ fs interface {
+ btrfstree.TreeOperator
+ Superblock() (*btrfstree.Superblock, error)
+ diskio.ReaderAt[btrfsvol.LogicalAddr]
+ },
+ treeID btrfsprim.ObjID,
+ noChecksums bool,
+) *Subvolume {
+ sv := &Subvolume{
+ fs: fs,
+ TreeID: treeID,
+ noChecksums: noChecksums,
+ }
+
+ root, err := sv.fs.TreeSearch(btrfsprim.ROOT_TREE_OBJECTID, btrfstree.SearchRootItem(sv.TreeID))
+ if err != nil {
+ sv.rootErr = err
+ } else {
+ switch rootBody := root.Body.(type) {
+ case *btrfsitem.Root:
+ sv.rootVal = rootBody.Clone()
+ 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.bareInodeCache.MaxLen = textui.Tunable(128)
- sv.fullInodeCache.MaxLen = textui.Tunable(128)
- sv.dirCache.MaxLen = textui.Tunable(128)
- sv.fileCache.MaxLen = textui.Tunable(128)
- })
+ sv.bareInodeCache.MaxLen = textui.Tunable(128)
+ sv.fullInodeCache.MaxLen = textui.Tunable(128)
+ sv.dirCache.MaxLen = textui.Tunable(128)
+ sv.fileCache.MaxLen = textui.Tunable(128)
+
+ return sv
+}
+
+func (sv *Subvolume) NewChildSubvolume(childID btrfsprim.ObjID) *Subvolume {
+ return NewSubvolume(sv.fs, childID, sv.noChecksums)
}
func (sv *Subvolume) GetRootInode() (btrfsprim.ObjID, error) {
- sv.init()
return sv.rootVal.RootDirID, sv.rootErr
}
func (sv *Subvolume) LoadBareInode(inode btrfsprim.ObjID) (*BareInode, error) {
- sv.init()
val := containers.LoadOrElse[btrfsprim.ObjID, *BareInode](&sv.bareInodeCache, inode, func(inode btrfsprim.ObjID) (val *BareInode) {
val = &BareInode{
Inode: inode,
}
- item, err := sv.FS.TreeLookup(sv.TreeID, btrfsprim.Key{
+ item, err := sv.fs.TreeLookup(sv.TreeID, btrfsprim.Key{
ObjectID: inode,
ItemType: btrfsitem.INODE_ITEM_KEY,
Offset: 0,
@@ -144,7 +158,6 @@ func (sv *Subvolume) LoadBareInode(inode btrfsprim.ObjID) (*BareInode, error) {
}
func (sv *Subvolume) LoadFullInode(inode btrfsprim.ObjID) (*FullInode, error) {
- sv.init()
val := containers.LoadOrElse[btrfsprim.ObjID, *FullInode](&sv.fullInodeCache, inode, func(indoe btrfsprim.ObjID) (val *FullInode) {
val = &FullInode{
BareInode: BareInode{
@@ -152,7 +165,7 @@ func (sv *Subvolume) LoadFullInode(inode btrfsprim.ObjID) (*FullInode, error) {
},
XAttrs: make(map[string]string),
}
- items, err := sv.FS.TreeSearchAll(sv.TreeID, btrfstree.SearchObject(inode))
+ items, err := sv.fs.TreeSearchAll(sv.TreeID, btrfstree.SearchObject(inode))
if err != nil {
val.Errs = append(val.Errs, err)
if len(items) == 0 {
@@ -199,7 +212,6 @@ func (sv *Subvolume) LoadFullInode(inode btrfsprim.ObjID) (*FullInode, error) {
}
func (sv *Subvolume) LoadDir(inode btrfsprim.ObjID) (*Dir, error) {
- sv.init()
val := containers.LoadOrElse[btrfsprim.ObjID, *Dir](&sv.dirCache, inode, func(inode btrfsprim.ObjID) (val *Dir) {
val = new(Dir)
fullInode, err := sv.LoadFullInode(inode)
@@ -336,7 +348,6 @@ func (dir *Dir) AbsPath() (string, error) {
}
func (sv *Subvolume) LoadFile(inode btrfsprim.ObjID) (*File, error) {
- sv.init()
val := containers.LoadOrElse[btrfsprim.ObjID, *File](&sv.fileCache, inode, func(inode btrfsprim.ObjID) (val *File) {
val = new(File)
fullInode, err := sv.LoadFullInode(inode)
@@ -448,7 +459,7 @@ func (file *File) maybeShortReadAt(dat []byte, off int64) (int, error) {
case btrfsitem.FILE_EXTENT_INLINE:
return copy(dat, extent.BodyInline[offsetWithinExt:offsetWithinExt+readSize]), nil
case btrfsitem.FILE_EXTENT_REG, btrfsitem.FILE_EXTENT_PREALLOC:
- sb, err := file.SV.FS.Superblock()
+ sb, err := file.SV.fs.Superblock()
if err != nil {
return 0, err
}
@@ -457,7 +468,7 @@ func (file *File) maybeShortReadAt(dat []byte, off int64) (int, error) {
Add(btrfsvol.AddrDelta(offsetWithinExt))
var block [btrfssum.BlockSize]byte
blockBeg := (beg / btrfssum.BlockSize) * btrfssum.BlockSize
- n, err := file.SV.FS.ReadAt(block[:], blockBeg)
+ n, err := file.SV.fs.ReadAt(block[:], blockBeg)
if n > int(beg-blockBeg) {
n = copy(dat[:readSize], block[beg-blockBeg:])
} else {
@@ -466,8 +477,8 @@ func (file *File) maybeShortReadAt(dat []byte, off int64) (int, error) {
if err != nil {
return 0, err
}
- if !file.SV.NoChecksums {
- sumRun, err := LookupCSum(file.SV.FS, sb.ChecksumType, blockBeg)
+ if !file.SV.noChecksums {
+ sumRun, err := LookupCSum(file.SV.fs, sb.ChecksumType, blockBeg)
if err != nil {
return 0, fmt.Errorf("checksum@%v: %w", blockBeg, err)
}