github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/cmd/restic/cmd_cat.go (about)

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