iso9660: Try to match file names from Rock Ridge extension
diff --git a/src/filo-fs-iso9660.adb b/src/filo-fs-iso9660.adb
index eb21610..c542ee0 100644
--- a/src/filo-fs-iso9660.adb
+++ b/src/filo-fs-iso9660.adb
@@ -16,11 +16,21 @@
 
 package body FILO.FS.ISO9660 is
 
+   subtype SUSP_Signature is String (1 .. 2);
+
+   --------------------------------------------------------------------------
+
    function Is_Mounted (State : T) return Boolean is (State.S >= Mounted);
    function Is_Open (State : T) return Boolean is (State.S = File_Opened);
 
    --------------------------------------------------------------------------
 
+   function "+" (Rec : FS_Record; Off : FSBlock_Length) return FS_Record
+   is
+      (Block => Rec.Block, Offset => Rec.Offset + Off, Size => Rec.Size - Off)
+   with
+      Pre => Rec.Size >= Off;
+
    procedure Read
      (Buf      :    out Buffer_Type;
       Rec      : in     FS_Record;
@@ -92,6 +102,96 @@
       State.S := Mounted;
    end Mount;
 
+   procedure Find_SUSP_Record
+     (State          : in     T;
+      Rec            :    out FS_Record;
+      Dir_Rec_Pos    : in     File_Offset;
+      Dir_Rec_Len    : in     FSBlock_Length;
+      Searched_Sig   : in     SUSP_Signature)
+   with
+      Pre => FSBlock_Index (Dir_Rec_Pos mod FSBlock_Size) <= FSBlock_Size - Dir_Rec_Len
+   is
+      subtype Signature_Buf is Buffer_Type (0 .. 1);
+      function Signature (Str : SUSP_Signature) return Signature_Buf
+      is
+         ((Character'Pos (Str (Str'First)), Character'Pos (Str (Str'First + 1))));
+
+      Cur : FS_Record :=
+        (Block  => State.Inode.Extents (0).Start + FSBlock (Dir_Rec_Pos / FSBlock_Size),
+         Offset => FSBlock_Index (Dir_Rec_Pos mod FSBlock_Size),
+         Size   => Dir_Rec_Len);
+
+      Continuation : FS_Record := (others => <>);
+
+      SUSP_Head : Buffer_Type (0 .. 3);
+
+      Success : Boolean;
+   begin
+      Rec := (others => <>);
+      if Cur.Size = 0 then
+         return;
+      end if;
+
+      if Cur.Offset mod 2 = 1 then
+         Cur := Cur + 1;
+      end if;
+
+      for I in 1 .. 2**16 loop -- arbitrary limit to avoid endless loop
+         if Cur.Size < SUSP_Head'Length then
+            if Continuation.Size = 0 then
+               return;
+            else
+               Cur := Continuation;
+               Continuation := (others => <>);
+            end if;
+         end if;
+
+         Read (SUSP_Head, Cur, Success);
+         declare
+            Rec_Len : constant Natural := Natural (SUSP_Head (2));
+            SUSP_CE : Buffer_Type (0 .. 27);
+         begin
+            if not Success or else Rec_Len not in 4 .. Cur.Size then
+               return;
+            end if;
+
+            if SUSP_Head (0 .. 1) = Signature (Searched_Sig) then
+               Rec :=
+                 (Block  => Cur.Block,
+                  Offset => Cur.Offset,
+                  Size   => Rec_Len);
+               return;
+            elsif SUSP_Head (0 .. 1) = Signature ("CE") then
+               if Rec_Len < SUSP_CE'Length then
+                  return;
+               end if;
+
+               Read (SUSP_CE, Cur, Success);
+               if not Success then
+                  return;
+               end if;
+               declare
+                  CE_Extent : constant Unsigned_32 := Read_LE32 (SUSP_CE,  4);
+                  CE_Offset : constant Unsigned_32 := Read_LE32 (SUSP_CE, 12);
+                  CE_Size   : constant Unsigned_32 := Read_LE32 (SUSP_CE, 20);
+               begin
+                  if (CE_Offset > FSBlock_Size or CE_Size > FSBlock_Size) or else
+                     CE_Offset > FSBlock_Size - CE_Size
+                  then
+                     return;
+                  end if;
+                  Continuation :=
+                    (Block  => FSBlock (CE_Extent),
+                     Offset => FSBlock_Index (CE_Offset),
+                     Size   => FSBlock_Length (CE_Size));
+               end;
+            end if;
+
+            Cur := Cur + Rec_Len;
+         end;
+      end loop;
+   end Find_SUSP_Record;
+
    procedure Open
      (State    : in out T;
       I        : in     Inode_Index;
@@ -230,6 +330,29 @@
                      Upper_Case_Equal (File_Name, ISO_Name);
       end Match_ISO_Name;
 
+      procedure Match_Rock_Ridge_Name
+        (Pos      : in     File_Offset;
+         Len      : in     Natural;
+         Success  : in out Boolean)
+      with
+         Post => State = State'Old
+      is
+         Name_Rec : FS_Record;
+      begin
+         Find_SUSP_Record (State, Name_Rec, Pos, Len, "NM");
+         if Name_Rec.Size /= File_Name'Length + 5 then
+            Success := False;
+            return;
+         end if;
+
+         declare
+            RR_Name : Buffer_Type (0 .. File_Name'Length - 1);
+         begin
+            Read (RR_Name, Name_Rec + 5, Success);
+            Success := Success and then Equal (File_Name, RR_Name);
+         end;
+      end Match_Rock_Ridge_Name;
+
       Found_Inode : Inode_Index;
       Dir_Pos : File_Length;
    begin
@@ -297,8 +420,14 @@
                      return;
                   end if;
 
-
-                  Match_ISO_Name (Dir_Rec_Name_Length, Record_Dir_Pos, Success);
+                  -- First skip ISO name and try Rock Ridge extension:
+                  Match_Rock_Ridge_Name
+                    (Pos      => Record_Dir_Pos + File_Offset (Dir_Rec_Name_Length),
+                     Len      => Dir_Rec_Length - Dir_Rec_Head'Length - Dir_Rec_Name_Length,
+                     Success  => Success);
+                  if not Success then
+                     Match_ISO_Name (Dir_Rec_Name_Length, Record_Dir_Pos, Success);
+                  end if;
 
                   if Success then
                      Found_Inode :=