| // Copyright 2020 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 "src/storage/extractor/c/extractor.h" |
| |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <zircon/errors.h> |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <cstdio> |
| #include <tuple> |
| |
| #include <fbl/unique_fd.h> |
| #include <gtest/gtest.h> |
| |
| #include "src/storage/extractor/cpp/extractor.h" |
| |
| namespace extractor { |
| namespace { |
| |
| std::tuple<fbl::unique_fd, fbl::unique_fd> setup_fds() { |
| char kInFile[20] = "/tmp/payload.XXXXXX"; |
| char kOutFile[20] = "/tmp/payload.XXXXXX"; |
| |
| fbl::unique_fd out_file(mkstemp(kOutFile)); |
| fbl::unique_fd in_file(mkstemp(kInFile)); |
| EXPECT_TRUE(out_file); |
| EXPECT_TRUE(in_file); |
| const uint16_t kBlockSize = 8192; |
| const uint16_t kBlockCount = 10; |
| char buf[kBlockSize]; |
| for (int i = 0; i < kBlockCount; i++) { |
| memset(buf, i, sizeof(buf)); |
| EXPECT_EQ(write(in_file.get(), buf, sizeof(buf)), static_cast<ssize_t>(sizeof(buf))); |
| } |
| return std::make_tuple(std::move(in_file), std::move(out_file)); |
| } |
| |
| TEST(Extractor, CreateInvalidAlignment) { |
| ExtractorOptions options = { |
| .force_dump_pii = false, |
| .add_checksum = false, |
| .alignment = 0, |
| .compress = false, |
| }; |
| fbl::unique_fd in; |
| fbl::unique_fd out; |
| auto extractor = Extractor::Create(std::move(in), options, std::move(out)); |
| ASSERT_TRUE(extractor.is_error()); |
| ASSERT_EQ(extractor.error_value(), ZX_ERR_INVALID_ARGS); |
| } |
| |
| TEST(Extractor, CreateFailureWithInvalidFd) { |
| ExtractorOptions options = { |
| .force_dump_pii = false, |
| .add_checksum = false, |
| .alignment = 1, |
| .compress = false, |
| }; |
| fbl::unique_fd in; |
| fbl::unique_fd out; |
| auto extractor = Extractor::Create(std::move(in), options, std::move(out)); |
| ASSERT_TRUE(extractor.is_error()); |
| ASSERT_EQ(extractor.error_value(), ZX_ERR_INVALID_ARGS); |
| } |
| |
| TEST(Extractor, Create) { |
| ExtractorOptions options = { |
| .force_dump_pii = false, |
| .add_checksum = false, |
| .alignment = 1, |
| .compress = false, |
| }; |
| fbl::unique_fd in; |
| fbl::unique_fd out; |
| std::tie(in, out) = setup_fds(); |
| auto extractor_or = Extractor::Create(std::move(in), options, out.duplicate()); |
| ASSERT_TRUE(extractor_or.is_ok()); |
| |
| // If we don't issue a write, then file size should be 0. |
| struct stat stats; |
| ASSERT_EQ(fstat(out.get(), &stats), 0); |
| ASSERT_EQ(stats.st_size, 0); |
| } |
| |
| TEST(Extractor, AddInvalidExtent) { |
| ExtractorOptions options = { |
| .force_dump_pii = false, |
| .add_checksum = false, |
| .alignment = 1, |
| .compress = false, |
| }; |
| fbl::unique_fd in; |
| fbl::unique_fd out; |
| std::tie(in, out) = setup_fds(); |
| auto extractor_or = Extractor::Create(std::move(in), options, std::move(out)); |
| ASSERT_TRUE(extractor_or.is_ok()); |
| auto extractor = std::move(extractor_or.value()); |
| ExtentProperties properties = {.extent_kind = ExtentKind::Data, |
| .data_kind = DataKind::Unmodified}; |
| ASSERT_EQ(extractor->Add(10, 0, properties).error_value(), ZX_ERR_OUT_OF_RANGE); |
| } |
| |
| void verify_block(int fd, size_t size, off_t offset, uint8_t content) { |
| uint8_t buffer[size]; |
| ASSERT_EQ(pread(fd, buffer, size, offset), static_cast<ssize_t>(size)); |
| for (size_t i = 0; i < size; i++) { |
| ASSERT_EQ(buffer[i], content); |
| } |
| } |
| |
| TEST(Extractor, AddUnaligned) { |
| ExtractorOptions options = { |
| .force_dump_pii = false, |
| .add_checksum = false, |
| .alignment = 1024, |
| .compress = false, |
| }; |
| |
| fbl::unique_fd in; |
| fbl::unique_fd out; |
| std::tie(in, out) = setup_fds(); |
| auto extractor_or = Extractor::Create(std::move(in), options, std::move(out)); |
| ASSERT_TRUE(extractor_or.is_ok()); |
| auto extractor = std::move(extractor_or.value()); |
| ExtentProperties properties = {.extent_kind = ExtentKind::Data, |
| .data_kind = DataKind::Unmodified}; |
| // Alignment is 1024 bytes and test is adding extent at offset 10. |
| ASSERT_EQ(extractor->Add(10, 1, properties).error_value(), ZX_ERR_INVALID_ARGS); |
| } |
| |
| TEST(Extractor, Write) { |
| constexpr uint16_t kBlockSize = 8192; |
| ExtractorOptions options = { |
| .force_dump_pii = false, |
| .add_checksum = false, |
| .alignment = kBlockSize, |
| .compress = false, |
| }; |
| |
| fbl::unique_fd in; |
| fbl::unique_fd out; |
| std::tie(in, out) = setup_fds(); |
| auto extractor_or = Extractor::Create(in.duplicate(), options, out.duplicate()); |
| ASSERT_TRUE(extractor_or.is_ok()); |
| auto extractor = std::move(extractor_or.value()); |
| ExtentProperties properties = {.extent_kind = ExtentKind::Data, |
| .data_kind = DataKind::Unmodified}; |
| // Add a block starting at offset 5*8192 |
| ASSERT_TRUE(extractor->Add(5 * kBlockSize, kBlockSize, properties).is_ok()); |
| // Add two blocks starting at offset 8192 |
| ASSERT_TRUE(extractor->Add(1 * kBlockSize, 2 * kBlockSize, properties).is_ok()); |
| |
| ASSERT_TRUE(extractor->Write().is_ok()); |
| |
| EXPECT_EQ(lseek(out.get(), SEEK_SET, 0), 0); |
| struct stat stats; |
| EXPECT_EQ(fstat(out.get(), &stats), 0); |
| |
| // We should have 5 block in image file. Header, extent cluster, and 3 data blocks. |
| ASSERT_EQ(stats.st_size, 5 * kBlockSize); |
| |
| // We know the content of data blocks written. Verify that those blocks are |
| // in the image file. |
| // input file's block 'n' contens all 'n'. |
| // Skip first two block of the image file as they contain image header. Block |
| // 2, 3 4 should have out data. |
| verify_block(out.get(), kBlockSize, 2 * kBlockSize, 1); |
| verify_block(out.get(), kBlockSize, 3 * kBlockSize, 2); |
| verify_block(out.get(), kBlockSize, 4 * kBlockSize, 5); |
| } |
| |
| } // namespace |
| |
| } // namespace extractor |