mirror of
https://github.com/kopia/kopia.git
synced 2026-03-01 12:58:30 -05:00
When upgrading from legacy to epoch manager-based index, we will write an intentionally-corrupted index blob, such that old clients won't be able to understand it when they read the repository index using legacy format. The error message emitted by very old clients is not great, but it's safer to do that rather than corrupt the repository. Note that this additional safety has a delay of up to 15 minutes which is the time required for old clients to stop relying on index list cache in case of very long-running snapshots, server or KopiaUI.
93 lines
2.1 KiB
Go
93 lines
2.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/kopia/kopia/internal/epoch"
|
|
"github.com/kopia/kopia/repo"
|
|
"github.com/kopia/kopia/repo/blob"
|
|
"github.com/kopia/kopia/repo/content"
|
|
)
|
|
|
|
type commandBlobList struct {
|
|
blobListPrefix string
|
|
blobListPrefixExclude []string
|
|
blobListMinSize int64
|
|
blobListMaxSize int64
|
|
dataOnly bool
|
|
|
|
jo jsonOutput
|
|
out textOutput
|
|
}
|
|
|
|
func (c *commandBlobList) setup(svc appServices, parent commandParent) {
|
|
cmd := parent.Command("list", "List BLOBs").Alias("ls")
|
|
cmd.Flag("prefix", "Blob ID prefix").StringVar(&c.blobListPrefix)
|
|
cmd.Flag("exclude-prefix", "Blob ID prefixes to exclude").StringsVar(&c.blobListPrefixExclude)
|
|
cmd.Flag("min-size", "Minimum size").Int64Var(&c.blobListMinSize)
|
|
cmd.Flag("max-size", "Maximum size").Int64Var(&c.blobListMaxSize)
|
|
cmd.Flag("data-only", "Only list data blobs").BoolVar(&c.dataOnly)
|
|
c.jo.setup(svc, cmd)
|
|
c.out.setup(svc)
|
|
cmd.Action(svc.directRepositoryReadAction(c.run))
|
|
}
|
|
|
|
func (c *commandBlobList) run(ctx context.Context, rep repo.DirectRepository) error {
|
|
var jl jsonList
|
|
|
|
jl.begin(&c.jo)
|
|
defer jl.end()
|
|
|
|
// nolint:wrapcheck
|
|
return rep.BlobReader().ListBlobs(ctx, blob.ID(c.blobListPrefix), func(b blob.Metadata) error {
|
|
if !c.shouldInclude(b) {
|
|
return nil
|
|
}
|
|
|
|
if c.jo.jsonOutput {
|
|
jl.emit(b)
|
|
} else {
|
|
c.out.printStdout("%-70v %10v %v\n", b.BlobID, b.Length, formatTimestamp(b.Timestamp))
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (c *commandBlobList) shouldInclude(b blob.Metadata) bool {
|
|
if c.dataOnly {
|
|
if strings.HasPrefix(string(b.BlobID), content.LegacyIndexBlobPrefix) {
|
|
return false
|
|
}
|
|
|
|
if strings.HasPrefix(string(b.BlobID), epoch.EpochManagerIndexUberPrefix) {
|
|
return false
|
|
}
|
|
|
|
if strings.HasPrefix(string(b.BlobID), content.TextLogBlobPrefix) {
|
|
return false
|
|
}
|
|
|
|
if strings.HasPrefix(string(b.BlobID), "kopia.") {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if c.blobListMaxSize != 0 && b.Length > c.blobListMaxSize {
|
|
return false
|
|
}
|
|
|
|
if c.blobListMinSize != 0 && b.Length < c.blobListMinSize {
|
|
return false
|
|
}
|
|
|
|
for _, ex := range c.blobListPrefixExclude {
|
|
if strings.HasPrefix(string(b.BlobID), ex) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|