mirror of
https://github.com/restic/restic.git
synced 2026-04-23 05:09:25 +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:
@@ -304,7 +304,7 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption
|
||||
if len(removeSnIDs) > 0 {
|
||||
if !opts.DryRun {
|
||||
bar := printer.NewCounter("files deleted")
|
||||
err := restic.ParallelRemove(ctx, repo, removeSnIDs, restic.SnapshotFile, func(id restic.ID, err error) error {
|
||||
err := restic.ParallelRemove(ctx, repo, removeSnIDs, restic.WriteableSnapshotFile, func(id restic.ID, err error) error {
|
||||
if err != nil {
|
||||
printer.E("unable to remove %v/%v from the repository\n", restic.SnapshotFile, id)
|
||||
} else {
|
||||
|
||||
@@ -168,7 +168,7 @@ func runRecover(ctx context.Context, gopts GlobalOptions) error {
|
||||
|
||||
}
|
||||
|
||||
func createSnapshot(ctx context.Context, name, hostname string, tags []string, repo restic.SaverUnpacked, tree *restic.ID) error {
|
||||
func createSnapshot(ctx context.Context, name, hostname string, tags []string, repo restic.SaverUnpacked[restic.WriteableFileType], tree *restic.ID) error {
|
||||
sn, err := restic.NewSnapshot([]string{name}, tags, hostname, time.Now())
|
||||
if err != nil {
|
||||
return errors.Fatalf("unable to save snapshot: %v", err)
|
||||
|
||||
@@ -194,7 +194,7 @@ func filterAndReplaceSnapshot(ctx context.Context, repo restic.Repository, sn *r
|
||||
if dryRun {
|
||||
Verbosef("would delete empty snapshot\n")
|
||||
} else {
|
||||
if err = repo.RemoveUnpacked(ctx, restic.SnapshotFile, *sn.ID()); err != nil {
|
||||
if err = repo.RemoveUnpacked(ctx, restic.WriteableSnapshotFile, *sn.ID()); err != nil {
|
||||
return false, err
|
||||
}
|
||||
debug.Log("removed empty snapshot %v", sn.ID())
|
||||
@@ -253,7 +253,7 @@ func filterAndReplaceSnapshot(ctx context.Context, repo restic.Repository, sn *r
|
||||
Verbosef("saved new snapshot %v\n", id.Str())
|
||||
|
||||
if forget {
|
||||
if err = repo.RemoveUnpacked(ctx, restic.SnapshotFile, *sn.ID()); err != nil {
|
||||
if err = repo.RemoveUnpacked(ctx, restic.WriteableSnapshotFile, *sn.ID()); err != nil {
|
||||
return false, err
|
||||
}
|
||||
debug.Log("removed old snapshot %v", sn.ID())
|
||||
|
||||
@@ -90,7 +90,7 @@ func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Sna
|
||||
debug.Log("new snapshot saved as %v", id)
|
||||
|
||||
// Remove the old snapshot.
|
||||
if err = repo.RemoveUnpacked(ctx, restic.SnapshotFile, *sn.ID()); err != nil {
|
||||
if err = repo.RemoveUnpacked(ctx, restic.WriteableSnapshotFile, *sn.ID()); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -45,9 +45,9 @@ func runUnlock(ctx context.Context, opts UnlockOptions, gopts GlobalOptions) err
|
||||
return err
|
||||
}
|
||||
|
||||
fn := restic.RemoveStaleLocks
|
||||
fn := repository.RemoveStaleLocks
|
||||
if opts.RemoveAll {
|
||||
fn = restic.RemoveAllLocks
|
||||
fn = repository.RemoveAllLocks
|
||||
}
|
||||
|
||||
processed, err := fn(ctx, repo)
|
||||
|
||||
@@ -275,17 +275,30 @@ func listTreePacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
|
||||
return treePacks
|
||||
}
|
||||
|
||||
func captureBackend(gopts *GlobalOptions) func() backend.Backend {
|
||||
var be backend.Backend
|
||||
gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) {
|
||||
be = r
|
||||
return r, nil
|
||||
}
|
||||
return func() backend.Backend {
|
||||
return be
|
||||
}
|
||||
}
|
||||
|
||||
func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) {
|
||||
ctx, r, unlock, err := openWithExclusiveLock(context.TODO(), gopts, false)
|
||||
be := captureBackend(&gopts)
|
||||
ctx, _, unlock, err := openWithExclusiveLock(context.TODO(), gopts, false)
|
||||
rtest.OK(t, err)
|
||||
defer unlock()
|
||||
|
||||
for id := range remove {
|
||||
rtest.OK(t, r.RemoveUnpacked(ctx, restic.PackFile, id))
|
||||
rtest.OK(t, be().Remove(ctx, backend.Handle{Type: restic.PackFile, Name: id.String()}))
|
||||
}
|
||||
}
|
||||
|
||||
func removePacksExcept(gopts GlobalOptions, t testing.TB, keep restic.IDSet, removeTreePacks bool) {
|
||||
be := captureBackend(&gopts)
|
||||
ctx, r, unlock, err := openWithExclusiveLock(context.TODO(), gopts, false)
|
||||
rtest.OK(t, err)
|
||||
defer unlock()
|
||||
@@ -305,7 +318,7 @@ func removePacksExcept(gopts GlobalOptions, t testing.TB, keep restic.IDSet, rem
|
||||
if treePacks.Has(id) != removeTreePacks || keep.Has(id) {
|
||||
return nil
|
||||
}
|
||||
return r.RemoveUnpacked(ctx, restic.PackFile, id)
|
||||
return be().Remove(ctx, backend.Handle{Type: restic.PackFile, Name: id.String()})
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user