vold: remove f2fs reserved_blocks from storage total size

F2FS reserved_blocks should not be included for storage total size,
since it can't be used by users and users must not be aware of this
space.

Bug: 394342775
Test: check the total storage size of Setting App
Change-Id: I4eba69938dee3edfce2db37e6703edccc685b63d
Signed-off-by: Daeho Jeong <daehojeong@google.com>
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index d932ec8..657f051 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -1247,7 +1247,8 @@
     return android::vold::OpenAppFuseFile(uid, mountId, fileId, flags);
 }
 
-static android::status_t getDeviceSize(std::string& device, int64_t* storageSize) {
+static android::status_t getDeviceSize(std::string& device, int64_t* storageSize,
+                                       bool isF2fsPrimary) {
     // Follow any symbolic links
     std::string dataDevice;
     if (!android::base::Realpath(device, &dataDevice)) {
@@ -1256,14 +1257,35 @@
 
     // Handle mapped volumes.
     auto& dm = android::dm::DeviceMapper::Instance();
-    for (;;) {
-        auto parent = dm.GetParentBlockDeviceByPath(dataDevice);
-        if (!parent.has_value()) break;
-        dataDevice = *parent;
+    std::string dmPath = dataDevice;
+    if (dm.IsDmBlockDevice(dataDevice)) {
+        for (;;) {
+            auto parent = dm.GetParentBlockDeviceByPath(dataDevice);
+            if (!parent.has_value()) break;
+            dataDevice = *parent;
+        }
+    } else if (isF2fsPrimary) {
+        if (!dm.GetDmDevicePathByName(android::base::Basename(device), &dmPath)) {
+            LOG(WARNING) << "No proper dm device for " << device;
+            isF2fsPrimary = false;
+        }
+    }
+
+    // Find a device name for F2FS primary partition
+    std::string f2fsReservedBlocksSysfs;
+    std::size_t leaf;
+    if (isF2fsPrimary) {
+        leaf = dmPath.rfind('/');
+        if (leaf == std::string::npos) {
+            LOG(WARNING) << "dm device " << dmPath << " is not a path";
+            isF2fsPrimary = false;
+        }
+        f2fsReservedBlocksSysfs =
+                std::string() + "/sys/fs/f2fs/" + dmPath.substr(leaf + 1) + "/reserved_blocks";
     }
 
     // Get the potential /sys/block entry
-    std::size_t leaf = dataDevice.rfind('/');
+    leaf = dataDevice.rfind('/');
     if (leaf == std::string::npos) {
         LOG(ERROR) << "data device " << dataDevice << " is not a path";
         return EINVAL;
@@ -1293,14 +1315,32 @@
     }
 
     // Read the size file and be done
+    int64_t sizeNum;
     std::stringstream ssSize(size);
-    ssSize >> *storageSize;
+    ssSize >> sizeNum;
     if (ssSize.fail()) {
         LOG(ERROR) << sizeFile << " cannot be read as an integer";
         return EINVAL;
     }
 
-    *storageSize *= 512;
+    sizeNum *= 512;
+    if (isF2fsPrimary) {
+        int64_t reservedBlocksNum = 0;
+        if (!android::base::ReadFileToString(f2fsReservedBlocksSysfs, &size, true)) {
+            LOG(WARNING) << "Could not find valid entry from " << f2fsReservedBlocksSysfs;
+        } else {
+            std::stringstream reservedBlocks(size);
+            reservedBlocks >> reservedBlocksNum;
+            if (reservedBlocks.fail()) {
+                LOG(WARNING) << f2fsReservedBlocksSysfs << " cannot be read as an integer";
+                reservedBlocksNum = 0;
+            }
+        }
+        int64_t blockSize = android::base::GetIntProperty("ro.boot.hardware.cpu.pagesize", 0);
+        sizeNum -= reservedBlocksNum * blockSize;
+    }
+
+    *storageSize = sizeNum;
     return OK;
 }
 
@@ -1313,14 +1353,14 @@
         return EINVAL;
     }
 
-    status = getDeviceSize(entry->blk_device, storageSize);
+    status = getDeviceSize(entry->blk_device, storageSize, entry->fs_type == "f2fs");
     if (status != OK) {
         return status;
     }
 
     for (auto device : entry->user_devices) {
         int64_t deviceStorageSize;
-        status = getDeviceSize(device, &deviceStorageSize);
+        status = getDeviceSize(device, &deviceStorageSize, false);
         if (status != OK) {
             return status;
         }