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 }