summaryrefslogtreecommitdiff
path: root/cmd/btrfs-rec
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-07-22 21:18:40 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2023-07-23 00:53:05 -0600
commitcbb2379d822bf391e4f9d1ed7dab8fca0aa85c6d (patch)
treeede006affa164dd50ff9c21c7632ec691abf6640 /cmd/btrfs-rec
parent17a9ea959d2f77b1ba55d45ac3feaf918f42f7dd (diff)
wip: rebuildmappings process: Don't require --pvlukeshu/process-without-pv
Diffstat (limited to 'cmd/btrfs-rec')
-rw-r--r--cmd/btrfs-rec/inspect/rebuildmappings/file_phony.go49
-rw-r--r--cmd/btrfs-rec/inspect_listnodes.go2
-rw-r--r--cmd/btrfs-rec/inspect_rebuildmappings.go29
-rw-r--r--cmd/btrfs-rec/main.go17
4 files changed, 90 insertions, 7 deletions
diff --git a/cmd/btrfs-rec/inspect/rebuildmappings/file_phony.go b/cmd/btrfs-rec/inspect/rebuildmappings/file_phony.go
new file mode 100644
index 0000000..a41a16b
--- /dev/null
+++ b/cmd/btrfs-rec/inspect/rebuildmappings/file_phony.go
@@ -0,0 +1,49 @@
+// Copyright (C) 2023 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package rebuildmappings
+
+import (
+ "fmt"
+
+ "git.lukeshu.com/btrfs-progs-ng/lib/binstruct"
+ "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/diskio"
+ "git.lukeshu.com/btrfs-progs-ng/lib/slices"
+)
+
+type phonyFile struct {
+ size btrfsvol.PhysicalAddr
+ sb btrfstree.Superblock
+}
+
+var _ diskio.File[btrfsvol.PhysicalAddr] = (*phonyFile)(nil)
+
+func NewPhonyFile(size btrfsvol.PhysicalAddr, sb btrfstree.Superblock) *phonyFile {
+ return &phonyFile{
+ size: size,
+ sb: sb,
+ }
+}
+
+func (f *phonyFile) Name() string { return fmt.Sprintf("phony_file:device_id=%v", f.sb.DevItem.DevID) }
+func (f *phonyFile) Size() btrfsvol.PhysicalAddr { return f.size }
+func (*phonyFile) Close() error { return nil }
+
+func (f *phonyFile) ReadAt(p []byte, off btrfsvol.PhysicalAddr) (int, error) {
+ if len(p) == int(btrfs.SuperblockSize) && slices.Contains(off, btrfs.SuperblockAddrs) {
+ bs, err := binstruct.Marshal(f.sb)
+ if err != nil {
+ return 0, err
+ }
+ return copy(p, bs), nil
+ }
+ panic(fmt.Errorf("%T: should not happen: ReadAt should not be called for a phony file", f))
+}
+
+func (f *phonyFile) WriteAt([]byte, btrfsvol.PhysicalAddr) (int, error) {
+ panic(fmt.Errorf("%T: should not happen: WriteAt should not be called for a phony file", f))
+}
diff --git a/cmd/btrfs-rec/inspect_listnodes.go b/cmd/btrfs-rec/inspect_listnodes.go
index d9b24ed..eb088e8 100644
--- a/cmd/btrfs-rec/inspect_listnodes.go
+++ b/cmd/btrfs-rec/inspect_listnodes.go
@@ -28,7 +28,7 @@ func init() {
"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 {
+ RunE: runWithRawFS(nil, func(fs *btrfs.FS, cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
nodeList, err := btrfsutil.ListNodes(ctx, fs)
diff --git a/cmd/btrfs-rec/inspect_rebuildmappings.go b/cmd/btrfs-rec/inspect_rebuildmappings.go
index e883b93..4c194c7 100644
--- a/cmd/btrfs-rec/inspect_rebuildmappings.go
+++ b/cmd/btrfs-rec/inspect_rebuildmappings.go
@@ -5,6 +5,7 @@
package main
import (
+ "fmt"
"os"
"git.lukeshu.com/go/lowmemjson"
@@ -37,7 +38,7 @@ func init() {
"\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 {
+ RunE: runWithRawFS(nil, func(fs *btrfs.FS, cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
scanResults, err := rebuildmappings.ScanDevices(ctx, fs)
@@ -67,7 +68,7 @@ func init() {
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) {
+ RunE: runWithRawFS(nil, func(fs *btrfs.FS, cmd *cobra.Command, _ []string) (err error) {
ctx := cmd.Context()
devResults, err := rebuildmappings.ScanDevices(ctx, fs)
@@ -94,6 +95,7 @@ func init() {
}),
})
+ var scanResults rebuildmappings.ScanResult
cmd.AddCommand(&cobra.Command{
Use: "process",
Short: "Rebuild the mappings based on previously read data",
@@ -108,6 +110,29 @@ func init() {
}
dlog.Infof(ctx, "... done reading %q", args[0])
+ pvDevices := fs.LV.PhysicalVolumes()
+ for _, devID := range maps.SortedKeys(scanResults.Devices) {
+ if maps.HasKey(pvDevices, devID) {
+ continue
+ }
+ devFile := &btrfs.Device{
+ File: rebuildmappings.NewPhonyFile(
+ scanResults.Devices[devID].Size,
+ scanResults.Devices[devID].Superblock.Val),
+ }
+ if err := fs.AddDevice(ctx, devFile); err != nil {
+ return fmt.Errorf("device file: %q: %w", devFile.Name(), err)
+ }
+ }
+ for _, mapping := range scanResults.Mappings {
+ if err := fs.LV.AddMapping(mapping); err != nil {
+ return err
+ }
+ }
+ return nil
+ }, func(fs *btrfs.FS, cmd *cobra.Command, _ []string) error {
+ ctx := cmd.Context()
+
if err := rebuildmappings.RebuildMappings(ctx, fs, scanResults.Devices); err != nil {
return err
}
diff --git a/cmd/btrfs-rec/main.go b/cmd/btrfs-rec/main.go
index 39ba9ef..30b7e3d 100644
--- a/cmd/btrfs-rec/main.go
+++ b/cmd/btrfs-rec/main.go
@@ -158,7 +158,10 @@ func run(runE func(*cobra.Command, []string) error) func(*cobra.Command, []strin
}
}
-func runWithRawFS(runE func(*btrfs.FS, *cobra.Command, []string) error) func(*cobra.Command, []string) error {
+func runWithRawFS(
+ fallbackInit func(*btrfs.FS, *cobra.Command, []string) error,
+ 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 {
@@ -166,7 +169,7 @@ func runWithRawFS(runE func(*btrfs.FS, *cobra.Command, []string) error) func(*co
}
}
- if len(globalFlags.pvs) == 0 {
+ if len(globalFlags.pvs) == 0 && fallbackInit == nil {
// 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"))
@@ -179,6 +182,12 @@ func runWithRawFS(runE func(*btrfs.FS, *cobra.Command, []string) error) func(*co
maybeSetErr(fs.Close())
}()
+ if fallbackInit != nil {
+ if err := fallbackInit(fs, cmd, args); err != nil {
+ return err
+ }
+ }
+
if globalFlags.mappings != "" {
mappingsJSON, err := readJSONFile[[]btrfsvol.Mapping](cmd.Context(), globalFlags.mappings)
if err != nil {
@@ -196,7 +205,7 @@ func runWithRawFS(runE func(*btrfs.FS, *cobra.Command, []string) error) func(*co
}
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 runWithRawFS(nil, func(fs *btrfs.FS, cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
var nodeList []btrfsvol.LogicalAddr
@@ -245,7 +254,7 @@ func _runWithReadableFS(wantNodeList bool, runE func(btrfs.ReadableFS, []btrfsvo
if wantNodeList || globalFlags.rebuild || globalFlags.treeRoots != "" {
return runWithRawFSAndNodeList(inner)(cmd, args)
}
- return runWithRawFS(func(fs *btrfs.FS, cmd *cobra.Command, args []string) error {
+ return runWithRawFS(nil, func(fs *btrfs.FS, cmd *cobra.Command, args []string) error {
return inner(fs, nil, cmd, args)
})(cmd, args)
}