| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <zircon/syscalls.h> |
| |
| #include <iostream> |
| #include <tuple> |
| #include <vector> |
| |
| #include <fbl/unique_fd.h> |
| |
| #include "src/storage/fs_test/fs_test_fixture.h" |
| |
| namespace fs_test { |
| namespace { |
| |
| using ParamType = |
| std::tuple<TestFilesystemOptions, |
| std::tuple</*write_offset=*/size_t, /*read_offset=*/size_t, /*write_size=*/size_t>>; |
| |
| class SparseTest : public BaseFilesystemTest, public testing::WithParamInterface<ParamType> { |
| public: |
| SparseTest() : BaseFilesystemTest(std::get<0>(GetParam())) {} |
| |
| size_t write_offset() const { return std::get<0>(std::get<1>(GetParam())); } |
| size_t read_offset() const { return std::get<1>(std::get<1>(GetParam())); } |
| size_t write_size() const { return std::get<2>(std::get<1>(GetParam())); } |
| }; |
| |
| TEST_P(SparseTest, ReadAfterSparseWriteReturnsCorrectData) { |
| const std::string my_file = GetPath("my_file"); |
| fbl::unique_fd fd(open(my_file.c_str(), O_RDWR | O_CREAT, 0644)); |
| ASSERT_TRUE(fd); |
| |
| // Create a random write buffer of data |
| std::vector<uint8_t> wbuf(write_size()); |
| unsigned int seed = static_cast<unsigned int>(zx_ticks_get()); |
| std::cout << "Sparse test using seed: " << seed << std::endl; |
| for (size_t i = 0; i < write_size(); i++) { |
| wbuf[i] = (uint8_t)rand_r(&seed); |
| } |
| |
| // Dump write buffer to file |
| ASSERT_EQ(pwrite(fd.get(), &wbuf[0], write_size(), write_offset()), |
| static_cast<ssize_t>(write_size())); |
| |
| // Reopen file |
| ASSERT_EQ(close(fd.release()), 0); |
| fd.reset(open(my_file.c_str(), O_RDWR, 0644)); |
| ASSERT_TRUE(fd); |
| |
| // Access read buffer from file |
| const size_t file_size = write_offset() + write_size(); |
| const size_t bytes_to_read = |
| (file_size - read_offset()) > write_size() ? write_size() : (file_size - read_offset()); |
| ASSERT_GT(bytes_to_read, 0ul) << "We want to test writing AND reading"; |
| std::vector<uint8_t> rbuf(bytes_to_read); |
| ASSERT_EQ(pread(fd.get(), &rbuf[0], bytes_to_read, read_offset()), |
| static_cast<ssize_t>(bytes_to_read)); |
| |
| const size_t sparse_length = |
| (read_offset() < write_offset()) ? write_offset() - read_offset() : 0; |
| |
| if (sparse_length > 0) { |
| for (size_t i = 0; i < sparse_length; i++) { |
| ASSERT_EQ(rbuf[i], 0) << "This portion of file should be sparse; but isn't"; |
| } |
| } |
| |
| const size_t wbuf_offset = (read_offset() < write_offset()) ? 0 : read_offset() - write_offset(); |
| const size_t valid_length = bytes_to_read - sparse_length; |
| |
| if (valid_length > 0) { |
| for (size_t i = 0; i < valid_length; i++) { |
| ASSERT_EQ(rbuf[sparse_length + i], wbuf[wbuf_offset + i]); |
| } |
| } |
| |
| // Clean up |
| ASSERT_EQ(close(fd.release()), 0); |
| ASSERT_EQ(unlink(my_file.c_str()), 0); |
| } |
| |
| std::string GetParamDescription(const testing::TestParamInfo<ParamType>& param) { |
| std::stringstream s; |
| s << std::get<0>(param.param) << "WithWriteOffset" << std::get<0>(std::get<1>(param.param)) |
| << "ReadOffset" << std::get<1>(std::get<1>(param.param)) << "WriteSize" |
| << std::get<2>(std::get<1>(param.param)); |
| return s.str(); |
| } |
| |
| std::vector<TestFilesystemOptions> AllTestFilesystemsWithCustomDisk() { |
| std::vector<TestFilesystemOptions> filesystems; |
| for (TestFilesystemOptions options : AllTestFilesystems()) { |
| options.device_block_count = 1LLU << 24; |
| options.device_block_size = 1LLU << 9; |
| options.fvm_slice_size = 1LLU << 23; |
| options.zero_fill = true; |
| filesystems.push_back(options); |
| } |
| return filesystems; |
| } |
| |
| constexpr size_t kBlockSize = 8192; |
| constexpr size_t kDirectBlocks = 16; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| /*no prefix*/, SparseTest, |
| testing::Combine( |
| testing::ValuesIn(AllTestFilesystemsWithCustomDisk()), |
| testing::Values(std::make_tuple(0, 0, kBlockSize), |
| std::make_tuple(kBlockSize / 2, 0, kBlockSize), |
| std::make_tuple(kBlockSize / 2, kBlockSize, kBlockSize), |
| std::make_tuple(kBlockSize, 0, kBlockSize), |
| std::make_tuple(kBlockSize, kBlockSize / 2, kBlockSize), |
| std::make_tuple(kBlockSize* kDirectBlocks, |
| kBlockSize* kDirectBlocks - kBlockSize, kBlockSize * 2), |
| std::make_tuple(kBlockSize* kDirectBlocks, |
| kBlockSize* kDirectBlocks - kBlockSize, kBlockSize * 32), |
| std::make_tuple(kBlockSize* kDirectBlocks + kBlockSize, |
| kBlockSize* kDirectBlocks - kBlockSize, kBlockSize * 32), |
| std::make_tuple(kBlockSize* kDirectBlocks + kBlockSize, |
| kBlockSize* kDirectBlocks + 2 * kBlockSize, |
| kBlockSize * 32))), |
| GetParamDescription); |
| |
| } // namespace |
| } // namespace fs_test |