Kaynağa Gözat

Ext4Pkg: Add uninitialized extents support

Uninitialized extents are special extents that have blocks allocated,
but are specified as uninitialized and therefore, reads behave the
same as file holes (reads 0), while writes already have blocks allocated.

Cc: Leif Lindholm <leif@nuviainc.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Signed-off-by: Pedro Falcato <pedro.falcato@gmail.com>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
Pedro Falcato 2 yıl önce
ebeveyn
işleme
2c6da5c2cb

+ 8 - 0
Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h

@@ -448,6 +448,14 @@ typedef struct {
   UINT32    eb_checksum;
 } EXT4_EXTENT_TAIL;
 
+/**
+ * EXT4 has this feature called uninitialized extents:
+ * An extent has a maximum of 32768 blocks (2^15 or 1 << 15).
+ * When we find an extent with > 32768 blocks, this extent is called uninitialized.
+ * Long story short, it's an extent that behaves as a file hole but has blocks already allocated.
+ */
+#define EXT4_EXTENT_MAX_INITIALIZED  (1 << 15)
+
 typedef UINT64  EXT4_BLOCK_NR;
 typedef UINT32  EXT4_INO_NR;
 

+ 21 - 0
Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h

@@ -1131,4 +1131,25 @@ Ext4SuperblockCheckMagic (
   IN EFI_BLOCK_IO_PROTOCOL  *BlockIo
   );
 
+/**
+   Check if the extent is uninitialized
+
+   @param[in] Extent    Pointer to the EXT4_EXTENT
+
+   @returns True if uninitialized, else false.
+**/
+#define EXT4_EXTENT_IS_UNINITIALIZED(Extent) ((Extent)->ee_len > EXT4_EXTENT_MAX_INITIALIZED)
+
+/**
+   Retrieves the extent's length, dealing with uninitialized extents in the process.
+
+   @param[in] Extent      Pointer to the EXT4_EXTENT
+
+   @returns Extent's length, in filesystem blocks.
+**/
+EXT4_BLOCK_NR
+Ext4GetExtentLength (
+  IN CONST EXT4_EXTENT  *Extent
+  );
+
 #endif

+ 22 - 2
Features/Ext4Pkg/Ext4Dxe/Extents.c

@@ -332,7 +332,7 @@ Ext4GetExtent (
     return EFI_NO_MAPPING;
   }
 
-  if (!(LogicalBlock >= Ext->ee_block && Ext->ee_block + Ext->ee_len > LogicalBlock)) {
+  if (!(LogicalBlock >= Ext->ee_block && Ext->ee_block + Ext4GetExtentLength (Ext) > LogicalBlock)) {
     // This extent does not cover the block
     if (Buffer != NULL) {
       FreePool (Buffer);
@@ -413,7 +413,7 @@ Ext4ExtentsMapKeyCompare (
   Extent = UserStruct;
   Block  = (UINT32)(UINTN)StandaloneKey;
 
-  if (Block >= Extent->ee_block && Block < Extent->ee_block + Extent->ee_len) {
+  if (Block >= Extent->ee_block && Block < Extent->ee_block + Ext4GetExtentLength (Extent)) {
     return 0;
   }
 
@@ -593,3 +593,23 @@ Ext4CheckExtentChecksum (
 
   return Tail->eb_checksum == Ext4CalculateExtentChecksum (ExtHeader, File);
 }
+
+/**
+   Retrieves the extent's length, dealing with uninitialized extents in the process.
+
+   @param[in] Extent      Pointer to the EXT4_EXTENT
+
+   @returns Extent's length, in filesystem blocks.
+**/
+EXT4_BLOCK_NR
+Ext4GetExtentLength (
+  IN CONST EXT4_EXTENT  *Extent
+  )
+{
+  // If it's an unintialized extent, the true length is ee_len - 2^15
+  if (EXT4_EXTENT_IS_UNINITIALIZED (Extent)) {
+    return Extent->ee_len - EXT4_EXTENT_MAX_INITIALIZED;
+  }
+
+  return Extent->ee_len;
+}

+ 11 - 3
Features/Ext4Pkg/Ext4Dxe/Inode.c

@@ -144,11 +144,19 @@ Ext4Read (
 
     HasBackingExtent = Status != EFI_NO_MAPPING;
 
-    if (!HasBackingExtent) {
+    if (!HasBackingExtent || EXT4_EXTENT_IS_UNINITIALIZED (&Extent)) {
+
       HoleOff = BlockOff;
-      HoleLen = Partition->BlockSize - HoleOff;
+
+      if (!HasBackingExtent) {
+        HoleLen = Partition->BlockSize - HoleOff;
+      } else {
+        // Uninitialized extents behave exactly the same as file holes.
+        HoleLen = Ext4GetExtentLength (&Extent) - HoleOff;
+      }
+
       WasRead = HoleLen > RemainingRead ? RemainingRead : HoleLen;
-      // Potential improvement: In the future, we could get the hole's tota
+      // Potential improvement: In the future, we could get the file hole's total
       // size and memset all that
       SetMem (Buffer, WasRead, 0);
     } else {