diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp index a6dee4f43..83b5a1295 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -38,11 +39,13 @@ DEFINE_bool(show_merged, false, DEFINE_bool(verify_merge_sequence, false, "Verify merge order sequencing"); DEFINE_bool(show_merge_sequence, false, "Show merge order sequence"); DEFINE_bool(show_raw_ops, false, "Show raw ops directly from the underlying parser"); +DEFINE_string(extract_to, "", "Extract the COW contents to the given file"); namespace android { namespace snapshot { using android::base::borrowed_fd; +using android::base::unique_fd; void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*, unsigned int, const char* message) { @@ -53,7 +56,7 @@ void MyLogger(android::base::LogId, android::base::LogSeverity severity, const c } } -static void ShowBad(CowReader& reader, const struct CowOperation* op) { +static void ShowBad(CowReader& reader, const CowOperation* op) { size_t count; auto buffer = std::make_unique(op->data_length); @@ -104,12 +107,21 @@ static bool ShowRawOpStream(borrowed_fd fd) { } static bool Inspect(const std::string& path) { - android::base::unique_fd fd(open(path.c_str(), O_RDONLY)); + unique_fd fd(open(path.c_str(), O_RDONLY)); if (fd < 0) { PLOG(ERROR) << "open failed: " << path; return false; } + unique_fd extract_to; + if (!FLAGS_extract_to.empty()) { + extract_to.reset(open(FLAGS_extract_to.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0664)); + if (extract_to < 0) { + PLOG(ERROR) << "could not open " << FLAGS_extract_to << " for writing"; + return false; + } + } + CowReader reader; auto start_time = std::chrono::steady_clock::now(); @@ -186,12 +198,23 @@ static bool Inspect(const std::string& path) { if (!FLAGS_silent && FLAGS_show_ops) std::cout << *op << "\n"; - if (FLAGS_decompress && op->type == kCowReplaceOp && op->compression != kCowCompressNone) { + if ((FLAGS_decompress || extract_to >= 0) && op->type == kCowReplaceOp) { if (reader.ReadData(op, buffer.data(), buffer.size()) < 0) { std::cerr << "Failed to decompress for :" << *op << "\n"; success = false; if (FLAGS_show_bad_data) ShowBad(reader, op); } + if (extract_to >= 0) { + off_t offset = uint64_t(op->new_block) * header.block_size; + if (!android::base::WriteFullyAtOffset(extract_to, buffer.data(), buffer.size(), + offset)) { + PLOG(ERROR) << "failed to write block " << op->new_block; + return false; + } + } + } else if (extract_to >= 0 && !IsMetadataOp(*op) && op->type != kCowZeroOp) { + PLOG(ERROR) << "Cannot extract op yet: " << *op; + return false; } if (op->type == kCowSequenceOp && FLAGS_show_merge_sequence) {