Merge changes Ic18443d4,I9e9af999,I10e099fe,I00cf6ec9,Ia1ea4ba7 into main am: 7bd9162b14

Original change: https://android-review.googlesource.com/c/platform/system/core/+/2695353

Change-Id: If3042ba391fa3e876ae59deff138d622d64f3b7a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index fc260b6..fe7f99c 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -90,6 +90,7 @@
     ramdisk_available: true,
     vendor_ramdisk_available: true,
     recovery_available: true,
+    host_supported: true,
 }
 
 cc_defaults {
@@ -222,6 +223,7 @@
     srcs: [
         "testing/dm_user_harness.cpp",
         "testing/harness.cpp",
+        "testing/host_harness.cpp",
         "user-space-merge/snapuserd_test.cpp",
     ],
     shared_libs: [
@@ -255,4 +257,5 @@
     },
     auto_gen_config: true,
     require_root: false,
+    host_supported: true,
 }
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
index 0d83f47..7ab75dc 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
@@ -14,6 +14,8 @@
 
 #pragma once
 
+#include <linux/types.h>
+
 namespace android {
 namespace snapshot {
 
@@ -70,7 +72,7 @@
 
     /* In sectors */
     uint32_t chunk_size;
-} __packed;
+} __attribute__((packed));
 
 // A disk exception is a mapping of old_chunk to new_chunk
 // old_chunk is the chunk ID of a dm-snapshot device.
