mirror of
https://github.com/restic/restic.git
synced 2025-03-09 00:00:02 +01:00
check: enable --read-data-* for specified snapshots - rebase step 1
Add code to cmd/restic/cmd_check.go to detect snapshots Resolved conflict for cmd/restic/cmd_check.go - runCheck integrated newCheckCommand(...)
This commit is contained in:
parent
5ddda7f5e9
commit
6f77d4ddf8
3 changed files with 74 additions and 5 deletions
8
changelog/unreleased/issue-3326
Normal file
8
changelog/unreleased/issue-3326
Normal file
|
@ -0,0 +1,8 @@
|
|||
check: enable --read-data-subset and --read-data for specified snapshot(s)
|
||||
|
||||
When snapshots are specified on the command line, the metadata for these
|
||||
snapshots will be read and a set of packfiles will be created representing the data
|
||||
parts of these snapshots.
|
||||
|
||||
https://github.com/restic/restic/issues/3326
|
||||
https://github.com/restic/restic/pull/5213
|
|
@ -73,6 +73,7 @@ type CheckOptions struct {
|
|||
ReadDataSubset string
|
||||
CheckUnused bool
|
||||
WithCache bool
|
||||
restic.SnapshotFilter
|
||||
}
|
||||
|
||||
func (opts *CheckOptions) AddFlags(f *pflag.FlagSet) {
|
||||
|
@ -86,6 +87,7 @@ func (opts *CheckOptions) AddFlags(f *pflag.FlagSet) {
|
|||
panic(err)
|
||||
}
|
||||
f.BoolVar(&opts.WithCache, "with-cache", false, "use existing cache, only read uncached data from repository")
|
||||
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
|
||||
}
|
||||
|
||||
func checkFlags(opts CheckOptions) error {
|
||||
|
@ -222,9 +224,6 @@ func prepareCheckCache(opts CheckOptions, gopts *GlobalOptions, printer progress
|
|||
|
||||
func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args []string, term *termstatus.Terminal) (checkSummary, error) {
|
||||
summary := checkSummary{MessageType: "summary"}
|
||||
if len(args) != 0 {
|
||||
return summary, errors.Fatal("the check command expects no arguments, only options - please see `restic help check` for usage and flags")
|
||||
}
|
||||
|
||||
var printer progress.Printer
|
||||
if !gopts.JSON {
|
||||
|
@ -258,6 +257,21 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
|||
return summary, ctx.Err()
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
snapshotLister, err := restic.MemorizeList(ctx, repo, restic.SnapshotFile)
|
||||
if err != nil {
|
||||
return summary, err
|
||||
}
|
||||
|
||||
// run down the tree, take note of the data packfiles involved
|
||||
for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, &opts.SnapshotFilter, args) {
|
||||
err := chkr.FindDataPackfiles(ctx, repo, sn)
|
||||
if err != nil {
|
||||
return summary, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errorsFound := false
|
||||
for _, hint := range hints {
|
||||
switch hint.(type) {
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/restic/restic/internal/repository/pack"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/ui/progress"
|
||||
"github.com/restic/restic/internal/walker"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
|
@ -29,6 +30,7 @@ type Checker struct {
|
|||
sync.Mutex
|
||||
M restic.BlobSet
|
||||
}
|
||||
packSet restic.IDSet
|
||||
trackUnused bool
|
||||
|
||||
masterIndex *index.MasterIndex
|
||||
|
@ -41,6 +43,7 @@ type Checker struct {
|
|||
func New(repo restic.Repository, trackUnused bool) *Checker {
|
||||
c := &Checker{
|
||||
packs: make(map[restic.ID]int64),
|
||||
packSet: restic.NewIDSet(),
|
||||
masterIndex: index.NewMasterIndex(),
|
||||
repo: repo,
|
||||
trackUnused: trackUnused,
|
||||
|
@ -431,12 +434,24 @@ func (c *Checker) UnusedBlobs(ctx context.Context) (blobs restic.BlobHandles, er
|
|||
|
||||
// CountPacks returns the number of packs in the repository.
|
||||
func (c *Checker) CountPacks() uint64 {
|
||||
return uint64(len(c.packs))
|
||||
if len(c.packSet) == 0 {
|
||||
return uint64(len(c.packs))
|
||||
} else {
|
||||
return uint64(len(c.packSet))
|
||||
}
|
||||
}
|
||||
|
||||
// GetPacks returns IDSet of packs in the repository
|
||||
func (c *Checker) GetPacks() map[restic.ID]int64 {
|
||||
return c.packs
|
||||
if len(c.packSet) == 0 {
|
||||
return c.packs
|
||||
} else {
|
||||
result := map[restic.ID]int64{}
|
||||
for packID := range c.packSet {
|
||||
result[packID] = c.packs[packID]
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// ReadData loads all data from the repository and checks the integrity.
|
||||
|
@ -522,3 +537,35 @@ func (c *Checker) ReadPacks(ctx context.Context, packs map[restic.ID]int64, p *p
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find data packfiles for checking repository based on snapshots
|
||||
func (c *Checker) FindDataPackfiles(ctx context.Context, repo *repository.Repository, sn *restic.Snapshot) error {
|
||||
err := walker.Walk(ctx, repo, *sn.Tree, walker.WalkVisitor{ProcessNode: func(parentTreeID restic.ID, _ string, node *restic.Node, err error) error {
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to load tree %s\n ... which belongs to snapshot %s - reason %v\n", parentTreeID, sn.ID, err)
|
||||
return walker.ErrSkipNode
|
||||
}
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.Type == restic.NodeTypeFile {
|
||||
for _, content := range node.Content {
|
||||
result := repo.LookupBlob(restic.DataBlob, content)
|
||||
if len(result) == 0 {
|
||||
panic("checker.FindDataPackfiles: datablob not mapped!")
|
||||
} else if len(result) > 1 {
|
||||
panic("checker.FindDataPackfiles: datablob found several times!")
|
||||
}
|
||||
c.packSet.Insert(result[0].PackID)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}})
|
||||
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("walker.Walk does not want to walk - reason %v\n", err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue