From 058f8c36f6bf9ae2600bf7da33d680ced41eae9a Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 6 Mar 2023 10:42:51 -0700 Subject: cmd/btrfs-rec: Have each subcommand call runWithRawFS itself --- cmd/btrfs-rec/inspect_rebuildmappings.go | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'cmd/btrfs-rec/inspect_rebuildmappings.go') diff --git a/cmd/btrfs-rec/inspect_rebuildmappings.go b/cmd/btrfs-rec/inspect_rebuildmappings.go index 005fd5d..81660b0 100644 --- a/cmd/btrfs-rec/inspect_rebuildmappings.go +++ b/cmd/btrfs-rec/inspect_rebuildmappings.go @@ -17,21 +17,19 @@ import ( ) func init() { - inspectors = append(inspectors, subcommand{ - Command: cobra.Command{ - Use: "rebuild-mappings SCAN_RESULT.json", - Short: "Rebuild broken chunk/dev-extent/blockgroup trees", - Long: "" + - "The rebuilt information is printed as JSON on stdout, and can\n" + - "be loaded by the --mappings flag.\n" + - "\n" + - "This is very similar to `btrfs rescue chunk-recover`, but (1)\n" + - "does a better job, (2) is less buggy, and (3) doesn't actually\n" + - "write the info back to the filesystem; instead writing it\n" + - "out-of-band to stdout.", - Args: cliutil.WrapPositionalArgs(cobra.ExactArgs(1)), - }, - RunE: func(fs *btrfs.FS, cmd *cobra.Command, args []string) error { + inspectors.AddCommand(&cobra.Command{ + Use: "rebuild-mappings SCAN_RESULT.json", + Short: "Rebuild broken chunk/dev-extent/blockgroup trees", + Long: "" + + "The rebuilt information is printed as JSON on stdout, and can\n" + + "be loaded by the --mappings flag.\n" + + "\n" + + "This is very similar to `btrfs rescue chunk-recover`, but (1)\n" + + "does a better job, (2) is less buggy, and (3) doesn't actually\n" + + "write the info back to the filesystem; instead writing it\n" + + "out-of-band to stdout.", + Args: cliutil.WrapPositionalArgs(cobra.ExactArgs(1)), + RunE: runWithRawFS(func(fs *btrfs.FS, cmd *cobra.Command, args []string) error { ctx := cmd.Context() dlog.Infof(ctx, "Reading %q...", args[0]) @@ -56,6 +54,6 @@ func init() { dlog.Info(ctx, "... done writing") return nil - }, + }), }) } -- cgit v1.2.3-54-g00ecf From 4278c185aafb0fb197e52cdb9eb9bd9d36b61c4d Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Tue, 7 Mar 2023 01:35:08 -0700 Subject: cmd/btrfs-rec: Smash scandevices and rebuild-mappings together --- cmd/btrfs-rec/inspect_lstrees.go | 2 +- cmd/btrfs-rec/inspect_rebuildmappings.go | 71 ++++++++++++++++++++++++++++++-- cmd/btrfs-rec/inspect_scandevices.go | 69 ------------------------------- cmd/btrfs-rec/util.go | 25 +++++++++++ scripts/main.sh | 8 ++-- 5 files changed, 97 insertions(+), 78 deletions(-) delete mode 100644 cmd/btrfs-rec/inspect_scandevices.go (limited to 'cmd/btrfs-rec/inspect_rebuildmappings.go') diff --git a/cmd/btrfs-rec/inspect_lstrees.go b/cmd/btrfs-rec/inspect_lstrees.go index be72860..1ff7671 100644 --- a/cmd/btrfs-rec/inspect_lstrees.go +++ b/cmd/btrfs-rec/inspect_lstrees.go @@ -114,7 +114,7 @@ func init() { return nil }), } - cmd.Flags().StringVar(&scandevicesFilename, "scandevices", "", "Output of 'scandevices' to use for a lost+found tree") + cmd.Flags().StringVar(&scandevicesFilename, "scandevices", "", "Output of 'btrfs-recs inspect rebuild-mappings scan' to use for a lost+found tree") noError(cmd.MarkFlagFilename("scandevices")) inspectors.AddCommand(cmd) diff --git a/cmd/btrfs-rec/inspect_rebuildmappings.go b/cmd/btrfs-rec/inspect_rebuildmappings.go index 81660b0..55e3408 100644 --- a/cmd/btrfs-rec/inspect_rebuildmappings.go +++ b/cmd/btrfs-rec/inspect_rebuildmappings.go @@ -17,8 +17,8 @@ import ( ) func init() { - inspectors.AddCommand(&cobra.Command{ - Use: "rebuild-mappings SCAN_RESULT.json", + cmd := &cobra.Command{ + Use: "rebuild-mappings", Short: "Rebuild broken chunk/dev-extent/blockgroup trees", Long: "" + "The rebuilt information is printed as JSON on stdout, and can\n" + @@ -27,8 +27,69 @@ func init() { "This is very similar to `btrfs rescue chunk-recover`, but (1)\n" + "does a better job, (2) is less buggy, and (3) doesn't actually\n" + "write the info back to the filesystem; instead writing it\n" + - "out-of-band to stdout.", - Args: cliutil.WrapPositionalArgs(cobra.ExactArgs(1)), + "out-of-band to stdout.\n" + + "\n" + + "The I/O and the CPU parts of this can be split up as:\n" + + "\n" + + "\tbtrfs-rec inspect rebuild-mappings scan > SCAN.json # read\n" + + "\tbtrfs-rec inspect rebuild-mappings process SCAN.json # CPU\n", + Args: cliutil.WrapPositionalArgs(cobra.NoArgs), + RunE: runWithRawFS(func(fs *btrfs.FS, cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + scanResults, err := rebuildmappings.ScanDevices(ctx, fs) + if err != nil { + return err + } + + if err := rebuildmappings.RebuildMappings(ctx, fs, scanResults); err != nil { + return err + } + + dlog.Infof(ctx, "Writing reconstructed mappings to stdout...") + if err := writeJSONFile(os.Stdout, fs.LV.Mappings(), lowmemjson.ReEncoderConfig{ + Indent: "\t", + ForceTrailingNewlines: true, + CompactIfUnder: 120, //nolint:gomnd // This is what looks nice. + }); err != nil { + return err + } + dlog.Info(ctx, "... done writing") + + return nil + }), + } + + cmd.AddCommand(&cobra.Command{ + Use: "scan", + Short: "Read from the filesystem all data nescessary to rebuild the mappings", + Args: cliutil.WrapPositionalArgs(cobra.NoArgs), + RunE: runWithRawFS(func(fs *btrfs.FS, cmd *cobra.Command, _ []string) (err error) { + ctx := cmd.Context() + + scanResults, err := rebuildmappings.ScanDevices(ctx, fs) + if err != nil { + return err + } + + dlog.Info(ctx, "Writing scan results to stdout...") + if err := writeJSONFile(os.Stdout, scanResults, lowmemjson.ReEncoderConfig{ + Indent: "\t", + ForceTrailingNewlines: true, + CompactIfUnder: 16, //nolint:gomnd // This is what looks nice. + }); err != nil { + return err + } + dlog.Info(ctx, "... done writing") + + return nil + }), + }) + + cmd.AddCommand(&cobra.Command{ + Use: "process", + Short: "Rebuild the mappings based on previously read data", + Args: cliutil.WrapPositionalArgs(cobra.ExactArgs(1)), RunE: runWithRawFS(func(fs *btrfs.FS, cmd *cobra.Command, args []string) error { ctx := cmd.Context() @@ -56,4 +117,6 @@ func init() { return nil }), }) + + inspectors.AddCommand(cmd) } diff --git a/cmd/btrfs-rec/inspect_scandevices.go b/cmd/btrfs-rec/inspect_scandevices.go deleted file mode 100644 index 4172c7c..0000000 --- a/cmd/btrfs-rec/inspect_scandevices.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2022-2023 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package main - -import ( - "context" - "os" - - "git.lukeshu.com/go/lowmemjson" - "github.com/datawire/dlib/dlog" - "github.com/datawire/ocibuild/pkg/cliutil" - "github.com/spf13/cobra" - - "git.lukeshu.com/btrfs-progs-ng/cmd/btrfs-rec/inspect/rebuildmappings" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" - "git.lukeshu.com/btrfs-progs-ng/lib/containers" - "git.lukeshu.com/btrfs-progs-ng/lib/maps" -) - -func init() { - inspectors.AddCommand(&cobra.Command{ - Use: "scandevices", - Args: cliutil.WrapPositionalArgs(cobra.NoArgs), - RunE: runWithRawFS(func(fs *btrfs.FS, cmd *cobra.Command, _ []string) (err error) { - ctx := cmd.Context() - - results, err := rebuildmappings.ScanDevices(ctx, fs) - if err != nil { - return err - } - - dlog.Info(ctx, "Writing scan results to stdout...") - if err := writeJSONFile(os.Stdout, results, lowmemjson.ReEncoderConfig{ - Indent: "\t", - ForceTrailingNewlines: true, - CompactIfUnder: 16, //nolint:gomnd // This is what looks nice. - }); err != nil { - return err - } - dlog.Info(ctx, "... done writing") - - return nil - }), - }) -} - -func readNodeList(ctx context.Context, filename string) ([]btrfsvol.LogicalAddr, error) { - if filename == "" { - return nil, nil - } - results, err := readJSONFile[rebuildmappings.ScanDevicesResult](ctx, filename) - if err != nil { - return nil, err - } - var cnt int - for _, devResults := range results { - cnt += len(devResults.FoundNodes) - } - set := make(containers.Set[btrfsvol.LogicalAddr], cnt) - for _, devResults := range results { - for laddr := range devResults.FoundNodes { - set.Insert(laddr) - } - } - return maps.SortedKeys(set), nil -} diff --git a/cmd/btrfs-rec/util.go b/cmd/btrfs-rec/util.go index 3d751a6..2e4bb84 100644 --- a/cmd/btrfs-rec/util.go +++ b/cmd/btrfs-rec/util.go @@ -13,6 +13,10 @@ import ( "git.lukeshu.com/go/lowmemjson" "github.com/datawire/dlib/dlog" + "git.lukeshu.com/btrfs-progs-ng/cmd/btrfs-rec/inspect/rebuildmappings" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" + "git.lukeshu.com/btrfs-progs-ng/lib/containers" + "git.lukeshu.com/btrfs-progs-ng/lib/maps" "git.lukeshu.com/btrfs-progs-ng/lib/streamio" ) @@ -47,3 +51,24 @@ func writeJSONFile(w io.Writer, obj any, cfg lowmemjson.ReEncoderConfig) (err er }() return lowmemjson.NewEncoder(lowmemjson.NewReEncoder(buffer, cfg)).Encode(obj) } + +func readNodeList(ctx context.Context, filename string) ([]btrfsvol.LogicalAddr, error) { + if filename == "" { + return nil, nil + } + results, err := readJSONFile[rebuildmappings.ScanDevicesResult](ctx, filename) + if err != nil { + return nil, err + } + var cnt int + for _, devResults := range results { + cnt += len(devResults.FoundNodes) + } + set := make(containers.Set[btrfsvol.LogicalAddr], cnt) + for _, devResults := range results { + for laddr := range devResults.FoundNodes { + set.Insert(laddr) + } + } + return maps.SortedKeys(set), nil +} diff --git a/scripts/main.sh b/scripts/main.sh index 4aa96ea..67fb330 100755 --- a/scripts/main.sh +++ b/scripts/main.sh @@ -32,9 +32,9 @@ export GOMEMLIMIT="$(awk '/^MemTotal:/{ print $2 "KiB" }' Date: Tue, 7 Mar 2023 01:52:07 -0700 Subject: cmd/btrfs-rec: Fix newlines in `inspect rebuild-mappings --help` --- cmd/btrfs-rec/inspect_rebuildmappings.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'cmd/btrfs-rec/inspect_rebuildmappings.go') diff --git a/cmd/btrfs-rec/inspect_rebuildmappings.go b/cmd/btrfs-rec/inspect_rebuildmappings.go index 55e3408..9c6b1cc 100644 --- a/cmd/btrfs-rec/inspect_rebuildmappings.go +++ b/cmd/btrfs-rec/inspect_rebuildmappings.go @@ -21,12 +21,12 @@ func init() { Use: "rebuild-mappings", Short: "Rebuild broken chunk/dev-extent/blockgroup trees", Long: "" + - "The rebuilt information is printed as JSON on stdout, and can\n" + + "The rebuilt information is printed as JSON on stdout, and can " + "be loaded by the --mappings flag.\n" + "\n" + - "This is very similar to `btrfs rescue chunk-recover`, but (1)\n" + - "does a better job, (2) is less buggy, and (3) doesn't actually\n" + - "write the info back to the filesystem; instead writing it\n" + + "This is very similar to `btrfs rescue chunk-recover`, but (1) " + + "does a better job, (2) is less buggy, and (3) doesn't actually " + + "write the info back to the filesystem; instead writing it " + "out-of-band to stdout.\n" + "\n" + "The I/O and the CPU parts of this can be split up as:\n" + -- cgit v1.2.3-54-g00ecf From 3949bd3ff240bc6d06dd08f6e3183e72571e0e1d Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Tue, 7 Mar 2023 12:10:42 -0700 Subject: Expose node-lists as a thing on the CLI --- cmd/btrfs-rec/inspect_listnodes.go | 51 ++++++++++++++++++++++++ cmd/btrfs-rec/inspect_lstrees.go | 23 ++++++++--- cmd/btrfs-rec/inspect_rebuildmappings.go | 44 +++++++++++++++++++++ cmd/btrfs-rec/inspect_rebuildtrees.go | 46 +++++++++++++++------- cmd/btrfs-rec/util.go | 25 ------------ lib/btrfsutil/listnodes.go | 66 ++++++++++++++++++++++++++++++++ scripts/main.sh | 6 ++- 7 files changed, 214 insertions(+), 47 deletions(-) create mode 100644 cmd/btrfs-rec/inspect_listnodes.go create mode 100644 lib/btrfsutil/listnodes.go (limited to 'cmd/btrfs-rec/inspect_rebuildmappings.go') diff --git a/cmd/btrfs-rec/inspect_listnodes.go b/cmd/btrfs-rec/inspect_listnodes.go new file mode 100644 index 0000000..d9b24ed --- /dev/null +++ b/cmd/btrfs-rec/inspect_listnodes.go @@ -0,0 +1,51 @@ +// Copyright (C) 2022-2023 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package main + +import ( + "os" + + "git.lukeshu.com/go/lowmemjson" + "github.com/datawire/dlib/dlog" + "github.com/datawire/ocibuild/pkg/cliutil" + "github.com/spf13/cobra" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfsutil" +) + +func init() { + inspectors.AddCommand(&cobra.Command{ + Use: "list-nodes", + Short: "Scan the filesystem for btree nodes", + Long: "" + + "This scans the filesystem sector-by-sector looking for nodes. " + + "If you are needing to rebuild the chunk/dev-extent/blockgroup " + + "trees with `btrfs-rec inspect rebuild-mappings` anyway, you may " + + "want to instead use `btrfs-rec inspect rebuild-mappings list-nodes` " + + "to take advantage of the sector-by-sector scan that's already " + + "performed by `btrfs-rec inspect rebuild-mappings scan`.", + Args: cliutil.WrapPositionalArgs(cobra.ExactArgs(1)), + RunE: runWithRawFS(func(fs *btrfs.FS, cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + nodeList, err := btrfsutil.ListNodes(ctx, fs) + if err != nil { + return err + } + + dlog.Infof(ctx, "Writing nodes to stdout...") + if err := writeJSONFile(os.Stdout, nodeList, lowmemjson.ReEncoderConfig{ + Indent: "\t", + ForceTrailingNewlines: true, + }); err != nil { + return err + } + dlog.Info(ctx, "... done writing") + + return nil + }), + }) +} diff --git a/cmd/btrfs-rec/inspect_lstrees.go b/cmd/btrfs-rec/inspect_lstrees.go index 1ff7671..05c3a57 100644 --- a/cmd/btrfs-rec/inspect_lstrees.go +++ b/cmd/btrfs-rec/inspect_lstrees.go @@ -26,14 +26,24 @@ import ( ) func init() { - var scandevicesFilename string + var nodeListFilename string cmd := &cobra.Command{ Use: "ls-trees", Short: "A brief view what types of items are in each tree", - Args: cliutil.WrapPositionalArgs(cobra.NoArgs), + 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 { ctx := cmd.Context() - nodeList, err := readNodeList(ctx, scandevicesFilename) + + 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 } @@ -86,7 +96,7 @@ func init() { }, }) - if scandevicesFilename != "" { + { treeErrCnt = 0 treeItemCnt = make(map[btrfsitem.Type]int) textui.Fprintf(os.Stdout, "lost+found\n") @@ -114,8 +124,9 @@ func init() { return nil }), } - cmd.Flags().StringVar(&scandevicesFilename, "scandevices", "", "Output of 'btrfs-recs inspect rebuild-mappings scan' to use for a lost+found tree") - noError(cmd.MarkFlagFilename("scandevices")) + 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_rebuildmappings.go b/cmd/btrfs-rec/inspect_rebuildmappings.go index 9c6b1cc..b215e7a 100644 --- a/cmd/btrfs-rec/inspect_rebuildmappings.go +++ b/cmd/btrfs-rec/inspect_rebuildmappings.go @@ -14,6 +14,9 @@ import ( "git.lukeshu.com/btrfs-progs-ng/cmd/btrfs-rec/inspect/rebuildmappings" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" + "git.lukeshu.com/btrfs-progs-ng/lib/containers" + "git.lukeshu.com/btrfs-progs-ng/lib/maps" ) func init() { @@ -118,5 +121,46 @@ func init() { }), }) + cmd.AddCommand(&cobra.Command{ + Use: "list-nodes", + Short: "Produce a listing of btree nodes from previously read data", + Long: "" + + "This is a variant of `btrfs-rec inspect list-nodes` that takes " + + "advantage of using previously read data from " + + "`btrfs-rec inspect rebuild-nodes scan`.", + Args: cliutil.WrapPositionalArgs(cobra.ExactArgs(1)), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + scanResults, err := readJSONFile[rebuildmappings.ScanDevicesResult](ctx, args[0]) + if err != nil { + return err + } + + var cnt int + for _, devResults := range scanResults { + cnt += len(devResults.FoundNodes) + } + set := make(containers.Set[btrfsvol.LogicalAddr], cnt) + for _, devResults := range scanResults { + for laddr := range devResults.FoundNodes { + set.Insert(laddr) + } + } + nodeList := maps.SortedKeys(set) + + dlog.Infof(ctx, "Writing nodes to stdout...") + if err := writeJSONFile(os.Stdout, nodeList, lowmemjson.ReEncoderConfig{ + Indent: "\t", + ForceTrailingNewlines: true, + }); err != nil { + return err + } + dlog.Info(ctx, "... done writing") + + return nil + }, + }) + inspectors.AddCommand(cmd) } diff --git a/cmd/btrfs-rec/inspect_rebuildtrees.go b/cmd/btrfs-rec/inspect_rebuildtrees.go index f66f287..676533a 100644 --- a/cmd/btrfs-rec/inspect_rebuildtrees.go +++ b/cmd/btrfs-rec/inspect_rebuildtrees.go @@ -5,7 +5,6 @@ package main import ( - "context" "os" "runtime" "time" @@ -17,25 +16,39 @@ 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() { - inspectors.AddCommand(&cobra.Command{ - Use: "rebuild-trees NODESCAN.json", - Args: cliutil.WrapPositionalArgs(cobra.ExactArgs(1)), + var nodeListFilename string + cmd := &cobra.Command{ + Use: "rebuild-trees", + Long: "" + + "Rebuild broken btrees based on missing items that are implied " + + "by present items. This requires functioning " + + "chunk/dev-extent/blockgroup trees, which can be rebuilt " + + "separately with `btrfs-rec inspect rebuild-mappings`.\n" + + "\n" + + "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 { ctx := cmd.Context() - // This is wrapped in a func in order to *ensure* that `nodeList` goes out of scope once - // `rebuilder` has been created. - rebuilder, err := func(ctx context.Context) (rebuildtrees.Rebuilder, error) { - nodeList, err := readNodeList(ctx, args[0]) - if err != nil { - return nil, err - } - return rebuildtrees.NewRebuilder(ctx, fs, nodeList) - }(ctx) + 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 } @@ -65,5 +78,10 @@ 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/util.go b/cmd/btrfs-rec/util.go index 2e4bb84..3d751a6 100644 --- a/cmd/btrfs-rec/util.go +++ b/cmd/btrfs-rec/util.go @@ -13,10 +13,6 @@ import ( "git.lukeshu.com/go/lowmemjson" "github.com/datawire/dlib/dlog" - "git.lukeshu.com/btrfs-progs-ng/cmd/btrfs-rec/inspect/rebuildmappings" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" - "git.lukeshu.com/btrfs-progs-ng/lib/containers" - "git.lukeshu.com/btrfs-progs-ng/lib/maps" "git.lukeshu.com/btrfs-progs-ng/lib/streamio" ) @@ -51,24 +47,3 @@ func writeJSONFile(w io.Writer, obj any, cfg lowmemjson.ReEncoderConfig) (err er }() return lowmemjson.NewEncoder(lowmemjson.NewReEncoder(buffer, cfg)).Encode(obj) } - -func readNodeList(ctx context.Context, filename string) ([]btrfsvol.LogicalAddr, error) { - if filename == "" { - return nil, nil - } - results, err := readJSONFile[rebuildmappings.ScanDevicesResult](ctx, filename) - if err != nil { - return nil, err - } - var cnt int - for _, devResults := range results { - cnt += len(devResults.FoundNodes) - } - set := make(containers.Set[btrfsvol.LogicalAddr], cnt) - for _, devResults := range results { - for laddr := range devResults.FoundNodes { - set.Insert(laddr) - } - } - return maps.SortedKeys(set), nil -} diff --git a/lib/btrfsutil/listnodes.go b/lib/btrfsutil/listnodes.go new file mode 100644 index 0000000..16300da --- /dev/null +++ b/lib/btrfsutil/listnodes.go @@ -0,0 +1,66 @@ +// Copyright (C) 2023 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package btrfsutil + +import ( + "context" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" + "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/textui" +) + +type nodeScanner struct { + nodes containers.Set[btrfsvol.LogicalAddr] +} + +type nodeStats struct { + numNodes int +} + +func (s nodeStats) String() string { + return textui.Sprintf("found: %d nodes", s.numNodes) +} + +var _ DeviceScanner[nodeStats, containers.Set[btrfsvol.LogicalAddr]] = (*nodeScanner)(nil) + +func newNodeScanner(ctx context.Context, sb btrfstree.Superblock, numBytes btrfsvol.PhysicalAddr, numSectors int) DeviceScanner[nodeStats, containers.Set[btrfsvol.LogicalAddr]] { + s := new(nodeScanner) + s.nodes = make(containers.Set[btrfsvol.LogicalAddr]) + return s +} + +func (s *nodeScanner) ScanStats() nodeStats { + return nodeStats{numNodes: len(s.nodes)} +} + +func (*nodeScanner) ScanSector(ctx context.Context, dev *btrfs.Device, paddr btrfsvol.PhysicalAddr) error { + return nil +} + +func (s *nodeScanner) ScanNode(ctx context.Context, nodeRef *diskio.Ref[btrfsvol.PhysicalAddr, btrfstree.Node]) error { + s.nodes.Insert(nodeRef.Data.Head.Addr) + return nil +} + +func (s *nodeScanner) ScanDone(ctx context.Context) (containers.Set[btrfsvol.LogicalAddr], error) { + return s.nodes, nil +} + +func ListNodes(ctx context.Context, fs *btrfs.FS) ([]btrfsvol.LogicalAddr, error) { + perDev, err := ScanDevices[nodeStats, containers.Set[btrfsvol.LogicalAddr]](ctx, fs, newNodeScanner) + if err != nil { + return nil, err + } + set := make(containers.Set[btrfsvol.LogicalAddr]) + for _, devSet := range perDev { + set.InsertFrom(devSet) + } + return maps.SortedKeys(set), nil +} diff --git a/scripts/main.sh b/scripts/main.sh index 2ea5213..dddb9bb 100755 --- a/scripts/main.sh +++ b/scripts/main.sh @@ -33,6 +33,8 @@ export GOMEMLIMIT="$(awk '/^MemTotal:/{ print $2 "KiB" }' Date: Fri, 10 Mar 2023 22:02:23 -0700 Subject: cmd/btrfs-rec: inspect rebuild-mappings list-nodes: Set up logging and stuff --- cmd/btrfs-rec/inspect_rebuildmappings.go | 4 +-- cmd/btrfs-rec/main.go | 59 +++++++++++++++++++------------- 2 files changed, 38 insertions(+), 25 deletions(-) (limited to 'cmd/btrfs-rec/inspect_rebuildmappings.go') diff --git a/cmd/btrfs-rec/inspect_rebuildmappings.go b/cmd/btrfs-rec/inspect_rebuildmappings.go index b215e7a..43c45b1 100644 --- a/cmd/btrfs-rec/inspect_rebuildmappings.go +++ b/cmd/btrfs-rec/inspect_rebuildmappings.go @@ -129,7 +129,7 @@ func init() { "advantage of using previously read data from " + "`btrfs-rec inspect rebuild-nodes scan`.", Args: cliutil.WrapPositionalArgs(cobra.ExactArgs(1)), - RunE: func(cmd *cobra.Command, args []string) error { + RunE: run(func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() scanResults, err := readJSONFile[rebuildmappings.ScanDevicesResult](ctx, args[0]) @@ -159,7 +159,7 @@ func init() { dlog.Info(ctx, "... done writing") return nil - }, + }), }) inspectors.AddCommand(cmd) diff --git a/cmd/btrfs-rec/main.go b/cmd/btrfs-rec/main.go index 343a41f..15f1964 100644 --- a/cmd/btrfs-rec/main.go +++ b/cmd/btrfs-rec/main.go @@ -107,7 +107,7 @@ func main() { } } -func runWithRawFS(runE func(*btrfs.FS, *cobra.Command, []string) error) func(*cobra.Command, []string) error { +func run(runE func(*cobra.Command, []string) error) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() logger := textui.NewLogger(os.Stderr, globalFlags.logLevel.Level) @@ -126,37 +126,50 @@ func runWithRawFS(runE func(*btrfs.FS, *cobra.Command, []string) error) func(*co err = _err } } + defer func() { maybeSetErr(globalFlags.stopProfiling()) }() - if len(globalFlags.pvs) == 0 { - // We do this here instead of calling argparser.MarkPersistentFlagRequired("pv") so that - // it doesn't interfere with the `help` sub-command. - return cliutil.FlagErrorFunc(cmd, fmt.Errorf("must specify 1 or more physical volumes with --pv")) + cmd.SetContext(ctx) + return runE(cmd, args) + }) + return grp.Wait() + } +} + +func runWithRawFS(runE func(*btrfs.FS, *cobra.Command, []string) error) func(*cobra.Command, []string) error { + return run(func(cmd *cobra.Command, args []string) (err error) { + maybeSetErr := func(_err error) { + if _err != nil && err == nil { + err = _err } - fs, err := btrfsutil.Open(ctx, globalFlags.openFlag, globalFlags.pvs...) + } + + if len(globalFlags.pvs) == 0 { + // We do this here instead of calling argparser.MarkPersistentFlagRequired("pv") so that + // it doesn't interfere with the `help` sub-command. + return cliutil.FlagErrorFunc(cmd, fmt.Errorf("must specify 1 or more physical volumes with --pv")) + } + fs, err := btrfsutil.Open(cmd.Context(), globalFlags.openFlag, globalFlags.pvs...) + if err != nil { + return err + } + defer func() { + maybeSetErr(fs.Close()) + }() + + if globalFlags.mappings != "" { + mappingsJSON, err := readJSONFile[[]btrfsvol.Mapping](cmd.Context(), globalFlags.mappings) if err != nil { return err } - defer func() { - maybeSetErr(fs.Close()) - }() - - if globalFlags.mappings != "" { - mappingsJSON, err := readJSONFile[[]btrfsvol.Mapping](ctx, globalFlags.mappings) - if err != nil { + for _, mapping := range mappingsJSON { + if err := fs.LV.AddMapping(mapping); err != nil { return err } - for _, mapping := range mappingsJSON { - if err := fs.LV.AddMapping(mapping); err != nil { - return err - } - } } + } - cmd.SetContext(ctx) - return runE(fs, cmd, args) - }) - return grp.Wait() - } + return runE(fs, cmd, args) + }) } -- cgit v1.2.3-54-g00ecf