diff options
author | 2024-01-30 05:49:23 +0000 | |
---|---|---|
committer | 2024-01-30 19:13:18 +0000 | |
commit | 451cfcf9d09515ef60d76bd8551fc68c6e3bf621 (patch) | |
tree | 1aeae33920e5c84d3b5c7694a8159a363fdddff5 | |
parent | 33e9f1a70d5f58639b524f40bf39a8e233c04ba8 (diff) |
Revert "Preserve file sparsity in FdFile::Copy"
This reverts commit 7e0689862ab045327374985711f8d43cffdee271.
Reason for revert: Broke odrefresh on kernel version 5.15.131-android14-11-g6a84e0e0f704-ab11305240
Bug: 318706268
Change-Id: I11daf4ee0f26fece99890a4637ede61de962ad4b
-rw-r--r-- | libartbase/base/unix_file/fd_file.cc | 77 | ||||
-rw-r--r-- | libartbase/base/unix_file/fd_file_test.cc | 128 |
2 files changed, 6 insertions, 199 deletions
diff --git a/libartbase/base/unix_file/fd_file.cc b/libartbase/base/unix_file/fd_file.cc index bb34b7538c..c5307e249b 100644 --- a/libartbase/base/unix_file/fd_file.cc +++ b/libartbase/base/unix_file/fd_file.cc @@ -483,83 +483,16 @@ bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { if (size == 0) { return true; } - - off_t out_length = lseek(Fd(), 0, SEEK_END); - if (out_length != 0) { - // Output file is not empty. - // - // Currently there is no use-case for copying into non-empty files. To support the use-case, - // when needed, the implementation should handle the case in which the input file has an empty - // block whereas the corresponding location in the output file already contains some data. The - // current implementation would preserve that data instead of overwriting it with zeroes. - errno = EINVAL; - return false; - } #ifdef __linux__ // Use sendfile(), available for files since linux kernel 2.6.33. - // Use lseek with SEEK_HOLE/SEEK_DATA to skip empty blocks, available since kernel 3.1. - int64_t offset_diff = -off; off_t end = off + sz; - off_t out_offset = 0; while (off != end) { - off = lseek(input_file->Fd(), off, SEEK_DATA); - if (off == -1) { - if (errno != ENXIO) { - return false; - } - - // This is only expected to happen when there is no non-empty block between the current - // position and the end of the input file. In this case, no more copying is to be done, and - // the output file length going to be adjusted by `SetLength` below. - off = end; - } - DCHECK_GE(off, 0); - - if (off >= end) { - // Next non-empty block is beyond the location pointed by `end` - no more data to be copied. - // Update the offsets for both files and set the output file length as expected - as in, to - // the values which would have been set if the block at the end of output file would have - // been non-empty. - off = lseek(input_file->Fd(), end, SEEK_SET); - if (off == -1) { - return false; - } - DCHECK_EQ(off, end); - - out_offset = lseek(Fd(), end + offset_diff, SEEK_SET); - if (out_offset == -1) { - return false; - } - DCHECK_EQ(out_offset, end + offset_diff); - - if (SetLength(out_offset) != 0) { - return false; - } - } else { - // Find the position of the next empty block. - // It could be a gap within the input file or the implicit empty block at the end of the file. - off_t hole_offset = lseek(input_file->Fd(), off, SEEK_HOLE); - if (hole_offset == -1) { - return false; - } - DCHECK_GT(hole_offset, off); - - // Update output position to match the position of the data to be written from the input file. - out_offset = lseek(Fd(), off + offset_diff, SEEK_SET); - if (out_offset == -1) { - return false; - } - DCHECK_EQ(out_offset, off + offset_diff); - - // Write the data. If end points in the middle of the current data block, only write the data - // until that position. - int result = TEMP_FAILURE_RETRY( - sendfile(Fd(), input_file->Fd(), &off, std::min(end, hole_offset) - off)); - if (result == -1) { - return false; - } - // Ignore the number of bytes in `result`, sendfile() already updated `off`. + int result = TEMP_FAILURE_RETRY( + sendfile(Fd(), input_file->Fd(), &off, end - off)); + if (result == -1) { + return false; } + // Ignore the number of bytes in `result`, sendfile() already updated `off`. } #else if (lseek(input_file->Fd(), off, SEEK_SET) != off) { diff --git a/libartbase/base/unix_file/fd_file_test.cc b/libartbase/base/unix_file/fd_file_test.cc index f89eab9da8..92f8308b8c 100644 --- a/libartbase/base/unix_file/fd_file_test.cc +++ b/libartbase/base/unix_file/fd_file_test.cc @@ -14,11 +14,8 @@ * limitations under the License. */ -#include <string.h> - #include "base/common_art_test.h" // For ScratchFile #include "base/file_utils.h" -#include "base/stl_util.h" #include "gtest/gtest.h" #include "fd_file.h" #include "random_access_file_test.h" @@ -173,7 +170,7 @@ TEST_F(FdFileTest, Copy) { ASSERT_EQ(static_cast<int64_t>(sizeof(src_data)), src.GetLength()); art::ScratchFile dest_tmp; - FdFile dest(dest_tmp.GetFilename(), O_RDWR, false); + FdFile dest(src_tmp.GetFilename(), O_RDWR, false); ASSERT_GE(dest.Fd(), 0); ASSERT_TRUE(dest.IsOpened()); @@ -189,129 +186,6 @@ TEST_F(FdFileTest, Copy) { ASSERT_EQ(0, src.Close()); } -#ifdef __linux__ -TEST_F(FdFileTest, CopySparse) { - // The test validates that FdFile::Copy preserves sparsity of the file i.e. doesn't write zeroes - // to the output file in locations corresponding to empty blocks in the input file. - constexpr size_t kChunkSize = 64 * art::KB; - - std::vector<int8_t> data_buffer(/*count=*/kChunkSize, /*value=*/1); - std::vector<int8_t> zero_buffer(/*count=*/kChunkSize, /*value=*/0); - std::vector<int8_t> check_buffer(/*count=*/kChunkSize); - - auto verify = [&](size_t empty_prefix, size_t empty_suffix, size_t input_offset) { - constexpr size_t kEstimateMaxFileMetadataSize = 131072; - constexpr size_t kStatBlockSize = 512; - constexpr int kChunksNumber = 16; - art::ScratchFile src_tmp; - FdFile src(src_tmp.GetFilename(), O_RDWR, /*check_usage=*/false); - ASSERT_TRUE(src.IsOpened()); - - /* - * Layout of the source file: - * - Skipped part: - * [ optional <input_offset> empty block ] - * - * - Copied part: - * [ optional <empty_prefix> empty block ] - * [ <kChunkSize> data block ] -\ - * [ <kChunkSize> empty block ] | - * [ <kChunkSize> data block ] | - * [ <kChunkSize> empty block ] > (2 * kChunksNumber - 1) kChunkSize blocks - * [ <kChunkSize> data block ] | - * [ ... ] | - * [ <kChunkSize> data block ] -/ - * [ optional <empty_suffix> empty block ] - */ - int rc = lseek(src.Fd(), input_offset, SEEK_SET); - ASSERT_EQ(rc, input_offset); - - rc = lseek(src.Fd(), empty_prefix, SEEK_CUR); - ASSERT_EQ(rc, input_offset + empty_prefix); - for (size_t i = 0; i < kChunksNumber; i++) { - // Write data leaving chunk size of unwritten space in between them - ASSERT_TRUE(src.WriteFully(data_buffer.data(), kChunkSize)); - rc = lseek(src.Fd(), kChunkSize, SEEK_CUR); - ASSERT_NE(rc, -1); - } - - ASSERT_EQ(0, src.SetLength(src.GetLength() + empty_suffix)); - ASSERT_EQ(0, src.Flush()); - - size_t expected_length = (2 * kChunksNumber - 1) * kChunkSize + empty_prefix + empty_suffix; - ASSERT_EQ(static_cast<int64_t>(expected_length), src.GetLength() - input_offset); - - struct stat src_stat; - rc = fstat(src.Fd(), &src_stat); - ASSERT_EQ(rc, 0); - ASSERT_GE(src_stat.st_blocks, kChunksNumber * kChunkSize / kStatBlockSize); - ASSERT_LE(src_stat.st_blocks, - kEstimateMaxFileMetadataSize + kChunksNumber * kChunkSize / kStatBlockSize); - - art::ScratchFile dest_tmp; - FdFile dest(dest_tmp.GetFilename(), O_RDWR, /*check_usage=*/false); - ASSERT_TRUE(dest.IsOpened()); - - ASSERT_TRUE(dest.Copy(&src, input_offset, src.GetLength() - input_offset)); - ASSERT_EQ(0, dest.Flush()); - - ASSERT_EQ(static_cast<int64_t>(expected_length), dest.GetLength()); - - // Both dest and src file offsets are expected to be at the end of file. - ASSERT_EQ(lseek(dest.Fd(), 0, SEEK_CUR), dest.GetLength()); - ASSERT_EQ(lseek(src.Fd(), 0, SEEK_CUR), src.GetLength()); - - struct stat dest_stat; - rc = fstat(dest.Fd(), &dest_stat); - ASSERT_EQ(rc, 0); - ASSERT_EQ(dest_stat.st_blocks, src_stat.st_blocks); - - rc = lseek(dest.Fd(), 0, SEEK_SET); - ASSERT_EQ(rc, 0); - - ASSERT_TRUE(dest.ReadFully(check_buffer.data(), empty_prefix)); - ASSERT_EQ(0, memcmp(check_buffer.data(), zero_buffer.data(), empty_prefix)); - - for (size_t i = 0; i < 2 * kChunksNumber - 1; i++) { - ASSERT_TRUE(dest.ReadFully(check_buffer.data(), kChunkSize)); - - if (i % 2 == 0) { - ASSERT_EQ(0, memcmp(check_buffer.data(), data_buffer.data(), kChunkSize)); - } else { - ASSERT_EQ(0, memcmp(check_buffer.data(), zero_buffer.data(), kChunkSize)); - } - } - - ASSERT_TRUE(dest.ReadFully(check_buffer.data(), empty_suffix)); - ASSERT_EQ(0, memcmp(check_buffer.data(), zero_buffer.data(), empty_suffix)); - - ASSERT_EQ(0, dest.Close()); - ASSERT_EQ(0, src.Close()); - }; - - auto subtest = [&](size_t empty_prefix, size_t empty_suffix) { - verify(empty_prefix, empty_suffix, 0); - verify(empty_prefix, empty_suffix, kChunkSize); - }; - - // No empty prefix or suffix - subtest(0, 0); - - for (size_t skip_size1 = 1; skip_size1 <= kChunkSize; skip_size1 <<= 2) { - // Empty prefix only - subtest(skip_size1, 0); - - // Empty suffix only - subtest(0, skip_size1); - - // Empty prefix and suffix - for (size_t skip_size2 = 1; skip_size2 <= kChunkSize; skip_size2 <<= 2) { - subtest(skip_size1, skip_size2); - } - } -} -#endif - TEST_F(FdFileTest, MoveConstructor) { // New scratch file, zero-length. art::ScratchFile tmp; |