The goal of vfs is to have a very clear separation between an independent
filesystem and -dependent data structures and
operations. This is done by using two layers, an independent filesystem
one and an independent one. Surprising ! When adding a new filesystem
in a vfs interface we simply needed to do the lower layer, the
filesystem dependent routines. This low level code is then plugged into
the vfs upper layer. The advantage is that in the lower layer you only
need to code basic filesystem operations (check filesystem superbloc
informations, write a file, read a file, ...). All the very
complicated functions, in particular namei and lookup, does not needed
to be rewrite.
The code of the upper layer is located in all the vfs_* files in
kern/. the important structures definitions are in sys/namei.h
and sys/vnode*.h. Just take a look at these files if you want to
know how the kernel does the abstraction needed. A Compatible
Filesystem Interface will also describes these structures that are the
base of the vfs interface. The most important thing to remember is
that the upper level only manipulates vnodes (virtual inodes) instead
of inodes and blocks in the lower layer. The two layers communicates
using vectors of operations that defined each filesystem. This is the
main concept of vfs.
We only needed to know how work the interface of vfs
toward a filesystem. Taking a look to sys/vnode.h, we
can see a list of filesystem types.
These tags are used in the vnode struct for knowing the type of
filesystem we must use in the lower layer :
/* * Vnode tag types. * These are for the benefit of external programs only (e.g., pstat) * and should NEVER be inspected by the kernel. * * Note that v_tag is actually used to tell MFS from FFS, and EXT2FS from * the rest, so don't believe the above comment! */ enum vtagtype { VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_MSDOSFS, VT_LFS, VT_LOFS, VT_FDESC, VT_PORTAL, VT_NULL, VT_UMAP, VT_KERNFS, VT_PROCFS, VT_AFS, VT_ISOFS, VT_UNION, VT_ADOSFS, VT_EXT2FS, VT_NCPFS, VT_VFS, VT_XFS }; ... struct vnode { ... enum vtagtype v_tag; /* type of underlying data */ ... };
/* * Types of memory to be allocated */ #define M_FREE 0 /* should be on free list */ ... #define M_EXT2FSNODE 72 /* EXT2FS vnode private part */ ... #define INITKMEMNAMES { \ "free", /* 0 M_FREE */ \ ... "EXT2FS node", /* 72 M_EXT2FSNODE */ \ ... }
The last file for the interface is kern/vfs_conf.c. That file contains the global configuration of the vfs interface lower layer. The first thing is including external function definitions After that the array of functions pointers are built for the use of the filesystem. These structs are the vfsops (vfs operations; operations needed to initialise, mount, umount, sync, ...) and the vnodeops (virtual inode operations; operations that access inodes of the filesystem from the vnode given by the system like read, write, open, readlink, ...).
#ifdef FFS #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ffs/ffs_extern.h> #endif #ifdef EXT2FS #include <ufs/ext2fs/ext2fs_extern.h> #endif ... /* * Set up the filesystem operations for vnodes. * The types are defined in mount.h. */ #ifdef FFS extern struct vfsops ffs_vfsops; #endif ... /* * Set up the filesystem operations for vnodes. */ static struct vfsconf vfsconflist[] = { /* Fast Filesystem */ #ifdef FFS { &ffs_vfsops, MOUNT_FFS, 1, 0, MNT_LOCAL, ffs_mountroot, NULL }, #endif ... } /* * vfs_opv_descs enumerates the list of vnode classes, each with it's own * vnode operation vector. It is consulted at system boot to build operation * vectors. It is NULL terminated. */ extern struct vnodeopv_desc ffs_vnodeop_opv_desc; extern struct vnodeopv_desc ffs_specop_opv_desc; extern struct vnodeopv_desc ffs_fifoop_opv_desc; ... struct vnodeopv_desc *vfs_opv_descs[] = { #ifdef FFS &ffs_vnodeop_opv_desc, &ffs_specop_opv_desc, #ifdef FIFO &ffs_fifoop_opv_desc, #endif #endif