Filesystem
This is implemented in
filesystem
In this kernel, the filesystem is implemented by several layers, and components.
When you try to open a file path, the filesystem
will look for the best entity that contains this path.
And this is achieved by the mapping system.
Check FAT for more information about the FAT filesystem specifically.
Then, when you open a file, you can specify several flags (implemented in OpenOptions
):
read
- Open the file for reading.write
- Open the file for writing.create
- Create the file if it doesn't exist.create_new
- Fail if the file exists.truncate
- Truncate the file if it exists.append
(implicitwrite
) - Append to the file if it exists.
With these, you can create new files and choose which mode to open them with. Of course the filesystem may refuse to create the file if the
operation is not supported, such as with /devices
directory mappings.
Mapping
This is implemented in
fs::mapping
The mapping, is a structure we use to map a path prefix to a Filesystem
.
For example, currently we have the following mappings:
'/' -> FAT (filesystem backed by disk)
'/devices' -> Devices (a virtual filesystem)
When you open a path, it will find the best mapping, i.e. the longest prefix that matches the path.
Then will use the resulting Filesystem
manager to open the file.
For example, if you open the path /devices/console
, it will use the Devices
filesystem manager to open the file /console
.
Internally, this mapping is stored in a recursive tree structure of MappingNode
,
each will contain:
- The
Filesystem
object. - Weak ref to parent (to not get into trouble when dropping)
- childern BTreeMap (child component name ->
MappingNode
)
So, it will be something like this:
- / (root) = {
fs: object
parent: None
children: {
"devices": {
fs: object
parent: /
children: {}
}
}
}
This is used so that we can treverse between two mappings easily.
i.e., if we are at the beginning of the mapping, and encountered ..
path, we can go back to the parent mapping.
Also, when going forward in mapping, we can check if the component is a child mapping of this mapping and switch to it easily.
With this treversal, we can build canonical path for a node.
Filesystem trait
The "manager" here is an implementor of the Filesystem
trait, which is a simple interface that controls all
the filesystem operations.
Operations supported are:
open_root
- Open the root directory, this is the entry point when treversing the filesystem.read_dir
- Read the directory entries from aDirectoryNode
.treverse_dir
- Look through the dir, and returnNode
that matches the entry name or error if not found.create_node
- Create a new file or directory inside aDirectoryNode
.read_file
- Read the file contents from aFileNode
.write_file
- Write the file contents to aFileNode
.flush_file
- Force the driver to flush the content to physical media (i.e. clear cache if any).close_file
- Send a message that we are closing the file, if you notice, we don't haveopen_file
, but instead, the user can treverse the filesystem withopen_root
andread_dir
until the file node is found, then it can be used directly. This function is used to alert the filesystem to clean up any resources that it might have allocated for this file.set_file_size
- Set the file size to a custom value, this is similar totruncate
in Unix systems,write_file
, will increase the file size if needed.unmount
- Unmount the filesystem, this is called when the filesystem is no longer needed, and it should clean up all resources. You might say we don't useDrop
, but there are several reasons I went with this.- We can't add
Drop
as a trait dependancy toFilesystem
, so I wanted something related to the trait itself. - Some filesystems might not be able to be dropped like the
DEVICES
global filesystem, but we still want to tell it to clean itself or the parts that can be cleaned before shutdown/reboot.
- We can't add
Node
See Node
The Filesystem
will give us an Node
when we open a directory or a file, and this Node
can be either FileNode
or DirectoryNode
I'm calling
Node
even though FAT doesn't have this concept, but I'm using it to represent the file information.
Partition tables
Currently we only support the MBR partition table, and we can only read the first partition, we don't check the partition type, and just forward it to the FAT filesystem.
Devices
See Devices
This is a basic dictionary that maps a device name, to a Arc<dyn Device>
. Then, when its opened, the device clone is
returned in a special FileNode
, so we can act upon it as a file.