blob: cee9944cebe0fb459c498796ce3f358eaf1c8beb [file] [log] [blame]
// Copyright 2021 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 <lib/fdio/fd.h>
#include <lib/zx/status.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <cstdint>
#include <iostream>
#include <map>
#include <memory>
#include <optional>
#include <utility>
#include <fbl/unique_fd.h>
#include <safemath/safe_math.h>
#include "src/storage/extractor/c/extractor.h"
#include "src/storage/extractor/cpp/extractor.h"
#include "src/storage/fvm/format.h"
namespace extractor {
namespace {
// Walks the fvm and collects interesting metadata.
class FvmWalker {
public:
static zx::status<std::unique_ptr<FvmWalker>> Create(fbl::unique_fd input_fd,
Extractor& extractor);
zx::status<> Walk();
private:
// Returns maximum addressable byte in the fvm.
uint64_t ByteLimit() const { return info_.fvm_partition_size; }
// Walks the fvm partition and marks all bytes as reported by ByteLimit() as unmapped
zx::status<> WalkPartition() const;
// Walks different segments, like partition table and allocation table, of the fvm partition.
// Marks them as data unmodified.
zx::status<> WalkSegments() const;
const fvm::Header& Info() const { return info_; }
FvmWalker(fbl::unique_fd input_fd, Extractor& extractor);
// Not copyable or movable
FvmWalker(const FvmWalker&) = delete;
FvmWalker& operator=(const FvmWalker&) = delete;
FvmWalker(FvmWalker&&) = delete;
FvmWalker& operator=(FvmWalker&&) = delete;
// Loads superblock located at start_offset. If the copy of superblock has valid
// magic values, the function returns zx::ok().
zx::status<> TryLoadSuperblock(uint64_t start_offset);
// Tries to load the primary superblock. If primary superblock is corrupt,
// dumps the first copy of metadata based on max sizes for the
// partition and allocation tables
zx::status<> LoadSuperblock();
// The primary superblock, if valid
fvm::Header info_;
// Pointer to extractor.
Extractor& extractor_;
// File from where the fvm is parsed/loaded.
fbl::unique_fd input_fd_;
};
FvmWalker::FvmWalker(fbl::unique_fd input_fd, Extractor& extractor)
: extractor_(extractor), input_fd_(std::move(input_fd)) {}
zx::status<std::unique_ptr<FvmWalker>> FvmWalker::Create(fbl::unique_fd input_fd,
Extractor& extractor) {
auto walker = std::unique_ptr<FvmWalker>(new FvmWalker(std::move(input_fd), extractor));
if (auto status = walker->LoadSuperblock(); status.is_error()) {
std::cerr << "Loading primary superblock failed" << std::endl;
return zx::error(status.error_value());
}
return zx::ok(std::move(walker));
}
zx::status<> FvmWalker::Walk() {
if (auto status = WalkPartition(); status.is_error()) {
std::cerr << "Walking partition failed" << std::endl;
return status;
}
if (auto status = WalkSegments(); status.is_error()) {
std::cerr << "Walking segments failed" << std::endl;
return status;
}
return zx::ok();
}
zx::status<> FvmWalker::WalkPartition() const {
auto max_offset = ByteLimit();
ExtentProperties properties{.extent_kind = ExtentKind::Unmmapped, .data_kind = DataKind::Skipped};
return extractor_.Add(0, max_offset, properties);
}
zx::status<> FvmWalker::WalkSegments() const {
ExtentProperties properties{.extent_kind = ExtentKind::Data, .data_kind = DataKind::Unmodified};
if (auto status = extractor_.AddBlocks(info_.GetSuperblockOffset(fvm::SuperblockType::kPrimary),
1, properties);
status.is_error()) {
std::cerr << "FAIL: Add first superblock copy" << std::endl;
return status;
}
if (auto status =
extractor_.AddBlocks(info_.GetPartitionTableOffset() / fvm::kBlockSize,
info_.GetPartitionTableByteSize() / fvm::kBlockSize, properties);
status.is_error()) {
std::cerr << "FAIL: Add first partition table copy" << std::endl;
return status;
}
if (auto status = extractor_.AddBlocks(info_.GetAllocationTableOffset() / fvm::kBlockSize,
info_.GetAllocationTableUsedByteSize() / fvm::kBlockSize,
properties);
status.is_error()) {
std::cerr << "FAIL: Add first allocation table copy" << std::endl;
return status;
}
size_t secondarySuperblockOffset =
info_.GetSuperblockOffset(fvm::SuperblockType::kSecondary) / fvm::kBlockSize;
if (auto status = extractor_.AddBlocks(secondarySuperblockOffset, 1, properties);
status.is_error()) {
std::cerr << "FAIL: Add second superblock copy" << std::endl;
return status;
}
if (auto status = extractor_.AddBlocks(
secondarySuperblockOffset + info_.GetPartitionTableOffset() / fvm::kBlockSize,
info_.GetPartitionTableByteSize() / fvm::kBlockSize, properties);
status.is_error()) {
std::cerr << "FAIL: Add second partition table copy" << std::endl;
return status;
}
if (auto status = extractor_.AddBlocks(
secondarySuperblockOffset + info_.GetAllocationTableOffset() / fvm::kBlockSize,
info_.GetAllocationTableUsedByteSize() / fvm::kBlockSize, properties);
status.is_error()) {
std::cerr << "FAIL: Add second allocation table copy" << std::endl;
return status;
}
return zx::ok();
}
zx::status<> FvmWalker::TryLoadSuperblock(uint64_t start_offset) {
off_t pread_offset;
if (!safemath::MakeCheckedNum(start_offset).Cast<off_t>().AssignIfValid(&pread_offset)) {
return zx::error(ZX_ERR_OUT_OF_RANGE);
}
char buffer[fvm::kBlockSize];
pread(input_fd_.get(), buffer, fvm::kBlockSize, pread_offset);
memcpy(&info_, buffer, sizeof(info_));
if (info_.magic == fvm::kMagic) {
return zx::ok();
}
std::cerr << "Magic does not match" << std::endl;
return zx::error(ZX_ERR_BAD_STATE);
}
zx::status<> FvmWalker::LoadSuperblock() {
auto load_status = TryLoadSuperblock(0);
if (load_status.is_ok()) {
return load_status;
}
// If we fail to load the superblock, just dump the primary superblock
ExtentProperties properties{.extent_kind = ExtentKind::Data, .data_kind = DataKind::Unmodified};
if (auto status = extractor_.AddBlocks(0, 1, properties); status.is_error()) {
std::cerr << "FAIL: Add primary superblock" << std::endl;
return status;
}
return load_status;
}
} // namespace
zx::status<> FvmExtract(fbl::unique_fd input_fd, Extractor& extractor) {
auto walker_or = FvmWalker::Create(std::move(input_fd), extractor);
if (walker_or.is_error()) {
std::cerr << "Walker: Init failure: " << walker_or.error_value() << std::endl;
return zx::error(walker_or.error_value());
}
std::unique_ptr<FvmWalker> walker = std::move(walker_or.value());
return walker->Walk();
}
} // namespace extractor