github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/internal/repository/repository.go (about) 1 package repository 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "os" 9 10 "github.com/restic/restic/internal/cache" 11 "github.com/restic/restic/internal/errors" 12 "github.com/restic/restic/internal/restic" 13 14 "github.com/restic/restic/internal/backend" 15 "github.com/restic/restic/internal/crypto" 16 "github.com/restic/restic/internal/debug" 17 "github.com/restic/restic/internal/pack" 18 ) 19 20 // Repository is used to access a repository in a backend. 21 type Repository struct { 22 be restic.Backend 23 cfg restic.Config 24 key *crypto.Key 25 keyName string 26 idx *MasterIndex 27 restic.Cache 28 29 treePM *packerManager 30 dataPM *packerManager 31 } 32 33 // New returns a new repository with backend be. 34 func New(be restic.Backend) *Repository { 35 repo := &Repository{ 36 be: be, 37 idx: NewMasterIndex(), 38 dataPM: newPackerManager(be, nil), 39 treePM: newPackerManager(be, nil), 40 } 41 42 return repo 43 } 44 45 // Config returns the repository configuration. 46 func (r *Repository) Config() restic.Config { 47 return r.cfg 48 } 49 50 // UseCache replaces the backend with the wrapped cache. 51 func (r *Repository) UseCache(c restic.Cache) { 52 if c == nil { 53 return 54 } 55 debug.Log("using cache") 56 r.Cache = c 57 r.be = c.Wrap(r.be) 58 } 59 60 // PrefixLength returns the number of bytes required so that all prefixes of 61 // all IDs of type t are unique. 62 func (r *Repository) PrefixLength(t restic.FileType) (int, error) { 63 return restic.PrefixLength(r.be, t) 64 } 65 66 // LoadAndDecrypt loads and decrypts data identified by t and id from the 67 // backend. 68 func (r *Repository) LoadAndDecrypt(ctx context.Context, t restic.FileType, id restic.ID) (buf []byte, err error) { 69 debug.Log("load %v with id %v", t, id.Str()) 70 71 h := restic.Handle{Type: t, Name: id.String()} 72 buf, err = backend.LoadAll(ctx, r.be, h) 73 if err != nil { 74 debug.Log("error loading %v: %v", h, err) 75 return nil, err 76 } 77 78 if t != restic.ConfigFile && !restic.Hash(buf).Equal(id) { 79 return nil, errors.Errorf("load %v: invalid data returned", h) 80 } 81 82 nonce, ciphertext := buf[:r.key.NonceSize()], buf[r.key.NonceSize():] 83 plaintext, err := r.key.Open(ciphertext[:0], nonce, ciphertext, nil) 84 if err != nil { 85 return nil, err 86 } 87 88 return plaintext, nil 89 } 90 91 // sortCachedPacks moves all cached pack files to the front of blobs. 92 func (r *Repository) sortCachedPacks(blobs []restic.PackedBlob) []restic.PackedBlob { 93 if r.Cache == nil { 94 return blobs 95 } 96 97 cached := make([]restic.PackedBlob, 0, len(blobs)/2) 98 noncached := make([]restic.PackedBlob, 0, len(blobs)/2) 99 100 for _, blob := range blobs { 101 if r.Cache.Has(restic.Handle{Type: restic.DataFile, Name: blob.PackID.String()}) { 102 cached = append(cached, blob) 103 continue 104 } 105 noncached = append(noncached, blob) 106 } 107 108 return append(cached, noncached...) 109 } 110 111 // loadBlob tries to load and decrypt content identified by t and id from a 112 // pack from the backend, the result is stored in plaintextBuf, which must be 113 // large enough to hold the complete blob. 114 func (r *Repository) loadBlob(ctx context.Context, id restic.ID, t restic.BlobType, plaintextBuf []byte) (int, error) { 115 debug.Log("load %v with id %v (buf len %v, cap %d)", t, id.Str(), len(plaintextBuf), cap(plaintextBuf)) 116 117 // lookup packs 118 blobs, err := r.idx.Lookup(id, t) 119 if err != nil { 120 debug.Log("id %v not found in index: %v", id.Str(), err) 121 return 0, err 122 } 123 124 // try cached pack files first 125 blobs = r.sortCachedPacks(blobs) 126 127 var lastError error 128 for _, blob := range blobs { 129 debug.Log("blob %v/%v found: %v", t, id.Str(), blob) 130 131 if blob.Type != t { 132 debug.Log("blob %v has wrong block type, want %v", blob, t) 133 } 134 135 // load blob from pack 136 h := restic.Handle{Type: restic.DataFile, Name: blob.PackID.String()} 137 138 if uint(cap(plaintextBuf)) < blob.Length { 139 return 0, errors.Errorf("buffer is too small: %v < %v", cap(plaintextBuf), blob.Length) 140 } 141 142 plaintextBuf = plaintextBuf[:blob.Length] 143 144 n, err := restic.ReadAt(ctx, r.be, h, int64(blob.Offset), plaintextBuf) 145 if err != nil { 146 debug.Log("error loading blob %v: %v", blob, err) 147 lastError = err 148 continue 149 } 150 151 if uint(n) != blob.Length { 152 lastError = errors.Errorf("error loading blob %v: wrong length returned, want %d, got %d", 153 id.Str(), blob.Length, uint(n)) 154 debug.Log("lastError: %v", lastError) 155 continue 156 } 157 158 // decrypt 159 nonce, ciphertext := plaintextBuf[:r.key.NonceSize()], plaintextBuf[r.key.NonceSize():] 160 plaintext, err := r.key.Open(ciphertext[:0], nonce, ciphertext, nil) 161 if err != nil { 162 lastError = errors.Errorf("decrypting blob %v failed: %v", id, err) 163 continue 164 } 165 166 // check hash 167 if !restic.Hash(plaintext).Equal(id) { 168 lastError = errors.Errorf("blob %v returned invalid hash", id) 169 continue 170 } 171 172 // move decrypted data to the start of the provided buffer 173 copy(plaintextBuf[0:], plaintext) 174 return len(plaintext), nil 175 } 176 177 if lastError != nil { 178 return 0, lastError 179 } 180 181 return 0, errors.Errorf("loading blob %v from %v packs failed", id.Str(), len(blobs)) 182 } 183 184 // LoadJSONUnpacked decrypts the data and afterwards calls json.Unmarshal on 185 // the item. 186 func (r *Repository) LoadJSONUnpacked(ctx context.Context, t restic.FileType, id restic.ID, item interface{}) (err error) { 187 buf, err := r.LoadAndDecrypt(ctx, t, id) 188 if err != nil { 189 return err 190 } 191 192 return json.Unmarshal(buf, item) 193 } 194 195 // LookupBlobSize returns the size of blob id. 196 func (r *Repository) LookupBlobSize(id restic.ID, tpe restic.BlobType) (uint, error) { 197 return r.idx.LookupSize(id, tpe) 198 } 199 200 // SaveAndEncrypt encrypts data and stores it to the backend as type t. If data 201 // is small enough, it will be packed together with other small blobs. 202 func (r *Repository) SaveAndEncrypt(ctx context.Context, t restic.BlobType, data []byte, id *restic.ID) (restic.ID, error) { 203 if id == nil { 204 // compute plaintext hash 205 hashedID := restic.Hash(data) 206 id = &hashedID 207 } 208 209 debug.Log("save id %v (%v, %d bytes)", id.Str(), t, len(data)) 210 211 // get buf from the pool 212 ciphertext := getBuf() 213 defer freeBuf(ciphertext) 214 215 ciphertext = ciphertext[:0] 216 nonce := crypto.NewRandomNonce() 217 ciphertext = append(ciphertext, nonce...) 218 219 // encrypt blob 220 ciphertext = r.key.Seal(ciphertext, nonce, data, nil) 221 222 // find suitable packer and add blob 223 var pm *packerManager 224 225 switch t { 226 case restic.TreeBlob: 227 pm = r.treePM 228 case restic.DataBlob: 229 pm = r.dataPM 230 default: 231 panic(fmt.Sprintf("invalid type: %v", t)) 232 } 233 234 packer, err := pm.findPacker() 235 if err != nil { 236 return restic.ID{}, err 237 } 238 239 // save ciphertext 240 _, err = packer.Add(t, *id, ciphertext) 241 if err != nil { 242 return restic.ID{}, err 243 } 244 245 // if the pack is not full enough, put back to the list 246 if packer.Size() < minPackSize { 247 debug.Log("pack is not full enough (%d bytes)", packer.Size()) 248 pm.insertPacker(packer) 249 return *id, nil 250 } 251 252 // else write the pack to the backend 253 return *id, r.savePacker(t, packer) 254 } 255 256 // SaveJSONUnpacked serialises item as JSON and encrypts and saves it in the 257 // backend as type t, without a pack. It returns the storage hash. 258 func (r *Repository) SaveJSONUnpacked(ctx context.Context, t restic.FileType, item interface{}) (restic.ID, error) { 259 debug.Log("save new blob %v", t) 260 plaintext, err := json.Marshal(item) 261 if err != nil { 262 return restic.ID{}, errors.Wrap(err, "json.Marshal") 263 } 264 265 return r.SaveUnpacked(ctx, t, plaintext) 266 } 267 268 // SaveUnpacked encrypts data and stores it in the backend. Returned is the 269 // storage hash. 270 func (r *Repository) SaveUnpacked(ctx context.Context, t restic.FileType, p []byte) (id restic.ID, err error) { 271 ciphertext := restic.NewBlobBuffer(len(p)) 272 ciphertext = ciphertext[:0] 273 nonce := crypto.NewRandomNonce() 274 ciphertext = append(ciphertext, nonce...) 275 276 ciphertext = r.key.Seal(ciphertext, nonce, p, nil) 277 278 id = restic.Hash(ciphertext) 279 h := restic.Handle{Type: t, Name: id.String()} 280 281 err = r.be.Save(ctx, h, bytes.NewReader(ciphertext)) 282 if err != nil { 283 debug.Log("error saving blob %v: %v", h, err) 284 return restic.ID{}, err 285 } 286 287 debug.Log("blob %v saved", h) 288 return id, nil 289 } 290 291 // Flush saves all remaining packs. 292 func (r *Repository) Flush() error { 293 pms := []struct { 294 t restic.BlobType 295 pm *packerManager 296 }{ 297 {restic.DataBlob, r.dataPM}, 298 {restic.TreeBlob, r.treePM}, 299 } 300 301 for _, p := range pms { 302 p.pm.pm.Lock() 303 304 debug.Log("manually flushing %d packs", len(p.pm.packers)) 305 for _, packer := range p.pm.packers { 306 err := r.savePacker(p.t, packer) 307 if err != nil { 308 p.pm.pm.Unlock() 309 return err 310 } 311 } 312 p.pm.packers = p.pm.packers[:0] 313 p.pm.pm.Unlock() 314 } 315 316 return nil 317 } 318 319 // Backend returns the backend for the repository. 320 func (r *Repository) Backend() restic.Backend { 321 return r.be 322 } 323 324 // Index returns the currently used MasterIndex. 325 func (r *Repository) Index() restic.Index { 326 return r.idx 327 } 328 329 // SetIndex instructs the repository to use the given index. 330 func (r *Repository) SetIndex(i restic.Index) { 331 r.idx = i.(*MasterIndex) 332 } 333 334 // SaveIndex saves an index in the repository. 335 func SaveIndex(ctx context.Context, repo restic.Repository, index *Index) (restic.ID, error) { 336 buf := bytes.NewBuffer(nil) 337 338 err := index.Finalize(buf) 339 if err != nil { 340 return restic.ID{}, err 341 } 342 343 return repo.SaveUnpacked(ctx, restic.IndexFile, buf.Bytes()) 344 } 345 346 // saveIndex saves all indexes in the backend. 347 func (r *Repository) saveIndex(ctx context.Context, indexes ...*Index) error { 348 for i, idx := range indexes { 349 debug.Log("Saving index %d", i) 350 351 sid, err := SaveIndex(ctx, r, idx) 352 if err != nil { 353 return err 354 } 355 356 debug.Log("Saved index %d as %v", i, sid.Str()) 357 } 358 359 return nil 360 } 361 362 // SaveIndex saves all new indexes in the backend. 363 func (r *Repository) SaveIndex(ctx context.Context) error { 364 return r.saveIndex(ctx, r.idx.NotFinalIndexes()...) 365 } 366 367 // SaveFullIndex saves all full indexes in the backend. 368 func (r *Repository) SaveFullIndex(ctx context.Context) error { 369 return r.saveIndex(ctx, r.idx.FullIndexes()...) 370 } 371 372 const loadIndexParallelism = 20 373 374 // LoadIndex loads all index files from the backend in parallel and stores them 375 // in the master index. The first error that occurred is returned. 376 func (r *Repository) LoadIndex(ctx context.Context) error { 377 debug.Log("Loading index") 378 379 errCh := make(chan error, 1) 380 indexes := make(chan *Index) 381 382 worker := func(ctx context.Context, id restic.ID) error { 383 idx, err := LoadIndex(ctx, r, id) 384 if err != nil { 385 fmt.Fprintf(os.Stderr, "%v, ignoring\n", err) 386 return nil 387 } 388 389 select { 390 case indexes <- idx: 391 case <-ctx.Done(): 392 } 393 394 return nil 395 } 396 397 go func() { 398 defer close(indexes) 399 errCh <- FilesInParallel(ctx, r.be, restic.IndexFile, loadIndexParallelism, 400 ParallelWorkFuncParseID(worker)) 401 }() 402 403 validIndex := restic.NewIDSet() 404 for idx := range indexes { 405 id, err := idx.ID() 406 if err == nil { 407 validIndex.Insert(id) 408 } 409 r.idx.Insert(idx) 410 } 411 412 if r.Cache != nil { 413 // clear old index files 414 err := r.Cache.Clear(restic.IndexFile, validIndex) 415 if err != nil { 416 fmt.Fprintf(os.Stderr, "error clearing index files in cache: %v\n", err) 417 } 418 419 packs := restic.NewIDSet() 420 for _, idx := range r.idx.All() { 421 for id := range idx.Packs() { 422 packs.Insert(id) 423 } 424 } 425 426 // clear old data files 427 err = r.Cache.Clear(restic.DataFile, packs) 428 if err != nil { 429 fmt.Fprintf(os.Stderr, "error clearing data files in cache: %v\n", err) 430 } 431 432 treePacks := restic.NewIDSet() 433 for _, idx := range r.idx.All() { 434 for _, id := range idx.TreePacks() { 435 treePacks.Insert(id) 436 } 437 } 438 439 // use readahead 440 cache := r.Cache.(*cache.Cache) 441 cache.PerformReadahead = func(h restic.Handle) bool { 442 if h.Type != restic.DataFile { 443 return false 444 } 445 446 id, err := restic.ParseID(h.Name) 447 if err != nil { 448 return false 449 } 450 451 return treePacks.Has(id) 452 } 453 } 454 455 if err := <-errCh; err != nil { 456 return err 457 } 458 459 return nil 460 } 461 462 // LoadIndex loads the index id from backend and returns it. 463 func LoadIndex(ctx context.Context, repo restic.Repository, id restic.ID) (*Index, error) { 464 idx, err := LoadIndexWithDecoder(ctx, repo, id, DecodeIndex) 465 if err == nil { 466 return idx, nil 467 } 468 469 if errors.Cause(err) == ErrOldIndexFormat { 470 fmt.Fprintf(os.Stderr, "index %v has old format\n", id.Str()) 471 return LoadIndexWithDecoder(ctx, repo, id, DecodeOldIndex) 472 } 473 474 return nil, err 475 } 476 477 // SearchKey finds a key with the supplied password, afterwards the config is 478 // read and parsed. It tries at most maxKeys key files in the repo. 479 func (r *Repository) SearchKey(ctx context.Context, password string, maxKeys int) error { 480 key, err := SearchKey(ctx, r, password, maxKeys) 481 if err != nil { 482 return err 483 } 484 485 r.key = key.master 486 r.dataPM.key = key.master 487 r.treePM.key = key.master 488 r.keyName = key.Name() 489 r.cfg, err = restic.LoadConfig(ctx, r) 490 return err 491 } 492 493 // Init creates a new master key with the supplied password, initializes and 494 // saves the repository config. 495 func (r *Repository) Init(ctx context.Context, password string) error { 496 has, err := r.be.Test(ctx, restic.Handle{Type: restic.ConfigFile}) 497 if err != nil { 498 return err 499 } 500 if has { 501 return errors.New("repository master key and config already initialized") 502 } 503 504 cfg, err := restic.CreateConfig() 505 if err != nil { 506 return err 507 } 508 509 return r.init(ctx, password, cfg) 510 } 511 512 // init creates a new master key with the supplied password and uses it to save 513 // the config into the repo. 514 func (r *Repository) init(ctx context.Context, password string, cfg restic.Config) error { 515 key, err := createMasterKey(r, password) 516 if err != nil { 517 return err 518 } 519 520 r.key = key.master 521 r.dataPM.key = key.master 522 r.treePM.key = key.master 523 r.keyName = key.Name() 524 r.cfg = cfg 525 _, err = r.SaveJSONUnpacked(ctx, restic.ConfigFile, cfg) 526 return err 527 } 528 529 // Key returns the current master key. 530 func (r *Repository) Key() *crypto.Key { 531 return r.key 532 } 533 534 // KeyName returns the name of the current key in the backend. 535 func (r *Repository) KeyName() string { 536 return r.keyName 537 } 538 539 // List returns a channel that yields all IDs of type t in the backend. 540 func (r *Repository) List(ctx context.Context, t restic.FileType) <-chan restic.ID { 541 out := make(chan restic.ID) 542 go func() { 543 defer close(out) 544 for strID := range r.be.List(ctx, t) { 545 if id, err := restic.ParseID(strID); err == nil { 546 select { 547 case out <- id: 548 case <-ctx.Done(): 549 return 550 } 551 } 552 } 553 }() 554 return out 555 } 556 557 // ListPack returns the list of blobs saved in the pack id and the length of 558 // the file as stored in the backend. 559 func (r *Repository) ListPack(ctx context.Context, id restic.ID) ([]restic.Blob, int64, error) { 560 h := restic.Handle{Type: restic.DataFile, Name: id.String()} 561 562 blobInfo, err := r.Backend().Stat(ctx, h) 563 if err != nil { 564 return nil, 0, err 565 } 566 567 blobs, err := pack.List(r.Key(), restic.ReaderAt(r.Backend(), h), blobInfo.Size) 568 if err != nil { 569 return nil, 0, err 570 } 571 572 return blobs, blobInfo.Size, nil 573 } 574 575 // Delete calls backend.Delete() if implemented, and returns an error 576 // otherwise. 577 func (r *Repository) Delete(ctx context.Context) error { 578 return r.be.Delete(ctx) 579 } 580 581 // Close closes the repository by closing the backend. 582 func (r *Repository) Close() error { 583 return r.be.Close() 584 } 585 586 // LoadBlob loads a blob of type t from the repository to the buffer. buf must 587 // be large enough to hold the encrypted blob, since it is used as scratch 588 // space. 589 func (r *Repository) LoadBlob(ctx context.Context, t restic.BlobType, id restic.ID, buf []byte) (int, error) { 590 debug.Log("load blob %v into buf (len %v, cap %v)", id.Str(), len(buf), cap(buf)) 591 size, err := r.idx.LookupSize(id, t) 592 if err != nil { 593 return 0, err 594 } 595 596 if cap(buf) < restic.CiphertextLength(int(size)) { 597 return 0, errors.Errorf("buffer is too small for data blob (%d < %d)", cap(buf), restic.CiphertextLength(int(size))) 598 } 599 600 n, err := r.loadBlob(ctx, id, t, buf) 601 if err != nil { 602 return 0, err 603 } 604 buf = buf[:n] 605 606 debug.Log("loaded %d bytes into buf %p", len(buf), buf) 607 608 return len(buf), err 609 } 610 611 // SaveBlob saves a blob of type t into the repository. If id is the null id, it 612 // will be computed and returned. 613 func (r *Repository) SaveBlob(ctx context.Context, t restic.BlobType, buf []byte, id restic.ID) (restic.ID, error) { 614 var i *restic.ID 615 if !id.IsNull() { 616 i = &id 617 } 618 return r.SaveAndEncrypt(ctx, t, buf, i) 619 } 620 621 // LoadTree loads a tree from the repository. 622 func (r *Repository) LoadTree(ctx context.Context, id restic.ID) (*restic.Tree, error) { 623 debug.Log("load tree %v", id.Str()) 624 625 size, err := r.idx.LookupSize(id, restic.TreeBlob) 626 if err != nil { 627 return nil, err 628 } 629 630 debug.Log("size is %d, create buffer", size) 631 buf := restic.NewBlobBuffer(int(size)) 632 633 n, err := r.loadBlob(ctx, id, restic.TreeBlob, buf) 634 if err != nil { 635 return nil, err 636 } 637 buf = buf[:n] 638 639 t := &restic.Tree{} 640 err = json.Unmarshal(buf, t) 641 if err != nil { 642 return nil, err 643 } 644 645 return t, nil 646 } 647 648 // SaveTree stores a tree into the repository and returns the ID. The ID is 649 // checked against the index. The tree is only stored when the index does not 650 // contain the ID. 651 func (r *Repository) SaveTree(ctx context.Context, t *restic.Tree) (restic.ID, error) { 652 buf, err := json.Marshal(t) 653 if err != nil { 654 return restic.ID{}, errors.Wrap(err, "MarshalJSON") 655 } 656 657 // append a newline so that the data is always consistent (json.Encoder 658 // adds a newline after each object) 659 buf = append(buf, '\n') 660 661 id := restic.Hash(buf) 662 if r.idx.Has(id, restic.TreeBlob) { 663 return id, nil 664 } 665 666 _, err = r.SaveBlob(ctx, restic.TreeBlob, buf, id) 667 return id, err 668 }