ext2: Implement Open() for an inode by number
diff --git a/src/filo-fs-ext2.adb b/src/filo-fs-ext2.adb
index 1a151b9..00a5985 100644
--- a/src/filo-fs-ext2.adb
+++ b/src/filo-fs-ext2.adb
@@ -34,6 +34,8 @@
    FEATURE_INCOMPAT_EXTENTS   : constant := 16#0040#;
    FEATURE_INCOMPAT_64BIT     : constant := 16#0080#;
 
+   EXT4_EXTENTS_FL            : constant := 16#8_0000#;
+
    procedure Mount
      (State    : in out T;
       Part_Len : in     Partition_Length;
@@ -73,8 +75,8 @@
       declare
          S_Inodes_Per_Group : constant Unsigned_32 := Read_LE32 (Super_Block, 10 * 4);
       begin
-         if S_Inodes_Per_Group in 1 .. Unsigned_32 (Positive'Last) then
-            State.Inodes_Per_Group := Positive (S_Inodes_Per_Group);
+         if S_Inodes_Per_Group in 1 .. Unsigned_32'Last then
+            State.Inodes_Per_Group := Inode_Index (S_Inodes_Per_Group);
          else
             Success := False;
             return;
@@ -570,6 +572,97 @@
    end Extent_Block_Map;
 
    procedure Open
+     (State    : in out T;
+      Inode    : in     Inode_Index;
+      Success  : out Boolean)
+   with
+      Pre => Is_Mounted (State) and not Is_Open (State),
+      Post => Success = Is_Open (State)
+   is
+      type Group_Index is new Natural;
+      subtype Desc_In_Block_Index is Group_Index
+         range 0 .. Group_Index (2 ** Log_Block_Size'Last / Desc_Size'First - 1);
+
+      Block_Size : constant Natural := 2 ** State.Block_Size_Bits;
+
+      Inodes_Per_Block : constant Inode_Index := Inode_Index (Block_Size / State.Inode_Size);
+      Desc_Per_Block : constant Group_Index := Group_Index (Block_Size / State.Desc_Size);
+
+      subtype Group_Off is Natural range 0 .. Desc_Size'Last;
+      function Group_Byte_Offset (Idx : Group_Index; Off : Group_Off) return Natural
+      is
+         (Natural (Idx) * State.Desc_Size + Off);
+
+      function Group_Inode_Table (Buf : Buffer_Type; Idx : Group_Index) return FSBlock_Offset
+      is
+        (FSBlock_Offset (
+         (if State.Feature_64Bit
+          then Shift_Left (Unsigned_64 (Read_LE32 (Buf, Group_Byte_Offset (Idx, 40))), 32)
+          else 0)
+         or
+         Unsigned_64 (Read_LE32 (Buf, Group_Byte_Offset (Idx, 8)))))
+      with
+         Pre => Buf'Length >= Group_Byte_Offset (Idx, 44);
+
+      subtype Inode_Off is Natural range 0 .. Inode_Size'Last;
+      function Inode_Byte_Offset (Idx : Inode_Index; Off : Inode_Off) return Natural
+      is
+         (Natural (Idx) * State.Inode_Size + Off);
+
+      function Inode_Flags (Buf : Buffer_Type; Idx : Inode_Index) return Unsigned_32
+      is
+        (Read_LE32 (Buf, Inode_Byte_Offset (Idx, 32)))
+      with
+         Pre => Buf'Length >= Inode_Byte_Offset (Idx, 36);
+
+      Group : constant Group_Index := Group_Index ((Inode - 1) / State.Inodes_Per_Group);
+      Desc_Block : constant FSBlock_Offset :=
+         1 + State.First_Data_Block + FSBlock_Offset (Group / Desc_Per_Block);
+      Cache_Start, Cache_End : Max_Block_Index;
+   begin
+      Cache_FSBlock
+        (State       => State,
+         Phys        => Desc_Block,
+         Level       => Natural'Min (Natural (Group / Desc_Per_Block), Block_Cache_Index'Last),
+         Cache_Start => Cache_Start,
+         Cache_End   => Cache_End,
+         Success     => Success);
+      if not Success then
+         return;
+      end if;
+
+      declare
+         Desc : constant Desc_In_Block_Index := Group mod Desc_Per_Block;
+         Inode_In_Group : constant Inode_Index := (Inode - 1) mod State.Inodes_Per_Group;
+         Inode_Block : constant FSBlock_Offset :=
+            Group_Inode_Table (State.Block_Cache (Cache_Start .. Cache_End), Group) +
+            FSBlock_Offset (Inode_In_Group / Inodes_Per_Block);
+      begin
+         Cache_FSBlock
+           (State       => State,
+            Phys        => Inode_Block,
+            Level       => Block_Cache_Index'Last,
+            Cache_Start => Cache_Start,
+            Cache_End   => Cache_End,
+            Success     => Success);
+         if not Success then
+            return;
+         end if;
+
+         declare
+            Inode_In_Block : constant Inode_Index := Inode_In_Group mod Inodes_Per_Block;
+            I_Flags : constant Unsigned_32 :=
+               Inode_Flags (State.Block_Cache (Cache_Start .. Cache_End), Inode_In_Block);
+         begin
+            State.Inode.Use_Extents := State.Feature_Extents and (I_Flags and EXT4_EXTENTS_FL) /= 0;
+            State.Inode.Extents := State.Block_Cache (Cache_Start + 40 .. Cache_Start + 100 - 1);
+            Reset_Cache_Logical (State);
+            State.S := File_Opened;
+         end;
+      end;
+   end Open;
+
+   procedure Open
      (State       : in out T;
       File_Len    :    out File_Length;
       File_Path   : in     String;
diff --git a/src/filo-fs-ext2.ads b/src/filo-fs-ext2.ads
index fbe6a35..03c05a9 100644
--- a/src/filo-fs-ext2.ads
+++ b/src/filo-fs-ext2.ads
@@ -65,11 +65,13 @@
    subtype Inode_Extents_Index is Natural range 0 .. 59;
    subtype Inode_Extents is Buffer_Type (Inode_Extents_Index);
 
+   type Inode_Index is new Unsigned_32 range 1 .. Unsigned_32'Last;
    subtype Inode_Size is Positive range 128 .. Positive (Unsigned_16'Last);
    subtype Desc_Size  is Positive range  32 .. 2 ** 15; -- power-of-2 that fits in 16 bits
 
    type Inode_Info is record
-      Extents  : Buffer_Type (Inode_Extents_Index) := (others => 16#00#);
+      Use_Extents : Boolean := False;
+      Extents     : Buffer_Type (Inode_Extents_Index) := (others => 16#00#);
    end record;
 
    type T is record
@@ -77,7 +79,7 @@
       Part_Len             : Partition_Length := 0;
       First_Data_Block     : FSBlock_Offset := 0;
       Block_Size_Bits      : Log_Block_Size := 10;
-      Inodes_Per_Group     : Positive := 1;
+      Inodes_Per_Group     : Inode_Index := 1;
       Inode_Size           : Ext2.Inode_Size := Ext2.Inode_Size'First;
       Desc_Size            : Ext2.Desc_Size := Ext2.Desc_Size'First;
       Feature_Extents      : Boolean := False;