Merge "Simplified stats_event.c; exposed getter methods"
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index ce1f9ef..d3ee659 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1351,38 +1351,9 @@
     return ret;
 }
 
-static std::string GetUserdataBlockDevice() {
-    Fstab fstab;
-    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
-        LERROR << "Failed to read /proc/mounts";
-        return "";
-    }
-    auto entry = GetEntryForMountPoint(&fstab, "/data");
-    if (entry == nullptr) {
-        LERROR << "Didn't find /data mount point in /proc/mounts";
-        return "";
-    }
-    return entry->blk_device;
-}
-
 int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
-    const std::string& block_device = GetUserdataBlockDevice();
-    LINFO << "Userdata is mounted on " << block_device;
-    auto entry = std::find_if(fstab->begin(), fstab->end(), [&block_device](const FstabEntry& e) {
-        if (e.mount_point != "/data") {
-            return false;
-        }
-        if (e.blk_device == block_device) {
-            return true;
-        }
-        DeviceMapper& dm = DeviceMapper::Instance();
-        std::string path;
-        if (!dm.GetDmDevicePathByName("userdata", &path)) {
-            return false;
-        }
-        return path == block_device;
-    });
-    if (entry == fstab->end()) {
+    auto entry = GetMountedEntryForUserdata(fstab);
+    if (entry == nullptr) {
         LERROR << "Can't find /data in fstab";
         return -1;
     }
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 534eae4..e279af9 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -40,6 +40,7 @@
 using android::base::ParseByteCount;
 using android::base::ParseInt;
 using android::base::ReadFileToString;
+using android::base::Readlink;
 using android::base::Split;
 using android::base::StartsWith;
 
@@ -804,6 +805,89 @@
     return entries;
 }
 
+static std::string ResolveBlockDevice(const std::string& block_device) {
+    if (!StartsWith(block_device, "/dev/block/")) {
+        LWARNING << block_device << " is not a block device";
+        return block_device;
+    }
+    std::string name = block_device.substr(5);
+    if (!StartsWith(name, "block/dm-")) {
+        // Not a dm-device, but might be a symlink. Optimistically try to readlink.
+        std::string result;
+        if (Readlink(block_device, &result)) {
+            return result;
+        } else if (errno == EINVAL) {
+            // After all, it wasn't a symlink.
+            return block_device;
+        } else {
+            LERROR << "Failed to readlink " << block_device;
+            return "";
+        }
+    }
+    // It's a dm-device, let's find what's inside!
+    std::string sys_dir = "/sys/" + name;
+    while (true) {
+        std::string slaves_dir = sys_dir + "/slaves";
+        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(slaves_dir.c_str()), closedir);
+        if (!dir) {
+            LERROR << "Failed to open " << slaves_dir;
+            return "";
+        }
+        std::string sub_device_name = "";
+        for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {
+            if (entry->d_type != DT_LNK) continue;
+            if (!sub_device_name.empty()) {
+                LERROR << "Too many slaves in " << slaves_dir;
+                return "";
+            }
+            sub_device_name = entry->d_name;
+        }
+        if (sub_device_name.empty()) {
+            LERROR << "No slaves in " << slaves_dir;
+            return "";
+        }
+        if (!StartsWith(sub_device_name, "dm-")) {
+            // Not a dm-device! We can stop now.
+            return "/dev/block/" + sub_device_name;
+        }
+        // Still a dm-device, keep digging.
+        sys_dir = "/sys/block/" + sub_device_name;
+    }
+}
+
+FstabEntry* GetMountedEntryForUserdata(Fstab* fstab) {
+    Fstab mounts;
+    if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
+        LERROR << "Failed to read /proc/mounts";
+        return nullptr;
+    }
+    auto mounted_entry = GetEntryForMountPoint(&mounts, "/data");
+    if (mounted_entry == nullptr) {
+        LWARNING << "/data is not mounted";
+        return nullptr;
+    }
+    std::string resolved_block_device = ResolveBlockDevice(mounted_entry->blk_device);
+    if (resolved_block_device.empty()) {
+        return nullptr;
+    }
+    LINFO << "/data is mounted on " << resolved_block_device;
+    for (auto& entry : *fstab) {
+        if (entry.mount_point != "/data") {
+            continue;
+        }
+        std::string block_device;
+        if (!Readlink(entry.blk_device, &block_device)) {
+            LWARNING << "Failed to readlink " << entry.blk_device;
+            block_device = entry.blk_device;
+        }
+        if (block_device == resolved_block_device) {
+            return &entry;
+        }
+    }
+    LERROR << "Didn't find entry that was used to mount /data";
+    return nullptr;
+}
+
 std::set<std::string> GetBootDevices() {
     // First check the kernel commandline, then try the device tree otherwise
     std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 6ce6160..e625170 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -100,6 +100,7 @@
 FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
 // The Fstab can contain multiple entries for the same mount point with different configurations.
 std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path);
