| 
									
										
										
										
											2016-08-01 20:04:23 +02:00
										 |  |  | package repository_test | 
					
						
							| 
									
										
										
										
											2016-08-01 18:55:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-06-05 23:56:59 +02:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-08-01 18:55:07 +02:00
										 |  |  | 	"math/rand" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							| 
									
										
										
										
											2020-11-02 12:55:34 +01:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2017-07-23 14:21:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-01 11:40:12 +02:00
										 |  |  | 	"github.com/restic/restic/internal/backend" | 
					
						
							| 
									
										
										
										
											2022-06-12 14:43:43 +02:00
										 |  |  | 	"github.com/restic/restic/internal/index" | 
					
						
							| 
									
										
										
										
											2017-07-23 14:21:03 +02:00
										 |  |  | 	"github.com/restic/restic/internal/repository" | 
					
						
							| 
									
										
										
										
											2017-07-24 17:42:25 +02:00
										 |  |  | 	"github.com/restic/restic/internal/restic" | 
					
						
							| 
									
										
										
										
											2022-07-30 17:33:40 +02:00
										 |  |  | 	rtest "github.com/restic/restic/internal/test" | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	"golang.org/x/sync/errgroup" | 
					
						
							| 
									
										
										
										
											2016-08-01 18:55:07 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | func randomSize(min, max int) int { | 
					
						
							|  |  |  | 	return rand.Intn(max-min) + min | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-14 11:48:40 +02:00
										 |  |  | func createRandomBlobs(t testing.TB, repo restic.Repository, blobs int, pData float32, smallBlobs bool) { | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	var wg errgroup.Group | 
					
						
							|  |  |  | 	repo.StartPackUploader(context.TODO(), &wg) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 	for i := 0; i < blobs; i++ { | 
					
						
							|  |  |  | 		var ( | 
					
						
							| 
									
										
										
										
											2016-08-31 20:58:57 +02:00
										 |  |  | 			tpe    restic.BlobType | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 			length int | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if rand.Float32() < pData { | 
					
						
							| 
									
										
										
										
											2016-08-31 23:07:50 +02:00
										 |  |  | 			tpe = restic.DataBlob | 
					
						
							| 
									
										
										
										
											2024-04-14 11:48:40 +02:00
										 |  |  | 			if smallBlobs { | 
					
						
							|  |  |  | 				length = randomSize(1*1024, 20*1024) // 1KiB to 20KiB of data | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				length = randomSize(10*1024, 1024*1024) // 10KiB to 1MiB of data | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2016-08-31 23:07:50 +02:00
										 |  |  | 			tpe = restic.TreeBlob | 
					
						
							| 
									
										
										
										
											2016-08-04 20:42:11 +02:00
										 |  |  | 			length = randomSize(1*1024, 20*1024) // 1KiB to 20KiB | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-09 09:54:24 +01:00
										 |  |  | 		buf := make([]byte, length) | 
					
						
							|  |  |  | 		rand.Read(buf) | 
					
						
							| 
									
										
										
										
											2016-08-04 20:42:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-01 14:26:57 +02:00
										 |  |  | 		id, exists, _, err := repo.SaveBlob(context.TODO(), tpe, buf, restic.ID{}, false) | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("SaveFrom() error %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-06 22:20:44 +02:00
										 |  |  | 		if exists { | 
					
						
							|  |  |  | 			t.Errorf("duplicate blob %v/%v ignored", id, restic.DataBlob) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 		if rand.Float32() < 0.2 { | 
					
						
							| 
									
										
										
										
											2017-11-22 06:27:29 -05:00
										 |  |  | 			if err = repo.Flush(context.Background()); err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 				t.Fatalf("repo.Flush() returned error %v", err) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 			repo.StartPackUploader(context.TODO(), &wg) | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-22 06:27:29 -05:00
										 |  |  | 	if err := repo.Flush(context.Background()); err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 		t.Fatalf("repo.Flush() returned error %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 21:54:47 +01:00
										 |  |  | func createRandomWrongBlob(t testing.TB, repo restic.Repository) restic.BlobHandle { | 
					
						
							| 
									
										
										
										
											2020-03-31 17:04:48 +02:00
										 |  |  | 	length := randomSize(10*1024, 1024*1024) // 10KiB to 1MiB of data | 
					
						
							|  |  |  | 	buf := make([]byte, length) | 
					
						
							|  |  |  | 	rand.Read(buf) | 
					
						
							|  |  |  | 	id := restic.Hash(buf) | 
					
						
							|  |  |  | 	// invert first data byte | 
					
						
							|  |  |  | 	buf[0] ^= 0xff | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 22:52:05 +02:00
										 |  |  | 	var wg errgroup.Group | 
					
						
							|  |  |  | 	repo.StartPackUploader(context.TODO(), &wg) | 
					
						
							| 
									
										
										
										
											2022-05-01 14:26:57 +02:00
										 |  |  | 	_, _, _, err := repo.SaveBlob(context.TODO(), restic.DataBlob, buf, id, false) | 
					
						
							| 
									
										
										
										
											2020-03-31 17:04:48 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("SaveFrom() error %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := repo.Flush(context.Background()); err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("repo.Flush() returned error %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-20 21:54:47 +01:00
										 |  |  | 	return restic.BlobHandle{ID: id, Type: restic.DataBlob} | 
					
						
							| 
									
										
										
										
											2020-03-31 17:04:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | // selectBlobs splits the list of all blobs randomly into two lists. A blob | 
					
						
							| 
									
										
										
										
											2023-12-06 13:11:55 +01:00
										 |  |  | // will be contained in the firstone with probability p. | 
					
						
							| 
									
										
										
										
											2016-09-03 13:34:04 +02:00
										 |  |  | func selectBlobs(t *testing.T, repo restic.Repository, p float32) (list1, list2 restic.BlobSet) { | 
					
						
							| 
									
										
										
										
											2016-08-31 23:07:50 +02:00
										 |  |  | 	list1 = restic.NewBlobSet() | 
					
						
							|  |  |  | 	list2 = restic.NewBlobSet() | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-31 23:07:50 +02:00
										 |  |  | 	blobs := restic.NewBlobSet() | 
					
						
							| 
									
										
										
										
											2016-08-04 20:42:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-16 11:16:38 +02:00
										 |  |  | 	err := repo.List(context.TODO(), restic.PackFile, func(id restic.ID, size int64) error { | 
					
						
							| 
									
										
										
										
											2018-01-23 21:43:21 -05:00
										 |  |  | 		entries, _, err := repo.ListPack(context.TODO(), id, size) | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("error listing pack %v: %v", id, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for _, entry := range entries { | 
					
						
							| 
									
										
										
										
											2016-08-31 23:07:50 +02:00
										 |  |  | 			h := restic.BlobHandle{ID: entry.ID, Type: entry.Type} | 
					
						
							| 
									
										
										
										
											2016-08-04 20:42:11 +02:00
										 |  |  | 			if blobs.Has(h) { | 
					
						
							|  |  |  | 				t.Errorf("ignoring duplicate blob %v", h) | 
					
						
							| 
									
										
										
										
											2018-01-21 17:25:36 +01:00
										 |  |  | 				return nil | 
					
						
							| 
									
										
										
										
											2016-08-04 20:42:11 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			blobs.Insert(h) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 			if rand.Float32() <= p { | 
					
						
							| 
									
										
										
										
											2016-08-31 23:07:50 +02:00
										 |  |  | 				list1.Insert(restic.BlobHandle{ID: entry.ID, Type: entry.Type}) | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2016-08-31 23:07:50 +02:00
										 |  |  | 				list2.Insert(restic.BlobHandle{ID: entry.ID, Type: entry.Type}) | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-01-21 17:25:36 +01:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return list1, list2 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-19 22:44:50 +01:00
										 |  |  | func listPacks(t *testing.T, repo restic.Lister) restic.IDSet { | 
					
						
							| 
									
										
										
										
											2024-04-14 11:53:08 +02:00
										 |  |  | 	return listFiles(t, repo, restic.PackFile) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func listFiles(t *testing.T, repo restic.Lister, tpe backend.FileType) restic.IDSet { | 
					
						
							| 
									
										
										
										
											2016-08-31 20:29:54 +02:00
										 |  |  | 	list := restic.NewIDSet() | 
					
						
							| 
									
										
										
										
											2024-04-14 11:53:08 +02:00
										 |  |  | 	err := repo.List(context.TODO(), tpe, func(id restic.ID, size int64) error { | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 		list.Insert(id) | 
					
						
							| 
									
										
										
										
											2018-01-21 17:25:36 +01:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return list | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-03 13:34:04 +02:00
										 |  |  | func findPacksForBlobs(t *testing.T, repo restic.Repository, blobs restic.BlobSet) restic.IDSet { | 
					
						
							| 
									
										
										
										
											2016-08-31 20:29:54 +02:00
										 |  |  | 	packs := restic.NewIDSet() | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	idx := repo.Index() | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 	for h := range blobs { | 
					
						
							| 
									
										
										
										
											2020-11-05 22:18:00 +01:00
										 |  |  | 		list := idx.Lookup(h) | 
					
						
							| 
									
										
										
										
											2020-06-14 13:26:10 +02:00
										 |  |  | 		if len(list) == 0 { | 
					
						
							| 
									
										
										
										
											2018-01-12 01:20:12 -05:00
										 |  |  | 			t.Fatal("Failed to find blob", h.ID.Str(), "with type", h.Type) | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 		for _, pb := range list { | 
					
						
							|  |  |  | 			packs.Insert(pb.PackID) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-08-01 20:24:15 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return packs | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-03 13:34:04 +02:00
										 |  |  | func repack(t *testing.T, repo restic.Repository, packs restic.IDSet, blobs restic.BlobSet) { | 
					
						
							| 
									
										
										
										
											2021-09-12 00:03:41 +02:00
										 |  |  | 	repackedBlobs, err := repository.Repack(context.TODO(), repo, repo, packs, blobs, nil) | 
					
						
							| 
									
										
										
										
											2016-08-01 18:55:07 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-15 14:40:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for id := range repackedBlobs { | 
					
						
							| 
									
										
										
										
											2024-05-10 01:16:23 +02:00
										 |  |  | 		err = repo.RemoveUnpacked(context.TODO(), restic.PackFile, id) | 
					
						
							| 
									
										
										
										
											2017-06-15 14:40:34 +02:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatal(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-01 18:55:07 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-03 13:34:04 +02:00
										 |  |  | func rebuildIndex(t *testing.T, repo restic.Repository) { | 
					
						
							| 
									
										
										
										
											2022-06-12 14:43:43 +02:00
										 |  |  | 	err := repo.SetIndex(index.NewMasterIndex()) | 
					
						
							| 
									
										
										
										
											2024-01-20 15:58:06 +01:00
										 |  |  | 	rtest.OK(t, err) | 
					
						
							| 
									
										
										
										
											2020-10-10 22:29:55 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	packs := make(map[restic.ID]int64) | 
					
						
							|  |  |  | 	err = repo.List(context.TODO(), restic.PackFile, func(id restic.ID, size int64) error { | 
					
						
							|  |  |  | 		packs[id] = size | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2024-01-20 15:58:06 +01:00
										 |  |  | 	rtest.OK(t, err) | 
					
						
							| 
									
										
										
										
											2020-10-10 22:29:55 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	_, err = repo.(*repository.Repository).CreateIndexFromPacks(context.TODO(), packs, nil) | 
					
						
							| 
									
										
										
										
											2024-01-20 15:58:06 +01:00
										 |  |  | 	rtest.OK(t, err) | 
					
						
							| 
									
										
										
										
											2017-03-01 13:44:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 15:58:06 +01:00
										 |  |  | 	var obsoleteIndexes restic.IDs | 
					
						
							| 
									
										
										
										
											2018-01-21 17:25:36 +01:00
										 |  |  | 	err = repo.List(context.TODO(), restic.IndexFile, func(id restic.ID, size int64) error { | 
					
						
							| 
									
										
										
										
											2024-01-20 15:58:06 +01:00
										 |  |  | 		obsoleteIndexes = append(obsoleteIndexes, id) | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2018-01-21 17:25:36 +01:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2024-01-20 15:58:06 +01:00
										 |  |  | 	rtest.OK(t, err) | 
					
						
							| 
									
										
										
										
											2017-03-01 13:44:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 15:58:06 +01:00
										 |  |  | 	err = repo.Index().Save(context.TODO(), repo, restic.NewIDSet(), obsoleteIndexes, restic.MasterIndexSaveOpts{}) | 
					
						
							|  |  |  | 	rtest.OK(t, err) | 
					
						
							| 
									
										
										
										
											2016-08-01 18:55:07 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-03 13:34:04 +02:00
										 |  |  | func reloadIndex(t *testing.T, repo restic.Repository) { | 
					
						
							| 
									
										
										
										
											2022-06-12 14:43:43 +02:00
										 |  |  | 	err := repo.SetIndex(index.NewMasterIndex()) | 
					
						
							| 
									
										
										
										
											2021-01-30 16:32:00 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-15 22:48:30 -04:00
										 |  |  | 	if err := repo.LoadIndex(context.TODO(), nil); err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-01 18:55:07 +02:00
										 |  |  | 		t.Fatalf("error loading new index: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestRepack(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-04-29 23:16:16 +02:00
										 |  |  | 	repository.TestAllVersions(t, testRepack) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func testRepack(t *testing.T, version uint) { | 
					
						
							| 
									
										
										
										
											2024-05-10 16:59:09 +02:00
										 |  |  | 	repo, _ := repository.TestRepositoryWithVersion(t, version) | 
					
						
							| 
									
										
										
										
											2016-08-01 18:55:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-02 12:55:34 +01:00
										 |  |  | 	seed := time.Now().UnixNano() | 
					
						
							| 
									
										
										
										
											2018-01-23 21:41:39 +01:00
										 |  |  | 	rand.Seed(seed) | 
					
						
							|  |  |  | 	t.Logf("rand seed is %v", seed) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-14 11:48:40 +02:00
										 |  |  | 	// add a small amount of blobs twice to create multiple pack files | 
					
						
							|  |  |  | 	createRandomBlobs(t, repo, 10, 0.7, false) | 
					
						
							|  |  |  | 	createRandomBlobs(t, repo, 10, 0.7, false) | 
					
						
							| 
									
										
										
										
											2016-08-01 18:55:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	packsBefore := listPacks(t, repo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Running repack on empty ID sets should not do anything at all. | 
					
						
							|  |  |  | 	repack(t, repo, nil, nil) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	packsAfter := listPacks(t, repo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !packsAfter.Equals(packsBefore) { | 
					
						
							|  |  |  | 		t.Fatalf("packs are not equal, Repack modified something. Before:\n  %v\nAfter:\n  %v", | 
					
						
							|  |  |  | 			packsBefore, packsAfter) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	removeBlobs, keepBlobs := selectBlobs(t, repo, 0.2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	removePacks := findPacksForBlobs(t, repo, removeBlobs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	repack(t, repo, removePacks, keepBlobs) | 
					
						
							|  |  |  | 	rebuildIndex(t, repo) | 
					
						
							|  |  |  | 	reloadIndex(t, repo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	packsAfter = listPacks(t, repo) | 
					
						
							|  |  |  | 	for id := range removePacks { | 
					
						
							|  |  |  | 		if packsAfter.Has(id) { | 
					
						
							|  |  |  | 			t.Errorf("pack %v still present although it should have been repacked and removed", id.Str()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	idx := repo.Index() | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for h := range keepBlobs { | 
					
						
							| 
									
										
										
										
											2020-11-05 22:18:00 +01:00
										 |  |  | 		list := idx.Lookup(h) | 
					
						
							| 
									
										
										
										
											2020-06-14 13:26:10 +02:00
										 |  |  | 		if len(list) == 0 { | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 			t.Errorf("unable to find blob %v in repo", h.ID.Str()) | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2016-08-01 18:55:07 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 		if len(list) != 1 { | 
					
						
							|  |  |  | 			t.Errorf("expected one pack in the list, got: %v", list) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		pb := list[0] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 18:55:07 +02:00
										 |  |  | 		if removePacks.Has(pb.PackID) { | 
					
						
							|  |  |  | 			t.Errorf("lookup returned pack ID %v that should've been removed", pb.PackID) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 	for h := range removeBlobs { | 
					
						
							| 
									
										
										
										
											2020-06-14 13:26:10 +02:00
										 |  |  | 		if _, found := repo.LookupBlobSize(h.ID, h.Type); found { | 
					
						
							| 
									
										
										
										
											2016-08-03 22:38:05 +02:00
										 |  |  | 			t.Errorf("blob %v still contained in the repo", h) | 
					
						
							| 
									
										
										
										
											2016-08-01 18:55:07 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-31 17:04:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-12 00:03:41 +02:00
										 |  |  | func TestRepackCopy(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-04-29 23:16:16 +02:00
										 |  |  | 	repository.TestAllVersions(t, testRepackCopy) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-28 11:40:31 +02:00
										 |  |  | type oneConnectionRepo struct { | 
					
						
							|  |  |  | 	restic.Repository | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r oneConnectionRepo) Connections() uint { | 
					
						
							|  |  |  | 	return 1 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-29 23:16:16 +02:00
										 |  |  | func testRepackCopy(t *testing.T, version uint) { | 
					
						
							| 
									
										
										
										
											2024-05-10 16:59:09 +02:00
										 |  |  | 	repo, _ := repository.TestRepositoryWithVersion(t, version) | 
					
						
							|  |  |  | 	dstRepo, _ := repository.TestRepositoryWithVersion(t, version) | 
					
						
							| 
									
										
										
										
											2021-09-12 00:03:41 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-28 11:40:31 +02:00
										 |  |  | 	// test with minimal possible connection count | 
					
						
							|  |  |  | 	repoWrapped := &oneConnectionRepo{repo} | 
					
						
							|  |  |  | 	dstRepoWrapped := &oneConnectionRepo{dstRepo} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-12 00:03:41 +02:00
										 |  |  | 	seed := time.Now().UnixNano() | 
					
						
							|  |  |  | 	rand.Seed(seed) | 
					
						
							|  |  |  | 	t.Logf("rand seed is %v", seed) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-14 11:48:40 +02:00
										 |  |  | 	// add a small amount of blobs twice to create multiple pack files | 
					
						
							|  |  |  | 	createRandomBlobs(t, repo, 10, 0.7, false) | 
					
						
							|  |  |  | 	createRandomBlobs(t, repo, 10, 0.7, false) | 
					
						
							| 
									
										
										
										
											2021-09-12 00:03:41 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	_, keepBlobs := selectBlobs(t, repo, 0.2) | 
					
						
							|  |  |  | 	copyPacks := findPacksForBlobs(t, repo, keepBlobs) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-28 11:40:31 +02:00
										 |  |  | 	_, err := repository.Repack(context.TODO(), repoWrapped, dstRepoWrapped, copyPacks, keepBlobs, nil) | 
					
						
							| 
									
										
										
										
											2021-09-12 00:03:41 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	rebuildIndex(t, dstRepo) | 
					
						
							|  |  |  | 	reloadIndex(t, dstRepo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	idx := dstRepo.Index() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for h := range keepBlobs { | 
					
						
							|  |  |  | 		list := idx.Lookup(h) | 
					
						
							|  |  |  | 		if len(list) == 0 { | 
					
						
							|  |  |  | 			t.Errorf("unable to find blob %v in repo", h.ID.Str()) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if len(list) != 1 { | 
					
						
							|  |  |  | 			t.Errorf("expected one pack in the list, got: %v", list) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-31 17:04:48 +02:00
										 |  |  | func TestRepackWrongBlob(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-04-29 23:16:16 +02:00
										 |  |  | 	repository.TestAllVersions(t, testRepackWrongBlob) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func testRepackWrongBlob(t *testing.T, version uint) { | 
					
						
							| 
									
										
										
										
											2024-02-03 17:47:48 +01:00
										 |  |  | 	// disable verification to allow adding corrupted blobs to the repository | 
					
						
							| 
									
										
										
										
											2024-05-10 16:59:09 +02:00
										 |  |  | 	repo, _ := repository.TestRepositoryWithBackend(t, nil, version, repository.Options{NoExtraVerify: true}) | 
					
						
							| 
									
										
										
										
											2020-03-31 17:04:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-02 12:55:34 +01:00
										 |  |  | 	seed := time.Now().UnixNano() | 
					
						
							| 
									
										
										
										
											2020-03-31 17:04:48 +02:00
										 |  |  | 	rand.Seed(seed) | 
					
						
							|  |  |  | 	t.Logf("rand seed is %v", seed) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-14 11:48:40 +02:00
										 |  |  | 	createRandomBlobs(t, repo, 5, 0.7, false) | 
					
						
							| 
									
										
										
										
											2020-03-31 17:04:48 +02:00
										 |  |  | 	createRandomWrongBlob(t, repo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// just keep all blobs, but also rewrite every pack | 
					
						
							|  |  |  | 	_, keepBlobs := selectBlobs(t, repo, 0) | 
					
						
							|  |  |  | 	rewritePacks := findPacksForBlobs(t, repo, keepBlobs) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-12 00:03:41 +02:00
										 |  |  | 	_, err := repository.Repack(context.TODO(), repo, repo, rewritePacks, keepBlobs, nil) | 
					
						
							| 
									
										
										
										
											2020-03-31 17:04:48 +02:00
										 |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		t.Fatal("expected repack to fail but got no error") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-02 12:53:45 +01:00
										 |  |  | 	t.Logf("found expected error: %v", err) | 
					
						
							| 
									
										
										
										
											2020-03-31 17:04:48 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-07-30 17:33:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestRepackBlobFallback(t *testing.T) { | 
					
						
							|  |  |  | 	repository.TestAllVersions(t, testRepackBlobFallback) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func testRepackBlobFallback(t *testing.T, version uint) { | 
					
						
							| 
									
										
										
										
											2024-02-03 17:47:48 +01:00
										 |  |  | 	// disable verification to allow adding corrupted blobs to the repository | 
					
						
							| 
									
										
										
										
											2024-05-10 16:59:09 +02:00
										 |  |  | 	repo, _ := repository.TestRepositoryWithBackend(t, nil, version, repository.Options{NoExtraVerify: true}) | 
					
						
							| 
									
										
										
										
											2022-07-30 17:33:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	seed := time.Now().UnixNano() | 
					
						
							|  |  |  | 	rand.Seed(seed) | 
					
						
							|  |  |  | 	t.Logf("rand seed is %v", seed) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	length := randomSize(10*1024, 1024*1024) // 10KiB to 1MiB of data | 
					
						
							|  |  |  | 	buf := make([]byte, length) | 
					
						
							|  |  |  | 	rand.Read(buf) | 
					
						
							|  |  |  | 	id := restic.Hash(buf) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// corrupted copy | 
					
						
							|  |  |  | 	modbuf := make([]byte, len(buf)) | 
					
						
							|  |  |  | 	copy(modbuf, buf) | 
					
						
							|  |  |  | 	// invert first data byte | 
					
						
							|  |  |  | 	modbuf[0] ^= 0xff | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// create pack with broken copy | 
					
						
							|  |  |  | 	var wg errgroup.Group | 
					
						
							|  |  |  | 	repo.StartPackUploader(context.TODO(), &wg) | 
					
						
							|  |  |  | 	_, _, _, err := repo.SaveBlob(context.TODO(), restic.DataBlob, modbuf, id, false) | 
					
						
							|  |  |  | 	rtest.OK(t, err) | 
					
						
							|  |  |  | 	rtest.OK(t, repo.Flush(context.Background())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// find pack with damaged blob | 
					
						
							|  |  |  | 	keepBlobs := restic.NewBlobSet(restic.BlobHandle{Type: restic.DataBlob, ID: id}) | 
					
						
							|  |  |  | 	rewritePacks := findPacksForBlobs(t, repo, keepBlobs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// create pack with valid copy | 
					
						
							|  |  |  | 	repo.StartPackUploader(context.TODO(), &wg) | 
					
						
							|  |  |  | 	_, _, _, err = repo.SaveBlob(context.TODO(), restic.DataBlob, buf, id, true) | 
					
						
							|  |  |  | 	rtest.OK(t, err) | 
					
						
							|  |  |  | 	rtest.OK(t, repo.Flush(context.Background())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// repack must fallback to valid copy | 
					
						
							|  |  |  | 	_, err = repository.Repack(context.TODO(), repo, repo, rewritePacks, keepBlobs, nil) | 
					
						
							|  |  |  | 	rtest.OK(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	keepBlobs = restic.NewBlobSet(restic.BlobHandle{Type: restic.DataBlob, ID: id}) | 
					
						
							|  |  |  | 	packs := findPacksForBlobs(t, repo, keepBlobs) | 
					
						
							|  |  |  | 	rtest.Assert(t, len(packs) == 3, "unexpected number of copies: %v", len(packs)) | 
					
						
							|  |  |  | } |