Files
Ada_Drivers_Library/docs/filesystem.md
2017-10-27 11:54:10 +02:00

7.7 KiB

File system

Accessing the file system

The user interface to read and write files in Ada_Drivers_Library is located in the File_IO package.

At initialization, there is no file system available, so you have to mount one before using the operations described below. Please read the section Mounting a file system to learn how to add a file system.

Opening a file

To open a file, use the Open function:

   FD : File_Descriptor;
begin
   if Open (FD, "/host/tmp/file.txt", Read_Only) /= OK then
      --  Error handling...
   end if;

You can open the file in Read_Only, Write_Only or Read_Write mode.

Reading from a file

Once the file descriptor is open in Read_Only or Read_Write mode you can use the Read function to get data from the file:


   Data : HAL.UInt8_Array (1 .. 10);
begin
   if Read (FD, Data'Address, Data'Length) /= Data'Length then
      --  Error handling...
   end if;

This uses the 'Address attribute to get the address of the data in memory. You also have to specify the size of the data in number of bytes. The return value is the number of bytes actually read.

A a safer and cleaner alternative is to use the generic function Generic_Read and instantiate it for the type of data that you want to read:

   type Custom_Type is [...];

   function Read is new Generic_Read (Custom_Type);
   Data : Custom_Type;
begin
   if Read (FD, Data) /= OK then
      --  Error handling...
   end if;

Writing to a file

Writing operations are very similar to the reading operations:


   Data : HAL.UInt8_Array (1 .. 10) := (others => 0);
begin
   if Write (FD, Data'Address, Data'Length) /= Data'Length then
      --  Error handling...
   end if;

And the generic version:

   type Custom_Type is [...];

   function Write is new Generic_Write (Custom_Type);
   Data : Custom_Type;
begin
   if Write (FD, Data) /= OK then
      --  Error handling...
   end if;

Seek and Offset

You can use the Offset and Seek function to manipulate the file descriptor offset.

Offset will give you the current value of the Offset.

   Current_Offset : File_Size;
begin
   Current_Offset := Offset (FD);

The function Seek is used to change the offset:

   Amount : File_Size := 10;
begin
   if Seek (FD, Forward, Amount) /= OK then
      --  Error handling...
   end if;

There are 4 seek modes available:

  • Forward: Increase the offset by the given Amount
  • backward: Decrease the offset by the given Amount
  • From_Start: Set to offset to Amount
  • From_End: Set the offset to the size of the file minus Amount

Flush

Use Flush to force all buffered data to be written on the file.

Close file

Use Close to close the file descriptor and free the associated resources.

Opening a directory

To open a directory, use the Open function:

   DD : Directory_Descriptor;
begin
   if Open (DD, "/host/tmp/") /= OK then
      --  Error handling...
   end if;

Traversing a directory

Use the Read function read a Directory_Entry:

type Directory_Entry (Name_Length : Natural) is record
      Name         : String (1 .. Name_Length);
      Subdirectory : Boolean;
      Read_Only    : Boolean;
      Hidden       : Boolean;
      Symlink      : Boolean;
      Size         : File_Size;
end record;

Be careful, Directory_Entry is a discriminated type which means that once you have declared a variable of this type you can only assign a value with the same discriminant.

For this reason, it's safer to always declare a new variable for each call to Read. Here is a typical example of how to traverse all the entries of a directory:

loop
   declare
      E : constant Directory_Entry := Read (DD);
   begin

      exit when E = Invalid_Dir_Entry;

      --  Use the directory entry here

   end;
end loop;

Once you went through all the entries of the directory, the Read function will return Invalid_Dir_Entry.

You can use the Reset function to start reading from the first entry again.

Close directory

Use Close to close the directory descriptor and free the associated resources.

Mounting a file system

To mount a file system you will need a file system driver. Please find below detailed instructions on how to instantiate and mount the different file systems available in Ada_Drivers_Library.

   if Mount_Volume ("mount_point", A_File_System_Driver) /= OK then
      --  Error handling...
   end

You can now access file in the volume, for example /mount_point/tmp/file.txt.

Mounting a drive

It is also possible to mount a disk drive that has one or more file system. Please note that for the moment only FAT file systems are supported.

In Ada_Drivers_Library disk drive are accessed with a Block_Driver interface.

   if Mount_Drive ("mount_point", A_Block_Driver) /= OK then
      --  Error handling...
   end if;

File system drivers available

File Allocation Table (FAT) drives

Work in progress...

ARM Semihosting file system

The ARM semihosting file system driver uses the ARM semihosting interface to provide a bridge to the host file system.

It is implemented in the package Semihosting.Filesystem.

Limitation

Given the semihosting operation available, it is not possible to implement a complete file system driver, for instance listing files in a directory. However this implementation allows you to open, read and write files on the host computer.

How to mount an ARM semihosting file system

To use the ARM semihosting file system you first have to declare it:


with Semihosting.Filesystem; use Semihosting.Filesystem;

package body My_Package is

  Semihosting_FS : aliased SHFS;
  

and finally, mount the file system:

   if Mount_Volume ("host", Semihosting_FS'Access) /= OK then
      --  Error handling
   end if;

You can now use the File_IO package to access the host file system.

For instance, opening /host/tmp/test.txt with File_IO.Open will actually open /tmp/test.txt on the host machine.

Native file system

The native file system is used on non embedded platform (Windows, Linux, etc.) to provide access to the OS file system through the common interface of Ada_Drivers_Library. This is mostly useful for testing purposes.

It is implemented in the package Filesystem.Native.

How to mount a native file system

To use the native file system you first have to declare it:


with Filesystem.Native; use Filesystem.Native;

package body My_Package is

  Native_FS : aliased Native_FS_Driver;
  

then use the Create function to specify which directory on the host file system will be the root of the Native file system driver:

   if Native_FS.Create ("/home/username") /= OK then
      --  Error handling
   end if;

and finally, mount the file system:

   if Mount_Volume ("mount_point", FS'Access) /= OK then
      --  Error handling
   end if;

You can now use the File_IO package to access the native file system.

For instance, opening /mount_point/test.txt with File_IO.Open will actually open /home/username/test.txt on your machine.

Writing a new driver

You may want to write your own file system driver because you use a custom format or if you want add support for a format that is not available in Ada_Drivers_Library.

File system drivers have to implement the HAL.Filesystem.Filesystem_Driver interface.

If your file system is located on a disk drive or any other type of mass storage device, the driver must use the HAL.Block_Driver interface to make sure that the code can be re-used on different drives. The FAT driver is an example of that.