From 0f853e1ead10347be8c5715d6bb797dd5e643e2f Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 11 Jul 2022 00:43:59 -0600 Subject: wip btrfs-rec --- cmd/btrfs-rec/main.go | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 10 ++-- go.sum | 20 +++++-- 3 files changed, 169 insertions(+), 6 deletions(-) create mode 100644 cmd/btrfs-rec/main.go diff --git a/cmd/btrfs-rec/main.go b/cmd/btrfs-rec/main.go new file mode 100644 index 0000000..9b285b1 --- /dev/null +++ b/cmd/btrfs-rec/main.go @@ -0,0 +1,145 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package main + +import ( + "context" + "fmt" + "os" + + "github.com/datawire/dlib/dgroup" + "github.com/datawire/dlib/dlog" + "github.com/datawire/ocibuild/pkg/cliutil" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsutil" +) + +type logLevelFlag struct { + logrus.Level +} + +func (lvl *logLevelFlag) Type() string { return "loglevel" } +func (lvl *logLevelFlag) Set(str string) error { + var err error + lvl.Level, err = logrus.ParseLevel(str) + return err +} + +var _ pflag.Value = (*logLevelFlag)(nil) + +type subcommand struct { + cobra.Command + RunE func(*btrfs.FS, *cobra.Command, []string) error +} + +var inspectors, repairers []subcommand + +func main() { + logLevelFlag := logLevelFlag{ + Level: logrus.InfoLevel, + } + var pvsFlag []string + + argparser := &cobra.Command{ + Use: "btrfs-rec {[flags]|SUBCOMMAND}", + Short: "Recover (data from) a broken btrfs filesystem", + + Args: cliutil.WrapPositionalArgs(cliutil.OnlySubcommands), + RunE: cliutil.RunSubcommands, + + SilenceErrors: true, // main() will handle this after .ExecuteContext() returns + SilenceUsage: true, // our FlagErrorFunc will handle it + + CompletionOptions: cobra.CompletionOptions{ //nolint:exhaustivestruct + DisableDefaultCmd: true, + }, + } + argparser.SetFlagErrorFunc(cliutil.FlagErrorFunc) + argparser.SetHelpTemplate(cliutil.HelpTemplate) + argparser.PersistentFlags().Var(&logLevelFlag, "verbosity", "set the verbosity") + argparser.PersistentFlags().StringArrayVar(&pvsFlag, "pv", nil, "open the file `physical_volume` as part of the filesystem") + if err := argparser.MarkPersistentFlagFilename("pv"); err != nil { + panic(err) + } + if err := argparser.MarkPersistentFlagRequired("pv"); err != nil { + panic(err) + } + + var openFlag int = os.O_RDONLY + + argparserInspect := &cobra.Command{ + Use: "inpsect {[flags]|SUBCOMMAND}", + Short: "Inspect (but don't modify) a broken btrfs filesystem", + + Args: cliutil.WrapPositionalArgs(cliutil.OnlySubcommands), + RunE: cliutil.RunSubcommands, + } + argparser.AddCommand(argparserInspect) + + argparserRepair := &cobra.Command{ + Use: "repair {[flags]|SUBCOMMAND}", + Short: "Repair a broken btrfs filesystem", + + Args: cliutil.WrapPositionalArgs(cliutil.OnlySubcommands), + RunE: cliutil.RunSubcommands, + + PersistentPreRunE: func(_ *cobra.Command, _ []string) error { + openFlag = os.O_RDWR + return nil + }, + } + argparser.AddCommand(argparserRepair) + + for _, cmdgrp := range []struct { + parent *cobra.Command + children []subcommand + }{ + {argparserInspect, inspectors}, + {argparserRepair, repairers}, + } { + for _, child := range cmdgrp.children { + cmd := child.Command + runE := child.RunE + cmd.RunE = func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + logger := logrus.New() + logger.SetLevel(logLevelFlag.Level) + ctx = dlog.WithLogger(ctx, dlog.WrapLogrus(logger)) + + grp := dgroup.NewGroup(ctx, dgroup.GroupConfig{ + EnableSignalHandling: true, + }) + grp.Go("main", func(ctx context.Context) (err error) { + maybeSetErr := func(_err error) { + if _err != nil && err == nil { + err = _err + } + } + fs, err := btrfsutil.Open(openFlag, pvsFlag...) + if err != nil { + return err + } + defer func() { + maybeSetErr(fs.Close()) + }() + + cmd.SetContext(ctx) + return runE(fs, cmd, args) + }) + return grp.Wait() + } + cmdgrp.parent.AddCommand(&cmd) + } + } + + if err := argparser.ExecuteContext(context.Background()); err != nil { + fmt.Fprintf(os.Stderr, "%v: error: %v\n", argparser.CommandPath(), err) + os.Exit(1) + } +} diff --git a/go.mod b/go.mod index d8b9435..06fba4f 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,12 @@ go 1.18 require ( github.com/datawire/dlib v1.3.0 + github.com/datawire/ocibuild v0.0.3-0.20220423003204-fc6a4e9f90dc github.com/hashicorp/golang-lru v0.5.4 github.com/jacobsa/fuse v0.0.0-20220702091825-13117049f383 - github.com/sirupsen/logrus v1.6.0 + github.com/sirupsen/logrus v1.8.1 + github.com/spf13/cobra v1.5.0 + github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.1 golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf golang.org/x/text v0.3.7 @@ -18,11 +21,12 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) replace github.com/jacobsa/fuse => github.com/lukeshu/jacobsa-fuse v0.0.0-20220706162300-f42bfdd0fc53 diff --git a/go.sum b/go.sum index 6257be3..54ec6ae 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,15 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/datawire/dlib v1.3.0 h1:KkmyXU1kwm3oPBk1ypR70YbcOlEXWzEbx5RE0iRXTGk= github.com/datawire/dlib v1.3.0/go.mod h1:NiGDmetmbkBvtznpWSx6C0vA0s0LK9aHna3LJDqjruk= +github.com/datawire/ocibuild v0.0.3-0.20220423003204-fc6a4e9f90dc h1:KxDUPTmAaElhEBYke7Vlw9I/EM/a5zl8LT1yCHkm44I= +github.com/datawire/ocibuild v0.0.3-0.20220423003204-fc6a4e9f90dc/go.mod h1:d10QlP0Pm/xTS9V9UP2AOJ99IlFvmQ4CLP/9bIggczo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -19,8 +23,14 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -31,9 +41,11 @@ golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220526153639-5463443f8c37 h1:lUkvobShwKsOesNfWWlCS5q7fnbG1MEliIzwu886fn8= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= @@ -42,5 +54,7 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -- cgit v1.2.3-2-g168b