summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-03-23 21:33:19 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2023-03-23 21:33:19 -0600
commitbf5eed5af5c34b8cf9dc2985a7c4475602929bb1 (patch)
tree9cb910940a1e16982f5c5012a4fbeb37ba172f82
parentf0a9faf21dbe508d57da3b18be9121559c70876a (diff)
parent418553acc64567ebc95122e28b07657526c92923 (diff)
Merge branch 'lukeshu/tree-api-pt1-changes'
-rw-r--r--cmd/btrfs-rec/inspect/lsfiles/lsfiles.go3
-rw-r--r--cmd/btrfs-rec/inspect/mount/mount.go1
-rw-r--r--cmd/btrfs-rec/inspect_lsfiles.go1
-rw-r--r--lib/btrfs/btrfsitem/item_root.go5
-rw-r--r--lib/btrfs/btrfstree/btree_forrest.go5
-rw-r--r--lib/btrfs/btrfstree/readnode.go34
-rw-r--r--lib/btrfs/btrfstree/types_node.go4
-rw-r--r--lib/btrfs/io2_lv.go3
-rw-r--r--lib/btrfs/io3_btree.go37
-rw-r--r--lib/btrfs/io4_fs.go31
-rw-r--r--lib/btrfsutil/old_rebuilt_forrest.go9
-rw-r--r--lib/btrfsutil/rebuilt_readitem.go9
-rw-r--r--lib/btrfsutil/rebuilt_tree.go2
13 files changed, 88 insertions, 56 deletions
diff --git a/cmd/btrfs-rec/inspect/lsfiles/lsfiles.go b/cmd/btrfs-rec/inspect/lsfiles/lsfiles.go
index a713b8a..e42050f 100644
--- a/cmd/btrfs-rec/inspect/lsfiles/lsfiles.go
+++ b/cmd/btrfs-rec/inspect/lsfiles/lsfiles.go
@@ -8,6 +8,7 @@
package lsfiles
import (
+ "context"
"errors"
"fmt"
"io"
@@ -27,6 +28,7 @@ import (
)
func LsFiles(
+ ctx context.Context,
out io.Writer,
fs interface {
btrfstree.TreeOperator
@@ -42,6 +44,7 @@ func LsFiles(
}()
printSubvol(out, "", true, "/", btrfs.NewSubvolume(
+ ctx,
fs,
btrfsprim.FS_TREE_OBJECTID,
false,
diff --git a/cmd/btrfs-rec/inspect/mount/mount.go b/cmd/btrfs-rec/inspect/mount/mount.go
index 4049393..d4d2e0a 100644
--- a/cmd/btrfs-rec/inspect/mount/mount.go
+++ b/cmd/btrfs-rec/inspect/mount/mount.go
@@ -53,6 +53,7 @@ func MountRO(ctx context.Context, fs *btrfs.FS, mountpoint string, noChecksums b
rootSubvol := &subvolume{
Subvolume: btrfs.NewSubvolume(
+ ctx,
btrfsutil.NewOldRebuiltForrest(ctx, fs),
btrfsprim.FS_TREE_OBJECTID,
noChecksums,
diff --git a/cmd/btrfs-rec/inspect_lsfiles.go b/cmd/btrfs-rec/inspect_lsfiles.go
index 04b5ec5..00a4873 100644
--- a/cmd/btrfs-rec/inspect_lsfiles.go
+++ b/cmd/btrfs-rec/inspect_lsfiles.go
@@ -30,6 +30,7 @@ func init() {
}()
return lsfiles.LsFiles(
+ cmd.Context(),
out,
btrfsutil.NewOldRebuiltForrest(cmd.Context(), fs))
}),
diff --git a/lib/btrfs/btrfsitem/item_root.go b/lib/btrfs/btrfsitem/item_root.go
index 4ffad9a..8bdc3df 100644
--- a/lib/btrfs/btrfsitem/item_root.go
+++ b/lib/btrfs/btrfsitem/item_root.go
@@ -22,6 +22,11 @@ import (
// key.offset = one of:
// - 0 if objectid is one of the BTRFS_*_TREE_OBJECTID defines or a non-snapshot volume; or
// - transaction_id of when this snapshot was created
+//
+// This tree might contain nodes with node.Head.Owner set to the
+// root.ParentUUID tree, *if* the node.Head.Generation is
+// less-than-or-equal-to the root's key.offset. The "or-equal-to"
+// part of that might be surprising, which is why I called it out.
type Root struct { // trivial ROOT_ITEM=132
Inode Inode `bin:"off=0x000, siz=0xa0"` // ???
Generation btrfsprim.Generation `bin:"off=0x0a0, siz=0x08"`
diff --git a/lib/btrfs/btrfstree/btree_forrest.go b/lib/btrfs/btrfstree/btree_forrest.go
index 8f8e2de..38a2721 100644
--- a/lib/btrfs/btrfstree/btree_forrest.go
+++ b/lib/btrfs/btrfstree/btree_forrest.go
@@ -21,6 +21,8 @@ type TreeRoot struct {
RootNode btrfsvol.LogicalAddr
Level uint8
Generation btrfsprim.Generation
+
+ RootInode btrfsprim.ObjID // only for subvolume trees
}
// LookupTreeRoot is a utility function to help with implementing the
@@ -59,7 +61,7 @@ func LookupTreeRoot(_ context.Context, fs TreeOperator, sb Superblock, treeID bt
rootItem, err := fs.TreeSearch(btrfsprim.ROOT_TREE_OBJECTID, SearchRootItem(treeID))
if err != nil {
if errors.Is(err, ErrNoItem) {
- err = ErrNoTree
+ err = fmt.Errorf("%w: %s", ErrNoTree, err)
}
return nil, fmt.Errorf("tree %s: %w", treeID.Format(btrfsprim.ROOT_TREE_OBJECTID), err)
}
@@ -70,6 +72,7 @@ func LookupTreeRoot(_ context.Context, fs TreeOperator, sb Superblock, treeID bt
RootNode: rootItemBody.ByteNr,
Level: rootItemBody.Level,
Generation: rootItemBody.Generation,
+ RootInode: rootItemBody.RootDirID,
}, nil
case *btrfsitem.Error:
return nil, fmt.Errorf("malformed ROOT_ITEM for tree %v: %w", treeID, rootItemBody.Err)
diff --git a/lib/btrfs/btrfstree/readnode.go b/lib/btrfs/btrfstree/readnode.go
index c2e3b0f..ac82c62 100644
--- a/lib/btrfs/btrfstree/readnode.go
+++ b/lib/btrfs/btrfstree/readnode.go
@@ -20,13 +20,13 @@ type NodeFile interface {
// ParentTree, given a tree ID, returns that tree's parent
// tree, if it has one.
//
- // - non-zero, true : the parent tree ID
+ // - non-zero, ?, true : the parent tree ID
//
- // - 0, true : the tree does not have a parent
+ // - 0, 0, true : the tree does not have a parent
//
- // - any, false : the tree's parent information could not be
+ // - ?, ?, false : the tree's parent information could not be
// looked up
- ParentTree(btrfsprim.ObjID) (btrfsprim.ObjID, bool)
+ ParentTree(btrfsprim.ObjID) (btrfsprim.ObjID, btrfsprim.Generation, bool)
}
// FSReadNode is a utility function to help with implementing the
@@ -40,25 +40,33 @@ func FSReadNode(
return nil, fmt.Errorf("btrfs.FS.ReadNode: %w", err)
}
- var treeParents []btrfsprim.ObjID
- checkOwner := func(owner btrfsprim.ObjID) error {
- exp := path.Node(-1).FromTree
+ checkOwner := func(owner btrfsprim.ObjID, gen btrfsprim.Generation) error {
+ var treeParents []btrfsprim.ObjID
+
+ tree := path.Node(-1).FromTree
for {
- if owner == exp {
+ if owner == tree {
+ // OK!
return nil
}
- treeParents = append(treeParents, exp)
- var ok bool
- exp, ok = fs.ParentTree(exp)
- if !ok {
+
+ treeParents = append(treeParents, tree)
+ parent, parentGen, parentOK := fs.ParentTree(tree)
+ if !parentOK {
// Failed look up parent info; fail open.
return nil
}
- if exp == 0 {
+
+ if parent == 0 {
// End of the line.
return fmt.Errorf("expected owner in %v but claims to have owner=%v",
treeParents, owner)
}
+ if gen > parentGen {
+ return fmt.Errorf("claimed owner=%v might be acceptable in this tree (if generation<=%v) but not with claimed generation=%v",
+ owner, parentGen, gen)
+ }
+ tree = parent
}
}
diff --git a/lib/btrfs/btrfstree/types_node.go b/lib/btrfs/btrfstree/types_node.go
index 622f23c..bfcbbf4 100644
--- a/lib/btrfs/btrfstree/types_node.go
+++ b/lib/btrfs/btrfstree/types_node.go
@@ -417,7 +417,7 @@ type NodeExpectations struct {
// Things knowable from the parent.
Level containers.Optional[uint8]
Generation containers.Optional[btrfsprim.Generation]
- Owner func(btrfsprim.ObjID) error
+ Owner func(btrfsprim.ObjID, btrfsprim.Generation) error
MinItem containers.Optional[btrfsprim.Key]
// Things knowable from the structure of the tree.
MaxItem containers.Optional[btrfsprim.Key]
@@ -547,7 +547,7 @@ func (exp NodeExpectations) Check(node *Node) error {
exp.Generation.Val, node.Head.Generation))
}
if exp.Owner != nil {
- if err := exp.Owner(node.Head.Owner); err != nil {
+ if err := exp.Owner(node.Head.Owner, node.Head.Generation); err != nil {
errs = append(errs, err)
}
}
diff --git a/lib/btrfs/io2_lv.go b/lib/btrfs/io2_lv.go
index 9f4e53f..03b2107 100644
--- a/lib/btrfs/io2_lv.go
+++ b/lib/btrfs/io2_lv.go
@@ -27,9 +27,8 @@ type FS struct {
cacheSuperblocks []*diskio.Ref[btrfsvol.PhysicalAddr, btrfstree.Superblock]
cacheSuperblock *btrfstree.Superblock
- cacheObjID2UUID map[btrfsprim.ObjID]btrfsprim.UUID
+ cacheObjID2All map[btrfsprim.ObjID]treeInfo
cacheUUID2ObjID map[btrfsprim.UUID]btrfsprim.ObjID
- cacheTreeParent map[btrfsprim.ObjID]btrfsprim.UUID
}
var _ diskio.File[btrfsvol.LogicalAddr] = (*FS)(nil)
diff --git a/lib/btrfs/io3_btree.go b/lib/btrfs/io3_btree.go
index 6df88f5..b60f54a 100644
--- a/lib/btrfs/io3_btree.go
+++ b/lib/btrfs/io3_btree.go
@@ -25,13 +25,18 @@ var _ btrfstree.NodeSource = (*FS)(nil)
// btrfstree.NodeFile //////////////////////////////////////////////////////////
+type treeInfo struct {
+ UUID btrfsprim.UUID
+ ParentUUID btrfsprim.UUID
+ ParentGen btrfsprim.Generation
+}
+
func (fs *FS) populateTreeUUIDs(ctx context.Context) {
- if fs.cacheObjID2UUID != nil && fs.cacheUUID2ObjID != nil && fs.cacheTreeParent != nil {
+ if fs.cacheObjID2All != nil && fs.cacheUUID2ObjID != nil {
return
}
- fs.cacheObjID2UUID = make(map[btrfsprim.ObjID]btrfsprim.UUID)
+ fs.cacheObjID2All = make(map[btrfsprim.ObjID]treeInfo)
fs.cacheUUID2ObjID = make(map[btrfsprim.UUID]btrfsprim.ObjID)
- fs.cacheTreeParent = make(map[btrfsprim.ObjID]btrfsprim.UUID)
fs.TreeWalk(ctx, btrfsprim.ROOT_TREE_OBJECTID,
func(err *btrfstree.TreeError) {
// do nothing
@@ -42,8 +47,11 @@ func (fs *FS) populateTreeUUIDs(ctx context.Context) {
if !ok {
return
}
- fs.cacheObjID2UUID[item.Key.ObjectID] = itemBody.UUID
- fs.cacheTreeParent[item.Key.ObjectID] = itemBody.ParentUUID
+ fs.cacheObjID2All[item.Key.ObjectID] = treeInfo{
+ UUID: itemBody.UUID,
+ ParentUUID: itemBody.ParentUUID,
+ ParentGen: btrfsprim.Generation(item.Key.Offset),
+ }
fs.cacheUUID2ObjID[itemBody.UUID] = item.Key.ObjectID
},
},
@@ -51,27 +59,28 @@ func (fs *FS) populateTreeUUIDs(ctx context.Context) {
}
// ParentTree implements btrfstree.NodeFile.
-func (fs *FS) ParentTree(tree btrfsprim.ObjID) (btrfsprim.ObjID, bool) {
+func (fs *FS) ParentTree(tree btrfsprim.ObjID) (btrfsprim.ObjID, btrfsprim.Generation, bool) {
if tree < btrfsprim.FIRST_FREE_OBJECTID || tree > btrfsprim.LAST_FREE_OBJECTID {
// no parent
- return 0, true
+ return 0, 0, true
}
fs.populateTreeUUIDs(context.TODO())
- parentUUID, ok := fs.cacheTreeParent[tree]
+
+ all, ok := fs.cacheObjID2All[tree]
if !ok {
// could not look up parent info
- return 0, false
+ return 0, 0, false
}
- if parentUUID == (btrfsprim.UUID{}) {
+ if all.ParentUUID == (btrfsprim.UUID{}) {
// no parent
- return 0, true
+ return 0, 0, true
}
- parentObjID, ok := fs.cacheUUID2ObjID[parentUUID]
+ parentObjID, ok := fs.cacheUUID2ObjID[all.ParentUUID]
if !ok {
// could not look up parent info
- return 0, false
+ return 0, 0, false
}
- return parentObjID, true
+ return parentObjID, all.ParentGen, true
}
var _ btrfstree.NodeFile = (*FS)(nil)
diff --git a/lib/btrfs/io4_fs.go b/lib/btrfs/io4_fs.go
index 0445441..4a68695 100644
--- a/lib/btrfs/io4_fs.go
+++ b/lib/btrfs/io4_fs.go
@@ -5,6 +5,7 @@
package btrfs
import (
+ "context"
"fmt"
"io"
"path/filepath"
@@ -62,7 +63,8 @@ type File struct {
}
type Subvolume struct {
- fs interface {
+ ctx context.Context //nolint:containedctx // don't have an option while keeping the same API
+ fs interface {
btrfstree.TreeOperator
Superblock() (*btrfstree.Superblock, error)
diskio.ReaderAt[btrfsvol.LogicalAddr]
@@ -70,8 +72,8 @@ type Subvolume struct {
TreeID btrfsprim.ObjID
noChecksums bool
- rootVal btrfsitem.Root
- rootErr error
+ rootInfo btrfstree.TreeRoot
+ rootErr error
bareInodeCache containers.ARCache[btrfsprim.ObjID, *BareInode]
fullInodeCache containers.ARCache[btrfsprim.ObjID, *FullInode]
@@ -80,6 +82,7 @@ type Subvolume struct {
}
func NewSubvolume(
+ ctx context.Context,
fs interface {
btrfstree.TreeOperator
Superblock() (*btrfstree.Superblock, error)
@@ -94,19 +97,17 @@ func NewSubvolume(
noChecksums: noChecksums,
}
- root, err := sv.fs.TreeSearch(btrfsprim.ROOT_TREE_OBJECTID, btrfstree.SearchRootItem(sv.TreeID))
+ sb, err := sv.fs.Superblock()
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))
- }
+ return sv
+ }
+ rootInfo, err := btrfstree.LookupTreeRoot(ctx, sv.fs, *sb, sv.TreeID)
+ if err != nil {
+ sv.rootErr = err
+ return sv
}
+ sv.rootInfo = *rootInfo
sv.bareInodeCache.MaxLen = textui.Tunable(128)
sv.fullInodeCache.MaxLen = textui.Tunable(128)
@@ -117,11 +118,11 @@ func NewSubvolume(
}
func (sv *Subvolume) NewChildSubvolume(childID btrfsprim.ObjID) *Subvolume {
- return NewSubvolume(sv.fs, childID, sv.noChecksums)
+ return NewSubvolume(sv.ctx, sv.fs, childID, sv.noChecksums)
}
func (sv *Subvolume) GetRootInode() (btrfsprim.ObjID, error) {
- return sv.rootVal.RootDirID, sv.rootErr
+ return sv.rootInfo.RootInode, sv.rootErr
}
func (sv *Subvolume) LoadBareInode(inode btrfsprim.ObjID) (*BareInode, error) {
diff --git a/lib/btrfsutil/old_rebuilt_forrest.go b/lib/btrfsutil/old_rebuilt_forrest.go
index bb5ab59..5b99892 100644
--- a/lib/btrfsutil/old_rebuilt_forrest.go
+++ b/lib/btrfsutil/old_rebuilt_forrest.go
@@ -240,10 +240,11 @@ func (bt *OldRebuiltForrest) readNode(nodeInfo nodeInfo) *btrfstree.Node {
LAddr: containers.OptionalValue(nodeInfo.LAddr),
Level: containers.OptionalValue(nodeInfo.Level),
Generation: containers.OptionalValue(nodeInfo.Generation),
- Owner: func(treeID btrfsprim.ObjID) error {
- if treeID != nodeInfo.Owner {
- return fmt.Errorf("expected owner=%v but claims to have owner=%v",
- nodeInfo.Owner, treeID)
+ Owner: func(treeID btrfsprim.ObjID, gen btrfsprim.Generation) error {
+ if treeID != nodeInfo.Owner || gen != nodeInfo.Generation {
+ return fmt.Errorf("expected owner=%v generation=%v but claims to have owner=%v generation=%v",
+ nodeInfo.Owner, nodeInfo.Generation,
+ treeID, gen)
}
return nil
},
diff --git a/lib/btrfsutil/rebuilt_readitem.go b/lib/btrfsutil/rebuilt_readitem.go
index ff919f0..03a7cdc 100644
--- a/lib/btrfsutil/rebuilt_readitem.go
+++ b/lib/btrfsutil/rebuilt_readitem.go
@@ -42,10 +42,11 @@ func (ts *RebuiltForrest) readNode(ctx context.Context, laddr btrfsvol.LogicalAd
LAddr: containers.OptionalValue(laddr),
Level: containers.OptionalValue(graphInfo.Level),
Generation: containers.OptionalValue(graphInfo.Generation),
- Owner: func(treeID btrfsprim.ObjID) error {
- if treeID != graphInfo.Owner {
- return fmt.Errorf("expected owner=%v but claims to have owner=%v",
- graphInfo.Owner, treeID)
+ Owner: func(treeID btrfsprim.ObjID, gen btrfsprim.Generation) error {
+ if treeID != graphInfo.Owner || gen != graphInfo.Generation {
+ return fmt.Errorf("expected owner=%v generation=%v but claims to have owner=%v generation=%v",
+ graphInfo.Owner, graphInfo.Generation,
+ treeID, gen)
}
return nil
},
diff --git a/lib/btrfsutil/rebuilt_tree.go b/lib/btrfsutil/rebuilt_tree.go
index 3fff9b2..96d5a75 100644
--- a/lib/btrfsutil/rebuilt_tree.go
+++ b/lib/btrfsutil/rebuilt_tree.go
@@ -122,7 +122,7 @@ func (tree *RebuiltTree) isOwnerOK(owner btrfsprim.ObjID, gen btrfsprim.Generati
if owner == tree.ID {
return true
}
- if tree.Parent == nil || gen >= tree.ParentGen {
+ if tree.Parent == nil || gen > tree.ParentGen {
return false
}
tree = tree.Parent