Virtual Filesystem¶
Virtual Filesystem (VFS) is a subsystem which unifies all available filesystems into one big virtual filesystem. All filesystem operations are done through it. Different mounted filesystems are referenced with names, which are called mount-points or volumes.
VFS provides a set of file access functions (see File Operations) and a set of filesystem access functions (see Filesystem Operations). The file access functions can be used to open files on any filesystem, close open files, read and write open files, create new files and delete existing files.
The filesystem manipulation functions are used to mount filesystems into VFS, unmount filesystems, and get information about mounted filesystems (e.g. the amount of free space on a volume). A mechanism for forceful unmounting of all filesystems is also provided. This mechanism is needed when the system performs shutdown, to prevent filesystem corruption.
To be able to provide these services, VFS keeps track of mounted filesystems and open files. VFS is thread-safe and synchronizes all its own operations and data structures. However TFS, which is accessed through VFS, does not provide proper concurrent access, it simply allows only one operation at a time.
Return Values¶
All VFS operations return non-negative values as an indication of successful
operation and negative values as failures. The return value VFS_OK
is
defined to be zero, and indicates success. The rest of the pre-defined return
values are negative. The full list of is as follows:
VFS_OK
- The operation succeeded.
VFS_NOT_SUPPORTED
- The requested operation is not supported and thus failed.
VFS_INVALID_PARAMS
- The parameters given to the called function were invalid and the operation failed.
VFS_NOT_OPEN
- The operation was attempted on a file which was not open and thus failed.
VFS_NOT_FOUND
- The requested file or directory does not exist.
VFS_NO_SUCH_FS
- The referenced filesystem or mount-point does not exist.
VFS_LIMIT
- The operation failed because some internal limit was hit. Typically this limit is the maximum number of open files or the maximum number of mounted filesystems.
VFS_IN_USE
- The operation couldn’t be performed because the resource was busy. (Filesystem unmounting was attempted when filesystem has open files, for example.)
VFS_ERROR
- Generic error, might be hardware related.
VFS_UNUSABLE
- The VFS is not in use, probably because a forceful unmount has been requested by the system shutdown code.
Limits¶
VFS limits the length of strings in filesystem operations. Filesystem implementations and VFS file and filesystem access users must make sure to use these limits when interacting with VFS.
The maximum length of a filename is defined to be 15 characters plus one
character for the end of string marker, i.e. VFS_NAME_LENGTH
is set to 16.
The maximum path length, including the volume name (mount-point), possible
absolute directory path and filename is defined to be 255 plus one character
for the end of string marker, i.e. VFS_PATH_LENGTH
is set to 256.
Internal Data Structures¶
VFS has two primary data structures: the table of all mounted filesystems and the table of open files.
The table of all filesystems, vfs_table
, is structured as follows:
Type | Name | Description |
---|---|---|
sempahore_t * |
sem |
A binary semaphore used for exclusive access to the filesystems table. |
vfs_entry_t[CONFIG_MAX_FILESYSTEMS] |
filesystems |
The filesystems table itself. |
A vfs_entry_t
itself has the following fields:
Type | Name | Description |
---|---|---|
fs_t * |
filesystem |
The filesystem driver for this
mount-point. If NULL , this
entry is unused. |
char[VFS_NAME_LENGTH] |
mountpoint |
The name of this mount-point. |
The table is initialized to contain only NULL
filesystems. All access to
this table must be protected by acquiring the semaphore used to lock the table
(vfs_table.sem
). New filesystems can be added to this table whenever there
are free rows, but only filesystems with no open files can be removed from the
table.
The table of open files (openfile_table
) is structured as follows:
Type | Name | Description |
---|---|---|
semaphore_t * |
sem |
A binary semaphore used for exclusive access to this table. |
openfile_entry_t[CONFIG_MAX_OPEN_FILES] |
files |
Table of open files. |
The open files table is also protected by a semaphore (openfile_table.sem
).
Whenever the table is altered, this semaphore must be held.
An openfile_entry_t
itself has the following fields:
Type | Name | Description |
---|---|---|
fs_t * |
filesystem |
The filesystem in which this
open file is located. If NULL ,
this is a free entry. |
int |
fileid |
A filesystem defined id for this open file. Every file in a filesystem must have a unique id. Ids do not need to be globally unique. |
int |
seek_position |
The current seek position in the file. |
If access to both tables is needed, the semaphore for vfs_table
must be
held before the openfile_table
semaphore can be lowered. This convention is
used to prevent deadlocks.
In addition to these, VFS uses two semaphores and two integer variables to
track active filesystem operations. The first semaphore is vfs_op_sem
,
which is used as a lock to synchronize access to the three other variables. The
second semaphore, vfs_unmount_sem
, is used to signal pending unmount
operations when the VFS becomes idle.
The initial value of vfs_op_sem
is one and vfs_unmount_sem
is initially
zero. The integer vfs_ops
is a zero initialized counter which indicates the
number of active filesystem operations on any given moment. Finally, the
boolean vfs_usable
indicates whether VFS subsystem is in use. VFS is out of
use before it has been initialized and it is turned out of use when a forceful
unmount is started by the shutdown process.
VFS Operations¶
The virtual filesystem is initialized at the system bootup by calling the following function:
void vfs_init(void)
- Initializes the virtual filesystem. This function is called before virtual memory is initialized.
- Implementation:
- Create the semaphore
vfs_table.sem
(initial value 1) and the semaphoreopenfile_table.sem
(initial value 1). - Set all entries in both
vfs_table
andopenfile_table
to free. - Create the semaphore
vfs_op_sem
(initial value 1) and the semaphorevfs_unmount_sem
(initial value 0). - Set the number of active operations (
vfs_ops
) to zero. - Set the VFS usable flag (
vfs_usable
).
- Create the semaphore
When the system is being shut down, the following function is called to unmount all filesystems:
void vfs_deinit(void)
- Force unmounts on all filesystems. This function must be used only at system shutdown.
- Sets VFS into unusable state and waits until all active filesystem operations have been completed. After that, unmounts all filesystems.
- Implementation:
- Call
semaphore_P
onvfs_op_sem
. - Set VFS usable flag to false.
- If there are active operations (
vfs_ops
> 0): callsemaphore_V
onvfs_op_sem
, wait for operations to complete by callingsemaphore_P
onvfs_unmount_sem
, re-acquire thevfs_op_sem
with a call tosemaphore_P
. - Lock both data tables by calling
semaphore_P
on bothvfs_table.sem
andopenfile_table.sem
. - Loop through all filesystems and unmount them.
- Release semaphores by calling
semaphore_V
onopenfile_table.sem
,vfs_table.sem
andvfs_op_sem
.
- Call
To maintain count on active filesystem operations and to wake up pending forceful unmount, the following two internal functions are used. The first one is always called before any filesystem operation is started and the latter when the operation has finished.
static int vfs_start_op(void)
- Start a new VFS operation. A VFS operation is anything that touches a filesystem.
- Returns
VFS_OK
if the operation can continue, or error (negative value) if the operation cannot be started (VFS is unusable). If the operation cannot continue, it should not later callvfs_end_op
. - Implementation:
- Call
semaphore_P
onvfs_op_sem
. - If VFS is usable, increment
vfs_ops
by one. - Call
semaphore_V
onvfs_op_sem
. - If VFS was usable, return
VFS_OK
, else returnVFS_UNUSABLE
.
- Call
static void vfs_end_op(void)
- End a started VFS operation.
- Implementation:
- Call
semaphore_P
onvfs_op_sem
. - Decrement
vfs_ops
by one. - If VFS is not usable and the number of active operations is zero, wake
up pending forceful unmount by calling
semaphore_V
onvfs_unmount sem
. - Call
semaphore_V
onvfs_op_sem
.
- Call
File Operations¶
The primary function of the virtual filesystem is to provide unified access to all mounted filesystems. The filesystems are accessed through file operation functions.
Before a file can be read or written it must be opened by calling vfs_open
:
openfile_t vfs_open(char *pathname)
- Opens the file addressed by
pathname
. The name must include both the full pathname and the filename. (e.g.[root]shell.mips32
) - Returns an open file identifier. Open file identifiers are non-negative integers. On error, negative value is returned.
- Implementation:
- Call
vfs_start_op
. If an error is returned by it, return immediately with the error codeVFS_UNUSABLE
. - Parse
pathname
into volume name and filename parts. - If filename is not valid (too long, no mount point, etc.), call
vfs_end_op
and return with error codeVFS_ERROR
. - Acquire locks to the filesystem table and the open file table.
- Find a free entry in the open file table. If no free entry is found (the
table is full), free the locks, call
vfs_end_op
, and return with the error codeVFS_LIMIT
. - Find the filesystem specified by the volume name part of the pathname
in the filesystem table. If the volume is not found, return with the same
procedure as for a full open file table, except that the error code is
VFS_NO_SUCH_FS
. - Allocate the found free open file entry by setting its filesystem field.
- Free the filesystem and the open file table locks.
- Call the filesystem’s internal open function. If the return value
indicates an error, lock the open file table, mark the entry free and
free the lock. Call
vfs_end_op
and return the error given by the filesystem. - Save the file identifier returned by the filesystem in the open file table.
- Set file’s seek position to zero (beginning of the file).
- Call
vfs_end_op
. - Return the row number in the open file table as the open file identifier.
- Call
- Opens the file addressed by
Open files must be properly closed. If a filesystem has open files, the
filesystem cannot be unmounted except on shutdown where unmount is forced. The
closing is done by calling vfs_close
:
int vfs_close(openfile_t file)
- Closes an open file
file
. - Returns
VFS_OK
(zero) on success, negative on error. - Implementation:
- Call
vfs_start_op
. If an error is returned by it, return immediately with the error codeVFS_UNUSABLE
. - Lock the open file table.
- Verify that the given file is really open, otherwise, free the open file
table lock and return
VFS_INVALID_PARAMS
. - Call close on the actual filesystem for the file.
- Mark the entry in the open file table free.
- Free the open file table lock.
- Call
vfs_end_op
. - Return the return value given by the filesystem when close was called.
- Call
- Closes an open file
The seek position within the file can be changed by calling:
int vfs_seek(openfile_t file, int seek position)
- Seek the given open file to the given seek position.
- The position is not verified to be within the file’s size and behavior on exceeding the current size of the file is filesystem dependent.
- Returns
VFS_OK
on success, negative on error. - Implementation:
- Call
vfs_start_op
. If an error is returned by it, return immediately with the error codeVFS_UNUSABLE
. - Lock the open file table.
- Verify that the given file is really open, otherwise, free the open file
table lock and return
VFS_INVALID_PARAMS
. - Set the new seek position in open file table.
- Free the open file table.
- Call
vfs_end_op
. - Return
VFS_OK
.
- Call
int vfs_read(openfile_t file, void *buffer, int bufsize)
- Reads at most
bufsize
bytes from the given file into the buffer. The read is started from the current seek position and the seek position is updated to match the new position in the file after the read. - Returns the number of bytes actually read. On most filesystems, the requested number of bytes is always read when available, but this behaviour is not guaranteed. At least one byte is always read, unless the end of file or error is encountered. Zero indicates the end of file and negative values are errors.
- Implementation:
- Call
vfs_start_op
. If an error is returned by it, return immediately with the error codeVFS_UNUSABLE
. - Verify that the given file is really open, otherwise return
VFS_INVALID_PARAMS
. - Call the internal
read
function of the filesystem. - Lock the open file table.
- Update the seek position in the open file table.
- Free the open file table.
- Call
vfs_end_op
. - Return the value returned by the filesystem’s
read
.
- Call
- Reads at most
int vfs_write(openfile_t file, void *buffer, int datasize)
- Writes at most
datasize
bytes from the givenbuffer
into the openfile
. - The write is started from the current seek position and the seek position is updated to match the new place in the file.
- Returns the number of bytes written. All bytes are always written unless an unrecoverable error occurs (filesystem full, for example). Negative values are error conditions on which nothing was written.
- Implementation:
- Call
vfs_start_op
. If an error is returned by it, return immediately with the error codeVFS_UNUSABLE
. - Verify that the given file is really open, otherwise return
VFS_INVALID_PARAMS
. - Call the internal
write
function of the filesystem. - Lock the open file table.
- Update the seek position in the open file table.
- Free the open file table.
- Call
vfs_end_op
. - Return the value returned by the filesystem’s
write
.
- Call
- Writes at most
Files can be created and removed by the following two functions:
int vfs_create(char *pathname, int size)
- Creates a new file with given
pathname
. The size of the file will besize
. Thepathname
must include the mount-point (full name would be[root]shell.mips32
, for example). - Returns
VFS_OK
on success, negative on error. - Implementation:
- Call
vfs_start_op
. If an error is returned by it, return immediately with the error codeVFS_UNUSABLE
. - Parse the
pathname
into volume name and file name parts. - If the
pathname
was badly formatted or too long, callvfs_end_op
and return with the error codeVFS_ERROR
. - Lock the filesystem table. (This is to prevent unmounting of the filesystem during the operation. Unlike read or write, we do not have an open file to guarantee that unmount does not happen.)
- Find the filesystem from the filesystem table. If it is not found, free
the table, call
vfs_end_op
and return with the error codeVFS_NO_SUCH_FS
. - Call the internal
create
function of the filesystem. - Free the filesystem table.
- Call
vfs_end_op
. - Return the value returned by the filesystem’s
create
.
- Call
- Creates a new file with given
int vfs_remove(char *pathname)
- Removes the file with the given
pathname
. The pathname must include the mount-point (a full name would be[root]shell.mips32
, for example). - Returns
VFS_OK
on success, negative on failure. - Implementation:
- Call
vfs_start_op
. If an error is returned by it, return immediately with the error codeVFS_UNUSABLE
. - Parse the pathname into the volume name and file name parts.
- If the
pathname
was badly formatted or too long, callvfs_end_op
and return with the error codeVFS_ERROR
. - Lock the filesystem table. (This is to prevent unmounting of the filesystem during the operation. Unlike read or write, we do not have an open file to guarantee that unmount does not happen.)
- Find the filesystem from the filesystem table. If it is not found, free
the filesystem table, call vfs end op and return with the error code
VFS_NO_SUCH_FS
. - Call the internal
remove
function of the filesystem. - Free the filesystem table by calling semaphore V on vfs table.sem.
- Call
vfs_end_op
. - Return the value returned by the filesystem’s
remove
.
- Call
- Removes the file with the given
Filesystem Operations¶
In addition to providing an unified access to all filesystems, VFS also
provides functions to mount and unmount filesystems. Filesystems are
automatically mounted at boot time with the function vfs_mount_all
, which
is described below.
The file kudos/fs/filesystems.c
contains a table of all available
filesystem drivers. When an automatic mount is attempted, this table is
traversed by the filesystems_try_all
function to find a driver that matches
the filesystem on the disk, if any.
void vfs_mount_all(void)
Mounts all filesystems found on all disks attached to the system. Tries all known filesystems until a match is found. If no match is found, prints a warning and ignores the disk in question.
Called in the system boot up sequence.
Implementation:
- For each disk in the system do the following steps:
- Get the device entry for the disk by calling
device_get
. - Dig the generic block device entry from the device descriptor.
- Attempt to mount the filesystem on the disk by calling
vfs_mount_fs
withNULL
as the volumename (see below).
To attach a filesystem manually either of the following two functions can be used. The first one probes all available filesystem drivers to initialize one on the given disk and the latter requires the filesystem driver to be pre-initialized.
int vfs_mount_fs(gbd_t *disk, char *volumename)
Mounts the given disk to the given mountpoint (
volumename
).volumename
must be non-empty. The mount is performed by trying out all available filesystem drivers listed in thefilesystems
array inkudos/fs/fileystems.c
. The first match (if any) is used as the filesystem driver for the disk.If
NULL
is given as thevolumename
, the name returned by the filesystem driver is used as the mount-point.Returns
VFS_OK
(zero) on success, negative on error (no matching filesystem driver or too many mounted filesystems).Implementation:
- Try the
init
functions of all available filesystems inkudos/fs/filesystems.c
by callingfilesystems_try_all
. - If no matching filesystem driver was found, print warning and
return the error code
VFS_NO_SUCH_FS
. - If the
volumename
isNULL
, use the name stored intofs_t->volume
name by the filesystem driver. - If the
volumename
is an empty string, unmount the filesystem driver from the disk and returnVFS_INVALID_PARAMS
. - Call
vfs_mount
(see below) with the filesystem driver instance andvolumename
. - If
vfs_mount
returned an error, unmount the filesystem driver from the disk and return the error code given by it. - Return with
VFS_OK
.
- Try the
int vfs_mount(fs_t *fs, char *name)
- Mounts an initialized filesystem driver
fs
into the VFS mount-pointname
. - Returns
VFS_OK
on success, negative on error. Typical errors areVFS_LIMIT
(too many mounted filesystems) andVFS-ERROR
(mount-point was already in use). - Implementation:
- Call
vfs_start_op
. If an error is returned by it, return immediately with the error codeVFS_UNUSABLE
. - Lock the filesystem table by calling
semaphore_P
onvfs_table.sem
. - Find a free entry on the filesystem table.
- If the table was full, free it by calling
semaphore_V
onvfs_table.sem
, callvfs_end_op
and return the error codeVFS_LIMIT
. - Verify that the mount-point name is not in use. If it is, free
the filesystem table by calling
semaphore_V
onvfs_table.sem
, callvfs_end_op
and return the error codeVFS_ERROR
. - Set the
mountpoint
andfs
fields in the filesystem table to match this mount. - Free the filesystem table by calling
semaphore_V
onvfs_table.sem
. - Call
vfs_end_op
. - Return
VFS_OK
.
- Call
- Mounts an initialized filesystem driver
To find out the amount of free space on given filesystem volume, the following function can be used:
int vfs_getfree (char *filesystem)
Finds out the number of free bytes on the given filesystem, identified by its mount-point name.
Returns the number of free bytes, negative values are errors.
Implementation:
- Call
vfs_start_op
. If an error is returned by it, return immediately with the error codeVFS_UNUSABLE
. - Lock the filesystem table by calling
semaphore_P
onvfs_table.sem
. (This is to prevent unmounting of the filesystem during the operation. Unlike read or write, we do not have an open file to guarantee that unmount does not happen.) - Find the filesystem by its mount-point name
filesystem
. - If the filesystem is not found, free the filesystem table by calling
semaphore_V
onvfs_table.sem
, callvfs_end_op
and return the error codeVFS_NO_SUCH_FS
.
- Call filesystem’s
getfree
function. - Free the filesystem table by calling
semaphore_V
onvfs_table.sem
- Call
vfs_end_op
. - Return the value returned by filesystem’s
getfree
function.
- Call