Aeolos implements a Unix-like Virtual Filesystem (VFS) layer that provides a unified interface to multiple filesystem implementations. Currently, two filesystems are supported: ramfs (in-memory filesystem) and ttyfs (terminal/TTY filesystem).
VFS Architecture
The VFS uses a tree structure with two types of nodes:
inodes - Represent the actual file/directory data and metadata
tree nodes - Provide naming and hierarchy (multiple names can point to same inode)
This separation allows for hard links where multiple paths reference the same underlying data.
Node Types
From kernel/fs/vfs/vfs.h:19:
typedef enum {
VFS_NODE_FILE, // Regular file
VFS_NODE_FOLDER, // Directory
VFS_NODE_PIPE, // Named pipe (FIFO)
VFS_NODE_BLOCK_DEVICE, // Block device
VFS_NODE_CHAR_DEVICE, // Character device
VFS_NODE_MOUNTPOINT // Filesystem mount point
} vfs_node_type_t ;
Tree Node Structure
From kernel/fs/vfs/vfs.h:53:
struct _vfs_tnode_t {
char name [VFS_MAX_NAME_LEN]; // Name of this entry
vfs_inode_t * inode; // Pointer to inode
vfs_inode_t * parent; // Parent directory
vfs_tnode_t * sibling; // Next sibling in directory
};
Inode Structure
From kernel/fs/vfs/vfs.h:60:
struct _vfs_inode_t {
vfs_node_type_t type; // Type of node
size_t size; // Size in bytes
uint32_t perms; // Unix-style permissions
uint32_t uid; // Owner user ID
uint32_t refcount; // Number of references
vfs_fsinfo_t * fs; // Filesystem driver
void * ident; // Filesystem-specific data
vfs_tnode_t * mountpoint; // If mounted, points to mount location
vec_struct ( vfs_tnode_t * ) child; // Children (for directories)
};
Filesystem Driver Structure
From kernel/fs/vfs/vfs.h:35:
typedef struct vfs_fsinfo_t {
char name [ 16 ]; // Filesystem name
bool istemp; // True if temporary (ramfs, tmpfs)
vfs_inode_t * ( * mount)( vfs_inode_t * device);
int64_t ( * mknode)( vfs_tnode_t * this);
// Return value is number of bytes read/written
int64_t ( * read)( vfs_inode_t * this, size_t offset, size_t len, void * buff);
int64_t ( * write)( vfs_inode_t * this, size_t offset, size_t len, const void * buff);
// Return value is -1 on error, 0 on success
int64_t ( * sync)( vfs_inode_t * this);
int64_t ( * refresh)( vfs_inode_t * this);
int64_t ( * setlink)( vfs_tnode_t * this, vfs_inode_t * target);
int64_t ( * ioctl)( vfs_inode_t * this, int64_t req_param, void * req_data);
} vfs_fsinfo_t ;
VFS Initialization
The VFS is initialized in kernel/fs/vfs/vfs.c:54:
void vfs_init ()
{
klog_warn ( "partial stub \n " );
// initialize the root folder and mount ramfs there
vfs_root . inode = vfs_alloc_inode (VFS_NODE_FOLDER, 0 777 , 0 , NULL , NULL );
vfs_register_fs ( & ramfs);
vfs_mount ( NULL , "/" , "ramfs" );
klog_ok ( "done \n " );
}
This creates the root directory and mounts ramfs at /.
Filesystem Registration
Filesystems register themselves with vfs_register_fs() at kernel/fs/vfs/vfs.c:37:
void vfs_register_fs ( vfs_fsinfo_t * fs )
{
vec_push_back ( & vfs_fslist, fs);
}
Registered filesystems can be retrieved by name with vfs_get_fs() at kernel/fs/vfs/vfs.c:43:
vfs_fsinfo_t * vfs_get_fs ( char * name )
{
for ( size_t i = 0 ; i < vfs_fslist . len ; i ++ )
if ( strncmp (name, vfs_fslist . data [i]-> name , sizeof ((( vfs_fsinfo_t ) { 0 }). name )) == 0 )
return vfs_fslist . data [i];
klog_err ( "filesystem %s not found \n " , name);
return NULL ;
}
File Operations
Open File Descriptor
When a file is opened, a descriptor is created from kernel/fs/vfs/vfs.h:73:
typedef struct {
vfs_tnode_t * tnode; // Tree node
vfs_inode_t * inode; // Inode
vfs_openmode_t mode; // Read/write mode
size_t seek_pos; // Current position
} vfs_node_desc_t ;
Opening Files
Files are opened with vfs_open() which returns a handle (integer index into the task’s open file table):
vfs_handle_t vfs_open ( char * path , vfs_openmode_t mode );
Open modes from kernel/fs/vfs/vfs.h:28:
typedef enum {
VFS_MODE_READ,
VFS_MODE_WRITE,
VFS_MODE_READWRITE
} vfs_openmode_t ;
Reading and Writing
File I/O operations:
Reading
Writing
Seeking
Closing
int64_t vfs_read ( vfs_handle_t handle , size_t len , void * buff );
Creating and Linking
Create Node
Hard Link
Unlink
int64_t vfs_create ( char * path , vfs_node_type_t type );
Directory Operations
Directory entries are read with vfs_getdent() which fills a vfs_dirent_t structure from kernel/fs/vfs/vfs.h:81:
typedef struct {
vfs_node_type_t type;
size_t record_len;
char name [VFS_MAX_NAME_LEN];
} vfs_dirent_t ;
ramfs - RAM Filesystem
ramfs is a simple in-memory filesystem that stores all data in kernel heap memory.
ramfs Registration
From kernel/fs/ramfs/ramfs.c:7:
vfs_fsinfo_t ramfs = {
.name = "ramfs" ,
.istemp = true ,
.mount = ramfs_mount,
.mknode = ramfs_mknode,
.sync = ramfs_sync,
.refresh = ramfs_refresh,
.read = ramfs_read,
.write = ramfs_write,
.setlink = ramfs_setlink
};
ramfs Identification Structure
ramfs uses a simple structure to track file data at kernel/fs/ramfs/ramfs.c:20:
typedef struct {
size_t alloc_size; // Allocated buffer size
void * data; // Pointer to data buffer
} ramfs_ident_t ;
ramfs Operations
Mounting
From kernel/fs/ramfs/ramfs.c:85:
vfs_inode_t * ramfs_mount ( vfs_inode_t * at )
{
( void )at;
vfs_inode_t * ret = vfs_alloc_inode (VFS_NODE_MOUNTPOINT, 0 777 , 0 , & ramfs, NULL );
ret -> ident = create_ident ();
return ret;
}
Reading
From kernel/fs/ramfs/ramfs.c:32:
int64_t ramfs_read ( vfs_inode_t * this , size_t offset , size_t len , void * buff )
{
ramfs_ident_t * id = ( ramfs_ident_t * ) this -> ident ;
memcpy ((( uint8_t * ) id -> data ) + offset, buff, len);
return len;
}
Writing
From kernel/fs/ramfs/ramfs.c:39:
int64_t ramfs_write ( vfs_inode_t * this , size_t offset , size_t len , const void * buff )
{
ramfs_ident_t * id = ( ramfs_ident_t * ) this -> ident ;
memcpy (buff, (( uint8_t * ) id -> data ) + offset, len);
return len;
}
Syncing
The sync operation resizes the buffer if needed at kernel/fs/ramfs/ramfs.c:47:
int64_t ramfs_sync ( vfs_inode_t * this )
{
ramfs_ident_t * id = ( ramfs_ident_t * ) this -> ident ;
if ( this -> size > id -> alloc_size ) {
id -> alloc_size = this -> size ;
id -> data = kmrealloc ( id -> data , id -> alloc_size );
}
return 0 ;
}
ramfs automatically grows file buffers as needed during sync operations. Buffer sizes never shrink to avoid repeated allocations.
ttyfs - Terminal Filesystem
ttyfs provides terminal/TTY devices as files with circular input and output buffers.
ttyfs Registration
From kernel/fs/ttyfs/ttyfs.c:11:
vfs_fsinfo_t ttyfs = {
.name = "ttyfs" ,
.istemp = true ,
.mount = ttyfs_mount,
.mknode = ttyfs_mknode,
.sync = ttyfs_sync,
.refresh = ttyfs_refresh,
.read = ttyfs_read,
.write = ttyfs_write,
.setlink = ttyfs_setlink
};
ttyfs Identification Structure
From kernel/fs/ttyfs/ttyfs.c:23:
typedef struct {
char * buff_in, * buff_out; // Circular buffers
size_t in_start, in_size; // Input buffer state
size_t out_start, out_size; // Output buffer state
} ttyfs_ident_t ;
#define TTY_BUFFER_SIZE 16
ttyfs Reading
Reading from a TTY drains the input buffer at kernel/fs/ttyfs/ttyfs.c:43:
int64_t ttyfs_read ( vfs_inode_t * this , size_t offset , size_t len , void * buff )
{
ttyfs_ident_t * id = this -> ident ;
// no of bytes that should be read
size_t rlen = MIN (len, id -> in_size );
// read the bytes, wrap around if needed
for ( size_t i = 0 ; i < rlen; i ++ ) {
size_t index = ( id -> in_start + i) % TTY_BUFFER_SIZE;
(( char * )buff)[i] = id -> buff_in [index];
}
// update start and size of buffer
id -> in_start += rlen;
id -> in_start %= TTY_BUFFER_SIZE;
id -> in_size -= rlen;
// TODO: block if read less than len bytes
return rlen;
}
ttyfs Writing
Writing to a TTY appends to the output buffer at kernel/fs/ttyfs/ttyfs.c:65:
int64_t ttyfs_write ( vfs_inode_t * this , size_t offset , size_t len , const void * buff )
{
ttyfs_ident_t * id = this -> ident ;
// write bytes at end, wrap around if needed
size_t end = id -> out_start + id -> out_size ;
for ( size_t i = 0 ; i < len; i ++ ) {
size_t index = (end + i) % TTY_BUFFER_SIZE;
id -> buff_out [index] = (( char * )buff)[i];
}
// update start and size
if (len > TTY_BUFFER_SIZE - id -> out_size )
id -> out_start = (end + len) % TTY_BUFFER_SIZE;
id -> out_size = MIN (len + id -> out_size , TTY_BUFFER_SIZE);
return len;
}
ttyfs buffers are only 16 bytes. Writing more than the buffer size will overwrite old data. Future versions should implement blocking when buffers are full.
VFS Debugging
The VFS provides a debug function to dump the filesystem tree at kernel/fs/vfs/vfs.c:30:
void vfs_debug ()
{
klog_info ( "dumping nodes \n " );
dumpnodes_helper ( & vfs_root, 0 );
klog_printf ( " \n " );
}
This recursively prints the tree structure showing:
Node level (depth in tree)
Node name
Inode address
Reference count
Usage Example
From kernel/kmain.c:23, the first kernel task demonstrates VFS usage:
void kinit ( tid_t tid )
{
( void )tid;
klog_show ();
klog_ok ( "first kernel task started \n " );
initrd_init ((stv2_struct_tag_modules * ) stv2_find_struct_tag (bootinfo, STV2_STRUCT_TAG_MODULES_ID));
klog_printf ( " \n " );
char buff [ 4096 ] = { 0 };
vfs_handle_t fh = vfs_open ( "/docs/test.txt" , VFS_MODE_READ);
klog_info ( "reading \" /docs/test.txt \" : \n\n " );
int64_t nb = vfs_read (fh, 4096 , buff);
klog_printf ( " %s \n " , buff, nb);
klog_info ( "bytes read: %d \n " , nb);
vfs_close (fh);
vfs_debug ();
pmm_dumpstats ();
klog_warn ( "This OS is a work in progress. The computer will now halt." );
sched_kill (tid);
}
Limitations and Future Work
Path Resolution Path parsing and traversal logic is not fully implemented. The VFS needs complete path resolution with support for . and ...
Permissions Unix-style permissions are stored but not enforced. Access control checks need to be added.
Caching No inode or directory entry caching is implemented, which may impact performance.
Blocking I/O Read/write operations are non-blocking. Support for blocking on TTY input is needed.
Symbolic Links Only hard links are supported. Symbolic links are not yet implemented.
Mount Table No mount table or mount options are tracked. Only the root mount is supported.