@@ -78,7 +80,7 @@
 struct disk_exception {
     uint64_t old_chunk;
     uint64_t new_chunk;
-} __packed;
+} __attribute__((packed));
 
 // Control structures to communicate with dm-user
 // It comprises of header and a payload
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp
new file mode 100644
index 0000000..0d230ad
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp
@@ -0,0 +1,112 @@
+// 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 "host_harness.h"
+
+#include "snapuserd_logging.h"
+
+namespace android {
+namespace snapshot {
+
+void TestBlockServerQueue::WaitForShutdown() {
+    std::unique_lock lock(m_);
+    if (shutdown_) {
+        return;
+    }
+    cv_.wait(lock, [this]() -> bool { return shutdown_; });
+}
+
+void TestBlockServerQueue::Shutdown() {
+    std::unique_lock lock(m_);
+    shutdown_ = true;
+    cv_.notify_all();
+}
+
+TestBlockServer::TestBlockServer(std::shared_ptr<TestBlockServerQueue> queue,
+                                 const std::string& misc_name)
+    : queue_(queue), misc_name_(misc_name) {}
+
+bool TestBlockServer::ProcessRequests() {
+    queue_->WaitForShutdown();
+    return false;
+}
+
+void* TestBlockServer::GetResponseBuffer(size_t size, size_t to_write) {
+    std::string buffer(size, '\0');
+    buffered_.emplace_back(std::move(buffer), to_write);
+    return buffered_.back().first.data();
+}
+
+bool TestBlockServer::SendBufferedIo() {
+    for (const auto& [data, to_write] : buffered_) {
+        sent_io_ += data.substr(0, to_write);
+    }
+    buffered_.clear();
+    return true;
+}
+
+TestBlockServerOpener::TestBlockServerOpener(std::shared_ptr<TestBlockServerQueue> queue,
+                                             const std::string& misc_name)
+    : queue_(queue), misc_name_(misc_name) {}
+
+std::unique_ptr<IBlockServer> TestBlockServerOpener::Open(IBlockServer::Delegate*, size_t) {
+    return std::make_unique<TestBlockServer>(queue_, misc_name_);
+}
+
+std::shared_ptr<TestBlockServerOpener> TestBlockServerFactory::CreateTestOpener(
+        const std::string& misc_name) {
+    if (queues_.count(misc_name)) {
+        LOG(ERROR) << "Cannot create opener for " << misc_name << ", already exists";
+        return nullptr;
+    }
+    auto queue = std::make_shared<TestBlockServerQueue>();
+    queues_.emplace(misc_name, queue);
+    return std::make_shared<TestBlockServerOpener>(queue, misc_name);
+}
+
+std::shared_ptr<IBlockServerOpener> TestBlockServerFactory::CreateOpener(
+        const std::string& misc_name) {
+    return CreateTestOpener(misc_name);
+}
+
+bool TestBlockServerFactory::DeleteQueue(const std::string& misc_name) {
+    auto iter = queues_.find(misc_name);
+    if (iter == queues_.end()) {
+        LOG(ERROR) << "Cannot delete queue " << misc_name << ", not found";
+        return false;
+    }
+    iter->second->Shutdown();
+    queues_.erase(iter);
+    return true;
+}
+
+HostUserDevice::HostUserDevice(TestBlockServerFactory* factory, const std::string& misc_name)
+    : factory_(factory), misc_name_(misc_name) {}
+
+bool HostUserDevice::Destroy() {
+    return factory_->DeleteQueue(misc_name_);
+}
+
+std::unique_ptr<IUserDevice> HostTestHarness::CreateUserDevice(const std::string&,
+                                                               const std::string& misc_name,
+                                                               uint64_t) {
+    return std::make_unique<HostUserDevice>(&factory_, misc_name);
+}
+
+IBlockServerFactory* HostTestHarness::GetBlockServerFactory() {
+    return &factory_;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h
new file mode 100644
index 0000000..ec0bd29
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h
@@ -0,0 +1,105 @@
+// 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 <condition_variable>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "harness.h"
+
+namespace android {
+namespace snapshot {
+
+class TestBlockServerQueue final {
+  public:
+    void WaitForShutdown();
+    void Shutdown();
+
+  private:
+    std::mutex m_;
+    std::condition_variable cv_;
+    bool shutdown_ = false;
+};
+
+class TestBlockServer final : public IBlockServer {
+  public:
+    TestBlockServer(std::shared_ptr<TestBlockServerQueue> queue, const std::string& misc_name);
+    bool ProcessRequests() override;
+    void* GetResponseBuffer(size_t size, size_t to_write) override;
+    bool SendBufferedIo() override;
+
+    std::string&& sent_io() { return std::move(sent_io_); }
+
+  private:
+    std::shared_ptr<TestBlockServerQueue> queue_;
+    std::string misc_name_;
+    std::string sent_io_;
+    std::vector<std::pair<std::string, size_t>> buffered_;
+};
+
+class TestBlockServerOpener final : public IBlockServerOpener {
+  public:
+    TestBlockServerOpener(std::shared_ptr<TestBlockServerQueue> queue,
+                          const std::string& misc_name);
+    std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,
+                                       size_t buffer_size) override;
+
+  private:
+    std::shared_ptr<TestBlockServerQueue> queue_;
+    std::string misc_name_;
+};
+
+class TestBlockServerFactory final : public IBlockServerFactory {
+  public:
+    std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) override;
+    std::shared_ptr<TestBlockServerOpener> CreateTestOpener(const std::string& misc_name);
+    bool DeleteQueue(const std::string& misc_name);
+
+  private:
+    std::unordered_map<std::string, std::shared_ptr<TestBlockServerQueue>> queues_;
+};
+
+class TestBlockServerFactory;
+
+class HostUserDevice final : public IUserDevice {
+  public:
+    HostUserDevice(TestBlockServerFactory* factory, const std::string& misc_name);
+    const std::string& GetPath() override { return empty_path_; }
+    bool Destroy();
+
+  private:
+    TestBlockServerFactory* factory_;
+    std::string misc_name_;
+    std::string empty_path_;
+};
+
+class HostTestHarness final : public ITestHarness {
+  public:
+    std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
+                                                  const std::string& misc_name,
+                                                  uint64_t num_sectors) override;
+    IBlockServerFactory* GetBlockServerFactory() override;
+    bool HasUserDevice() override { return false; }
+
+  private:
+    TestBlockServerFactory factory_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
index a6a3eb8..6dbae81 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
@@ -33,9 +33,11 @@
     bool Run();
     bool Init() override;
     void CloseFds() override;
+    bool RequestSectors(uint64_t sector, uint64_t size) override;
+
+    IBlockServer* block_server() const { return block_server_.get(); }
 
   private:
-    bool RequestSectors(uint64_t sector, uint64_t size) override;
     bool SendBufferedIo();
 
     bool ProcessCowOp(const CowOperation* cow_op, void* buffer);
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index 9f7a91d..01fe06f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -39,8 +39,11 @@
 #include <snapuserd/dm_user_block_server.h>
 #include <storage_literals/storage_literals.h>
 #include "handler_manager.h"
+#include "merge_worker.h"
+#include "read_worker.h"
 #include "snapuserd_core.h"
 #include "testing/dm_user_harness.h"
+#include "testing/host_harness.h"
 #include "testing/temp_device.h"
 #include "utility.h"
 
@@ -55,6 +58,9 @@
 using namespace std::chrono_literals;
 using namespace android::dm;
 using namespace std;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
 
 class SnapuserdTestBase : public ::testing::Test {
   protected:
@@ -66,7 +72,7 @@
     std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
 
     std::unique_ptr<ITestHarness> harness_;
-    size_t size_ = 100_MiB;
+    size_t size_ = 10_MiB;
     int total_base_size_ = 0;
     std::string system_device_ctrl_name_;
     std::string system_device_name_;
@@ -80,7 +86,11 @@
 };
 
 void SnapuserdTestBase::SetUp() {
+#if __ANDROID__
     harness_ = std::make_unique<DmUserTestHarness>();
+#else
+    harness_ = std::make_unique<HostTestHarness>();
+#endif
 }
 
 void SnapuserdTestBase::TearDown() {}
@@ -108,8 +118,7 @@
 }
 
 std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateCowDeviceInternal() {
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
+    cow_system_ = std::make_unique<TemporaryFile>();
 
     CowOptions options;
     options.compression = "gz";
@@ -270,7 +279,9 @@
 }
 
 void SnapuserdTest::Shutdown() {
-    ASSERT_TRUE(dmuser_dev_->Destroy());
+    if (dmuser_dev_) {
+        ASSERT_TRUE(dmuser_dev_->Destroy());
+    }
 
     auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
     ASSERT_TRUE(handlers_->DeleteHandler(system_device_ctrl_name_));
@@ -770,6 +781,110 @@
     ValidateMerge();
 }
 
+class HandlerTest : public SnapuserdTestBase {
+  protected:
+    void SetUp() override;
+    void TearDown() override;
+
+    AssertionResult ReadSectors(sector_t sector, uint64_t size, void* buffer);
+
+    TestBlockServerFactory factory_;
+    std::shared_ptr<TestBlockServerOpener> opener_;
+    std::shared_ptr<SnapshotHandler> handler_;
+    std::unique_ptr<ReadWorker> read_worker_;
+    TestBlockServer* block_server_;
+    std::future<bool> handler_thread_;
+};
+
+void HandlerTest::SetUp() {
+    ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp());
+    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+    ASSERT_NO_FATAL_FAILURE(CreateCowDevice());
+    ASSERT_NO_FATAL_FAILURE(SetDeviceControlName());
+
+    opener_ = factory_.CreateTestOpener(system_device_ctrl_name_);
+    ASSERT_NE(opener_, nullptr);
+
+    handler_ = std::make_shared<SnapshotHandler>(system_device_ctrl_name_, cow_system_->path,
+                                                 base_dev_->GetPath(), base_dev_->GetPath(),
+                                                 opener_, 1, false, false);
+    ASSERT_TRUE(handler_->InitCowDevice());
+    ASSERT_TRUE(handler_->InitializeWorkers());
+
+    read_worker_ = std::make_unique<ReadWorker>(cow_system_->path, base_dev_->GetPath(),
+                                                system_device_ctrl_name_, base_dev_->GetPath(),
+                                                handler_->GetSharedPtr(), opener_);
+    ASSERT_TRUE(read_worker_->Init());
+    block_server_ = static_cast<TestBlockServer*>(read_worker_->block_server());
+
+    handler_thread_ = std::async(std::launch::async, &SnapshotHandler::Start, handler_.get());
+}
+
+void HandlerTest::TearDown() {
+    ASSERT_TRUE(factory_.DeleteQueue(system_device_ctrl_name_));
+    ASSERT_TRUE(handler_thread_.get());
+    SnapuserdTestBase::TearDown();
+}
+
+AssertionResult HandlerTest::ReadSectors(sector_t sector, uint64_t size, void* buffer) {
+    if (!read_worker_->RequestSectors(sector, size)) {
+        return AssertionFailure() << "request sectors failed";
+    }
+
+    std::string result = std::move(block_server_->sent_io());
+    if (result.size() != size) {
+        return AssertionFailure() << "size mismatch in result, got " << result.size()
+                                  << ", expected " << size;
+    }
+
+    memcpy(buffer, result.data(), size);
+    return AssertionSuccess();
+}
+
+// This test mirrors ReadSnapshotDeviceAndValidate.
+TEST_F(HandlerTest, Read) {
+    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
+
+    // COPY
+    loff_t offset = 0;
+    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);
+
+    // REPLACE
+    offset += size_;
+    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0);
+
+    // ZERO
+    offset += size_;
+    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0);
+
+    // REPLACE
+    offset += size_;
+    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
+
+    // XOR
+    offset += size_;
+    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
+}
+
+TEST_F(HandlerTest, ReadUnalignedSector) {
+    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
+
+    ASSERT_TRUE(ReadSectors(1, BLOCK_SZ, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get() + SECTOR_SIZE, BLOCK_SZ), 0);
+}
+
+TEST_F(HandlerTest, ReadUnalignedSize) {
+    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(SECTOR_SIZE);
+
+    ASSERT_TRUE(ReadSectors(0, SECTOR_SIZE, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), SECTOR_SIZE), 0);
+}
+
 }  // namespace snapshot
 }  // namespace android