| with Ada.Unchecked_Conversion; |
| with Interfaces.C; |
| with Interfaces.C.Strings; |
| |
| use Interfaces.C; |
| |
| package body FILO.FS.VFS |
| with |
| SPARK_Mode => Off |
| is |
| |
| State : T := Initial; |
| |
| Path_Max : constant := 4096; |
| Max_Link_Depth : constant := 32; |
| |
| subtype Path_Buffer is Buffer_Type (1 .. Path_Max); |
| Path_Buf : Path_Buffer; |
| |
| subtype Path_String is String (1 .. Path_Max); |
| Path : Path_String; |
| |
| function C_Mount return int |
| is |
| Success : Boolean; |
| begin |
| if not Is_Mounted (State) then |
| State := Initial; -- Work around not having an unmount in FILO. |
| end if; |
| Mount (State, Part_Len, Success); |
| return (if Success then 1 else 0); |
| end C_Mount; |
| |
| function C_Open (File_Path : Strings.chars_ptr) return int |
| is |
| function Component_Start (Path : String; Pos : Positive) return Positive is |
| begin |
| for I in Pos .. Path'Last loop |
| if Path (I) /= '/' then |
| return I; |
| end if; |
| end loop; |
| return Path'Last + 1; |
| end Component_Start; |
| |
| function Component_End (Path : String; Start : Positive) return Positive is |
| begin |
| for I in Start .. Path'Last loop |
| if Path (I) = '/' or Is_Space (Path (I)) then |
| return I - 1; |
| end if; |
| end loop; |
| return Path'Last; |
| end Component_End; |
| |
| function Path_End (Path : String; Start : Positive) return Positive is |
| begin |
| for I in Start .. Path'Last loop |
| if Is_Space (Path (I)) then |
| return I - 1; |
| end if; |
| end loop; |
| return Path'Last; |
| end Path_End; |
| |
| procedure Read_Link |
| (Path : in out Path_String; |
| Rest_First : in Positive; |
| Rest_Last : in Positive; |
| Link_Len : in Natural; |
| Success : out Boolean) |
| is |
| function As_String is new Ada.Unchecked_Conversion (Path_Buffer, Path_String); |
| |
| Rest_Len : constant Natural := Rest_Last - Rest_First + 1; |
| File_Pos : File_Offset := 0; |
| Read_Len : Natural; |
| begin |
| if Path_Max - Rest_Len < Link_Len then |
| Success := False; |
| return; |
| end if; |
| |
| Read |
| (State => State, |
| File_Pos => File_Pos, |
| Buf => Path_Buf (1 .. Link_Len), |
| Len => Read_Len); |
| Success := Read_Len = Link_Len; |
| |
| if Success then |
| Path (Link_Len + 1 .. Link_Len + Rest_Len) := Path (Rest_First .. Rest_Last); |
| Path (Link_Len + Rest_Len + 1 .. Path'Last) := (others => ' '); |
| Path (1 .. Link_Len) := As_String (Path_Buf) (1 .. Link_Len); |
| end if; |
| end Read_Link; |
| |
| Path_Len : constant size_t := Strings.Strlen (File_Path); |
| Root_Dir : Boolean := True; |
| begin |
| if not Is_Mounted (State) or Path_Len > Path_Max then |
| return 0; |
| end if; |
| |
| Path (1 .. Natural (Path_Len)) := Strings.Value (File_Path); |
| Path (Natural (Path_Len + 1) .. Path'Last) := (others => ' '); |
| |
| Link_Loop : |
| for I in 1 .. Max_Link_Depth loop |
| declare |
| Path_Pos : Positive := Path'First; |
| Path_Last : constant Positive := Path_End (Path, Path_Pos); |
| begin |
| Path_Loop : |
| loop |
| declare |
| Comp_First : constant Positive := Component_Start (Path, Path_Pos); |
| Comp_Last : constant Positive := Component_End (Path, Comp_First); |
| File_Type : FS.File_Type; |
| File_Len : File_Length; |
| Success : Boolean; |
| begin |
| if Comp_First > Comp_Last then |
| return 0; |
| end if; |
| |
| Open |
| (State => State, |
| File_Len => File_Len, |
| File_Type => File_Type, |
| File_Name => Path (Comp_First .. Comp_Last), |
| In_Root => Root_Dir, |
| Success => Success); |
| if not Success then |
| return 0; |
| end if; |
| Root_Dir := False; |
| |
| case File_Type is |
| when Dir => |
| if Comp_Last = Path_Last then |
| Close (State); |
| return 0; |
| end if; |
| Path_Pos := Comp_Last + 1; |
| when Regular => |
| if Comp_Last = Path_Last then |
| Set_File_Max (File_Len); |
| return 1; |
| else |
| Close (State); |
| return 0; |
| end if; |
| when Link => |
| if File_Len > Path_Max then |
| Success := False; |
| else |
| Read_Link |
| (Path => Path, |
| Rest_First => Comp_Last + 1, |
| Rest_Last => Path_Last, |
| Link_Len => Natural (File_Len), |
| Success => Success); |
| end if; |
| Close (State); |
| if not Success then |
| return 0; |
| end if; |
| Root_Dir := Path (1) = '/'; |
| exit Path_Loop; -- continue in Link_Loop |
| end case; |
| end; |
| end loop Path_Loop; |
| end; |
| end loop Link_Loop; |
| |
| return 0; |
| end C_Open; |
| |
| procedure C_Close is |
| begin |
| if Is_Open (State) then |
| Close (State); |
| end if; |
| end C_Close; |
| |
| function C_Read (Buf : System.Address; Len : int) return int |
| is |
| subtype Buffer_Range is Natural range 0 .. Integer (Len) - 1; |
| Buffer : Buffer_Type (Buffer_Range) |
| with |
| Convention => C, |
| Address => Buf; |
| |
| File_Pos : File_Offset := FILO.FS.File_Pos; |
| Read_Len : Natural; |
| begin |
| if Is_Open (State) then |
| Read (State, File_Pos, Buffer, Read_Len); |
| Set_File_Pos (File_Pos); |
| else |
| Read_Len := 0; |
| end if; |
| return int (Read_Len); |
| end C_Read; |
| |
| end FILO.FS.VFS; |