linux:fs:part_1

QDOS File Systems - Part 1

The first thing I found out about creating a new foreign Linux filesystem is this, there is not a lot of good information anywhere yet. There is plenty on creating virtual filesystems or native ones, but for foreign filesystems, b*gger all!

So, without further ado, here is the initial version of my filesystem. It consists of the file qdos.c as follows below. At the end of this code block, I shall try to explain what I'm doing here - as best I can.

/*
 *  linux/fs/qdos/qdos.c
 *
 *  Written 2008 by Norman Dunbar.
 *
 * Filesystem to support the Sinclair QL DS/DD floppy disc format.
 *
 */
 
#include <linux/module.h>
 
#include <linux/jiffies.h>
#include <linux/msdos_fs.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/buffer_head.h>
#include <linux/namei.h>
 
static int qdos_fill_super(struct super_block *s, void *data, int silent)
{
	return -EINVAL;
}
 
 
static int qdos_get_sb(struct file_system_type *fs_type,
			int flags, const char *dev_name,
			void *data, struct vfsmount *mnt)
{
	return get_sb_bdev(fs_type, flags, dev_name, data, qdos_fill_super, mnt);
 
}
 
static struct file_system_type qdos_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "qdos",
	.get_sb		= qdos_get_sb,
	.kill_sb	= kill_block_super,
	.fs_flags	= FS_REQUIRES_DEV | MS_RDONLY,
};
 
static int __init init_qdos_fs(void)
{
	return register_filesystem(&qdos_fs_type);
}
 
static void __exit exit_qdos_fs(void)
{
	unregister_filesystem(&qdos_fs_type);
}
 
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Sinclair QDOS floppy disc filesystem support");
MODULE_AUTHOR("Norman Dunbar");
 
module_init(init_qdos_fs)
module_exit(exit_qdos_fs)

Ok, here we go. The above code is probably best read from the bottom up. It is written in such a manner as to be loaded as a Linux Kernel Module or compiled into the Kernel - if that is what floats your boat! I much prefer the module approach as it allows me to load it, try it, unload it, fix it and so on. If I compiled it into the Kernel, each time I needed to change it, I'd have to rebuild a kernel and so on and so forth - too much trouble.

Lets see if I can explain what I'm doing here. As mentioned, reading from the bottom upwards.

As this is a module, I need to define the licence under which it will operate - in this case the GPL - and give a description and author for the code. These are documentary only, but the licence is not.

Following on from the module details, I'm telling the compilation system which two functions in my code are the module initialisation and module termination functions. These will be called once when the module is first loaded by the kernel and once when the module is unloaded from the kernel respectively. Note however that if the module is compiled directly into the kernel, the termination function will never be called - because the module will never be unloaded from the kernel.

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Sinclair QDOS floppy disc filesystem support");
MODULE_AUTHOR("Norman Dunbar");
 
module_init(init_qdos_fs)
module_exit(exit_qdos_fs)

Carrying on in a backwards manner, we define a structure qdos_fs_type which is a file_system_type structure. This defines various bits of information about the filesystem we are creating and is used to register and unregister the filesystem from Linux.

For our filesystem, the flags are set to say that (at the moment) we are defining a filesystem that needs a physical device - so it's not a virtual filesystem - and that it will be read only. The name of the filesystem will be qdos - this will be used when we manually mount a floppy using the root command :

mount -t qdos /dev/fd0 /media/floppy

Two fields in this structure are very important, get_sb and kill_sb are pointers to functions to create and kill the SuperBlock for this filesystem. In our filesystem we define our own code for get_sb but allow the kernel to destroy our superblock when the filesystem is unmounted.

static struct file_system_type qdos_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "qdos",
	.get_sb		= qdos_get_sb,
	.kill_sb	= kill_block_super,
	.fs_flags	= FS_REQUIRES_DEV | MS_RDONLY,
};
 
static int __init init_qdos_fs(void)
{
	return register_filesystem(&qdos_fs_type);
}
 
static void __exit exit_qdos_fs(void)
{
	unregister_filesystem(&qdos_fs_type);
}

As mentioned above, we define our own code to fill in the filesystem's superblock whenever a filesystem of this type is mounted. In our case, we use a kernel helper routine, get_sb_dev, to allocate the superblock in memory.fill in much of it for us, and then we fill in the rest ourselves. Our own routine is passed as a pointer as usual.

static int qdos_get_sb( struct file_system_type *fs_type,
			int flags, 
			const char *dev_name,
			void *data, 
			struct vfsmount *mnt)
{
	return get_sb_bdev(fs_type, flags, dev_name, data, qdos_fill_super, mnt);
 
}

And finally for now, here is our function that fills in the bits of the superblock that the kernel couldn't. As you can see, it does nothing except return an error code which tells the kernel that the attempt to mount the filesystem failed. For now this isn't much good - this code will be modified later - but it does allow us to register the filesystem with the kernel.

