diff --git a/lib/model/folder_sendrecv.go b/lib/model/folder_sendrecv.go index 7d28790db..0f0c267b9 100644 --- a/lib/model/folder_sendrecv.go +++ b/lib/model/folder_sendrecv.go @@ -1081,51 +1081,20 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, snap *db.Snapshot populateOffsets(file.Blocks) - blocks := make([]protocol.BlockInfo, 0, len(file.Blocks)) + blocks := append([]protocol.BlockInfo{}, file.Blocks...) reused := make([]int, 0, len(file.Blocks)) - // Check for an old temporary file which might have some blocks we could - // reuse. - tempBlocks, err := scanner.HashFile(f.ctx, f.mtimefs, tempName, file.BlockSize(), nil, false) - if err != nil { - var caseErr *fs.ErrCaseConflict - if errors.As(err, &caseErr) { - if rerr := f.mtimefs.Rename(caseErr.Real, tempName); rerr == nil { - tempBlocks, err = scanner.HashFile(f.ctx, f.mtimefs, tempName, file.BlockSize(), nil, false) - } - } + if f.Type != config.FolderTypeReceiveEncrypted { + blocks, reused = f.reuseBlocks(blocks, reused, file, tempName) } - if err == nil { - // Check for any reusable blocks in the temp file - tempCopyBlocks, _ := blockDiff(tempBlocks, file.Blocks) - // block.String() returns a string unique to the block - existingBlocks := make(map[string]struct{}, len(tempCopyBlocks)) - for _, block := range tempCopyBlocks { - existingBlocks[block.String()] = struct{}{} - } - - // Since the blocks are already there, we don't need to get them. - for i, block := range file.Blocks { - _, ok := existingBlocks[block.String()] - if !ok { - blocks = append(blocks, block) - } else { - reused = append(reused, i) - } - } - - // The sharedpullerstate will know which flags to use when opening the - // temp file depending if we are reusing any blocks or not. - if len(reused) == 0 { - // Otherwise, discard the file ourselves in order for the - // sharedpuller not to panic when it fails to exclusively create a - // file which already exists - f.inWritableDir(f.mtimefs.Remove, tempName) - } - } else { - // Copy the blocks, as we don't want to shuffle them on the FileInfo - blocks = append(blocks, file.Blocks...) + // The sharedpullerstate will know which flags to use when opening the + // temp file depending if we are reusing any blocks or not. + if len(reused) == 0 { + // Otherwise, discard the file ourselves in order for the + // sharedpuller not to panic when it fails to exclusively create a + // file which already exists + f.inWritableDir(f.mtimefs.Remove, tempName) } // Reorder blocks @@ -1150,6 +1119,45 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, snap *db.Snapshot copyChan <- cs } +func (f *sendReceiveFolder) reuseBlocks(blocks []protocol.BlockInfo, reused []int, file protocol.FileInfo, tempName string) ([]protocol.BlockInfo, []int) { + // Check for an old temporary file which might have some blocks we could + // reuse. + tempBlocks, err := scanner.HashFile(f.ctx, f.mtimefs, tempName, file.BlockSize(), nil, false) + if err != nil { + var caseErr *fs.ErrCaseConflict + if errors.As(err, &caseErr) { + if rerr := f.mtimefs.Rename(caseErr.Real, tempName); rerr == nil { + tempBlocks, err = scanner.HashFile(f.ctx, f.mtimefs, tempName, file.BlockSize(), nil, false) + } + } + } + if err != nil { + return blocks, reused + } + + // Check for any reusable blocks in the temp file + tempCopyBlocks, _ := blockDiff(tempBlocks, file.Blocks) + + // block.String() returns a string unique to the block + existingBlocks := make(map[string]struct{}, len(tempCopyBlocks)) + for _, block := range tempCopyBlocks { + existingBlocks[block.String()] = struct{}{} + } + + // Since the blocks are already there, we don't need to get them. + blocks = blocks[:0] + for i, block := range file.Blocks { + _, ok := existingBlocks[block.String()] + if !ok { + blocks = append(blocks, block) + } else { + reused = append(reused, i) + } + } + + return blocks, reused +} + // blockDiff returns lists of common and missing (to transform src into tgt) // blocks. Both block lists must have been created with the same block size. func blockDiff(src, tgt []protocol.BlockInfo) ([]protocol.BlockInfo, []protocol.BlockInfo) {