| 
									
										
										
										
											2015-05-09 23:52:03 +02:00
										 |  |  | package repository | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2015-09-23 22:27:48 +02:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	"io" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2015-10-12 23:59:17 +02:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/restic/restic/backend" | 
					
						
							| 
									
										
										
										
											2015-07-19 15:16:16 +02:00
										 |  |  | 	"github.com/restic/restic/crypto" | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	"github.com/restic/restic/debug" | 
					
						
							|  |  |  | 	"github.com/restic/restic/pack" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Index holds a lookup table for id -> pack. | 
					
						
							|  |  |  | type Index struct { | 
					
						
							|  |  |  | 	m    sync.Mutex | 
					
						
							| 
									
										
										
										
											2015-07-26 00:06:40 +02:00
										 |  |  | 	pack map[backend.ID]indexEntry | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | 	final      bool // set to true for all indexes read from the backend ("finalized") | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 	supersedes backend.IDs | 
					
						
							| 
									
										
										
										
											2015-10-12 23:59:17 +02:00
										 |  |  | 	created    time.Time | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type indexEntry struct { | 
					
						
							|  |  |  | 	tpe    pack.BlobType | 
					
						
							| 
									
										
										
										
											2015-10-25 14:35:08 +01:00
										 |  |  | 	packID backend.ID | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	offset uint | 
					
						
							|  |  |  | 	length uint | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewIndex returns a new index. | 
					
						
							|  |  |  | func NewIndex() *Index { | 
					
						
							|  |  |  | 	return &Index{ | 
					
						
							| 
									
										
										
										
											2015-10-12 23:59:17 +02:00
										 |  |  | 		pack:    make(map[backend.ID]indexEntry), | 
					
						
							|  |  |  | 		created: time.Now(), | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-25 14:35:08 +01:00
										 |  |  | func (idx *Index) store(t pack.BlobType, id backend.ID, pack backend.ID, offset, length uint) { | 
					
						
							| 
									
										
										
										
											2015-07-26 00:06:40 +02:00
										 |  |  | 	idx.pack[id] = indexEntry{ | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 		tpe:    t, | 
					
						
							|  |  |  | 		packID: pack, | 
					
						
							|  |  |  | 		offset: offset, | 
					
						
							|  |  |  | 		length: length, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | // Final returns true iff the index is already written to the repository, it is | 
					
						
							|  |  |  | // finalized. | 
					
						
							|  |  |  | func (idx *Index) Final() bool { | 
					
						
							|  |  |  | 	idx.m.Lock() | 
					
						
							|  |  |  | 	defer idx.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return idx.final | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 23:59:17 +02:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	indexMinBlobs = 20 | 
					
						
							|  |  |  | 	indexMaxBlobs = 2000 | 
					
						
							|  |  |  | 	indexMinAge   = 2 * time.Minute | 
					
						
							|  |  |  | 	indexMaxAge   = 15 * time.Minute | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-25 15:05:22 +01:00
										 |  |  | // IndexFull returns true iff the index is "full enough" to be saved as a preliminary index. | 
					
						
							|  |  |  | var IndexFull = func(idx *Index) bool { | 
					
						
							| 
									
										
										
										
											2015-10-12 23:59:17 +02:00
										 |  |  | 	idx.m.Lock() | 
					
						
							|  |  |  | 	defer idx.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debug.Log("Index.Full", "checking whether index %p is full", idx) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	packs := len(idx.pack) | 
					
						
							|  |  |  | 	age := time.Now().Sub(idx.created) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if age > indexMaxAge { | 
					
						
							|  |  |  | 		debug.Log("Index.Full", "index %p is old enough", idx, age) | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if packs < indexMinBlobs || age < indexMinAge { | 
					
						
							|  |  |  | 		debug.Log("Index.Full", "index %p only has %d packs or is too young (%v)", idx, packs, age) | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if packs > indexMaxBlobs { | 
					
						
							|  |  |  | 		debug.Log("Index.Full", "index %p has %d packs", idx, packs) | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debug.Log("Index.Full", "index %p is not full", idx) | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-23 22:27:48 +02:00
										 |  |  | // Store remembers the id and pack in the index. An existing entry will be | 
					
						
							|  |  |  | // silently overwritten. | 
					
						
							| 
									
										
										
										
											2015-10-25 14:35:08 +01:00
										 |  |  | func (idx *Index) Store(t pack.BlobType, id backend.ID, pack backend.ID, offset, length uint) { | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	idx.m.Lock() | 
					
						
							|  |  |  | 	defer idx.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | 	if idx.final { | 
					
						
							|  |  |  | 		panic("store new item in finalized index") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 	debug.Log("Index.Store", "pack %v contains id %v (%v), offset %v, length %v", | 
					
						
							|  |  |  | 		pack.Str(), id.Str(), t, offset, length) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | 	idx.store(t, id, pack, offset, length) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Lookup returns the pack for the id. | 
					
						
							| 
									
										
										
										
											2015-10-25 14:35:08 +01:00
										 |  |  | func (idx *Index) Lookup(id backend.ID) (packID backend.ID, tpe pack.BlobType, offset, length uint, err error) { | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	idx.m.Lock() | 
					
						
							|  |  |  | 	defer idx.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-26 00:06:40 +02:00
										 |  |  | 	if p, ok := idx.pack[id]; ok { | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 		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()) | 
					
						
							| 
									
										
										
										
											2015-10-25 14:35:08 +01:00
										 |  |  | 	return backend.ID{}, pack.Data, 0, 0, fmt.Errorf("id %v not found in index", id) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Has returns true iff the id is listed in the index. | 
					
						
							|  |  |  | func (idx *Index) Has(id backend.ID) bool { | 
					
						
							|  |  |  | 	_, _, _, _, err := idx.Lookup(id) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-19 15:16:16 +02:00
										 |  |  | // LookupSize returns the length of the cleartext content behind the | 
					
						
							|  |  |  | // given id | 
					
						
							|  |  |  | func (idx *Index) LookupSize(id backend.ID) (cleartextLength uint, err error) { | 
					
						
							|  |  |  | 	_, _, _, encryptedLength, err := idx.Lookup(id) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return encryptedLength - crypto.Extension, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | // Merge loads all items from other into idx. | 
					
						
							|  |  |  | func (idx *Index) Merge(other *Index) { | 
					
						
							|  |  |  | 	debug.Log("Index.Merge", "Merge index with %p", other) | 
					
						
							|  |  |  | 	idx.m.Lock() | 
					
						
							|  |  |  | 	defer idx.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for k, v := range other.pack { | 
					
						
							|  |  |  | 		if _, ok := idx.pack[k]; ok { | 
					
						
							| 
									
										
										
										
											2015-08-08 13:47:08 +02:00
										 |  |  | 			debug.Log("Index.Merge", "index already has key %v, updating", k.Str()) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		idx.pack[k] = v | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	debug.Log("Index.Merge", "done merging index") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | // Supersedes returns the list of indexes this index supersedes, if any. | 
					
						
							|  |  |  | func (idx *Index) Supersedes() backend.IDs { | 
					
						
							|  |  |  | 	return idx.supersedes | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-29 00:22:25 +02:00
										 |  |  | // PackedBlob is a blob already saved within a pack. | 
					
						
							|  |  |  | type PackedBlob struct { | 
					
						
							|  |  |  | 	pack.Blob | 
					
						
							|  |  |  | 	PackID backend.ID | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | // Each returns a channel that yields all blobs known to the index. If done is | 
					
						
							|  |  |  | // closed, the background goroutine terminates. This blocks any modification of | 
					
						
							|  |  |  | // the index. | 
					
						
							| 
									
										
										
										
											2015-06-29 00:22:25 +02:00
										 |  |  | func (idx *Index) Each(done chan struct{}) <-chan PackedBlob { | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	idx.m.Lock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-29 00:22:25 +02:00
										 |  |  | 	ch := make(chan PackedBlob) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		defer idx.m.Unlock() | 
					
						
							|  |  |  | 		defer func() { | 
					
						
							|  |  |  | 			close(ch) | 
					
						
							|  |  |  | 		}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-26 00:06:40 +02:00
										 |  |  | 		for id, blob := range idx.pack { | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 			select { | 
					
						
							|  |  |  | 			case <-done: | 
					
						
							|  |  |  | 				return | 
					
						
							| 
									
										
										
										
											2015-06-29 00:22:25 +02:00
										 |  |  | 			case ch <- PackedBlob{ | 
					
						
							|  |  |  | 				Blob: pack.Blob{ | 
					
						
							|  |  |  | 					ID:     id, | 
					
						
							|  |  |  | 					Offset: blob.offset, | 
					
						
							|  |  |  | 					Type:   blob.tpe, | 
					
						
							| 
									
										
										
										
											2015-08-08 13:47:08 +02:00
										 |  |  | 					Length: blob.length, | 
					
						
							| 
									
										
										
										
											2015-06-29 00:22:25 +02:00
										 |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2015-10-25 14:35:08 +01:00
										 |  |  | 				PackID: blob.packID, | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 			}: | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ch | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-25 15:28:01 +01:00
										 |  |  | // Packs returns all packs in this index | 
					
						
							|  |  |  | func (idx *Index) Packs() backend.IDSet { | 
					
						
							|  |  |  | 	idx.m.Lock() | 
					
						
							|  |  |  | 	defer idx.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	packs := backend.NewIDSet() | 
					
						
							|  |  |  | 	for _, entry := range idx.pack { | 
					
						
							|  |  |  | 		packs.Insert(entry.packID) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return packs | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | // Count returns the number of blobs of type t in the index. | 
					
						
							|  |  |  | func (idx *Index) Count(t pack.BlobType) (n uint) { | 
					
						
							|  |  |  | 	debug.Log("Index.Count", "counting blobs of type %v", t) | 
					
						
							|  |  |  | 	idx.m.Lock() | 
					
						
							|  |  |  | 	defer idx.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for id, blob := range idx.pack { | 
					
						
							|  |  |  | 		if blob.tpe == t { | 
					
						
							|  |  |  | 			n++ | 
					
						
							| 
									
										
										
										
											2015-08-08 13:47:08 +02:00
										 |  |  | 			debug.Log("Index.Count", "  blob %v counted: %v", id.Str(), blob) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | type packJSON struct { | 
					
						
							| 
									
										
										
										
											2015-07-26 00:06:40 +02:00
										 |  |  | 	ID    backend.ID `json:"id"` | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	Blobs []blobJSON `json:"blobs"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type blobJSON struct { | 
					
						
							| 
									
										
										
										
											2015-07-26 00:06:40 +02:00
										 |  |  | 	ID     backend.ID    `json:"id"` | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	Type   pack.BlobType `json:"type"` | 
					
						
							|  |  |  | 	Offset uint          `json:"offset"` | 
					
						
							|  |  |  | 	Length uint          `json:"length"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-25 14:25:48 +01:00
										 |  |  | // generatePackList returns a list of packs. | 
					
						
							|  |  |  | func (idx *Index) generatePackList() ([]*packJSON, error) { | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	list := []*packJSON{} | 
					
						
							| 
									
										
										
										
											2015-07-26 00:06:40 +02:00
										 |  |  | 	packs := make(map[backend.ID]*packJSON) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for id, blob := range idx.pack { | 
					
						
							| 
									
										
										
										
											2015-10-25 14:35:08 +01:00
										 |  |  | 		if blob.packID.IsNull() { | 
					
						
							|  |  |  | 			panic("null pack id") | 
					
						
							| 
									
										
										
										
											2015-10-11 20:45:42 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-11 19:25:02 +02:00
										 |  |  | 		debug.Log("Index.generatePackList", "handle blob %v", id.Str()) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-25 17:05:45 +02:00
										 |  |  | 		if blob.packID.IsNull() { | 
					
						
							| 
									
										
										
										
											2015-05-16 14:05:19 +02:00
										 |  |  | 			debug.Log("Index.generatePackList", "blob %q has no packID! (type %v, offset %v, length %v)", | 
					
						
							| 
									
										
										
										
											2015-08-08 13:47:08 +02:00
										 |  |  | 				id.Str(), blob.tpe, blob.offset, blob.length) | 
					
						
							| 
									
										
										
										
											2015-05-16 14:05:19 +02:00
										 |  |  | 			return nil, fmt.Errorf("unable to serialize index: pack for blob %v hasn't been written yet", id) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 		// see if pack is already in map | 
					
						
							| 
									
										
										
										
											2015-10-25 14:35:08 +01:00
										 |  |  | 		p, ok := packs[blob.packID] | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 		if !ok { | 
					
						
							|  |  |  | 			// else create new pack | 
					
						
							| 
									
										
										
										
											2015-10-25 14:35:08 +01:00
										 |  |  | 			p = &packJSON{ID: blob.packID} | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// and append it to the list and map | 
					
						
							|  |  |  | 			list = append(list, p) | 
					
						
							|  |  |  | 			packs[p.ID] = p | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// add blob | 
					
						
							|  |  |  | 		p.Blobs = append(p.Blobs, blobJSON{ | 
					
						
							|  |  |  | 			ID:     id, | 
					
						
							|  |  |  | 			Type:   blob.tpe, | 
					
						
							|  |  |  | 			Offset: blob.offset, | 
					
						
							|  |  |  | 			Length: blob.length, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-16 14:05:19 +02:00
										 |  |  | 	debug.Log("Index.generatePackList", "done") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return list, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | type jsonIndex struct { | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 	Supersedes backend.IDs `json:"supersedes,omitempty"` | 
					
						
							|  |  |  | 	Packs      []*packJSON `json:"packs"` | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type jsonOldIndex []*packJSON | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | // Encode writes the JSON serialization of the index to the writer w. | 
					
						
							|  |  |  | func (idx *Index) Encode(w io.Writer) error { | 
					
						
							|  |  |  | 	debug.Log("Index.Encode", "encoding index") | 
					
						
							|  |  |  | 	idx.m.Lock() | 
					
						
							|  |  |  | 	defer idx.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return idx.encode(w) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // encode writes the JSON serialization of the index to the writer w. | 
					
						
							|  |  |  | func (idx *Index) encode(w io.Writer) error { | 
					
						
							|  |  |  | 	debug.Log("Index.encode", "encoding index") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-25 14:25:48 +01:00
										 |  |  | 	list, err := idx.generatePackList() | 
					
						
							| 
									
										
										
										
											2015-05-16 14:05:19 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	enc := json.NewEncoder(w) | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | 	idxJSON := jsonIndex{ | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | 		Supersedes: idx.supersedes, | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | 		Packs:      list, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return enc.Encode(idxJSON) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | // Finalize sets the index to final and writes the JSON serialization to w. | 
					
						
							|  |  |  | func (idx *Index) Finalize(w io.Writer) error { | 
					
						
							| 
									
										
										
										
											2015-05-16 14:05:19 +02:00
										 |  |  | 	debug.Log("Index.Encode", "encoding index") | 
					
						
							|  |  |  | 	idx.m.Lock() | 
					
						
							|  |  |  | 	defer idx.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | 	idx.final = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return idx.encode(w) | 
					
						
							| 
									
										
										
										
											2015-05-16 14:05:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Dump writes the pretty-printed JSON representation of the index to w. | 
					
						
							|  |  |  | func (idx *Index) Dump(w io.Writer) error { | 
					
						
							|  |  |  | 	debug.Log("Index.Dump", "dumping index") | 
					
						
							|  |  |  | 	idx.m.Lock() | 
					
						
							|  |  |  | 	defer idx.m.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-25 14:25:48 +01:00
										 |  |  | 	list, err := idx.generatePackList() | 
					
						
							| 
									
										
										
										
											2015-05-16 14:05:19 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	buf, err := json.MarshalIndent(list, "", "  ") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_, err = w.Write(append(buf, '\n')) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debug.Log("Index.Dump", "done") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-26 21:58:03 +02:00
										 |  |  | // isErrOldIndex returns true if the error may be caused by an old index | 
					
						
							|  |  |  | // format. | 
					
						
							|  |  |  | func isErrOldIndex(err error) bool { | 
					
						
							|  |  |  | 	if e, ok := err.(*json.UnmarshalTypeError); ok && e.Value == "array" { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ErrOldIndexFormat means an index with the old format was detected. | 
					
						
							|  |  |  | var ErrOldIndexFormat = errors.New("index has old format") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | // DecodeIndex loads and unserializes an index from rd. | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | func DecodeIndex(rd io.Reader) (idx *Index, err error) { | 
					
						
							| 
									
										
										
										
											2015-04-26 17:44:38 +02:00
										 |  |  | 	debug.Log("Index.DecodeIndex", "Start decoding index") | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | 	idxJSON := jsonIndex{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dec := json.NewDecoder(rd) | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 	err = dec.Decode(&idxJSON) | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 		debug.Log("Index.DecodeIndex", "Error %v", err) | 
					
						
							| 
									
										
										
										
											2015-07-26 21:58:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if isErrOldIndex(err) { | 
					
						
							|  |  |  | 			debug.Log("Index.DecodeIndex", "index is probably old format, trying that") | 
					
						
							|  |  |  | 			err = ErrOldIndexFormat | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 	idx = NewIndex() | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | 	for _, pack := range idxJSON.Packs { | 
					
						
							|  |  |  | 		for _, blob := range pack.Blobs { | 
					
						
							| 
									
										
										
										
											2015-10-25 14:35:08 +01:00
										 |  |  | 			idx.store(blob.Type, blob.ID, pack.ID, blob.Offset, blob.Length) | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 	idx.supersedes = idxJSON.Supersedes | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | 	idx.final = true | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	debug.Log("Index.DecodeIndex", "done") | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 	return idx, err | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DecodeOldIndex loads and unserializes an index in the old format from rd. | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | func DecodeOldIndex(rd io.Reader) (idx *Index, err error) { | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | 	debug.Log("Index.DecodeOldIndex", "Start decoding old index") | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	list := []*packJSON{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dec := json.NewDecoder(rd) | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 	err = dec.Decode(&list) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | 		debug.Log("Index.DecodeOldIndex", "Error %#v", err) | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 	idx = NewIndex() | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 	for _, pack := range list { | 
					
						
							|  |  |  | 		for _, blob := range pack.Blobs { | 
					
						
							| 
									
										
										
										
											2015-10-25 14:35:08 +01:00
										 |  |  | 			idx.store(blob.Type, blob.ID, pack.ID, blob.Offset, blob.Length) | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-26 00:40:00 +02:00
										 |  |  | 	debug.Log("Index.DecodeOldIndex", "done") | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 	return idx, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ConvertIndexes loads all indexes from the repo and converts them to the new | 
					
						
							|  |  |  | // format (if necessary). When the conversion is succcessful, the old indexes | 
					
						
							|  |  |  | // are removed. | 
					
						
							|  |  |  | func ConvertIndexes(repo *Repository) error { | 
					
						
							|  |  |  | 	debug.Log("ConvertIndexes", "start") | 
					
						
							|  |  |  | 	done := make(chan struct{}) | 
					
						
							|  |  |  | 	defer close(done) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for id := range repo.List(backend.Index, done) { | 
					
						
							|  |  |  | 		debug.Log("ConvertIndexes", "checking index %v", id.Str()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		newID, err := ConvertIndex(repo, id) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			debug.Log("ConvertIndexes", "Converting index %v returns error: %v", id.Str(), err) | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if id != newID { | 
					
						
							|  |  |  | 			debug.Log("ConvertIndexes", "index %v converted to new format as %v", id.Str(), newID.Str()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debug.Log("ConvertIndexes", "done") | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	blob, err := repo.CreateEncryptedBlob(backend.Index) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return id, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	idx.supersedes = backend.IDs{id} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 22:34:12 +02:00
										 |  |  | 	err = idx.Encode(blob) | 
					
						
							| 
									
										
										
										
											2015-08-08 12:22:17 +02:00
										 |  |  | 	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 | 
					
						
							| 
									
										
										
										
											2015-04-26 17:10:31 +02:00
										 |  |  | } |