blob: 051885864d05cf60b6c9e9e10e1ff5beb0a6be36 [file] [log] [blame]
// 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;
options.add_checksum = false;
options.force_dump_pii = false;
options.alignment = 0;
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;
options.add_checksum = false;
options.force_dump_pii = false;
options.alignment = 1;
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_IO);
}
TEST(Extractor, Create) {
ExtractorOptions options;
options.add_checksum = false;
options.force_dump_pii = false;
options.alignment = 1;
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;
options.add_checksum = false;
options.force_dump_pii = false;
options.alignment = 1;
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;
options.add_checksum = false;
options.force_dump_pii = false;
options.alignment = 1024;
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;
options.add_checksum = false;
options.force_dump_pii = false;
options.alignment = kBlockSize;
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