mirror of
https://github.com/restic/restic.git
synced 2026-09-18 18:27:58 +00:00
repository: restrict SaveUnpacked and RemoveUnpacked
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.
This commit is contained in:
@@ -351,7 +351,7 @@ func (idx *Index) Encode(w io.Writer) error {
|
||||
}
|
||||
|
||||
// SaveIndex saves an index in the repository.
|
||||
func (idx *Index) SaveIndex(ctx context.Context, repo restic.SaverUnpacked) (restic.ID, error) {
|
||||
func (idx *Index) SaveIndex(ctx context.Context, repo restic.SaverUnpacked[restic.FileType]) (restic.ID, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
err := idx.Encode(buf)
|
||||
|
||||
@@ -321,7 +321,7 @@ type MasterIndexRewriteOpts struct {
|
||||
// This is used by repair index to only rewrite and delete the old indexes.
|
||||
//
|
||||
// Must not be called concurrently to any other MasterIndex operation.
|
||||
func (mi *MasterIndex) Rewrite(ctx context.Context, repo restic.Unpacked, excludePacks restic.IDSet, oldIndexes restic.IDSet, extraObsolete restic.IDs, opts MasterIndexRewriteOpts) error {
|
||||
func (mi *MasterIndex) Rewrite(ctx context.Context, repo restic.Unpacked[restic.FileType], excludePacks restic.IDSet, oldIndexes restic.IDSet, extraObsolete restic.IDs, opts MasterIndexRewriteOpts) error {
|
||||
for _, idx := range mi.idx {
|
||||
if !idx.Final() {
|
||||
panic("internal error - index must be saved before calling MasterIndex.Rewrite")
|
||||
@@ -499,7 +499,7 @@ func (mi *MasterIndex) Rewrite(ctx context.Context, repo restic.Unpacked, exclud
|
||||
// It is only intended for use by prune with the UnsafeRecovery option.
|
||||
//
|
||||
// Must not be called concurrently to any other MasterIndex operation.
|
||||
func (mi *MasterIndex) SaveFallback(ctx context.Context, repo restic.SaverRemoverUnpacked, excludePacks restic.IDSet, p *progress.Counter) error {
|
||||
func (mi *MasterIndex) SaveFallback(ctx context.Context, repo restic.SaverRemoverUnpacked[restic.FileType], excludePacks restic.IDSet, p *progress.Counter) error {
|
||||
p.SetMax(uint64(len(mi.Packs(excludePacks))))
|
||||
|
||||
mi.idxMutex.Lock()
|
||||
@@ -574,7 +574,7 @@ func (mi *MasterIndex) SaveFallback(ctx context.Context, repo restic.SaverRemove
|
||||
}
|
||||
|
||||
// saveIndex saves all indexes in the backend.
|
||||
func (mi *MasterIndex) saveIndex(ctx context.Context, r restic.SaverUnpacked, indexes ...*Index) error {
|
||||
func (mi *MasterIndex) saveIndex(ctx context.Context, r restic.SaverUnpacked[restic.FileType], indexes ...*Index) error {
|
||||
for i, idx := range indexes {
|
||||
debug.Log("Saving index %d", i)
|
||||
|
||||
@@ -590,12 +590,12 @@ func (mi *MasterIndex) saveIndex(ctx context.Context, r restic.SaverUnpacked, in
|
||||
}
|
||||
|
||||
// SaveIndex saves all new indexes in the backend.
|
||||
func (mi *MasterIndex) SaveIndex(ctx context.Context, r restic.SaverUnpacked) error {
|
||||
func (mi *MasterIndex) SaveIndex(ctx context.Context, r restic.SaverUnpacked[restic.FileType]) error {
|
||||
return mi.saveIndex(ctx, r, mi.finalizeNotFinalIndexes()...)
|
||||
}
|
||||
|
||||
// SaveFullIndex saves all full indexes in the backend.
|
||||
func (mi *MasterIndex) SaveFullIndex(ctx context.Context, r restic.SaverUnpacked) error {
|
||||
func (mi *MasterIndex) SaveFullIndex(ctx context.Context, r restic.SaverUnpacked[restic.FileType]) error {
|
||||
return mi.saveIndex(ctx, r, mi.finalizeFullIndexes()...)
|
||||
}
|
||||
|
||||
|
||||
@@ -346,13 +346,13 @@ var (
|
||||
depth = 3
|
||||
)
|
||||
|
||||
func createFilledRepo(t testing.TB, snapshots int, version uint) restic.Repository {
|
||||
repo, _ := repository.TestRepositoryWithVersion(t, version)
|
||||
func createFilledRepo(t testing.TB, snapshots int, version uint) (restic.Repository, restic.Unpacked[restic.FileType]) {
|
||||
repo, unpacked, _ := repository.TestRepositoryWithVersion(t, version)
|
||||
|
||||
for i := 0; i < snapshots; i++ {
|
||||
restic.TestCreateSnapshot(t, repo, snapshotTime.Add(time.Duration(i)*time.Second), depth)
|
||||
}
|
||||
return repo
|
||||
return repo, unpacked
|
||||
}
|
||||
|
||||
func TestIndexSave(t *testing.T) {
|
||||
@@ -362,15 +362,15 @@ func TestIndexSave(t *testing.T) {
|
||||
func testIndexSave(t *testing.T, version uint) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
saver func(idx *index.MasterIndex, repo restic.Repository) error
|
||||
saver func(idx *index.MasterIndex, repo restic.Unpacked[restic.FileType]) error
|
||||
}{
|
||||
{"rewrite no-op", func(idx *index.MasterIndex, repo restic.Repository) error {
|
||||
{"rewrite no-op", func(idx *index.MasterIndex, repo restic.Unpacked[restic.FileType]) error {
|
||||
return idx.Rewrite(context.TODO(), repo, nil, nil, nil, index.MasterIndexRewriteOpts{})
|
||||
}},
|
||||
{"rewrite skip-all", func(idx *index.MasterIndex, repo restic.Repository) error {
|
||||
{"rewrite skip-all", func(idx *index.MasterIndex, repo restic.Unpacked[restic.FileType]) error {
|
||||
return idx.Rewrite(context.TODO(), repo, nil, restic.NewIDSet(), nil, index.MasterIndexRewriteOpts{})
|
||||
}},
|
||||
{"SaveFallback", func(idx *index.MasterIndex, repo restic.Repository) error {
|
||||
{"SaveFallback", func(idx *index.MasterIndex, repo restic.Unpacked[restic.FileType]) error {
|
||||
err := restic.ParallelRemove(context.TODO(), repo, idx.IDs(), restic.IndexFile, nil, nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -379,7 +379,7 @@ func testIndexSave(t *testing.T, version uint) {
|
||||
}},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
repo := createFilledRepo(t, 3, version)
|
||||
repo, unpacked := createFilledRepo(t, 3, version)
|
||||
|
||||
idx := index.NewMasterIndex()
|
||||
rtest.OK(t, idx.Load(context.TODO(), repo, nil, nil))
|
||||
@@ -388,7 +388,7 @@ func testIndexSave(t *testing.T, version uint) {
|
||||
blobs[pb] = struct{}{}
|
||||
}))
|
||||
|
||||
rtest.OK(t, test.saver(idx, repo))
|
||||
rtest.OK(t, test.saver(idx, unpacked))
|
||||
idx = index.NewMasterIndex()
|
||||
rtest.OK(t, idx.Load(context.TODO(), repo, nil, nil))
|
||||
|
||||
@@ -411,7 +411,7 @@ func TestIndexSavePartial(t *testing.T) {
|
||||
}
|
||||
|
||||
func testIndexSavePartial(t *testing.T, version uint) {
|
||||
repo := createFilledRepo(t, 3, version)
|
||||
repo, unpacked := createFilledRepo(t, 3, version)
|
||||
|
||||
// capture blob list before adding fourth snapshot
|
||||
idx := index.NewMasterIndex()
|
||||
@@ -424,14 +424,14 @@ func testIndexSavePartial(t *testing.T, version uint) {
|
||||
// add+remove new snapshot and track its pack files
|
||||
packsBefore := listPacks(t, repo)
|
||||
sn := restic.TestCreateSnapshot(t, repo, snapshotTime.Add(time.Duration(4)*time.Second), depth)
|
||||
rtest.OK(t, repo.RemoveUnpacked(context.TODO(), restic.SnapshotFile, *sn.ID()))
|
||||
rtest.OK(t, repo.RemoveUnpacked(context.TODO(), restic.WriteableSnapshotFile, *sn.ID()))
|
||||
packsAfter := listPacks(t, repo)
|
||||
newPacks := packsAfter.Sub(packsBefore)
|
||||
|
||||
// rewrite index and remove pack files of new snapshot
|
||||
idx = index.NewMasterIndex()
|
||||
rtest.OK(t, idx.Load(context.TODO(), repo, nil, nil))
|
||||
rtest.OK(t, idx.Rewrite(context.TODO(), repo, newPacks, nil, nil, index.MasterIndexRewriteOpts{}))
|
||||
rtest.OK(t, idx.Rewrite(context.TODO(), unpacked, newPacks, nil, nil, index.MasterIndexRewriteOpts{}))
|
||||
|
||||
// check blobs
|
||||
idx = index.NewMasterIndex()
|
||||
@@ -446,7 +446,7 @@ func testIndexSavePartial(t *testing.T, version uint) {
|
||||
rtest.Equals(t, 0, len(blobs), "saved index is missing blobs")
|
||||
|
||||
// remove pack files to make check happy
|
||||
rtest.OK(t, restic.ParallelRemove(context.TODO(), repo, newPacks, restic.PackFile, nil, nil))
|
||||
rtest.OK(t, restic.ParallelRemove(context.TODO(), unpacked, newPacks, restic.PackFile, nil, nil))
|
||||
|
||||
checker.TestCheckRepo(t, repo, false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user