diff --git a/src/duplicacy_snapshot.go b/src/duplicacy_snapshot.go index 4fe8df7..e7511b6 100644 --- a/src/duplicacy_snapshot.go +++ b/src/duplicacy_snapshot.go @@ -15,7 +15,6 @@ import ( "strings" "time" "sort" - "bytes" "github.com/vmihailenco/msgpack" @@ -52,6 +51,7 @@ type Snapshot struct { // CreateEmptySnapshot creates an empty snapshot. func CreateEmptySnapshot(id string) (snapshto *Snapshot) { return &Snapshot{ + Version: 1, ID: id, Revision: 0, StartTime: time.Now().Unix(), @@ -112,22 +112,21 @@ func (snapshot *Snapshot)ListRemoteFiles(config *Config, chunkOperator *ChunkOpe } var chunk *Chunk - reader := sequenceReader{ - sequence: snapshot.FileSequence, - buffer: new(bytes.Buffer), - refillFunc: func(chunkHash string) []byte { - if chunk != nil { - config.PutChunk(chunk) - } - chunk = chunkOperator.Download(chunkHash, 0, true) - return chunk.GetBytes() - }, - } + reader := NewSequenceReader(snapshot.FileSequence, func(chunkHash string) []byte { + if chunk != nil { + config.PutChunk(chunk) + } + chunk = chunkOperator.Download(chunkHash, 0, true) + return chunk.GetBytes() + }) - if snapshot.Version == 0 { + // Normally if Version is 0 then the snapshot is created by CLI v2 but unfortunately CLI 3.0.1 does not set the + // version bit correctly when copying old backups. So we need to check the first byte -- if it is '[' then it is + // the old format. The new format starts with a string encoded in msgpack and the first byte can't be '['. + if snapshot.Version == 0 || reader.GetFirstByte() == '['{ LOG_INFO("SNAPSHOT_VERSION", "snapshot %s at revision %d is encoded in an old version format", snapshot.ID, snapshot.Revision) files := make([]*Entry, 0) - decoder := json.NewDecoder(&reader) + decoder := json.NewDecoder(reader) // read open bracket _, err := decoder.Token() @@ -156,7 +155,7 @@ func (snapshot *Snapshot)ListRemoteFiles(config *Config, chunkOperator *ChunkOpe } } } else if snapshot.Version == 1 { - decoder := msgpack.NewDecoder(&reader) + decoder := msgpack.NewDecoder(reader) lastEndChunk := 0 @@ -434,7 +433,7 @@ func (snapshot *Snapshot) MarshalJSON() ([]byte, error) { object := make(map[string]interface{}) - object["version"] = 1 + object["version"] = snapshot.Version object["id"] = snapshot.ID object["revision"] = snapshot.Revision object["options"] = snapshot.Options diff --git a/src/duplicacy_snapshotmanager.go b/src/duplicacy_snapshotmanager.go index c44c194..1a94f09 100644 --- a/src/duplicacy_snapshotmanager.go +++ b/src/duplicacy_snapshotmanager.go @@ -249,17 +249,27 @@ func (manager *SnapshotManager) DownloadSnapshot(snapshotID string, revision int // the memory before passing them to the json unmarshaller. type sequenceReader struct { sequence []string - buffer *bytes.Buffer + buffer *bytes.Reader index int refillFunc func(hash string) []byte } +func NewSequenceReader(sequence []string, refillFunc func(hash string) []byte) *sequenceReader { + newData := refillFunc(sequence[0]) + return &sequenceReader{ + sequence: sequence, + buffer: bytes.NewReader(newData), + index: 1, + refillFunc: refillFunc, + } +} + // Read reads a new chunk using the refill function when there is no more data in the buffer func (reader *sequenceReader) Read(data []byte) (n int, err error) { - if len(reader.buffer.Bytes()) == 0 { + if reader.buffer.Len() == 0 { if reader.index < len(reader.sequence) { newData := reader.refillFunc(reader.sequence[reader.index]) - reader.buffer.Write(newData) + reader.buffer = bytes.NewReader(newData) reader.index++ } else { return 0, io.EOF @@ -269,6 +279,16 @@ func (reader *sequenceReader) Read(data []byte) (n int, err error) { return reader.buffer.Read(data) } +func (reader *sequenceReader) GetFirstByte() byte { + b, err := reader.buffer.ReadByte() + reader.buffer.UnreadByte() + if err != nil { + return 0 + } else { + return b + } +} + func (manager *SnapshotManager) CreateChunkOperator(resurrect bool, rewriteChunks bool, threads int, allowFailures bool) { if manager.chunkOperator == nil { manager.chunkOperator = CreateChunkOperator(manager.config, manager.storage, manager.snapshotCache, resurrect, rewriteChunks, threads, allowFailures)