Skip to content

Copy only the remaining bytes in FileInfo.moveToNewLocation - #4916

Open
henriquejsza wants to merge 1 commit into
apache:masterfrom
henriquejsza:bk-4736-fileinfo-move-partial-transfer
Open

henriquejsza wants to merge 1 commit into
apache:masterfrom
henriquejsza:bk-4736-fileinfo-move-partial-transfer

Conversation

@henriquejsza

Copy link
Copy Markdown

Fix #4736

Motivation

FileInfo.moveToNewLocation copies the ledger index file to a new location with
FileChannel.transferTo:

long written = 0;
while (written < size) {
    long count = fc.transferTo(written, size, newFc);
    ...
    written += count;
}
if (written <= 0 && size > 0) {
    throw new IOException("Copying to new location " + rlocFile + " failed");
}

transferTo may transfer fewer bytes than requested. Because the count passed to
every call is size (not the number of bytes still to copy), a call after a
partial transfer reads from position written for up to size more bytes. When
the source file holds more than size bytes, that copies past the requested
range, so the new location can end up with more than size bytes. This matters
in practice: moveToNewLocation is called with getSizeSinceLastWrite(), which
can be smaller than the actual file size.

The post-loop written <= 0 && size > 0 check reported in the issue is
unreachable: when size > 0 the loop runs at least once and either throws or
increments written to a positive value.

Changes

  • Pass the remaining byte count (size - written) to transferTo on each
    iteration so exactly size bytes are copied.
  • Extract the copy loop into a small package-private transferFully helper and
    remove the unreachable post-loop check.
  • Add FileInfoTest covering a partial transfer, asserting that exactly size
    bytes are copied.

FileChannel.transferTo may transfer fewer bytes than requested. The copy
loop passed the full size as the count on every call, so after a partial
transfer a later call could read past the requested range and copy more
than size bytes when the source index file is larger than size (the common
case, since moveToNewLocation is called with getSizeSinceLastWrite()).

Pass the remaining byte count (size - written) on each call instead. This
also removes the now provably unreachable post-loop 'written <= 0 && size > 0'
check reported in the issue.

Add FileInfoTest covering a partial-transfer copy.

Signed-off-by: henriquejsza <henriquejsza@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Potential unreachable condition in FileInfo.moveToNewLocation

1 participant