Skip to content

Latest commit

 

History

History
 
 

vfscore

VFSCore: Unikraft's Virtual File System API

vfscore (Virtual File System Core) is an internal library of Unikraft that provides a generic interface of system calls related to filesystem management. There are multiple types of filesystems available in Unikraft (e.g: ramfs, 9pfs).

File Organization

The vfscore library consists of multiple files:

$ tree -L 2 lib/vfscore/
[...]
|-- vnode.c
|-- fops.c
|-- lookup.c
|-- stdio.c
|-- rootfs.c
|-- task.c
|-- syscalls.c
|-- vfs.h
`-- main.c
  • vnode.c defines functions that interact with a vnode structure.

  • fops.c implements a subset of functions that are defined in vfs.h, that are related with file operations.

  • lookup.c implements the subset of functions that are defined in vfs.h, that are related to searching for a specific path in a filesystem.

  • stdio.c exposes the functions that are used to interact with standard IO. You can notice that this is done very similar to defining a new filesystem, as it will be detailed later.

  • rootfs.c implements the function that is used to mount the root filesystem.

  • task.c implements some helper functions that are used to convert between different path conventions.

  • syscalls.c does the actual generic implementation of the sys_* function defined in vfs.h.

  • main.c defines the specific Unikraft system calls for interacting with a filesystem, which are based on the files described above. You can read more about adding a new system call for Unikraft here.

Key Functions and Data Structures

The vfscore library provides 2 structures to define operations on a filesystem (defined in lib/vfscore/include/vfscore/mount.h):

struct vfsops {
        int (*vfs_mount)        (struct mount *, const char *, int, const void*);
        ...
        struct vnops    *vfs_vnops;
};
struct vnops {
        vnop_open_t             vop_open;
        vnop_close_t            vop_close;
        vnop_read_t             vop_read;
        vnop_write_t            vop_write;
        vnop_seek_t             vop_seek;
        vnop_ioctl_t            vop_ioctl;
        ...
};

The first structure mainly defines the operation of mounting the filesystem, while the second defines the operations that can be executed on files (regular files, directories, etc.). The vnops structure can be seen as the file_operation structure in the Linux Kernel (more as an idea). More about this structure here.

The filesystem library will define two such structures through which it will provide the specified operations. To understand how these operations end up being used, let's examine the open system call

int
sys_open(char *path, int flags, mode_t mode, struct vfscore_file **fpp)
{
        struct vfscore_file *fp;
        struct vnode *vp;
        ...
        error = VOP_OPEN(vp, fp);
}

VOP_OPEN() is a macro that is defined as follows:

#define VOP_OPEN(VP, FP)           ((VP)->v_op->vop_open)(FP)

Register a New Filesystem

To map the desired function for your filesystem with the vfscore application, you need to declare a struct vnops structure. The example below show how this is done for ramfs:

struct vnops ramfs_vnops = {
    ramfs_open,             /* open */
    ramfs_close,            /* close */
    ramfs_read,             /* read */
    [...]
};

A filesytem will create its own structure and implement its own functions. If some of the functions are not used or not yet defined, you can use the vfscore_vop_nullop and cast it to the appropriate type:

#define ramfs_open      ((vnop_open_t)vfscore_vop_nullop)
#define ramfs_close     ((vnop_close_t)vfscore_vop_nullop)
#define ramfs_seek      ((vnop_seek_t)vfscore_vop_nullop)

Let's see now how to link the "file operations" of a filesystem to the vfscore library. For this, the library exposes a specific structure named vfscore_fs_type:

struct vfscore_fs_type {
        /* name of file system */
        const char *vs_name;
        /* initialize routine */
        int (*vs_init)(void);
        /* pointer to vfs operation */
        struct vfsops *vs_op;
};

Notice that this structure contains a pointer to the vfsops structure, which in turn contains the vnops structure. To register a filesystem, the vfscore library uses an additional section in the ELF. You can inspect the extra.ld file to see it.

As it was mentioned before, these sections come with helper macros, so this time is no exception either. The macro that registers a filesystem is:

 UK_FS_REGISTER(fssw)

Where the fssw argument is a vfscore_fs_type structure.

Notice that the system call will eventually call the registered operation. So, when adding support for a new filesystem in Unikraft, you only need to implement a subset (or all) functions in the vnops structure. You can take a look at how this is done for 9pfs filesystem.

Configuring Applications to Use vfscore

To use the vfscore library, you need to have a filesystem that actually implements the file operations. You can follow the same approach as in lib/ramfs/README.md