libsnapshot: Integrate with snapuserd.

This integrates libsnapshot with dm-user and snapuserd. Tests progress
significantly further now. Tests involving merging still fail as
snapuserd doesn't support this yet.

Bug: 168554689
Test: vts_libsnapshot_test
Change-Id: I464b683b464fe29a646f0f2823b7f4434a878614
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 84c93b1..d7a0b7e 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -35,9 +35,11 @@
         "update_metadata-protos",
     ],
     whole_static_libs: [
+        "libcutils",
         "libext2_uuid",
         "libext4_utils",
         "libfstab",
+        "libsnapshot_snapuserd",
     ],
     header_libs: [
         "libchrome",
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 4bbdca3..bf57ce9 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -37,6 +37,7 @@
 #include <libsnapshot/auto_device.h>
 #include <libsnapshot/return.h>
 #include <libsnapshot/snapshot_writer.h>
+#include <libsnapshot/snapuserd_client.h>
 
 #ifndef FRIEND_TEST
 #define FRIEND_TEST(test_set_name, individual_test) \
@@ -355,6 +356,9 @@
     // This is created lazily since it can connect via binder.
     bool EnsureImageManager();
 
+    // Ensure we're connected to snapuserd.
+    bool EnsureSnapuserdConnected();
+
     // Helper for first-stage init.
     bool ForceLocalImageManager();
 
@@ -411,6 +415,11 @@
                      const std::string& cow_device, const std::chrono::milliseconds& timeout_ms,
                      std::string* dev_path);
 
+    // Create a dm-user device for a given snapshot.
+    bool MapDmUserCow(LockedFile* lock, const std::string& name, const std::string& cow_file,
+                      const std::string& base_device, const std::chrono::milliseconds& timeout_ms,
+                      std::string* path);
+
     // Map a COW image that was previous created with CreateCowImage.
     std::optional<std::string> MapCowImage(const std::string& name,
                                            const std::chrono::milliseconds& timeout_ms);
@@ -639,6 +648,7 @@
     std::unique_ptr<IImageManager> images_;
     bool has_local_image_manager_ = false;
     bool in_factory_data_reset_ = false;
+    std::unique_ptr<SnapuserdClient> snapuserd_client_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
index 6bcd666..181ee33 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
@@ -91,7 +91,7 @@
     bool HandleClient(android::base::borrowed_fd fd, int revents);
     bool Recv(android::base::borrowed_fd fd, std::string* data);
     bool Sendmsg(android::base::borrowed_fd fd, const std::string& msg);
-    bool Receivemsg(android::base::borrowed_fd fd, const std::string& msg);
+    bool Receivemsg(android::base::borrowed_fd fd, const std::string& str);
 
     void ShutdownThreads();
     bool WaitForDelete(const std::string& control_device);
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 6574457..c88c01a 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -56,6 +56,7 @@
 using android::dm::DmTable;
 using android::dm::DmTargetLinear;
 using android::dm::DmTargetSnapshot;
+using android::dm::DmTargetUser;
 using android::dm::kSectorSize;
 using android::dm::SnapshotStorageMode;
 using android::fiemap::FiemapStatus;
@@ -115,6 +116,10 @@
     return snapshot_name + "-cow";
 }
 
+static std::string GetDmUserCowName(const std::string& snapshot_name) {
+    return snapshot_name + "-user-cow";
+}
+
 static std::string GetCowImageDeviceName(const std::string& snapshot_name) {
     return snapshot_name + "-cow-img";
 }
@@ -370,6 +375,45 @@
     return Return(images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags));
 }
 
+bool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name,
+                                   const std::string& cow_file, const std::string& base_device,
+                                   const std::chrono::milliseconds& timeout_ms, std::string* path) {
+    CHECK(lock);
+
+    auto& dm = DeviceMapper::Instance();
+
+    // Use the size of the base device for the COW device. It doesn't really
+    // matter, it just needs to look similar enough so the kernel doesn't complain
+    // about alignment or being too small.
+    uint64_t base_sectors = 0;
+    {
+        unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC));
+        if (fd < 0) {
+            PLOG(ERROR) << "open failed: " << base_device;
+            return false;
+        }
+        auto dev_size = get_block_device_size(fd);
+        if (!dev_size) {
+            PLOG(ERROR) << "Could not determine block device size: " << base_device;
+            return false;
+        }
+        base_sectors = dev_size / kSectorSize;
+    }
+
+    DmTable table;
+    table.Emplace<DmTargetUser>(0, base_sectors, name);
+    if (!dm.CreateDevice(name, table, path, timeout_ms)) {
+        return false;
+    }
+
+    if (!EnsureSnapuserdConnected()) {
+        return false;
+    }
+
+    auto control_device = "/dev/dm-user/" + name;
+    return snapuserd_client_->InitializeSnapuserd(cow_file, base_device, control_device);
+}
+
 bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
                                   const std::string& base_device, const std::string& cow_device,
                                   const std::chrono::milliseconds& timeout_ms,
@@ -1728,6 +1772,30 @@
         return true;
     }
 
+    if (IsCompressionEnabled()) {
+        auto name = GetDmUserCowName(params.GetPartitionName());
+
+        // :TODO: need to force init to process uevents for these in first-stage.
+        std::string cow_path;
+        if (!GetMappedImageDevicePath(cow_name, &cow_path)) {
+            LOG(ERROR) << "Could not determine path for: " << cow_name;
+            return false;
+        }
+
+        std::string new_cow_device;
+        if (!MapDmUserCow(lock, name, cow_path, base_path, remaining_time, &new_cow_device)) {
+            LOG(ERROR) << "Could not map dm-user device for partition "
+                       << params.GetPartitionName();
+            return false;
+        }
+        created_devices.EmplaceBack<AutoUnmapDevice>(&dm, name);
+
+        remaining_time = GetRemainingTime(params.timeout_ms, begin);
+        if (remaining_time.count() < 0) return false;
+
+        cow_device = new_cow_device;
+    }
+
     std::string path;
     if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
                      &path)) {
@@ -1847,6 +1915,22 @@
     if (!EnsureImageManager()) return false;
 
     auto& dm = DeviceMapper::Instance();
+
+    auto dm_user_name = GetDmUserCowName(name);
+    if (IsCompressionEnabled() && dm.GetState(dm_user_name) != DmDeviceState::INVALID) {
+        if (!EnsureSnapuserdConnected()) {
+            return false;
+        }
+        if (!dm.DeleteDevice(dm_user_name)) {
+            LOG(ERROR) << "Cannot unmap " << dm_user_name;
+            return false;
+        }
+        if (!snapuserd_client_->WaitForDeviceDelete("/dev/dm-user/" + dm_user_name)) {
+            LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete";
+            return false;
+        }
+    }
+
     auto cow_name = GetCowName(name);
     if (!dm.DeleteDeviceIfExists(cow_name)) {
         LOG(ERROR) << "Cannot unmap " << cow_name;
@@ -2117,6 +2201,20 @@
     return true;
 }
 
+bool SnapshotManager::EnsureSnapuserdConnected() {
+    if (!snapuserd_client_) {
+        if (!EnsureSnapuserdStarted()) {
+            return false;
+        }
+        snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
+        if (!snapuserd_client_) {
+            LOG(ERROR) << "Unable to connect to snapuserd";
+            return false;
+        }
+    }
+    return true;
+}
+
 bool SnapshotManager::ForceLocalImageManager() {
     images_ = android::fiemap::ImageManager::Open(gsid_dir_);
     if (!images_) {