snapuserd: Add an extractor tool.

This is similar to inspect_cow --extract-to, except it uses snapuserd.
It is a diagnostic host tool and uses the tooling added for host
testing.

Usage: snapuserd_extractor -cow COW_FILE -base BASE_FILE -out OUT_FILE
                           -num_sectors NUM_SECTORS

Unlike inspect_cow, this supports xor/copy operations.

The extractor code is separated into a utility file so we can use it for
additional tests later on.

Bug: N/A
Test: manual test
Change-Id: Ib7509508cba45e6c3a0db8c75454e33c2a503e03
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 47a8685..1b0c563 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -293,3 +293,48 @@
         "vts",
     ],
 }
+
+cc_binary_host {
+    name: "snapuserd_extractor",
+    defaults: [
+        "fs_mgr_defaults",
+        "libsnapshot_cow_defaults",
+    ],
+    srcs: [
+        "testing/dm_user_harness.cpp",
+        "testing/harness.cpp",
+        "testing/host_harness.cpp",
+        "user-space-merge/extractor.cpp",
+        "snapuserd_extractor.cpp",
+    ],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libbrotli",
+        "libcutils_sockets",
+        "libdm",
+        "libext2_uuid",
+        "libext4_utils",
+        "libfs_mgr_file_wait",
+        "libgflags",
+        "libsnapshot_cow",
+        "libsnapuserd",
+        "liburing",
+        "libz",
+    ],
+    include_dirs: [
+        "bionic/libc/kernel",
+        ".",
+    ],
+    header_libs: [
+        "libstorage_literals_headers",
+        "libfiemap_headers",
+    ],
+}
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp
new file mode 100644
index 0000000..f46cd5b
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gflags/gflags.h>
+#include "user-space-merge/extractor.h"
+
+using namespace std::string_literals;
+
+DEFINE_string(base, "", "Base device/image");
+DEFINE_string(cow, "", "COW device/image");
+DEFINE_string(out, "", "Output path");
+DEFINE_int32(num_sectors, 0, "Number of sectors to read");
+
+int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+    android::base::InitLogging(argv);
+    gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+    if (FLAGS_out.empty()) {
+        LOG(ERROR) << "Missing -out argument.";
+        return 1;
+    }
+    if (FLAGS_base.empty()) {
+        LOG(ERROR) << "Missing -base argument.";
+        return 1;
+    }
+    if (FLAGS_cow.empty()) {
+        LOG(ERROR) << "missing -out argument.";
+        return 1;
+    }
+    if (!FLAGS_num_sectors) {
+        LOG(ERROR) << "missing -num_sectors argument.";
+        return 1;
+    }
+
+    android::snapshot::Extractor extractor(FLAGS_base, FLAGS_cow);
+    if (!extractor.Init()) {
+        return 1;
+    }
+    if (!extractor.Extract(FLAGS_num_sectors, FLAGS_out)) {
+        return 1;
+    }
+    return 0;
+}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
new file mode 100644
index 0000000..c5718d5
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
@@ -0,0 +1,90 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "extractor.h"
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+using namespace std::string_literals;
+
+namespace android {
+namespace snapshot {
+
+Extractor::Extractor(const std::string& base_path, const std::string& cow_path)
+    : base_path_(base_path), cow_path_(cow_path), control_name_("test") {}
+
+bool Extractor::Init() {
+    auto opener = factory_.CreateTestOpener(control_name_);
+    handler_ = std::make_shared<SnapshotHandler>(control_name_, cow_path_, base_path_, base_path_,
+                                                 opener, 1, false, false);
+    if (!handler_->InitCowDevice()) {
+        return false;
+    }
+    if (!handler_->InitializeWorkers()) {
+        return false;
+    }
+
+    read_worker_ = std::make_unique<ReadWorker>(cow_path_, base_path_, control_name_, base_path_,
+                                                handler_->GetSharedPtr(), opener);
+    if (!read_worker_->Init()) {
+        return false;
+    }
+    block_server_ = static_cast<TestBlockServer*>(read_worker_->block_server());
+
+    handler_thread_ = std::async(std::launch::async, &SnapshotHandler::Start, handler_.get());
+    return true;
+}
+
+Extractor::~Extractor() {
+    factory_.DeleteQueue(control_name_);
+}
+
+bool Extractor::Extract(off_t num_sectors, const std::string& out_path) {
+    unique_fd out_fd(open(out_path.c_str(), O_RDWR | O_CLOEXEC | O_TRUNC | O_CREAT, 0664));
+    if (out_fd < 0) {
+        PLOG(ERROR) << "Could not open for writing: " << out_path;
+        return false;
+    }
+
+    for (off_t i = 0; i < num_sectors; i++) {
+        if (!read_worker_->RequestSectors(i, 512)) {
+            LOG(ERROR) << "Read sector " << i << " failed.";
+            return false;
+        }
+        std::string result = std::move(block_server_->sent_io());
+        off_t offset = i * 512;
+        if (!android::base::WriteFullyAtOffset(out_fd, result.data(), result.size(), offset)) {
+            PLOG(ERROR) << "write failed";
+            return false;
+        }
+    }
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h
new file mode 100644
index 0000000..65285b1
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <string>
+#include <thread>
+
+#include <android-base/unique_fd.h>
+#include "merge_worker.h"
+#include "read_worker.h"
+#include "snapuserd_core.h"
+#include "testing/host_harness.h"
+
+namespace android {
+namespace snapshot {
+
+class Extractor final {
+  public:
+    Extractor(const std::string& base_path, const std::string& cow_path);
+    ~Extractor();
+
+    bool Init();
+    bool Extract(off_t num_sectors, const std::string& out_path);
+
+  private:
+    std::string base_path_;
+    std::string cow_path_;
+
+    TestBlockServerFactory factory_;
+    HostTestHarness harness_;
+    std::string control_name_;
+    std::shared_ptr<SnapshotHandler> handler_;
+    std::unique_ptr<ReadWorker> read_worker_;
+    std::future<bool> handler_thread_;
+    TestBlockServer* block_server_ = nullptr;
+};
+
+}  // namespace snapshot
+}  // namespace android