summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-04-05 07:49:02 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2023-04-05 07:49:02 -0600
commit68eb7a16b9759646619a7d9dec2b62fa9d0c30cf (patch)
tree8bb4b70337a299f1dacb3c2858210d4bf6bd4b04 /cmd
parentb0f290078d531d2dcb5d34e809b0711ce9b6491e (diff)
parentd7dd6dfd7aeb40f06ff4fbe7f906d8feed64b95f (diff)
Merge branch 'lukeshu/misc'
Diffstat (limited to 'cmd')
-rw-r--r--cmd/btrfs-rec/inspect/rebuildmappings/process.go2
-rw-r--r--cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go28
-rw-r--r--cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go61
-rw-r--r--cmd/btrfs-rec/inspect/rebuildtrees/scan.go30
-rw-r--r--cmd/btrfs-rec/inspect_lstrees.go23
-rw-r--r--cmd/btrfs-rec/inspect_rebuildtrees.go24
-rw-r--r--cmd/btrfs-rec/main.go63
7 files changed, 131 insertions, 100 deletions
diff --git a/cmd/btrfs-rec/inspect/rebuildmappings/process.go b/cmd/btrfs-rec/inspect/rebuildmappings/process.go
index 7a49cc6..2e694b5 100644
--- a/cmd/btrfs-rec/inspect/rebuildmappings/process.go
+++ b/cmd/btrfs-rec/inspect/rebuildmappings/process.go
@@ -38,7 +38,7 @@ func RebuildMappings(ctx context.Context, fs *btrfs.FS, scanResults ScanDevicesR
devIDs := maps.SortedKeys(scanResults)
devices := fs.LV.PhysicalVolumes()
for _, devID := range devIDs {
- if _, ok := devices[devID]; !ok {
+ if !maps.HasKey(devices, devID) {
return fmt.Errorf("device ID %v mentioned in scan results is not part of the filesystem", devID)
}
devResults := scanResults[devID]
diff --git a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go
index 0d25ac3..427070a 100644
--- a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go
+++ b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go
@@ -198,11 +198,13 @@ func (o *rebuilder) processAddedItemQueue(ctx context.Context) error {
ctx := dlog.WithField(ctx, "btrfs.inspect.rebuild-trees.rebuild.settle.item", key)
tree := o.rebuilt.RebuiltTree(ctx, key.TreeID)
- incPtr, ok := tree.RebuiltItems(ctx).Load(key.Key)
+ incPtr, ok := tree.RebuiltAcquireItems(ctx).Load(key.Key)
+ tree.RebuiltReleaseItems()
if !ok {
panic(fmt.Errorf("should not happen: failed to load already-added item: %v", key))
}
- excPtr, ok := tree.RebuiltPotentialItems(ctx).Load(key.Key)
+ excPtr, ok := tree.RebuiltAcquirePotentialItems(ctx).Load(key.Key)
+ tree.RebuiltReleasePotentialItems()
if ok && tree.RebuiltShouldReplace(incPtr.Node, excPtr.Node) {
wantKey := wantWithTree{
TreeID: key.TreeID,
@@ -517,24 +519,12 @@ func (o *rebuilder) resolveTreeAugments(ctx context.Context, treeID btrfsprim.Ob
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func (queue *treeAugmentQueue) has(wantKey want) bool {
- if queue != nil {
- if queue.zero != nil {
- if _, ok := queue.zero[wantKey]; ok {
- return true
- }
- }
- if queue.single != nil {
- if _, ok := queue.single[wantKey]; ok {
- return true
- }
- }
- if queue.multi != nil {
- if _, ok := queue.multi[wantKey]; ok {
- return true
- }
- }
+ if queue == nil {
+ return false
}
- return false
+ return (queue.zero != nil && maps.HasKey(queue.zero, wantKey)) ||
+ (queue.single != nil && maps.HasKey(queue.single, wantKey)) ||
+ (queue.multi != nil && maps.HasKey(queue.multi, wantKey))
}
func (queue *treeAugmentQueue) store(wantKey want, choices containers.Set[btrfsvol.LogicalAddr]) {
diff --git a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go
index eff2a83..0526e93 100644
--- a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go
+++ b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go
@@ -43,18 +43,22 @@ func (o graphCallbacks) Want(ctx context.Context, reason string, treeID btrfspri
}
func (o *rebuilder) _want(ctx context.Context, wantKey wantWithTree) (key btrfsprim.Key, ok bool) {
- if o.rebuilt.RebuiltTree(ctx, wantKey.TreeID) == nil {
+ tree := o.rebuilt.RebuiltTree(ctx, wantKey.TreeID)
+ if tree == nil {
o.enqueueRetry(wantKey.TreeID)
return btrfsprim.Key{}, false
}
+ tgt := wantKey.Key.Key()
+
// check if we already have it
- tgt := wantKey.Key.Key()
- if key, _, ok := o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltItems(ctx).Search(func(key btrfsprim.Key, _ btrfsutil.ItemPtr) int {
+ key, _, ok = tree.RebuiltAcquireItems(ctx).Search(func(key btrfsprim.Key, _ btrfsutil.ItemPtr) int {
key.Offset = 0
return tgt.Compare(key)
- }); ok {
+ })
+ tree.RebuiltReleaseItems()
+ if ok {
return key, true
}
@@ -64,15 +68,16 @@ func (o *rebuilder) _want(ctx context.Context, wantKey wantWithTree) (key btrfsp
return btrfsprim.Key{}, false
}
wants := make(containers.Set[btrfsvol.LogicalAddr])
- o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltPotentialItems(ctx).Subrange(
+ tree.RebuiltAcquirePotentialItems(ctx).Subrange(
func(k btrfsprim.Key, _ btrfsutil.ItemPtr) int {
k.Offset = 0
return tgt.Compare(k)
},
func(_ btrfsprim.Key, v btrfsutil.ItemPtr) bool {
- wants.InsertFrom(o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltLeafToRoots(ctx, v.Node))
+ wants.InsertFrom(tree.RebuiltLeafToRoots(ctx, v.Node))
return true
})
+ tree.RebuiltReleasePotentialItems()
o.wantAugment(ctx, wantKey, wants)
return btrfsprim.Key{}, false
}
@@ -93,15 +98,19 @@ func (o graphCallbacks) WantOff(ctx context.Context, reason string, treeID btrfs
}
func (o *rebuilder) _wantOff(ctx context.Context, wantKey wantWithTree) (ok bool) {
- if o.rebuilt.RebuiltTree(ctx, wantKey.TreeID) == nil {
+ tree := o.rebuilt.RebuiltTree(ctx, wantKey.TreeID)
+ if tree == nil {
o.enqueueRetry(wantKey.TreeID)
return false
}
+ tgt := wantKey.Key.Key()
+
// check if we already have it
- tgt := wantKey.Key.Key()
- if _, ok := o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltItems(ctx).Load(tgt); ok {
+ _, ok = tree.RebuiltAcquireItems(ctx).Load(tgt)
+ tree.RebuiltReleaseItems()
+ if ok {
return true
}
@@ -111,12 +120,13 @@ func (o *rebuilder) _wantOff(ctx context.Context, wantKey wantWithTree) (ok bool
return false
}
wants := make(containers.Set[btrfsvol.LogicalAddr])
- o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltPotentialItems(ctx).Subrange(
+ tree.RebuiltAcquirePotentialItems(ctx).Subrange(
func(k btrfsprim.Key, _ btrfsutil.ItemPtr) int { return tgt.Compare(k) },
func(_ btrfsprim.Key, v btrfsutil.ItemPtr) bool {
- wants.InsertFrom(o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltLeafToRoots(ctx, v.Node))
+ wants.InsertFrom(tree.RebuiltLeafToRoots(ctx, v.Node))
return true
})
+ tree.RebuiltReleasePotentialItems()
o.wantAugment(ctx, wantKey, wants)
return false
}
@@ -134,16 +144,18 @@ func (o graphCallbacks) WantDirIndex(ctx context.Context, reason string, treeID
}
ctx = withWant(ctx, logFieldItemWant, reason, wantKey)
- if o.rebuilt.RebuiltTree(ctx, treeID) == nil {
+ tree := o.rebuilt.RebuiltTree(ctx, treeID)
+ if tree == nil {
o.enqueueRetry(treeID)
return
}
+ tgt := wantKey.Key.Key()
+
// check if we already have it
- tgt := wantKey.Key.Key()
found := false
- o.rebuilt.RebuiltTree(ctx, treeID).RebuiltItems(ctx).Subrange(
+ tree.RebuiltAcquireItems(ctx).Subrange(
func(key btrfsprim.Key, _ btrfsutil.ItemPtr) int {
key.Offset = 0
return tgt.Compare(key)
@@ -154,6 +166,7 @@ func (o graphCallbacks) WantDirIndex(ctx context.Context, reason string, treeID
}
return !found
})
+ tree.RebuiltReleaseItems()
if found {
return
}
@@ -164,17 +177,18 @@ func (o graphCallbacks) WantDirIndex(ctx context.Context, reason string, treeID
return
}
wants := make(containers.Set[btrfsvol.LogicalAddr])
- o.rebuilt.RebuiltTree(ctx, treeID).RebuiltPotentialItems(ctx).Subrange(
+ tree.RebuiltAcquirePotentialItems(ctx).Subrange(
func(key btrfsprim.Key, _ btrfsutil.ItemPtr) int {
key.Offset = 0
return tgt.Compare(key)
},
func(_ btrfsprim.Key, ptr btrfsutil.ItemPtr) bool {
if itemName, ok := o.scan.Names[ptr]; ok && bytes.Equal(itemName, name) {
- wants.InsertFrom(o.rebuilt.RebuiltTree(ctx, treeID).RebuiltLeafToRoots(ctx, ptr.Node))
+ wants.InsertFrom(tree.RebuiltLeafToRoots(ctx, ptr.Node))
}
return true
})
+ tree.RebuiltReleasePotentialItems()
o.wantAugment(ctx, wantKey, wants)
}
@@ -259,7 +273,8 @@ func (o graphCallbacks) _wantRange(
ctx = withWant(ctx, logFieldItemWant, reason, wantKey)
wantKey.Key.OffsetType = offsetRange
- if o.rebuilt.RebuiltTree(ctx, treeID) == nil {
+ tree := o.rebuilt.RebuiltTree(ctx, treeID)
+ if tree == nil {
o.enqueueRetry(treeID)
return
}
@@ -275,7 +290,7 @@ func (o graphCallbacks) _wantRange(
})
o._walkRange(
ctx,
- o.rebuilt.RebuiltTree(ctx, treeID).RebuiltItems(ctx),
+ tree.RebuiltAcquireItems(ctx),
treeID, objID, typ, beg, end,
func(runKey btrfsprim.Key, _ btrfsutil.ItemPtr, runBeg, runEnd uint64) {
var overlappingGaps []*containers.RBNode[gap]
@@ -315,12 +330,13 @@ func (o graphCallbacks) _wantRange(
})
}
})
+ tree.RebuiltReleaseItems()
// Step 2: Fill each gap.
if gaps.Len() == 0 {
return
}
- potentialItems := o.rebuilt.RebuiltTree(ctx, treeID).RebuiltPotentialItems(ctx)
+ potentialItems := tree.RebuiltAcquirePotentialItems(ctx)
gaps.Range(func(rbNode *containers.RBNode[gap]) bool {
gap := rbNode.Value
last := gap.Beg
@@ -340,7 +356,7 @@ func (o graphCallbacks) _wantRange(
wantKey.Key.OffsetLow = gap.Beg
wantKey.Key.OffsetHigh = gap.End
wantCtx := withWant(ctx, logFieldItemWant, reason, wantKey)
- o.wantAugment(wantCtx, wantKey, o.rebuilt.RebuiltTree(wantCtx, treeID).RebuiltLeafToRoots(wantCtx, v.Node))
+ o.wantAugment(wantCtx, wantKey, tree.RebuiltLeafToRoots(wantCtx, v.Node))
last = runEnd
})
if last < gap.End {
@@ -352,6 +368,7 @@ func (o graphCallbacks) _wantRange(
}
return true
})
+ tree.RebuiltReleasePotentialItems()
}
// WantCSum implements btrfscheck.GraphCallbacks.
@@ -372,7 +389,9 @@ func (o graphCallbacks) WantCSum(ctx context.Context, reason string, inodeTree,
o.enqueueRetry(inodeTree)
return
}
- inodePtr, ok := o.rebuilt.RebuiltTree(inodeCtx, inodeTree).RebuiltItems(inodeCtx).Load(inodeWant.Key.Key())
+ tree := o.rebuilt.RebuiltTree(inodeCtx, inodeTree)
+ inodePtr, ok := tree.RebuiltAcquireItems(inodeCtx).Load(inodeWant.Key.Key())
+ tree.RebuiltReleaseItems()
if !ok {
panic(fmt.Errorf("should not happen: could not load key: %v", inodeWant))
}
diff --git a/cmd/btrfs-rec/inspect/rebuildtrees/scan.go b/cmd/btrfs-rec/inspect/rebuildtrees/scan.go
index a6f9c7a..f266dab 100644
--- a/cmd/btrfs-rec/inspect/rebuildtrees/scan.go
+++ b/cmd/btrfs-rec/inspect/rebuildtrees/scan.go
@@ -41,31 +41,35 @@ type ScanDevicesResult struct {
Sizes map[btrfsutil.ItemPtr]SizeAndErr // EXTENT_CSUM and EXTENT_DATA
}
-func ScanDevices(ctx context.Context, fs *btrfs.FS, nodeList []btrfsvol.LogicalAddr) (ScanDevicesResult, error) {
+func ScanDevices(_ctx context.Context, fs *btrfs.FS, nodeList []btrfsvol.LogicalAddr) (ScanDevicesResult, error) {
+ // read-superblock /////////////////////////////////////////////////////////////
+ ctx := dlog.WithField(_ctx, "btrfs.inspect.rebuild-trees.read.substep", "read-superblock")
dlog.Info(ctx, "Reading superblock...")
sb, err := fs.Superblock()
if err != nil {
return ScanDevicesResult{}, err
}
- dlog.Infof(ctx, "Reading node data from FS...")
-
- var stats textui.Portion[int]
- stats.D = len(nodeList)
- progressWriter := textui.NewProgress[textui.Portion[int]](
- dlog.WithField(ctx, "btrfs.inspect.rebuild-trees.read.substep", "read-nodes"),
- dlog.LogLevelInfo, textui.Tunable(1*time.Second))
-
+ // read-roots //////////////////////////////////////////////////////////////////
+ ctx = dlog.WithField(_ctx, "btrfs.inspect.rebuild-trees.read.substep", "read-roots")
ret := ScanDevicesResult{
Superblock: *sb,
Graph: btrfsutil.NewGraph(ctx, *sb),
-
Flags: make(map[btrfsutil.ItemPtr]FlagsAndErr),
Names: make(map[btrfsutil.ItemPtr][]byte),
Sizes: make(map[btrfsutil.ItemPtr]SizeAndErr),
}
+ // read-nodes //////////////////////////////////////////////////////////////////
+ ctx = dlog.WithField(_ctx, "btrfs.inspect.rebuild-trees.read.substep", "read-nodes")
+ dlog.Infof(ctx, "Reading node data from FS...")
+ var stats textui.Portion[int]
+ stats.D = len(nodeList)
+ progressWriter := textui.NewProgress[textui.Portion[int]](
+ ctx,
+ dlog.LogLevelInfo,
+ textui.Tunable(1*time.Second))
progressWriter.Set(stats)
for _, laddr := range nodeList {
if err := ctx.Err(); err != nil {
@@ -78,11 +82,8 @@ func ScanDevices(ctx context.Context, fs *btrfs.FS, nodeList []btrfsvol.LogicalA
fs.ReleaseNode(node)
return ScanDevicesResult{}, err
}
-
ret.insertNode(node)
-
fs.ReleaseNode(node)
-
stats.N++
progressWriter.Set(stats)
}
@@ -92,7 +93,8 @@ func ScanDevices(ctx context.Context, fs *btrfs.FS, nodeList []btrfsvol.LogicalA
progressWriter.Done()
dlog.Info(ctx, "... done reading node data")
- ctx = dlog.WithField(ctx, "btrfs.inspect.rebuild-trees.read.substep", "check")
+ // check ///////////////////////////////////////////////////////////////////////
+ ctx = dlog.WithField(_ctx, "btrfs.inspect.rebuild-trees.read.substep", "check")
if err := ret.Graph.FinalCheck(ctx, fs); err != nil {
return ScanDevicesResult{}, err
}
diff --git a/cmd/btrfs-rec/inspect_lstrees.go b/cmd/btrfs-rec/inspect_lstrees.go
index 24622b4..decb75c 100644
--- a/cmd/btrfs-rec/inspect_lstrees.go
+++ b/cmd/btrfs-rec/inspect_lstrees.go
@@ -25,28 +25,16 @@ import (
)
func init() {
- var nodeListFilename string
- cmd := &cobra.Command{
+ inspectors.AddCommand(&cobra.Command{
Use: "ls-trees",
Short: "A brief view what types of items are in each tree",
Long: "" +
"If no --node-list is given, then a slow sector-by-sector scan " +
"will be used to find all lost+found nodes.",
Args: cliutil.WrapPositionalArgs(cobra.NoArgs),
- RunE: runWithRawFS(func(fs *btrfs.FS, cmd *cobra.Command, _ []string) error {
+ RunE: runWithReadableFSAndNodeList(func(fs btrfs.ReadableFS, nodeList []btrfsvol.LogicalAddr, cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
- var nodeList []btrfsvol.LogicalAddr
- var err error
- if nodeListFilename != "" {
- nodeList, err = readJSONFile[[]btrfsvol.LogicalAddr](ctx, nodeListFilename)
- } else {
- nodeList, err = btrfsutil.ListNodes(ctx, fs)
- }
- if err != nil {
- return err
- }
-
var treeErrCnt int
var treeItemCnt map[btrfsitem.Type]int
flush := func() {
@@ -124,10 +112,5 @@ func init() {
return nil
}),
- }
- cmd.Flags().StringVar(&nodeListFilename, "node-list", "",
- "Output of 'btrfs-recs inspect [rebuild-mappings] list-nodes' to use for a lost+found tree")
- noError(cmd.MarkFlagFilename("node-list"))
-
- inspectors.AddCommand(cmd)
+ })
}
diff --git a/cmd/btrfs-rec/inspect_rebuildtrees.go b/cmd/btrfs-rec/inspect_rebuildtrees.go
index 676533a..1e808a0 100644
--- a/cmd/btrfs-rec/inspect_rebuildtrees.go
+++ b/cmd/btrfs-rec/inspect_rebuildtrees.go
@@ -17,13 +17,11 @@ import (
"git.lukeshu.com/btrfs-progs-ng/cmd/btrfs-rec/inspect/rebuildtrees"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
- "git.lukeshu.com/btrfs-progs-ng/lib/btrfsutil"
"git.lukeshu.com/btrfs-progs-ng/lib/textui"
)
func init() {
- var nodeListFilename string
- cmd := &cobra.Command{
+ inspectors.AddCommand(&cobra.Command{
Use: "rebuild-trees",
Long: "" +
"Rebuild broken btrees based on missing items that are implied " +
@@ -34,20 +32,9 @@ func init() {
"If no --node-list is given, then a slow sector-by-sector scan " +
"will be used to find all nodes.",
Args: cliutil.WrapPositionalArgs(cobra.NoArgs),
- RunE: runWithRawFS(func(fs *btrfs.FS, cmd *cobra.Command, args []string) error {
+ RunE: runWithRawFSAndNodeList(func(fs *btrfs.FS, nodeList []btrfsvol.LogicalAddr, cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
- var nodeList []btrfsvol.LogicalAddr
- var err error
- if nodeListFilename != "" {
- nodeList, err = readJSONFile[[]btrfsvol.LogicalAddr](ctx, nodeListFilename)
- } else {
- nodeList, err = btrfsutil.ListNodes(ctx, fs)
- }
- if err != nil {
- return err
- }
-
rebuilder, err := rebuildtrees.NewRebuilder(ctx, fs, nodeList)
if err != nil {
return err
@@ -78,10 +65,5 @@ func init() {
return rebuildErr
}),
- }
- cmd.Flags().StringVar(&nodeListFilename, "node-list", "",
- "Output of 'btrfs-recs inspect [rebuild-mappings] list-nodes' to use for the node list")
- noError(cmd.MarkFlagFilename("node-list"))
-
- inspectors.AddCommand(cmd)
+ })
}
diff --git a/cmd/btrfs-rec/main.go b/cmd/btrfs-rec/main.go
index d221c3d..da3aced 100644
--- a/cmd/btrfs-rec/main.go
+++ b/cmd/btrfs-rec/main.go
@@ -48,7 +48,10 @@ var (
var globalFlags struct {
logLevel textui.LogLevelFlag
pvs []string
+
mappings string
+ nodeList string
+ rebuild bool
stopProfiling profile.StopFunc
@@ -86,12 +89,21 @@ func main() {
globalFlags.logLevel.Level = dlog.LogLevelInfo
argparser.PersistentFlags().Var(&globalFlags.logLevel, "verbosity", "set the verbosity")
- argparser.PersistentFlags().StringArrayVar(&globalFlags.pvs, "pv", nil, "open the file `physical_volume` as part of the filesystem")
+ argparser.PersistentFlags().StringArrayVar(&globalFlags.pvs, "pv", nil,
+ "open the file `physical_volume` as part of the filesystem")
noError(argparser.MarkPersistentFlagFilename("pv"))
- argparser.PersistentFlags().StringVar(&globalFlags.mappings, "mappings", "", "load chunk/dev-extent/blockgroup data from external JSON file `mappings.json`")
+ argparser.PersistentFlags().StringVar(&globalFlags.mappings, "mappings", "",
+ "load chunk/dev-extent/blockgroup data from external JSON file `mappings.json`")
noError(argparser.MarkPersistentFlagFilename("mappings"))
+ argparser.PersistentFlags().StringVar(&globalFlags.nodeList, "node-list", "",
+ "load node list (output of 'btrfs-recs inspect [rebuild-mappings] list-nodes') from external JSON file `nodes.json`")
+ noError(argparser.MarkPersistentFlagFilename("node-list"))
+
+ argparser.PersistentFlags().BoolVar(&globalFlags.rebuild, "rebuild", false,
+ "attempt to rebuild broken btrees when reading")
+
globalFlags.stopProfiling = profile.AddProfileFlags(argparser.PersistentFlags(), "profile.")
globalFlags.openFlag = os.O_RDONLY
@@ -176,8 +188,51 @@ func runWithRawFS(runE func(*btrfs.FS, *cobra.Command, []string) error) func(*co
})
}
-func runWithReadableFS(runE func(btrfs.ReadableFS, *cobra.Command, []string) error) func(*cobra.Command, []string) error {
+func runWithRawFSAndNodeList(runE func(*btrfs.FS, []btrfsvol.LogicalAddr, *cobra.Command, []string) error) func(*cobra.Command, []string) error {
return runWithRawFS(func(fs *btrfs.FS, cmd *cobra.Command, args []string) error {
- return runE(btrfsutil.NewOldRebuiltForrest(fs), cmd, args)
+ ctx := cmd.Context()
+
+ var nodeList []btrfsvol.LogicalAddr
+ var err error
+ if globalFlags.nodeList != "" {
+ nodeList, err = readJSONFile[[]btrfsvol.LogicalAddr](ctx, globalFlags.nodeList)
+ } else {
+ nodeList, err = btrfsutil.ListNodes(ctx, fs)
+ }
+ if err != nil {
+ return err
+ }
+
+ return runE(fs, nodeList, cmd, args)
+ })
+}
+
+func _runWithReadableFS(wantNodeList bool, runE func(btrfs.ReadableFS, []btrfsvol.LogicalAddr, *cobra.Command, []string) error) func(*cobra.Command, []string) error {
+ inner := func(fs *btrfs.FS, nodeList []btrfsvol.LogicalAddr, cmd *cobra.Command, args []string) error {
+ var rfs btrfs.ReadableFS = fs
+ if globalFlags.rebuild {
+ rfs = btrfsutil.NewOldRebuiltForrest(fs)
+ }
+
+ return runE(rfs, nodeList, cmd, args)
+ }
+
+ return func(cmd *cobra.Command, args []string) error {
+ if wantNodeList {
+ return runWithRawFSAndNodeList(inner)(cmd, args)
+ }
+ return runWithRawFS(func(fs *btrfs.FS, cmd *cobra.Command, args []string) error {
+ return inner(fs, nil, cmd, args)
+ })(cmd, args)
+ }
+}
+
+func runWithReadableFSAndNodeList(runE func(btrfs.ReadableFS, []btrfsvol.LogicalAddr, *cobra.Command, []string) error) func(*cobra.Command, []string) error {
+ return _runWithReadableFS(true, runE)
+}
+
+func runWithReadableFS(runE func(btrfs.ReadableFS, *cobra.Command, []string) error) func(*cobra.Command, []string) error {
+ return _runWithReadableFS(false, func(fs btrfs.ReadableFS, _ []btrfsvol.LogicalAddr, cmd *cobra.Command, args []string) error {
+ return runE(fs, cmd, args)
})
}