A Filesystem Experiment

This is part of an experiment to improve the L4Re user space.

Some ideas:

  1. Use an existing rom file to hold something approximating a block device.
  2. Use a server process to open the file and to expose operations on it.
  3. Write programs that interact with the server process using the IPC operations.
  4. Implement system library functions that employ IPC calls to the server.
  5. Couple the C library to the system library functions.

The operations defined for this kind of filesystem server could be implemented by other servers that have different kinds of storage. For instance, a lightweight "virtual" filesystem might hold nodes in memory and might not even support files of any considerable size.

Resources of Interest

"Project: A Simple Operating System" covers similar topics. Of particular interest is the "M2: System call interface" milestone which describes the role of musl-libc and how it provides C library functions to programs.

The Plan

Employing a rom file and accessing it from within a server, exposing operations that are implemented using standard file operations, is the first step:

initial_structureapplicationfilesystemproviderC libraryIPCrom/fs

Subsequently, for more realism, a device server can be introduced to host the filesystem data, even if at first the device server is really only calling the same file operations as before:

subsequent_structureapplicationfilesystemproviderIPCdevice(storage access)C libraryIPCrom/fs

Ultimately, the C library calls will be "backed" by the IPC mechanisms:

ultimate_structureapplicationC libraryfilesystemproviderC libraryIPCdevice(storage access)IPCstorage

Filesystem Operations

Various filesystem APIs exist such as those provided by FUSE (defined in fuse.h as fuse_operations).

To avoid unnecessary complication, some essential operations may be defined. In practice, it is desirable to provide operations that support familiar system call primitives such as...

It might also be nice to provide support for memory-mapped files:

This would require some kind of paging support, but perhaps this might be achieved by supporting the appropriate protocol in the filesystem server.

See a description of file operations and their implementation.

Referencing Filesystems and Objects

One fundamental operation involves looking paths up and determining how the referenced objects are to be accessed. This might occur via the principal capability for filesystem access.

lookupapplicationfilesystemopenresource/object...

A capability might be provided by creating a resource object, referring to the filesystem object and maintaining relevant state. This capability becomes the point of contact with the filesystem, utilising filesystem code plus object-specific state to manipulate the object.

Accessing a File

The process of accessing a file is as follows:

  1. An application obtains a buffer provided by a dataspace and writes a path into it.
  2. To open the named file, it sends a message to the filesystem providing a reference to the buffer. (An interrupt request object is also provided for notifications.)
  3. The filesystem creates an object for accessing the file and passes the buffer reference to this object.
  4. The filesystem returns a reference to this file or resource object back to the application.

The following client-side pseudocode can be formulated:

# Allocate dataspace and IRQ.

ds = alloc_dataspace()
irq = alloc_irq()

# Write path to buffer. (This is just a memory-writing operation.)

ds.write(path)

# Obtain a reference to the file object.

f = fs.open(ds, irq)
open_file(empty buffer)applicationapplication/home/user/fileIRQ(user) filesystemopen(empty buffer)resource object/home/user/file

Reading from the file involves sending a message requesting data to the file object. Data is written to the shared buffer and information about the updated buffer is communicated:

read_fileapplicationapplicationresource object/home/user/fileread(buffered data)(reply)

It should be possible to wait for updates in files. This can be done by creating an interrupt request (IRQ) object and passing it along with the dataspace to a filesystem server. Updates will then cause the invocation of the IRQ object, and where the client has elected to wait for updates, a notification will be delivered:

update_notificationsapplicationapplicationresource object/home/user/fileyieldIRQnotifynotify

A mechanism is required to clean up resource objects once clients are finished with them, regardless of whether this is done explicitly or not. Explicit closure of files can be done using a close operation. Implicit closure of files is done by registering an interrupt request (IRQ) object for the deletion condition on the IPC gate that exposes the resource:

closing_filesapplicationIPC gateterminationeventIRQdeletioneventresource threadresource object/home/user/filedeallocate

System Structure

The filesystem object accessed by applications would need to be configured with an identity in order to interpret the layout of the filesystem and the metadata associated with filesystem objects. This user-configured filesystem object, along with the resource objects created by it, would in turn access the underlying storage, most likely something resembling a block device.

structureinitfilesystemproviderconfigure for userapplicationuser-configuredfilesystemdevice(storage access)

Device Servers

A device server will expose an interface that allows clients to access buffers containing raw filesystem data. When communicating with a server, a client provides its own dataspace, into which data is written by the server upon reading from the device. To update the device's data, a write operation is performed after the client has modified the data within the dataspace.

For the most part, devices should behave like normal files.

Filesystem Servers

Filesystems will employ the primitives provided by the device servers to access the raw data. The data is then interpreted as providing the following:

The organisation of such things is one of the tricks to designing a good filesystem, but a simple approach could be employed initially. Eventually, existing filesystem implementations could be considered with ext2-based filesystems being supported using libext2fs.

Basic operations to support on files would be like those supported on devices:

Meanwhile, the means to navigate directory listings would also be needed.

Package Manifest

The following packages are currently used to explore this work:

The idl4re tool is also required.