mirror of
https://github.com/restic/restic.git
synced 2024-12-23 08:16:36 +00:00
e638b46a13
The io.Reader interface does not support contexts, such that it is necessary to embed the context into the backendReaderAt struct. This has the problem that a reader might suddenly stop working when it's contained context is canceled. However, this is now problem here as the reader instances never escape the calling function.
170 lines
3.6 KiB
Go
170 lines
3.6 KiB
Go
// +build debug
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/restic/restic/internal/errors"
|
|
"github.com/restic/restic/internal/pack"
|
|
"github.com/restic/restic/internal/repository"
|
|
"github.com/restic/restic/internal/restic"
|
|
)
|
|
|
|
var cmdDebug = &cobra.Command{
|
|
Use: "debug",
|
|
Short: "Debug commands",
|
|
}
|
|
|
|
var cmdDebugDump = &cobra.Command{
|
|
Use: "dump [indexes|snapshots|all|packs]",
|
|
Short: "Dump data structures",
|
|
Long: `
|
|
The "dump" command dumps data structures from the repository as JSON objects. It
|
|
is used for debugging purposes only.
|
|
|
|
EXIT STATUS
|
|
===========
|
|
|
|
Exit status is 0 if the command was successful, and non-zero if there was any error.
|
|
`,
|
|
DisableAutoGenTag: true,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return runDebugDump(globalOptions, args)
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
cmdRoot.AddCommand(cmdDebug)
|
|
cmdDebug.AddCommand(cmdDebugDump)
|
|
}
|
|
|
|
func prettyPrintJSON(wr io.Writer, item interface{}) error {
|
|
buf, err := json.MarshalIndent(item, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = wr.Write(append(buf, '\n'))
|
|
return err
|
|
}
|
|
|
|
func debugPrintSnapshots(ctx context.Context, repo *repository.Repository, wr io.Writer) error {
|
|
return repo.List(ctx, restic.SnapshotFile, func(id restic.ID, size int64) error {
|
|
snapshot, err := restic.LoadSnapshot(ctx, repo, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Fprintf(wr, "snapshot_id: %v\n", id)
|
|
|
|
return prettyPrintJSON(wr, snapshot)
|
|
})
|
|
}
|
|
|
|
// Pack is the struct used in printPacks.
|
|
type Pack struct {
|
|
Name string `json:"name"`
|
|
|
|
Blobs []Blob `json:"blobs"`
|
|
}
|
|
|
|
// Blob is the struct used in printPacks.
|
|
type Blob struct {
|
|
Type restic.BlobType `json:"type"`
|
|
Length uint `json:"length"`
|
|
ID restic.ID `json:"id"`
|
|
Offset uint `json:"offset"`
|
|
}
|
|
|
|
func printPacks(ctx context.Context, repo *repository.Repository, wr io.Writer) error {
|
|
|
|
return repo.List(ctx, restic.PackFile, func(id restic.ID, size int64) error {
|
|
h := restic.Handle{Type: restic.PackFile, Name: id.String()}
|
|
|
|
blobs, err := pack.List(repo.Key(), restic.ReaderAt(ctx, repo.Backend(), h), size)
|
|
if err != nil {
|
|
Warnf("error for pack %v: %v\n", id.Str(), err)
|
|
return nil
|
|
}
|
|
|
|
p := Pack{
|
|
Name: id.String(),
|
|
Blobs: make([]Blob, len(blobs)),
|
|
}
|
|
for i, blob := range blobs {
|
|
p.Blobs[i] = Blob{
|
|
Type: blob.Type,
|
|
Length: blob.Length,
|
|
ID: blob.ID,
|
|
Offset: blob.Offset,
|
|
}
|
|
}
|
|
|
|
return prettyPrintJSON(wr, p)
|
|
})
|
|
}
|
|
|
|
func dumpIndexes(ctx context.Context, repo restic.Repository, wr io.Writer) error {
|
|
return repo.List(ctx, restic.IndexFile, func(id restic.ID, size int64) error {
|
|
Printf("index_id: %v\n", id)
|
|
|
|
idx, err := repository.LoadIndex(ctx, repo, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return idx.Dump(wr)
|
|
})
|
|
}
|
|
|
|
func runDebugDump(gopts GlobalOptions, args []string) error {
|
|
if len(args) != 1 {
|
|
return errors.Fatal("type not specified")
|
|
}
|
|
|
|
repo, err := OpenRepository(gopts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !gopts.NoLock {
|
|
lock, err := lockRepo(gopts.ctx, repo)
|
|
defer unlockRepo(lock)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
tpe := args[0]
|
|
|
|
switch tpe {
|
|
case "indexes":
|
|
return dumpIndexes(gopts.ctx, repo, gopts.stdout)
|
|
case "snapshots":
|
|
return debugPrintSnapshots(gopts.ctx, repo, gopts.stdout)
|
|
case "packs":
|
|
return printPacks(gopts.ctx, repo, gopts.stdout)
|
|
case "all":
|
|
Printf("snapshots:\n")
|
|
err := debugPrintSnapshots(gopts.ctx, repo, gopts.stdout)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
Printf("\nindexes:\n")
|
|
err = dumpIndexes(gopts.ctx, repo, gopts.stdout)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
default:
|
|
return errors.Fatalf("no such type %q", tpe)
|
|
}
|
|
}
|