package cli import ( "bytes" "context" "encoding/json" "io" "github.com/pkg/errors" "github.com/kopia/kopia/internal/gather" "github.com/kopia/kopia/internal/iocopy" "github.com/kopia/kopia/repo" "github.com/kopia/kopia/repo/blob" ) type commandBlobShow struct { blobShowDecrypt bool blobShowIDs []string out textOutput } func (c *commandBlobShow) setup(svc appServices, parent commandParent) { cmd := parent.Command("show", "Show contents of BLOBs").Alias("cat") cmd.Flag("decrypt", "Decrypt blob if possible").BoolVar(&c.blobShowDecrypt) cmd.Arg("blobID", "Blob IDs").Required().StringsVar(&c.blobShowIDs) cmd.Action(svc.directRepositoryReadAction(c.run)) c.out.setup(svc) } func (c *commandBlobShow) run(ctx context.Context, rep repo.DirectRepository) error { for _, blobID := range c.blobShowIDs { if err := c.maybeDecryptBlob(ctx, c.out.stdout(), rep, blob.ID(blobID)); err != nil { return errors.Wrap(err, "error presenting blob") } } return nil } func (c *commandBlobShow) maybeDecryptBlob(ctx context.Context, w io.Writer, rep repo.DirectRepository, blobID blob.ID) error { var ( d gather.WriteBuffer b gather.Bytes ) if err := rep.BlobReader().GetBlob(ctx, blobID, 0, -1, &d); err != nil { return errors.Wrap(err, "error reading blob") } b = d.Bytes() if c.blobShowDecrypt && canDecryptBlob(blobID) { var tmp gather.WriteBuffer defer tmp.Close() if err := rep.Crypter().DecryptBLOB(b, blobID, &tmp); err != nil { return errors.Wrap(err, "error decrypting blob") } b = tmp.Bytes() } if isJSONBlob(blobID) { var buf bytes.Buffer if err := json.Indent(&buf, b.ToByteSlice(), "", " "); err != nil { return errors.Wrap(err, "invalid JSON") } b = gather.FromSlice(buf.Bytes()) } if err := iocopy.JustCopy(w, b.Reader()); err != nil { return errors.Wrap(err, "error copying data") } return nil } func canDecryptBlob(b blob.ID) bool { switch b[0] { case '_', 'n', 'm', 'l': return true default: return false } } func isJSONBlob(b blob.ID) bool { switch b[0] { case 'm', 'l': return true default: return false } }