summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jiakai Zhang <jiakaiz@google.com> 2024-01-30 05:49:23 +0000
committer Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2024-01-30 19:13:18 +0000
commit451cfcf9d09515ef60d76bd8551fc68c6e3bf621 (patch)
tree1aeae33920e5c84d3b5c7694a8159a363fdddff5
parent33e9f1a70d5f58639b524f40bf39a8e233c04ba8 (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.cc77
-rw-r--r--libartbase/base/unix_file/fd_file_test.cc128
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;