blob: 3be575028d86408c958b7fb14aba55f053e5fa9d [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 "extract-metadata.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <cstdint>
#include <filesystem>
#include <iomanip>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <fbl/unique_fd.h>
#include "src/storage/extractor/c/extractor.h"
#include "src/storage/extractor/cpp/extractor.h"
#include "src/storage/extractor/cpp/hex_dump_generator.h"
#include "src/storage/minfs/format.h"
namespace devmgr {
namespace {
void Dump(fbl::unique_fd image_fd, DumpMetadataOptions& dump_options) {
lseek(image_fd.get(), 0, SEEK_SET);
extractor::HexDumpGeneratorOptions options = {
.tag = dump_options.tag,
.bytes_per_line = dump_options.bytes_per_line,
.dump_offset = true,
.dump_checksum = true,
};
auto generator_or = extractor::HexDumpGenerator::Create(std::move(image_fd), options);
if (generator_or.is_error()) {
FX_LOGS(ERROR) << "Failed to create hex dump generator: "
<< zx_status_get_string(generator_or.error_value());
return;
}
auto generator = std::move(generator_or.value());
// Force start dump on a new line. FX_LOGS(ERROR) prints component name, file path and line number
// along with the message. This might wrap the log around and make it difficult to grep. So we add
// a new line here.
std::string buffer = "\n";
while (!generator->Done()) {
auto line_or = generator->GetNextLine();
if (line_or.is_error()) {
// Dump whatever we read so far and return.
FX_LOGS(ERROR) << buffer;
FX_LOGS(ERROR) << "Failed to get hex dump line"
<< zx_status_get_string(line_or.error_value());
return;
}
buffer.append(line_or.value());
if (buffer.length() > dump_options.stream_buffer_size) {
FX_LOGS(ERROR) << buffer;
// Force start dump on a new line. FX_LOGS(ERROR) prints component name, file path and line
// number along with the message. This might wrap the log around and make it difficult to
// grep. So we add a new line here.
buffer = "\n";
}
}
if (buffer.length() > 1) {
FX_LOGS(ERROR) << buffer;
}
}
} // namespace
bool ExtractMetadataEnabled() { return true; }
void MaybeDumpMetadata(fbl::unique_fd device_fd, DumpMetadataOptions options) {
// At the moment, extraction is supported only for minfs.
ZX_ASSERT(options.disk_format == DISK_FORMAT_MINFS);
if (options.bytes_per_line <= 0) {
FX_LOGS(ERROR) << "Invalid bytes_per_line:" << options.bytes_per_line << std::endl;
return;
}
if (options.stream_buffer_size <= 0) {
FX_LOGS(ERROR) << "Invalid stream_buffer_size:" << options.stream_buffer_size << std::endl;
return;
}
if (!device_fd) {
FX_LOGS(ERROR) << "Invalid device for extractor" << std::endl;
return;
}
std::string tmp_path = "/fs/tmp/extracted_image_XXXXXX";
fbl::unique_fd output_stream(mkstemp(tmp_path.data()));
if (!output_stream) {
FX_LOGS(ERROR) << "Failed to create image file: " << tmp_path;
return;
}
ExtractorOptions extractor_options{
.force_dump_pii = false,
.add_checksum = false,
.alignment = minfs::kMinfsBlockSize,
// TODO(fxbug.dev/67782): Enable compression.
.compress = false,
};
auto extractor_or = extractor::Extractor::Create(device_fd.duplicate(), extractor_options,
output_stream.duplicate());
if (extractor_or.is_error()) {
FX_LOGS(ERROR) << "Failed to create extractor: "
<< zx_status_get_string(extractor_or.error_value());
return;
}
if (auto status = extractor::MinfsExtract(std::move(device_fd), *extractor_or.value());
status.is_error()) {
FX_LOGS(ERROR) << "Failed to extract: " << zx_status_get_string(status.error_value());
return;
}
if (auto status = extractor_or.value()->Write(); status.is_error()) {
FX_LOGS(ERROR) << "Failed to write to the extracted file: "
<< zx_status_get_string(status.error_value());
return;
}
// Wait for all other component to stop writing to logs. This is not fool proof but helps to
// cluster logs together and decreases the chances of dropping logs.
zx::nanosleep(zx::deadline_after(options.log_settle_time));
FX_LOGS(ERROR) << std::endl
<< options.tag << ": Extracting minfs to serial." << std::endl
<< options.tag << ": Following lines that start with \"" << options.tag
<< "\" are from extractor." << std::endl
<< options.tag << ": Successful extraction ends with \"" << options.tag
<< ": Done extracting minfs to serial.\"" << std::endl
<< options.tag << ": Compression:" << (extractor_options.compress ? "on" : "off")
<< " Checksum:on Offset:on bytes_per_line:" << options.bytes_per_line << std::endl;
Dump(std::move(output_stream), options);
FX_LOGS(ERROR) << std::endl << options.tag << ": Done extracting minfs to serial" << std::endl;
// Wait for all the logs we have written to get flushed. This is not fool proof but helps to
// cluster logs together and decreases the chances of hex-dump logs interleaving with other logs.
zx::nanosleep(zx::deadline_after(options.log_settle_time));
}
} // namespace devmgr