github.com/mckael/restic@v0.8.3/cmd/restic/cmd_cat.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  
     8  	"github.com/spf13/cobra"
     9  
    10  	"github.com/restic/restic/internal/backend"
    11  	"github.com/restic/restic/internal/errors"
    12  	"github.com/restic/restic/internal/repository"
    13  	"github.com/restic/restic/internal/restic"
    14  )
    15  
    16  var cmdCat = &cobra.Command{
    17  	Use:   "cat [flags] [pack|blob|snapshot|index|key|masterkey|config|lock] ID",
    18  	Short: "Print internal objects to stdout",
    19  	Long: `
    20  The "cat" command is used to print internal objects to stdout.
    21  `,
    22  	DisableAutoGenTag: true,
    23  	RunE: func(cmd *cobra.Command, args []string) error {
    24  		return runCat(globalOptions, args)
    25  	},
    26  }
    27  
    28  func init() {
    29  	cmdRoot.AddCommand(cmdCat)
    30  }
    31  
    32  func runCat(gopts GlobalOptions, args []string) error {
    33  	if len(args) < 1 || (args[0] != "masterkey" && args[0] != "config" && len(args) != 2) {
    34  		return errors.Fatal("type or ID not specified")
    35  	}
    36  
    37  	repo, err := OpenRepository(gopts)
    38  	if err != nil {
    39  		return err
    40  	}
    41  
    42  	lock, err := lockRepo(repo)
    43  	defer unlockRepo(lock)
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	tpe := args[0]
    49  
    50  	var id restic.ID
    51  	if tpe != "masterkey" && tpe != "config" {
    52  		id, err = restic.ParseID(args[1])
    53  		if err != nil {
    54  			if tpe != "snapshot" {
    55  				return errors.Fatalf("unable to parse ID: %v\n", err)
    56  			}
    57  
    58  			// find snapshot id with prefix
    59  			id, err = restic.FindSnapshot(repo, args[1])
    60  			if err != nil {
    61  				return err
    62  			}
    63  		}
    64  	}
    65  
    66  	// handle all types that don't need an index
    67  	switch tpe {
    68  	case "config":
    69  		buf, err := json.MarshalIndent(repo.Config(), "", "  ")
    70  		if err != nil {
    71  			return err
    72  		}
    73  
    74  		fmt.Println(string(buf))
    75  		return nil
    76  	case "index":
    77  		buf, err := repo.LoadAndDecrypt(gopts.ctx, restic.IndexFile, id)
    78  		if err != nil {
    79  			return err
    80  		}
    81  
    82  		_, err = os.Stdout.Write(append(buf, '\n'))
    83  		return err
    84  
    85  	case "snapshot":
    86  		sn := &restic.Snapshot{}
    87  		err = repo.LoadJSONUnpacked(gopts.ctx, restic.SnapshotFile, id, sn)
    88  		if err != nil {
    89  			return err
    90  		}
    91  
    92  		buf, err := json.MarshalIndent(&sn, "", "  ")
    93  		if err != nil {
    94  			return err
    95  		}
    96  
    97  		fmt.Println(string(buf))
    98  
    99  		return nil
   100  	case "key":
   101  		h := restic.Handle{Type: restic.KeyFile, Name: id.String()}
   102  		buf, err := backend.LoadAll(gopts.ctx, repo.Backend(), h)
   103  		if err != nil {
   104  			return err
   105  		}
   106  
   107  		key := &repository.Key{}
   108  		err = json.Unmarshal(buf, key)
   109  		if err != nil {
   110  			return err
   111  		}
   112  
   113  		buf, err = json.MarshalIndent(&key, "", "  ")
   114  		if err != nil {
   115  			return err
   116  		}
   117  
   118  		fmt.Println(string(buf))
   119  		return nil
   120  	case "masterkey":
   121  		buf, err := json.MarshalIndent(repo.Key(), "", "  ")
   122  		if err != nil {
   123  			return err
   124  		}
   125  
   126  		fmt.Println(string(buf))
   127  		return nil
   128  	case "lock":
   129  		lock, err := restic.LoadLock(gopts.ctx, repo, id)
   130  		if err != nil {
   131  			return err
   132  		}
   133  
   134  		buf, err := json.MarshalIndent(&lock, "", "  ")
   135  		if err != nil {
   136  			return err
   137  		}
   138  
   139  		fmt.Println(string(buf))
   140  
   141  		return nil
   142  	}
   143  
   144  	// load index, handle all the other types
   145  	err = repo.LoadIndex(gopts.ctx)
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	switch tpe {
   151  	case "pack":
   152  		h := restic.Handle{Type: restic.DataFile, Name: id.String()}
   153  		buf, err := backend.LoadAll(gopts.ctx, repo.Backend(), h)
   154  		if err != nil {
   155  			return err
   156  		}
   157  
   158  		hash := restic.Hash(buf)
   159  		if !hash.Equal(id) {
   160  			fmt.Fprintf(stderr, "Warning: hash of data does not match ID, want\n  %v\ngot:\n  %v\n", id.String(), hash.String())
   161  		}
   162  
   163  		_, err = os.Stdout.Write(buf)
   164  		return err
   165  
   166  	case "blob":
   167  		for _, t := range []restic.BlobType{restic.DataBlob, restic.TreeBlob} {
   168  			list, found := repo.Index().Lookup(id, t)
   169  			if !found {
   170  				continue
   171  			}
   172  			blob := list[0]
   173  
   174  			buf := make([]byte, blob.Length)
   175  			n, err := repo.LoadBlob(gopts.ctx, t, id, buf)
   176  			if err != nil {
   177  				return err
   178  			}
   179  			buf = buf[:n]
   180  
   181  			_, err = os.Stdout.Write(buf)
   182  			return err
   183  		}
   184  
   185  		return errors.Fatal("blob not found")
   186  
   187  	default:
   188  		return errors.Fatal("invalid type")
   189  	}
   190  }