diff --git a/cmd/restic/cmd_debug.go b/cmd/restic/cmd_debug.go index 0a0b43295..efc022941 100644 --- a/cmd/restic/cmd_debug.go +++ b/cmd/restic/cmd_debug.go @@ -7,7 +7,6 @@ import ( "encoding/json" "fmt" "io" - "sync" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -18,7 +17,6 @@ import ( "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" - "github.com/restic/restic/internal/ui/progress" ) func registerDebugCommand(cmd *cobra.Command, globalOptions *global.Options) { @@ -118,50 +116,6 @@ func debugPrintSnapshots(ctx context.Context, repo *repository.Repository, wr io }) } -// 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, printer progress.Printer) error { - - var m sync.Mutex - return restic.ParallelList(ctx, repo, restic.PackFile, repo.Connections(), func(ctx context.Context, id restic.ID, size int64) error { - blobs, err := repo.ListPack(ctx, id, size) - if err != nil { - printer.E("error for pack %v: %v", 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, - } - } - - m.Lock() - defer m.Unlock() - return prettyPrintJSON(wr, p) - }) -} - func runDebugDump(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error { printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) @@ -183,7 +137,7 @@ func runDebugDump(ctx context.Context, gopts global.Options, args []string, term case "snapshots": return debugPrintSnapshots(ctx, repo, gopts.Term.OutputWriter()) case "packs": - return printPacks(ctx, repo, gopts.Term.OutputWriter(), printer) + return repository.DumpPacks(ctx, repo, gopts.Term.OutputWriter(), printer) case "all": printer.S("snapshots:") err := debugPrintSnapshots(ctx, repo, gopts.Term.OutputWriter()) diff --git a/internal/repository/debug.go b/internal/repository/debug.go index fd6533a82..c3d5bfcd5 100644 --- a/internal/repository/debug.go +++ b/internal/repository/debug.go @@ -6,10 +6,12 @@ import ( "context" "crypto/aes" "crypto/cipher" + "encoding/json" "fmt" "io" "os" "runtime" + "sync" "time" "github.com/klauspost/compress/zstd" @@ -22,6 +24,56 @@ import ( "github.com/restic/restic/internal/ui/progress" ) +type packDumpEntry struct { + Name string `json:"name"` + Blobs []packDumpBlob `json:"blobs"` +} + +type packDumpBlob struct { + Type restic.BlobType `json:"type"` + Length uint `json:"length"` + ID restic.ID `json:"id"` + Offset uint `json:"offset"` +} + +func writePackDumpJSON(wr io.Writer, item any) error { + buf, err := json.MarshalIndent(item, "", " ") + if err != nil { + return err + } + _, err = wr.Write(append(buf, '\n')) + return err +} + +// DumpPacks lists each pack file and writes its header blob layout as JSON to wr. +func DumpPacks(ctx context.Context, repo *Repository, wr io.Writer, printer progress.Printer) error { + var m sync.Mutex + return restic.ParallelList(ctx, repo, restic.PackFile, repo.Connections(), func(ctx context.Context, id restic.ID, size int64) error { + blobs, err := repo.ListPack(ctx, id, size) + if err != nil { + printer.E("error for pack %v: %v", id.Str(), err) + return nil + } + + p := packDumpEntry{ + Name: id.String(), + Blobs: make([]packDumpBlob, len(blobs)), + } + for i, blob := range blobs { + p.Blobs[i] = packDumpBlob{ + Type: blob.Type, + Length: blob.Length, + ID: blob.ID, + Offset: blob.Offset, + } + } + + m.Lock() + defer m.Unlock() + return writePackDumpJSON(wr, p) + }) +} + // DumpIndexes loads each on-disk index file and writes its debug dump to wr. func DumpIndexes(ctx context.Context, repo restic.ListerLoaderUnpacked, wr io.Writer, printer progress.Printer) error { return index.ForAllIndexes(ctx, repo, repo, func(id restic.ID, idx *index.Index, err error) error {