// LoadAndDecrypt loads and decrypts data identified by t and id from the // backend. func (r *Repository) LoadAndDecrypt(t backend.Type, id backend.ID) ([]byte, error) { debug.Log("Repo.Load", "load %v with id %v", t, id.Str()) rd, err := r.be.Get(t, id.String()) if err != nil { debug.Log("Repo.Load", "error loading %v: %v", id.Str(), err) return nil, err } buf, err := ioutil.ReadAll(rd) if err != nil { return nil, err } err = rd.Close() if err != nil { return nil, err } // check hash if !backend.Hash(buf).Equal(id) { return nil, errors.New("invalid data returned") } // decrypt plain, err := r.Decrypt(buf) if err != nil { return nil, err } return plain, nil }
func cmdBackup(t testing.TB, global GlobalOptions, target []string, parentID backend.ID) { cmd := &CmdBackup{global: &global} cmd.Parent = parentID.String() t.Logf("backing up %v", target) OK(t, cmd.Execute(target)) }
func (idx *Index) store(t pack.BlobType, id, pack backend.ID, offset, length uint, old bool) { idx.pack[id.String()] = indexEntry{ tpe: t, packID: pack, offset: offset, length: length, old: old, } }
func cmdBackupExcludes(t testing.TB, global GlobalOptions, target []string, parentID *backend.ID, excludes []string) { cmd := &CmdBackup{global: &global, Excludes: excludes} if parentID != nil { cmd.Parent = parentID.String() } t.Logf("backing up %v", target) OK(t, cmd.Execute(target)) }
// Remove removes the pack ID from the index. func (idx *Index) Remove(packID backend.ID) { idx.m.Lock() defer idx.m.Unlock() debug.Log("Index.Remove", "id %v removed", packID.Str()) s := packID.String() if _, ok := idx.pack[s]; ok { delete(idx.pack, s) } }
// Lookup returns the pack for the id. func (idx *Index) Lookup(id backend.ID) (packID backend.ID, tpe pack.BlobType, offset, length uint, err error) { idx.m.Lock() defer idx.m.Unlock() if p, ok := idx.pack[id.String()]; ok { debug.Log("Index.Lookup", "id %v found in pack %v at %d, length %d", id.Str(), p.packID.Str(), p.offset, p.length) return p.packID, p.tpe, p.offset, p.length, nil } debug.Log("Index.Lookup", "id %v not found", id.Str()) return nil, pack.Data, 0, 0, fmt.Errorf("id %v not found in index", id) }
func (c *Cache) filename(t backend.Type, subtype string, id backend.ID) (string, error) { filename := id.String() if subtype != "" { filename += "." + subtype } switch t { case backend.Snapshot: return filepath.Join(c.base, "snapshots", filename), nil } return "", fmt.Errorf("cache not supported for type %v", t) }
// checkPack reads a pack and checks the integrity of all blobs. func checkPack(r *repository.Repository, id backend.ID) error { debug.Log("Checker.checkPack", "checking pack %v", id.Str()) rd, err := r.Backend().Get(backend.Data, id.String()) if err != nil { return err } buf, err := ioutil.ReadAll(rd) if err != nil { return err } err = rd.Close() if err != nil { return err } unpacker, err := pack.NewUnpacker(r.Key(), bytes.NewReader(buf)) if err != nil { return err } var errs []error for i, blob := range unpacker.Entries { debug.Log("Checker.checkPack", " check blob %d: %v", i, blob.ID.Str()) plainBuf := make([]byte, blob.Length) plainBuf, err = crypto.Decrypt(r.Key(), plainBuf, buf[blob.Offset:blob.Offset+blob.Length]) if err != nil { debug.Log("Checker.checkPack", " error decrypting blob %v: %v", blob.ID.Str(), err) errs = append(errs, fmt.Errorf("blob %v: %v", i, err)) continue } hash := backend.Hash(plainBuf) if !hash.Equal(blob.ID) { debug.Log("Checker.checkPack", " ID does not match, want %v, got %v", blob.ID.Str(), hash.Str()) errs = append(errs, fmt.Errorf("ID does not match, want %v, got %v", blob.ID.Str(), hash.Str())) continue } } if len(errs) > 0 { return fmt.Errorf("pack %v contains %v errors: %v", id.Str(), len(errs), errs) } return nil }
// ConvertIndex loads the given index from the repo and converts them to the new // format (if necessary). When the conversion is succcessful, the old index // is removed. Returned is either the old id (if no conversion was needed) or // the new id. func ConvertIndex(repo *Repository, id backend.ID) (backend.ID, error) { debug.Log("ConvertIndex", "checking index %v", id.Str()) idx, err := LoadIndexWithDecoder(repo, id.String(), DecodeOldIndex) if err != nil { debug.Log("ConvertIndex", "LoadIndexWithDecoder(%v) returned error: %v", id.Str(), err) return id, err } buf := bytes.NewBuffer(nil) idx.supersedes = backend.IDs{id} err = idx.Encode(buf) if err != nil { debug.Log("ConvertIndex", "oldIdx.Encode() returned error: %v", err) return id, err } return repo.SaveUnpacked(backend.Index, buf.Bytes()) }
// SaveIndex saves all new packs in the index in the backend, returned is the // storage ID. func (r *Repository) SaveIndex() (backend.ID, error) { debug.Log("Repo.SaveIndex", "Saving index") // create blob blob, err := r.be.Create() if err != nil { return backend.ID{}, err } debug.Log("Repo.SaveIndex", "create new pack %p", blob) // hash hw := backend.NewHashingWriter(blob, sha256.New()) // encrypt blob ewr := crypto.EncryptTo(r.key, hw) err = r.idx.Encode(ewr) if err != nil { return backend.ID{}, err } err = ewr.Close() if err != nil { return backend.ID{}, err } // finalize blob in the backend sid := backend.ID{} copy(sid[:], hw.Sum(nil)) err = blob.Finalize(backend.Index, sid.String()) if err != nil { return backend.ID{}, err } debug.Log("Repo.SaveIndex", "Saved index as %v", sid.Str()) return sid, nil }
// SaveJSONUnpacked serialises item as JSON and encrypts and saves it in the // backend as type t, without a pack. It returns the storage hash. func (r *Repository) SaveJSONUnpacked(t backend.Type, item interface{}) (backend.ID, error) { // create file blob, err := r.be.Create() if err != nil { return backend.ID{}, err } debug.Log("Repo.SaveJSONUnpacked", "create new blob %v", t) // hash hw := backend.NewHashingWriter(blob, sha256.New()) // encrypt blob ewr := crypto.EncryptTo(r.key, hw) enc := json.NewEncoder(ewr) err = enc.Encode(item) if err != nil { return backend.ID{}, fmt.Errorf("json.Encode: %v", err) } err = ewr.Close() if err != nil { return backend.ID{}, err } // finalize blob in the backend hash := hw.Sum(nil) sid := backend.ID{} copy(sid[:], hash) err = blob.Finalize(t, sid.String()) if err != nil { debug.Log("Repo.SaveJSONUnpacked", "error saving blob %v as %v: %v", t, sid, err) return backend.ID{}, err } debug.Log("Repo.SaveJSONUnpacked", "new blob %v saved as %v", t, sid) return sid, nil }
// ConvertIndex loads the given index from the repo and converts them to the new // format (if necessary). When the conversion is succcessful, the old index // is removed. Returned is either the old id (if no conversion was needed) or // the new id. func ConvertIndex(repo *Repository, id backend.ID) (backend.ID, error) { debug.Log("ConvertIndex", "checking index %v", id.Str()) idx, err := LoadIndexWithDecoder(repo, id.String(), DecodeOldIndex) if err != nil { debug.Log("ConvertIndex", "LoadIndexWithDecoder(%v) returned error: %v", id.Str(), err) return id, err } blob, err := repo.CreateEncryptedBlob(backend.Index) if err != nil { return id, err } idx.supersedes = backend.IDs{id} err = idx.Encode(blob) if err != nil { debug.Log("ConvertIndex", "oldIdx.Encode() returned error: %v", err) return id, err } err = blob.Close() if err != nil { debug.Log("ConvertIndex", "blob.Close() returned error: %v", err) return id, err } newID := blob.ID() debug.Log("ConvertIndex", "index %v converted to new format as %v", id.Str(), newID.Str()) err = repo.be.Remove(backend.Index, id.String()) if err != nil { debug.Log("ConvertIndex", "backend.Remove(%v) returned error: %v", id.Str(), err) return id, err } return newID, nil }
// LoadAndDecrypt loads and decrypts data identified by t and id from the // backend. func (r *Repository) LoadAndDecrypt(t backend.Type, id backend.ID) ([]byte, error) { debug.Log("Repo.Load", "load %v with id %v", t, id.Str()) h := backend.Handle{Type: t, Name: id.String()} buf, err := backend.LoadAll(r.be, h, nil) if err != nil { debug.Log("Repo.Load", "error loading %v: %v", id.Str(), err) return nil, err } if t != backend.Config && !backend.Hash(buf).Equal(id) { return nil, errors.New("invalid data returned") } // decrypt plain, err := r.Decrypt(buf) if err != nil { return nil, err } return plain, nil }
// LoadJSONUnpacked decrypts the data and afterwards calls json.Unmarshal on // the item. func (r *Repository) LoadJSONUnpacked(t backend.Type, id backend.ID, item interface{}) error { // load blob from backend rd, err := r.be.Get(t, id.String()) if err != nil { return err } defer rd.Close() // decrypt decryptRd, err := crypto.DecryptFrom(r.key, rd) defer decryptRd.Close() if err != nil { return err } // decode decoder := json.NewDecoder(decryptRd) err = decoder.Decode(item) if err != nil { return err } return nil }
func lockExists(repo *repository.Repository, t testing.TB, id backend.ID) bool { exists, err := repo.Backend().Test(backend.Lock, id.String()) OK(t, err) return exists }
func removeLock(repo *repository.Repository, id backend.ID) error { return repo.Backend().Remove(backend.Lock, id.String()) }
func (cmd CmdCat) Execute(args []string) error { if len(args) < 1 || (args[0] != "masterkey" && args[0] != "config" && len(args) != 2) { return fmt.Errorf("type or ID not specified, Usage: %s", cmd.Usage()) } repo, err := cmd.global.OpenRepository() if err != nil { return err } lock, err := lockRepo(repo) defer unlockRepo(lock) if err != nil { return err } tpe := args[0] var id backend.ID if tpe != "masterkey" && tpe != "config" { id, err = backend.ParseID(args[1]) if err != nil { if tpe != "snapshot" { return err } // find snapshot id with prefix id, err = restic.FindSnapshot(repo, args[1]) if err != nil { return err } } } // handle all types that don't need an index switch tpe { case "config": buf, err := json.MarshalIndent(repo.Config, "", " ") if err != nil { return err } fmt.Println(string(buf)) return nil case "index": buf, err := repo.LoadAndDecrypt(backend.Index, id) if err != nil { return err } _, err = os.Stdout.Write(append(buf, '\n')) return err case "snapshot": sn := &restic.Snapshot{} err = repo.LoadJSONUnpacked(backend.Snapshot, id, sn) if err != nil { return err } buf, err := json.MarshalIndent(&sn, "", " ") if err != nil { return err } fmt.Println(string(buf)) return nil case "key": rd, err := repo.Backend().Get(backend.Key, id.String()) if err != nil { return err } dec := json.NewDecoder(rd) var key repository.Key err = dec.Decode(&key) if err != nil { return err } buf, err := json.MarshalIndent(&key, "", " ") if err != nil { return err } fmt.Println(string(buf)) return nil case "masterkey": buf, err := json.MarshalIndent(repo.Key(), "", " ") if err != nil { return err } fmt.Println(string(buf)) return nil case "lock": lock, err := restic.LoadLock(repo, id) if err != nil { return err } buf, err := json.MarshalIndent(&lock, "", " ") if err != nil { return err } fmt.Println(string(buf)) return nil } // load index, handle all the other types err = repo.LoadIndex() if err != nil { return err } switch tpe { case "pack": rd, err := repo.Backend().Get(backend.Data, id.String()) if err != nil { return err } _, err = io.Copy(os.Stdout, rd) return err case "blob": blob, err := repo.Index().Lookup(id) if err != nil { return err } buf := make([]byte, blob.Length) data, err := repo.LoadBlob(blob.Type, id, buf) if err != nil { return err } _, err = os.Stdout.Write(data) return err case "tree": debug.Log("cat", "cat tree %v", id.Str()) tree := restic.NewTree() err = repo.LoadJSONPack(pack.Tree, id, tree) if err != nil { debug.Log("cat", "unable to load tree %v: %v", id.Str(), err) return err } buf, err := json.MarshalIndent(&tree, "", " ") if err != nil { debug.Log("cat", "error json.MarshalIndent(): %v", err) return err } _, err = os.Stdout.Write(append(buf, '\n')) return nil default: return errors.New("invalid type") } }
func cmdRestore(t testing.TB, global GlobalOptions, dir string, snapshotID backend.ID) { cmd := &CmdRestore{global: &global} cmd.Execute([]string{snapshotID.String(), dir}) }
func cmdRestoreIncludes(t testing.TB, global GlobalOptions, dir string, snapshotID backend.ID, includes []string) { cmd := &CmdRestore{global: &global, Target: dir, Include: includes} OK(t, cmd.Execute([]string{snapshotID.String()})) }