2
2
mirror of https://github.com/octoleo/restic.git synced 2024-12-23 03:18:55 +00:00
restic/contenthandler.go

246 lines
5.1 KiB
Go
Raw Normal View History

2014-12-05 20:45:49 +00:00
package restic
2014-09-23 20:39:12 +00:00
import (
"encoding/json"
"errors"
2014-11-21 20:21:44 +00:00
"fmt"
2014-09-23 20:39:12 +00:00
2014-12-05 20:45:49 +00:00
"github.com/restic/restic/backend"
2014-09-23 20:39:12 +00:00
)
2014-11-30 21:34:21 +00:00
var ErrWrongData = errors.New("wrong data decrypt, checksum does not match")
2014-09-23 20:39:12 +00:00
type ContentHandler struct {
2014-12-21 17:10:19 +00:00
s Server
2014-09-23 20:39:12 +00:00
2014-11-21 20:21:44 +00:00
bl *BlobList
2014-09-23 20:39:12 +00:00
}
// NewContentHandler creates a new content handler.
2014-12-21 17:10:19 +00:00
func NewContentHandler(s Server) (*ContentHandler, error) {
2014-09-23 20:39:12 +00:00
ch := &ContentHandler{
2014-12-21 17:10:19 +00:00
s: s,
bl: NewBlobList(),
2014-09-23 20:39:12 +00:00
}
return ch, nil
}
2014-10-05 12:44:59 +00:00
// LoadSnapshot adds all blobs from a snapshot into the content handler and returns the snapshot.
2014-09-23 20:39:12 +00:00
func (ch *ContentHandler) LoadSnapshot(id backend.ID) (*Snapshot, error) {
sn, err := LoadSnapshot(ch, id)
if err != nil {
return nil, err
}
2014-11-23 21:26:01 +00:00
sn.bl, err = LoadBlobList(ch, sn.Map)
if err != nil {
return nil, err
}
ch.bl.Merge(sn.bl)
2014-11-21 20:21:44 +00:00
2014-09-23 20:39:12 +00:00
return sn, nil
}
2014-11-23 21:26:01 +00:00
// LoadAllMaps adds all blobs from all snapshots that can be decrypted
2014-09-23 20:39:12 +00:00
// into the content handler.
2014-11-23 21:26:01 +00:00
func (ch *ContentHandler) LoadAllMaps() error {
2014-09-23 20:39:12 +00:00
// add all maps from all snapshots that can be decrypted to the storage map
2014-12-21 16:02:49 +00:00
err := backend.EachID(ch.s, backend.Map, func(id backend.ID) {
2014-11-23 21:26:01 +00:00
bl, err := LoadBlobList(ch, id)
2014-09-23 20:39:12 +00:00
if err != nil {
return
}
2014-11-16 21:50:20 +00:00
2014-11-23 21:26:01 +00:00
ch.bl.Merge(bl)
2014-09-23 20:39:12 +00:00
})
if err != nil {
return err
}
return nil
}
// Save encrypts data and stores it to the backend as type t. If the data was
// already saved before, the blob is returned.
2014-11-21 20:21:44 +00:00
func (ch *ContentHandler) Save(t backend.Type, data []byte) (Blob, error) {
2014-09-23 20:39:12 +00:00
// compute plaintext hash
id := backend.Hash(data)
// test if the hash is already in the backend
2014-11-21 20:21:44 +00:00
blob, err := ch.bl.Find(Blob{ID: id})
if err == nil {
id.Free()
2014-09-23 20:39:12 +00:00
return blob, nil
}
// else create a new blob
2014-11-21 20:21:44 +00:00
blob = Blob{
2014-09-23 20:39:12 +00:00
ID: id,
Size: uint64(len(data)),
}
var ciphertext []byte
// for a bloblist/map, use a larger buffer
if t == backend.Map {
ciphertext = make([]byte, len(data)+CiphertextExtension)
} else {
// otherwise use buffer from pool
ciphertext = GetChunkBuf("ch.Save()")
defer FreeChunkBuf("ch.Save()", ciphertext)
}
2014-09-23 20:39:12 +00:00
// encrypt blob
2014-12-21 17:10:19 +00:00
n, err := ch.s.Encrypt(ciphertext, data)
2014-09-23 20:39:12 +00:00
if err != nil {
2014-11-21 20:21:44 +00:00
return Blob{}, err
2014-09-23 20:39:12 +00:00
}
ciphertext = ciphertext[:n]
2014-09-23 20:39:12 +00:00
// save blob
2014-12-21 16:02:49 +00:00
sid, err := ch.s.Create(t, ciphertext)
2014-09-23 20:39:12 +00:00
if err != nil {
2014-11-21 20:21:44 +00:00
return Blob{}, err
2014-09-23 20:39:12 +00:00
}
blob.Storage = sid
blob.StorageSize = uint64(len(ciphertext))
// insert blob into the storage map
2014-11-21 20:21:44 +00:00
ch.bl.Insert(blob)
2014-09-23 20:39:12 +00:00
return blob, nil
}
// SaveJSON serialises item as JSON and uses Save() to store it to the backend as type t.
2014-11-21 20:21:44 +00:00
func (ch *ContentHandler) SaveJSON(t backend.Type, item interface{}) (Blob, error) {
2014-09-23 20:39:12 +00:00
// convert to json
data, err := json.Marshal(item)
if err != nil {
2014-11-21 20:21:44 +00:00
return Blob{}, err
2014-09-23 20:39:12 +00:00
}
// compress and save data
return ch.Save(t, backend.Compress(data))
}
// Load tries to load and decrypt content identified by t and id from the backend.
func (ch *ContentHandler) Load(t backend.Type, id backend.ID) ([]byte, error) {
if t == backend.Snapshot {
// load data
2014-12-21 16:02:49 +00:00
buf, err := ch.s.Get(t, id)
2014-09-23 20:39:12 +00:00
if err != nil {
return nil, err
}
// decrypt
2014-12-21 17:10:19 +00:00
buf, err = ch.s.Decrypt(buf)
2014-09-23 20:39:12 +00:00
if err != nil {
return nil, err
}
return buf, nil
}
// lookup storage hash
2014-11-21 20:21:44 +00:00
blob, err := ch.bl.Find(Blob{ID: id})
if err != nil {
return nil, fmt.Errorf("Storage ID %s not found", id)
2014-09-23 20:39:12 +00:00
}
// load data
2014-12-21 16:02:49 +00:00
buf, err := ch.s.Get(t, blob.Storage)
2014-09-23 20:39:12 +00:00
if err != nil {
return nil, err
}
// check length
if len(buf) != int(blob.StorageSize) {
return nil, errors.New("Invalid storage length")
}
// decrypt
2014-12-21 17:10:19 +00:00
buf, err = ch.s.Decrypt(buf)
2014-09-23 20:39:12 +00:00
if err != nil {
return nil, err
}
// check length
if len(buf) != int(blob.Size) {
return nil, errors.New("Invalid length")
}
2014-11-30 21:34:21 +00:00
// check SHA256 sum
if !id.Equal(backend.Hash(buf)) {
return nil, ErrWrongData
}
2014-09-23 20:39:12 +00:00
return buf, nil
}
// Lookup returns the storage ID for the given blob
func (ch *ContentHandler) Lookup(id backend.ID) (backend.ID, error) {
// lookup storage hash
blob, err := ch.bl.Find(Blob{ID: id})
if err != nil {
return nil, err
}
return blob.Storage, nil
}
2014-09-23 20:39:12 +00:00
// LoadJSON calls Load() to get content from the backend and afterwards calls
// json.Unmarshal on the item.
func (ch *ContentHandler) LoadJSON(t backend.Type, id backend.ID, item interface{}) error {
// load from backend
buf, err := ch.Load(t, id)
if err != nil {
return err
}
// inflate and unmarshal
err = json.Unmarshal(backend.Uncompress(buf), item)
return err
}
// LoadJSONRaw loads data with the given storage id and type from the backend,
// decrypts it and calls json.Unmarshal on the item.
func (ch *ContentHandler) LoadJSONRaw(t backend.Type, id backend.ID, item interface{}) error {
// load data
2014-12-21 16:02:49 +00:00
buf, err := ch.s.Get(t, id)
2014-09-23 20:39:12 +00:00
if err != nil {
return err
}
// decrypt
2014-12-21 17:10:19 +00:00
buf, err = ch.s.Decrypt(buf)
2014-09-23 20:39:12 +00:00
if err != nil {
return err
}
// inflate and unmarshal
err = json.Unmarshal(backend.Uncompress(buf), item)
return err
}
// Test checks if a blob is in the repository. For Data and Tree blobs, the
// storage ID is looked up.
func (ch *ContentHandler) Test(t backend.Type, id backend.ID) (bool, error) {
if t == backend.Data || t == backend.Tree {
// lookup storage id
// lookup storage hash
blob, err := ch.bl.Find(Blob{ID: id})
if err != nil {
return false, fmt.Errorf("Storage ID %s not found", id)
}
id = blob.Storage
}
return ch.s.Test(t, id)
}