ext2: Support gaps in extents for sparse files
diff --git a/src/filo-fs-ext2.adb b/src/filo-fs-ext2.adb
index 89d2dbb..ee9265b 100644
--- a/src/filo-fs-ext2.adb
+++ b/src/filo-fs-ext2.adb
@@ -449,7 +449,6 @@
procedure Next_Ref
(Current : in FSBlock_Offset;
- Logical_Off : in FSBlock_Logical;
Depth : in Extent_Depth;
Next : out Extent_Idx;
Cache_Start : out Max_Block_Index;
@@ -457,14 +456,12 @@
Success : out Boolean)
with
Pre =>
- Logical_Off <= Logical and
Dynamic_Max_Index = State.Static.Block_Size / Extent_Header_Size - 1,
Post =>
State.Static = State.Static'Old and State.S = State.S'Old and
(if Success then
Next <= Dynamic_Max_Index and then
- Cache_End = Cache_Start + State.Static.Block_Size - 1 and then
- Extent_Logical (Cache (Cache_Start .. Cache_End), Next) <= Logical)
+ Cache_End = Cache_Start + State.Static.Block_Size - 1)
is
begin
Cache_FSBlock
@@ -491,9 +488,11 @@
Hdr_Magic = Extent_Header_Magic and then
Hdr_Depth = Depth and then
Hdr_Entries in Extent_Idx and then
- Hdr_Entries <= Dynamic_Max_Index and then
- First_Logical = Logical_Off;
- if not Success then
+ Hdr_Entries <= Dynamic_Max_Index;
+ if not Success or else
+ -- Is the searched `Logical' out of range?
+ First_Logical > Logical
+ then
Next := 1;
else
pragma Assert (Cache_End - Cache_Start + 1 = State.Static.Block_Size);
@@ -510,26 +509,28 @@
Cache_Start, Cache_End : Max_Block_Index;
Logical_Off, Length : FSBlock_Logical;
+ Physical_Off : FSBlock_Offset;
Idx : Extent_Idx;
begin
+ Physical := 0; -- Return `0' if the searched `Logical' is not allocated.
Success :=
Inode_Magic = Extent_Header_Magic and then
- Inode_Entries > 0 and then
Inode_Entries < Inline_Extents'Length / Extent_Header_Size and then
- First_Logical <= Logical and then
Depth in Extent_Depth;
- if not Success then
- Physical := 0;
+ if not Success or else
+ -- Is there no extent or the searched `Logical' out of range?
+ Inode_Entries = 0 or else First_Logical > Logical
+ then
return;
end if;
Idx := Bin_Search (Inline_Extents, Inode_Entries);
if Depth = 0 then
- Physical := Extent_Physical (Inline_Extents, Idx);
+ Physical_Off := Extent_Physical (Inline_Extents, Idx);
Logical_Off := Extent_Logical (Inline_Extents, Idx);
Length := Extent_Length (Inline_Extents, Idx);
else
- Physical := Index_Physical (Inline_Extents, Idx);
+ Physical_Off := Index_Physical (Inline_Extents, Idx);
Logical_Off := Index_Logical (Inline_Extents, Idx);
loop
pragma Loop_Invariant
@@ -540,8 +541,7 @@
Logical_Off <= Logical);
Depth := Depth - 1;
Next_Ref
- (Current => Physical,
- Logical_Off => Logical_Off,
+ (Current => Physical_Off,
Depth => Depth,
Next => Idx,
Cache_Start => Cache_Start,
@@ -552,22 +552,39 @@
end if;
exit when Depth = 0;
- Physical := Index_Physical (Cache (Cache_Start .. Cache_End), Idx);
+ Physical_Off := Index_Physical (Cache (Cache_Start .. Cache_End), Idx);
Logical_Off := Index_Logical (Cache (Cache_Start .. Cache_End), Idx);
+
+ -- Is the searched `Logical' out of range?
+ if Logical_Off > Logical then
+ return;
+ end if;
end loop;
- Physical := Extent_Physical (Cache (Cache_Start .. Cache_End), Idx);
+ Physical_Off := Extent_Physical (Cache (Cache_Start .. Cache_End), Idx);
Logical_Off := Extent_Logical (Cache (Cache_Start .. Cache_End), Idx);
Length := Extent_Length (Cache (Cache_Start .. Cache_End), Idx);
end if;
- Success :=
- (0 < Length and Length <= Initialized_Extent_Max_Len) and then
- Logical_Off <= FSBlock_Logical'Last - Length and then
- Logical < Logical_Off + Length and then
- FSBlock_Offset (Logical - Logical_Off) <= FSBlock_Offset'Last - Physical;
+ -- Is the searched `Logical' out of range?
+ if Logical_Off > Logical or else
+ -- Is this an empty extent?
+ (Length = 0 or Length > Initialized_Extent_Max_Len)
+ then
+ return;
+ end if;
+
+ Success := Logical_Off <= FSBlock_Logical'Last - Length + 1;
+ if not Success or else
+ -- Is the searched `Logical' after this extent?
+ Logical > Logical_Off + Length - 1
+ then
+ return;
+ end if;
+
+ Success := FSBlock_Offset (Logical - Logical_Off) <= FSBlock_Offset'Last - Physical_Off;
if Success then
- Physical := Physical + FSBlock_Offset (Logical - Logical_Off);
+ Physical := Physical_Off + FSBlock_Offset (Logical - Logical_Off);
end if;
end Extent_Block_Map;
@@ -946,19 +963,25 @@
end if;
exit when not Success;
- Cache_FSBlock
- (Static => Static,
- Cache => State.Cache,
- Phys => Physical,
- Level => File_Cache_Level,
- Cache_Start => Cache_Start,
- Cache_End => Cache_End,
- Success => Success);
- pragma Assert (Cache_Start <= Cache_End - (In_Block + Len_Here - 1));
- exit when not Success;
+ if Physical > 0 then
+ Cache_FSBlock
+ (Static => Static,
+ Cache => State.Cache,
+ Phys => Physical,
+ Level => File_Cache_Level,
+ Cache_Start => Cache_Start,
+ Cache_End => Cache_End,
+ Success => Success);
+ pragma Assert (Cache_Start <= Cache_End - (In_Block + Len_Here - 1));
+ exit when not Success;
- Buf (Pos .. Last) := Cache (
- Cache_Start + In_Block .. Cache_Start + In_Block + Len_Here - 1);
+ Buf (Pos .. Last) := Cache (
+ Cache_Start + In_Block .. Cache_Start + In_Block + Len_Here - 1);
+ else
+ -- Treat unallocated and unwritten blocks as all 00 (sparse files).
+ Buf (Pos .. Last) := (others => 16#00#);
+ end if;
+
File_Pos := File_Pos + File_Length (Len_Here);
Pos := Pos + Len_Here;
Len := Pos - Buf'First;