Files
restic/cmd/restic/cmd_repair_packs.go
Michael Eischer a816b827cf extract GlobalOptions into internal/global package
Rough steps:
```
mv cmd/restic/global* cmd/restic/secondary_repo* internal/global/
sed -i "s/package main/package global/" internal/global/*.go
Rename "GlobalOptions" to "Options" in internal/global/
Replace everywhere " GlobalOptions" -> " global.Options"
Replace everywhere "\*GlobalOptions" -> " *global.Options"
Make SecondaryRepoOptions public
Make create public
Make version public
```
2025-10-12 17:56:28 +02:00

97 lines
2.4 KiB
Go

package main
import (
"bytes"
"context"
"io"
"os"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
"github.com/spf13/cobra"
)
func newRepairPacksCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "packs [packIDs...]",
Short: "Salvage damaged pack files",
Long: `
The "repair packs" command extracts intact blobs from the specified pack files, rebuilds
the index to remove the damaged pack files and removes the pack files from the repository.
EXIT STATUS
===========
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runRepairPacks(cmd.Context(), *globalOptions, globalOptions.Term, args)
},
}
return cmd
}
func runRepairPacks(ctx context.Context, gopts global.Options, term ui.Terminal, args []string) error {
ids := restic.NewIDSet()
for _, arg := range args {
id, err := restic.ParseID(arg)
if err != nil {
return err
}
ids.Insert(id)
}
if len(ids) == 0 {
return errors.Fatal("no ids specified")
}
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
if err != nil {
return err
}
defer unlock()
err = repo.LoadIndex(ctx, printer)
if err != nil {
return errors.Fatalf("%s", err)
}
printer.P("saving backup copies of pack files to current folder")
for id := range ids {
buf, err := repo.LoadRaw(ctx, restic.PackFile, id)
// corrupted data is fine
if buf == nil {
return err
}
f, err := os.OpenFile("pack-"+id.String(), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o666)
if err != nil {
return err
}
if _, err := io.Copy(f, bytes.NewReader(buf)); err != nil {
_ = f.Close()
return err
}
if err := f.Close(); err != nil {
return err
}
}
err = repository.RepairPacks(ctx, repo, ids, printer)
if err != nil {
return errors.Fatalf("%s", err)
}
printer.E("\nUse `restic repair snapshots --forget` to remove the corrupted data blobs from all snapshots")
return nil
}