+FstabEntry* GetMountedEntryForUserdata(Fstab* fstab);
 
 // This method builds DSU fstab entries and transfer the fstab.
 //
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 6b842b3..e4d92ca 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -110,7 +110,7 @@
     return ReadFromImageFile(fd);
 }
 
-bool WriteToImageFile(int fd, const LpMetadata& input) {
+bool WriteToImageFile(borrowed_fd fd, const LpMetadata& input) {
     std::string geometry = SerializeGeometry(input.geometry);
     std::string metadata = SerializeMetadata(input);
 
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index a284d2e..88e5882 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -29,8 +29,6 @@
 // Helper function to serialize geometry and metadata to a normal file, for
 // flashing or debugging.
 std::unique_ptr<LpMetadata> ReadFromImageFile(int fd);
-bool WriteToImageFile(const char* file, const LpMetadata& metadata);
-bool WriteToImageFile(int fd, const LpMetadata& metadata);
 
 // We use an object to build the image file since it requires that data
 // pointers be held alive until the sparse file is destroyed. It's easier
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index cd860cd..04f8987 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -76,12 +76,15 @@
 // supported). It is a format specifically for storing only metadata.
 bool IsEmptySuperImage(const std::string& file);
 
-// Read/Write logical partition metadata to an image file, for diagnostics or
-// flashing. If no partition images are specified, the file will be in the
-// empty format.
+// Read/Write logical partition metadata and contents to an image file, for
+// flashing.
 bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
                       const std::map<std::string, std::string>& images, bool sparsify);
+
+// Read/Write logical partition metadata to an image file, for producing a
+// super_empty.img (for fastboot wipe-super/update-super) or for diagnostics.
 bool WriteToImageFile(const std::string& file, const LpMetadata& metadata);
+bool WriteToImageFile(android::base::borrowed_fd fd, const LpMetadata& metadata);
 std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file);
 std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
 
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 8a983ad..bb24069 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -83,8 +83,9 @@
 
 // Perform sanity checks so we don't accidentally overwrite valid metadata
 // with potentially invalid metadata, or random partition data with metadata.
-static bool ValidateAndSerializeMetadata(const IPartitionOpener& opener, const LpMetadata& metadata,
-                                         const std::string& slot_suffix, std::string* blob) {
+static bool ValidateAndSerializeMetadata([[maybe_unused]] const IPartitionOpener& opener,
+                                         const LpMetadata& metadata, const std::string& slot_suffix,
+                                         std::string* blob) {
     const LpMetadataGeometry& geometry = metadata.geometry;
 
     *blob = SerializeMetadata(metadata);
@@ -128,6 +129,10 @@
                    << block_device.first_logical_sector << " for size " << block_device.size;
             return false;
         }
+
+        // When flashing on the device, check partition sizes. Don't do this on
+        // the host since there is no way to verify.
+#if defined(__ANDROID__)
         BlockDeviceInfo info;
         if (!opener.GetInfo(partition_name, &info)) {
             PERROR << partition_name << ": ioctl";
@@ -138,6 +143,7 @@
                    << block_device.size << ", got " << info.size << ")";
             return false;
         }
+#endif
     }
 
     // Make sure all partition entries reference valid extents.
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index f45c1a4..8dec458 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -955,3 +955,14 @@
     ASSERT_NE(nullptr, GetEntryForMountPoint(&fstab, "/data"))
             << "Default fstab doesn't contain /data entry";
 }
+
+TEST(fs_mgr, UserdataMountedFromDefaultFstab) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
+    Fstab fstab;
+    ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
+    ASSERT_NE(nullptr, GetMountedEntryForUserdata(&fstab))
+            << "/data wasn't mounted from default fstab";
+}