static int qdos_fill_super(struct super_block *s, void *data, int silent)
{
	return -EINVAL;
}

And that's about all there is to part one. The rest of the code simply pulls in various header files that will be needed when writing a filesystem. Some of these we may not need and we may add others later on.

If you want to cut and paste the whole code section from the beginning of this page into a file named, in my case, ~norman/src/ql5a/qdos.c then feel free - I need all the help I can get! LOL

In order to compile this code you will need a few pre-requisites :

  • The development tools installed for your Linux distro.
  • Kernel Source for your distro. Usually installed into /usr/src/linux - well, linked there.
  • My build.sh file, typed in exactly as it appears below.
  • A Makefile.
#!/bin/bash
# Run this to build the qdos.ko module.
make -C /lib/modules/`uname -r`/build M=`pwd`

I have documented elsewhere how to build a stock kernel because the installed kernel on my test machine was not modern enough and I'm not allowed to change it.

To compile my qdos module against the running kernel, the above commands in build.sh will work, but what it you want to compile against the installed, but not running modern kernel?

Simple, use this command instead in the build.sh file.

#!/bin/bash
# Run this to build the qdos.ko module.
make -C /lib/modules/2.6.25.6-default/build M=`pwd`

This assumes your modern kernel is indeed called 2.6.25.6-default.

obj-m += qdos.o

With the above two (very brief) files in place in the source directory, it is simple to build the qdos.ko kernel module.

./build.sh
make: Entering directory '/usr/src/linux-2.6.22.17-0.1-obj/i386/default'
make -C ../../../linux-2.6.22.17-0.1 O=../linux-2.6.22.17-0.1-obj/i386/default
  CC [M]  /home/norman/src/ql5a/qdos.o
  Building modules, stage 2.
  MODPOST 1 modules
  LD [M]  /home/norman/src/ql5a/qdos.ko
make: Leaving directory '/usr/src/linux-2.6.22.17-0.1-obj/i386/default'

If all goes well with the compilation, you should see quite a few new files created by the kernel build process.

ls
build.sh    Makefile        qdos.c   qdos.h   qdos.mod.c  qdos.o    qdos.txt~
built-in.o  Module.symvers  qdos.c~  qdos.ko  qdos.mod.o  qdos.txt

All we are really interested in here is the qdos.ko file. That is the kernel module for our filesystem. You will note, I hope, that although the file is named qdos.ko our makefile calls it qdos.o. Panic not, although surprising at first, the kernel build knows what is going on.

All registered filesystems are stored in the virtual file /proc/filesystems. To see what filesystems your Linux system can mount, simply cat the file. In the example that follows, I'm omitting all the virtual filesystems and only listing ones that need devices, otherwise the list is quite long.

cat /proc/filesystems | grep -v nodev
    minix
    iso9660
    ext3
    vfat

It appears that my own Linux setup will mount devices with minix, iso9660, ext3 and vfat filesystems. So, no qdos stuff there yet!

Ok, to load the module we need to be root. Proceed as follows :

su
Password:
 
insmod qdos.ko
cat /proc/filesystems | grep -v nodev
    minix
    iso9660
    ext3
    vfat
    qdos

Note: If you compiled your module for a kernel different to the running one, you will not be able to install the module as described bove. Linux checks the kernel versions in the running kernel and the module you have compiled - if there is the slightest mismatch, it will prevent you installing the module. If you do try, the following will happen:

insmod qdos.ko
insmod: error inserting 'qdos.ko': -1 Invalid module format

Assuming all went well, we can see that our module has been loaded because we have a new registered filesystem named qdos - progress has been made - unfortunately, we cannot mount any qdos floppies yet because our code doesn't create a proper superblock. You can try it if you like - stick a qdos floppy in the drive and see what happens. Type this as root :

mount -t qdos /dev/fd0 /media/floppy/
 
mount: block device /dev/fd0 is write-protected, mounting read-only
mount: wrong fs type, bad option, bad superblock on /dev/fd0,
       missing codepage or helper program, or other error
       In some cases useful info is found in syslog - try
       dmesg | tail  or so

There's no need to go off looking for a reason, we told it to use an incomplete filesystem and it did. Time to unload our module again - it is not functioning at this point, so there's no point taking up kernel memory with it. Still as root, run the following commands.

lsmod | grep qdos
qdos                    5888  0
 
rmmod qdos.ko
lsmod | grep qdos
 
cat /proc/filesystems | grep -v nodev
    minix
    iso9660
    ext3
    vfat

The lsmod command lists the loaded modules, we are only interested in the qdos one - so we grep for it. It seems to be loaded. Next we unload it and check again - this time, it is not listed. Looking at the registered filesystems shows that qdos is no longer registered, so it is obvious that our module termination function has been called and successfully unregistered the qdos filesystem.


  • linux/fs/part_1.txt
  • Last modified: 2008/06/24 16:15
  • by norman