mirror of
https://github.com/rclone/rclone.git
synced 2024-11-07 17:14:44 +01:00
crypt: Fix data corruption on seek
This was caused by failing to reset the internal buffer on seek so old data was read first before the new data. The unit tests didn't detect this because they were reading to the end of the file to check integrity and thus emptying the internal buffer. Both code and unit tests were fixed up.
This commit is contained in:
parent
54d99d6ab2
commit
9d2dd2c49a
@ -604,17 +604,6 @@ func (fh *decrypter) Seek(offset int64, whence int) (int64, error) {
|
||||
return 0, fh.err
|
||||
}
|
||||
|
||||
// Can we seek it directly?
|
||||
if do, ok := fh.rc.(io.Seeker); ok {
|
||||
_, err := do.Seek(offset, 0)
|
||||
if err != nil {
|
||||
return 0, fh.finish(err)
|
||||
}
|
||||
} else {
|
||||
// if not reopen with seek
|
||||
_ = fh.rc.Close() // close underlying file
|
||||
fh.rc = nil
|
||||
|
||||
// blocks we need to seek, plus bytes we need to discard
|
||||
blocks, discard := offset/blockDataSize, offset%blockDataSize
|
||||
|
||||
@ -625,6 +614,18 @@ func (fh *decrypter) Seek(offset int64, whence int) (int64, error) {
|
||||
fh.nonce = fh.initialNonce
|
||||
fh.nonce.add(uint64(blocks))
|
||||
|
||||
// Can we seek underlying stream directly?
|
||||
if do, ok := fh.rc.(io.Seeker); ok {
|
||||
// Seek underlying stream directly
|
||||
_, err := do.Seek(underlyingOffset, 0)
|
||||
if err != nil {
|
||||
return 0, fh.finish(err)
|
||||
}
|
||||
} else {
|
||||
// if not reopen with seek
|
||||
_ = fh.rc.Close() // close underlying file
|
||||
fh.rc = nil
|
||||
|
||||
// Re-open the underlying object with the offset given
|
||||
rc, err := fh.open(underlyingOffset)
|
||||
if err != nil {
|
||||
@ -633,13 +634,17 @@ func (fh *decrypter) Seek(offset int64, whence int) (int64, error) {
|
||||
|
||||
// Set the file handle
|
||||
fh.rc = rc
|
||||
}
|
||||
|
||||
// Empty the buffer
|
||||
fh.bufIndex = 0
|
||||
fh.bufSize = 0
|
||||
|
||||
// Discard excess bytes
|
||||
_, err = io.CopyN(ioutil.Discard, fh, discard)
|
||||
_, err := io.CopyN(ioutil.Discard, fh, discard)
|
||||
if err != nil {
|
||||
return 0, fh.finish(err)
|
||||
}
|
||||
}
|
||||
|
||||
return offset, nil
|
||||
}
|
||||
|
@ -881,15 +881,26 @@ func TestNewDecrypterSeek(t *testing.T) {
|
||||
return ioutil.NopCloser(bytes.NewBuffer(ciphertext[int(underlyingOffset):])), nil
|
||||
}
|
||||
|
||||
inBlock := make([]byte, 1024)
|
||||
|
||||
// Check the seek worked by reading a block and checking it
|
||||
// against what it should be
|
||||
check := func(rc ReadSeekCloser, offset int) {
|
||||
n, err := io.ReadFull(rc, inBlock)
|
||||
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
seekedDecrypted := inBlock[:n]
|
||||
|
||||
require.Equal(t, plaintext[offset:offset+n], seekedDecrypted)
|
||||
}
|
||||
|
||||
// Now try decoding it with a open/seek
|
||||
for _, offset := range trials {
|
||||
rc, err := c.DecryptDataSeek(open, int64(offset))
|
||||
assert.NoError(t, err)
|
||||
|
||||
seekedDecrypted, err := ioutil.ReadAll(rc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, plaintext[offset:], seekedDecrypted)
|
||||
check(rc, offset)
|
||||
}
|
||||
|
||||
// Now try decoding it with a single open and lots of seeks
|
||||
@ -898,10 +909,7 @@ func TestNewDecrypterSeek(t *testing.T) {
|
||||
_, err := rc.Seek(int64(offset), 0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
seekedDecrypted, err := ioutil.ReadAll(rc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, plaintext[offset:], seekedDecrypted)
|
||||
check(rc, offset)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user