mirror of
https://github.com/restic/restic.git
synced 2025-03-13 10:20:52 +00:00

Those methods now only allow modifying snapshots. Internal data types used by the repository are now read-only. The repository-internal code can bypass the restrictions by wrapping the repository in an `internalRepository` type. The restriction itself is implemented by using a new datatype WriteableFileType in the SaveUnpacked and RemoveUnpacked methods. This statically ensures that code cannot bypass the access restrictions. The test changes are somewhat noisy as some of them modify repository internals and therefore require some way to bypass the access restrictions. This works by capturing an `internalRepository` or `Backend` when creating the Repository using a test helper function.
73 lines
1.8 KiB
Go
73 lines
1.8 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
|
|
"github.com/restic/restic/internal/restic"
|
|
"github.com/restic/restic/internal/ui/progress"
|
|
"golang.org/x/sync/errgroup"
|
|
)
|
|
|
|
func RepairPacks(ctx context.Context, repo *Repository, ids restic.IDSet, printer progress.Printer) error {
|
|
wg, wgCtx := errgroup.WithContext(ctx)
|
|
repo.StartPackUploader(wgCtx, wg)
|
|
|
|
printer.P("salvaging intact data from specified pack files")
|
|
bar := printer.NewCounter("pack files")
|
|
bar.SetMax(uint64(len(ids)))
|
|
defer bar.Done()
|
|
|
|
wg.Go(func() error {
|
|
// examine all data the indexes have for the pack file
|
|
for b := range repo.ListPacksFromIndex(wgCtx, ids) {
|
|
blobs := b.Blobs
|
|
if len(blobs) == 0 {
|
|
printer.E("no blobs found for pack %v", b.PackID)
|
|
bar.Add(1)
|
|
continue
|
|
}
|
|
|
|
err := repo.LoadBlobsFromPack(wgCtx, b.PackID, blobs, func(blob restic.BlobHandle, buf []byte, err error) error {
|
|
if err != nil {
|
|
printer.E("failed to load blob %v: %v", blob.ID, err)
|
|
return nil
|
|
}
|
|
id, _, _, err := repo.SaveBlob(wgCtx, blob.Type, buf, restic.ID{}, true)
|
|
if !id.Equal(blob.ID) {
|
|
panic("pack id mismatch during upload")
|
|
}
|
|
return err
|
|
})
|
|
// ignore truncated file parts
|
|
if err != nil && !errors.Is(err, io.ErrUnexpectedEOF) {
|
|
return err
|
|
}
|
|
bar.Add(1)
|
|
}
|
|
return repo.Flush(wgCtx)
|
|
})
|
|
|
|
err := wg.Wait()
|
|
bar.Done()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// remove salvaged packs from index
|
|
err = rewriteIndexFiles(ctx, repo, ids, nil, nil, printer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// cleanup
|
|
printer.P("removing salvaged pack files")
|
|
// if we fail to delete the damaged pack files, then prune will remove them later on
|
|
bar = printer.NewCounter("files deleted")
|
|
_ = restic.ParallelRemove(ctx, &internalRepository{repo}, ids, restic.PackFile, nil, bar)
|
|
bar.Done()
|
|
|
|
return nil
|
|
}
|