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 :=