func packIDTester(repo restic.Repository, inChan <-chan restic.ID, errChan chan<- error, wg *sync.WaitGroup, done <-chan struct{}) { debug.Log("worker start") defer debug.Log("worker done") defer wg.Done() for id := range inChan { ok, err := repo.Backend().Test(restic.DataFile, id.String()) if err != nil { err = PackError{ID: id, Err: err} } else { if !ok { err = PackError{ID: id, Err: errors.New("does not exist")} } } if err != nil { debug.Log("error checking for pack %s: %v", id.Str(), err) select { case <-done: return case errChan <- err: } continue } debug.Log("pack %s exists", id.Str()) } }
func countPacks(repo restic.Repository, t restic.FileType) (n uint) { for _ = range repo.Backend().List(t, nil) { n++ } return n }
// RebuildIndex lists all packs in the repo, writes a new index and removes all // old indexes. This operation should only be done with an exclusive lock in // place. func RebuildIndex(repo restic.Repository) error { debug.Log("start rebuilding index") done := make(chan struct{}) defer close(done) ch := make(chan worker.Job) go list.AllPacks(repo, ch, done) idx := NewIndex() for job := range ch { id := job.Data.(restic.ID) if job.Error != nil { fmt.Fprintf(os.Stderr, "error for pack %v: %v\n", id, job.Error) continue } res := job.Result.(list.Result) for _, entry := range res.Entries() { pb := restic.PackedBlob{ Blob: entry, PackID: res.PackID(), } idx.Store(pb) } } oldIndexes := restic.NewIDSet() for id := range repo.List(restic.IndexFile, done) { idx.AddToSupersedes(id) oldIndexes.Insert(id) } id, err := SaveIndex(repo, idx) if err != nil { debug.Log("error saving index: %v", err) return err } debug.Log("new index saved as %v", id.Str()) for indexID := range oldIndexes { err := repo.Backend().Remove(restic.IndexFile, indexID.String()) if err != nil { fmt.Fprintf(os.Stderr, "unable to remove index %v: %v\n", indexID.Str(), err) } } return nil }
// checkPack reads a pack and checks the integrity of all blobs. func checkPack(r restic.Repository, id restic.ID) error { debug.Log("checking pack %v", id.Str()) h := restic.Handle{Type: restic.DataFile, Name: id.String()} buf, err := backend.LoadAll(r.Backend(), h, nil) if err != nil { return err } hash := restic.Hash(buf) if !hash.Equal(id) { debug.Log("Pack ID does not match, want %v, got %v", id.Str(), hash.Str()) return errors.Errorf("Pack ID does not match, want %v, got %v", id.Str(), hash.Str()) } blobs, err := pack.List(r.Key(), bytes.NewReader(buf), int64(len(buf))) if err != nil { return err } var errs []error for i, blob := range blobs { debug.Log(" check blob %d: %v", i, blob.ID.Str()) plainBuf := make([]byte, blob.Length) n, err := crypto.Decrypt(r.Key(), plainBuf, buf[blob.Offset:blob.Offset+blob.Length]) if err != nil { debug.Log(" error decrypting blob %v: %v", blob.ID.Str(), err) errs = append(errs, errors.Errorf("blob %v: %v", i, err)) continue } plainBuf = plainBuf[:n] hash := restic.Hash(plainBuf) if !hash.Equal(blob.ID) { debug.Log(" Blob ID does not match, want %v, got %v", blob.ID.Str(), hash.Str()) errs = append(errs, errors.Errorf("Blob ID does not match, want %v, got %v", blob.ID.Str(), hash.Str())) continue } } if len(errs) > 0 { return errors.Errorf("pack %v contains %v errors: %v", id.Str(), len(errs), errs) } return nil }
// loadSnapshotTreeIDs loads all snapshots from backend and returns the tree IDs. func loadSnapshotTreeIDs(repo restic.Repository) (restic.IDs, []error) { var trees struct { IDs restic.IDs sync.Mutex } var errs struct { errs []error sync.Mutex } snapshotWorker := func(strID string, done <-chan struct{}) error { id, err := restic.ParseID(strID) if err != nil { return err } debug.Log("load snapshot %v", id.Str()) treeID, err := loadTreeFromSnapshot(repo, id) if err != nil { errs.Lock() errs.errs = append(errs.errs, err) errs.Unlock() return nil } debug.Log("snapshot %v has tree %v", id.Str(), treeID.Str()) trees.Lock() trees.IDs = append(trees.IDs, treeID) trees.Unlock() return nil } err := repository.FilesInParallel(repo.Backend(), restic.SnapshotFile, defaultParallelism, snapshotWorker) if err != nil { errs.errs = append(errs.errs, err) } return trees.IDs, errs.errs }
// Repack takes a list of packs together with a list of blobs contained in // these packs. Each pack is loaded and the blobs listed in keepBlobs is saved // into a new pack. Afterwards, the packs are removed. This operation requires // an exclusive lock on the repo. func Repack(repo restic.Repository, packs restic.IDSet, keepBlobs restic.BlobSet) (err error) { debug.Log("repacking %d packs while keeping %d blobs", len(packs), len(keepBlobs)) buf := make([]byte, 0, maxPackSize) for packID := range packs { // load the complete pack h := restic.Handle{Type: restic.DataFile, Name: packID.String()} l, err := repo.Backend().Load(h, buf[:cap(buf)], 0) if errors.Cause(err) == io.ErrUnexpectedEOF { err = nil buf = buf[:l] } if err != nil { return err } debug.Log("pack %v loaded (%d bytes)", packID.Str(), len(buf)) blobs, err := pack.List(repo.Key(), bytes.NewReader(buf), int64(len(buf))) if err != nil { return err } debug.Log("processing pack %v, blobs: %v", packID.Str(), len(blobs)) var plaintext []byte for _, entry := range blobs { h := restic.BlobHandle{ID: entry.ID, Type: entry.Type} if !keepBlobs.Has(h) { continue } debug.Log(" process blob %v", h) ciphertext := buf[entry.Offset : entry.Offset+entry.Length] plaintext = plaintext[:len(plaintext)] if len(plaintext) < len(ciphertext) { plaintext = make([]byte, len(ciphertext)) } debug.Log(" ciphertext %d, plaintext %d", len(plaintext), len(ciphertext)) n, err := crypto.Decrypt(repo.Key(), plaintext, ciphertext) if err != nil { return err } plaintext = plaintext[:n] _, err = repo.SaveBlob(entry.Type, plaintext, entry.ID) if err != nil { return err } debug.Log(" saved blob %v", entry.ID.Str()) keepBlobs.Delete(h) } } if err := repo.Flush(); err != nil { return err } for packID := range packs { err := repo.Backend().Remove(restic.DataFile, packID.String()) if err != nil { debug.Log("error removing pack %v: %v", packID.Str(), err) return err } debug.Log("removed pack %v", packID.Str()) } return nil }
func lockExists(repo restic.Repository, t testing.TB, id restic.ID) bool { exists, err := repo.Backend().Test(restic.LockFile, id.String()) OK(t, err) return exists }
func removeLock(repo restic.Repository, id restic.ID) error { return repo.Backend().Remove(restic.LockFile, id.String()) }