Skip to content

Filesystem

Douglas Thain edited this page Jan 11, 2019 · 5 revisions

Basekernel has an abstract filesystem interface (fs.h) and two filesystem implementations: cdromfs reads iso9660 cdroms (atapi device) and diskfs provides a simple unix-like read/write filesystem for hard disks (ata device).

Abstract Filesystem Interface (fs.h)

The abstract filesystem deals with three types of objects:

Name Details
struct fs the driver for a specific filesystem like cdromfs or diskfs
struct fs_volume an instance of a mounted filesystem on a device
struct fs_dirent an instance of a file or a directory on a volume

With kernel code, the steps to accessing a file on a specific device are this:

    const struct fs *fs = fs_get("cdromfs");
    struct device *device = device_open("atapi",2);
    struct fs_volume *v = fs_volume_mount(fs,device);
    struct fs_dirent *root = fs_volume_root(v);
    struct fs_dirent *d = fs_dirent_traverse(root,"/data/example.txt")
    while(fs_dirent_read(d, buffer, 1000) > 0) {
      // do something with read text
    }
    fs_dirent_close(d);
    fs_dirent_close(root);
    fs_volume_close(v);
    device_close(device);

Note that dirents, volumes, and devices are all reference counted, and each maintains a reference on its parent. So, it is safe to close the device, root directory, and volume if you have no other use for them: they will be deleted when the last reference is removed.

In most places throughout the kernel, it is not necessary to open a specific device or volume. Rather, each process already has a filesystem mounted and knows its root directory and current working directory. To map a path name into a fs_dirent, call fs_resolve which evaluates a path name within the context of the current process, and returns a fs_dirent. This object must be closed when no longer needed. For example:

struct fs_dirent *d = fs_resolve("/data/example.txt");
fs_dirent_read(d,buffer,length);
fs_dirent_close(d)

Common Filesystem Implementation (fs.c)

The common aspects of filesystems are in fs.c. Most functions here implements some basic sanity checking on a request, perform reference counting, and pass the operation on to the underlying filesystem.

A few functions have added functionality over the basic filesystem driver operations:

  • fs_dirent_traverse resolves multiple path elements by calling fs_dirent_lookup multiple times. For example, fs_dirent_traverse(d,"/a/b/c") calls fs_dirent_lookup(d,"a") then fs_dirent_lookup(x,"b") etc, and returns the final result.
  • fs_dirent_read reads arbitrary ranges from files by calling read_block on the underlying filesystem, and then assembling multiple or partial blocks as needed. Same with fs_dirent_write

User-level filesystem interface

Users can mount filesystems as a name and switch between them using chdir()

Current syscalls (syscall_handler.c)

This is the current interface for user-level programs

Name Details
open gets a file, or opens a new one if it doesn't exist, and returns a file pointer
close deallocates a file pointer
mount mounts a device as a certain type of filesystem and assigns it a name
read reads a specified number of bytes to a buffer
write writes a specified number of bytes to a buffer
chdir changes the current directory

Right now, chdir is used to specify the context of the file-based syscalls (open, close, read, write), and those syscalls do not take a full filename with a device name, i.e. C:/filename.

There are plenty more things we need to include, but this is enough for basic demo purposes.

Relevant process properties (process.h)

Name Details
ktable an array of kobject pointers, some representing files
root_dir the current working directory for the process
current_dir the current working directory for the process