<--Intro ^--VFS--^ inodes-->

How to write a Linux VFS filesystem module - Superblocks

March 4, 2004

Superblocks

The first thing that catches my eye when I browse through the source code for these two modules are structures that contain pointers to the majority of the functions within the modules. These structures are used as a "gateway" into the filesystem drivers, taking the abstracted ideas of a filesystem and providing a link to the implementation of these ideas to each of the filesystems.

For instance, the ramfs module has this structure:

static struct super_operations ramfs_ops = {
        statfs:         ramfs_statfs,
        put_inode:      force_delete,
};
The superblock describes information about the filesystem as a whole. You can look at the whole structure in /usr/src/linux/include/linux/fs.h, in the struct super_block definition. One of the fields there, called s_op, is of the type shown above, which is a structure of function pointers. It is in this superblock that the VFS will find the above structure to get to our functions that access our filesystem.

How does our super_operations structure get into a super_block structure?

Let's back up a bit and take a look at a lot of the utility bits that are in the romfs and ramfs modules. We'll look at ramfs for now, and maybe come back to romfs (ramfs is shorter).

Right near the bottom of the ramfs inode.c file, you will find the following:

static DECLARE_FSTYPE(ramfs_fs_type, "ramfs", ramfs_read_super, FS_LITTER);
static DECLARE_FSTYPE(rootfs_fs_type, "rootfs", ramfs_read_super, FS_NOMOUNT|FS_LITTER);

static int __init init_ramfs_fs(void)
{                                    
        return register_filesystem(&ramfs_fs_type);
}

static void __exit exit_ramfs_fs(void)
{
        unregister_filesystem(&ramfs_fs_type);
}
 
module_init(init_ramfs_fs)
module_exit(exit_ramfs_fs)

int __init init_rootfs(void)
{
        return register_filesystem(&rootfs_fs_type);
}
The first two lines, with DECLARE_FSTYPE, are two macros that declare two structures filled in with a bunch of values. These structures are then seen passed in in two of the functions, init_ramfs_fs() and init_rootfs().

**I have no idea what the init_rootfs() function is for.

The init_ramfs_fs() function (and its counterpart, exit_ramfs_fs()) can be seen passed to module_init() and module_exit(), which I assume are two more macros that do interesting things. At this point, I don't care what they are doing -- I'm taking it on faith that any VFS module I write will use its own init and exit functions in a similar manner.

Most of the above code is going to be boilerplate code that you put into any VFS, with a few letters changed to use your own name. The important part is back in the DECLARE_FSTYPE() macros, where we have initialized the new structures with ramfs_read_super. This is a function that exists in ramfs/inode.c, right near the end:

static struct super_block *ramfs_read_super(struct super_block * sb, void * data, int silent)
{
        struct inode * inode;
        struct dentry * root;

        sb->s_blocksize = PAGE_CACHE_SIZE;
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
        sb->s_magic = RAMFS_MAGIC;
        sb->s_op = &ramfs_ops;
        inode = ramfs_get_inode(sb, S_IFDIR | 0755, 0);
        if (!inode)
                return NULL;

        root = d_alloc_root(inode);
        if (!root) {
                iput(inode);
                return NULL;
        }
        sb->s_root = root;
        return sb;
}
This function is the starting point for the VFS to communicate to your filesystem, passed in through the init_ramfs_fs() function above. Passed in are the struct super_block that we are responsible for, and two other parameters which I haven't looked into yet, since they're not used above.

The s_blocksize and s_blocksize_bits are taken from a set of default values. The s_magic is a magic number for this filesystem, though no attempt to make it unique seems to be done. We also see that first structure being supplied to s_op, telling the VFS where to find superblock-related functions for our filesystem. The last little bit initializes the s_root value. This is a struct dentry pointer (discussed later), created from the inode (also discussed later) that represents the root of the filesystem. For now, we'll assume it's the basis of our whole filesystem, and that VFS will be using that to find out about the rest of our filesystem. The only thing we'll look at for now is that it two functions are used to populate s_root: ramfs_get_inode(), a local function, and d_alloc_root(), a function I'm sure I'll find is a system function somewhere. Before we move onto inodes, though, let's look again at our super_operations:

static struct super_operations ramfs_ops = {
        statfs:         ramfs_statfs,
        put_inode:      force_delete,
};
ramfs_statfs() is a function written by us, but what's force_delete()? Where is that defined? I haven't looked yet, but it's not one of ours!

Here's ramfs_statfs():

static int ramfs_statfs(struct super_block *sb, struct statfs *buf)
{
        buf->f_type = RAMFS_MAGIC;
        buf->f_bsize = PAGE_CACHE_SIZE;
        buf->f_namelen = 255;
        return 0;
}
<--Intro ^--VFS--^ inodes-->
©2002-2018 Wayne Pearson