mirror of
https://github.com/kopia/kopia.git
synced 2026-01-26 07:18:02 -05:00
* feat(cli): implementation for 'kopia snapshot fix' This allows modifications and fixes to the snapshots after they have been taken. Supported are: * `kopia snapshot fix remove-invalid-files [--verify-files-percent=X]` Removes all directory entries where the underlying files cannot be read based on index analysis (this does not read the files, only index structures so is reasonably quick). `--verify-files-percent=100` can be used to trigger full read for all files. * `kopia snapshot fix remove-files --object-id=<object-id>` Removes the object with a given ID from the entire snapshot tree. Useful when you accidentally snapshot a sensitive file. * `kopia snapshot fix remove-files --filename=<wildcard>` Removes the files with a given name from the entire snapshot tree. Useful when you accidentally snapshot a sensitive file. By default all snapshots are analyzed and rewritten. To limit the scope use: --source=user@host:/path --manifest-id=manifestID By default the rewrite operation writes new directory entries but does not replace the manifests. To do that pass `--commit`. Related #1906 Fixes #799 reorganized CLI per PR suggestion * additional logging for diff command * added Clone() method to snapshot manifst and directory entry * added a comprehensive test, moved DirRewriter to separate file * pr feedback * more pr feedback * improved logging output * disable test in -race configuration since it's way to slow * pr feedback
69 lines
2.1 KiB
Go
69 lines
2.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kopia/kopia/repo"
|
|
"github.com/kopia/kopia/repo/blob"
|
|
"github.com/kopia/kopia/snapshot"
|
|
"github.com/kopia/kopia/snapshot/snapshotfs"
|
|
)
|
|
|
|
type commandSnapshotFixInvalidFiles struct {
|
|
common commonRewriteSnapshots
|
|
|
|
verifyFilesPercent float64
|
|
verifier *snapshotfs.Verifier
|
|
|
|
invalidFileHandling string
|
|
|
|
failedFileCallback snapshotfs.RewriteFailedEntryCallback
|
|
}
|
|
|
|
func (c *commandSnapshotFixInvalidFiles) setup(svc appServices, parent commandParent) {
|
|
cmd := parent.Command("invalid-files", "Remove references to any invalid (unreadable) files from snapshots.")
|
|
c.common.setup(svc, cmd)
|
|
|
|
cmd.Flag("invalid-file-handling", "How to handle invalid files").Default(invalidEntryStub).EnumVar(&c.invalidFileHandling, invalidEntryFail, invalidEntryStub, invalidEntryKeep, invalidEntryRemove)
|
|
cmd.Flag("verify-files-percent", "Verify a percentage of files by fully downloading them [0.0 .. 100.0]").Default("0").Float64Var(&c.verifyFilesPercent)
|
|
|
|
cmd.Action(svc.repositoryWriterAction(c.run))
|
|
}
|
|
|
|
func (c *commandSnapshotFixInvalidFiles) rewriteEntry(ctx context.Context, dirRelativePath string, ent *snapshot.DirEntry) (*snapshot.DirEntry, error) {
|
|
fname := dirRelativePath + "/" + ent.Name
|
|
|
|
if ent.Type != snapshot.EntryTypeDirectory {
|
|
if err := c.verifier.VerifyFile(ctx, ent.ObjectID, fname); err != nil {
|
|
log(ctx).Warnf("removing invalid file %v due to: %v", fname, err)
|
|
|
|
return c.failedFileCallback(ctx, dirRelativePath, ent, err)
|
|
}
|
|
}
|
|
|
|
return ent, nil
|
|
}
|
|
|
|
func (c *commandSnapshotFixInvalidFiles) run(ctx context.Context, rep repo.RepositoryWriter) error {
|
|
c.failedFileCallback = failedEntryCallback(rep, c.invalidFileHandling)
|
|
|
|
opts := snapshotfs.VerifierOptions{
|
|
VerifyFilesPercent: c.verifyFilesPercent,
|
|
}
|
|
|
|
if dr, ok := rep.(repo.DirectRepository); ok {
|
|
blobMap, err := blob.ReadBlobMap(ctx, dr.BlobReader())
|
|
if err != nil {
|
|
return errors.Wrap(err, "unable to read blob map")
|
|
}
|
|
|
|
opts.BlobMap = blobMap
|
|
}
|
|
|
|
c.verifier = snapshotfs.NewVerifier(ctx, rep, opts)
|
|
|
|
return c.common.rewriteMatchingSnapshots(ctx, rep, c.rewriteEntry)
|
|
}
|