Node: lower layer code, Next: , Previous: lower layer structures, Up: Implementation



lower layer code

This section will guide you through your filesystem code general structure based on the existing filesystem. The easiest way to create good global filesystem environment is to copy existing arrangements. Your new filesystem should probably go into the ufs/myfs/ directory if it's an Hard Drive filesystem.

The first important file is myfs_extern.h. It's the file that will be included in the vfs interface so it must contains all the definitions of vfsops, vnops, and others global declarations. These declarations must be inner __BEGIN_DECLS and __END_DECLS macros. It must also contain the declaration as extern of the vnops pointer types. example :

__BEGIN_DECLS

/* myfs_vfsops.c */
int myfs_mountroot(void);
int myfs_mount(struct mount *, const char *, void *,
		   struct nameidata *, struct proc *);
int myfs_reload(struct mount *, struct ucred *, struct proc *);
...
/* myfs_vnops.c */
int myfs_create(void *);
int myfs_mknod(void *);
int myfs_open(void *);
...
__END_DECLS

extern int (**myfs_vnodeop_p)(void *);

Next comes the vfsops implementation. Most of the time, this is done in myfs_vfsops.c but if you have vfsops that needs a lot of code you can put it into another file like myfs_quota.c for quota subsystem for example. The first thing to do, is to build the vfsops vector corresponding to what functions you want to call for each corresponding operation. Take care to add the function you want at the right place otherwise it will never be called at the correct moment.
// vfs interface operations
struct vfsops myfs_vfsops = {
	myfs_mount,
	myfs_start,
	myfs_unmount,
	myfs_root,
	myfs_quotactl,
	myfs_statfs,
	myfs_sync,
	myfs_vget,
	myfs_fhtovp,
	myfs_vptofh,
	myfs_init,
	myfs_sysctl,
	myfs_check_export,
	vfs_stdextattrctl
};

Once you have the implementation of each vfs operations. There's no particular problem for these function once you respect the functions declarations you have done using the correct structs in parameters. Be careful that the current kernel code is written in K&R C so the parameters are not written as usual :
int 
myfs_foo(arg, bar)
        struct vfs_arg* arg;
        int bar;
{
...

If you don't want to implement a particular vfs operation that is not necessary, don't hesitate to return an EOPNOTSUPP error so the error will be reported to the user. If on the other hand, the system want you to make a silly thing that will corrupt your filesystem simply do a kernel panic. Even if it's not smart, this way you will not have a corrupted filesystem and you will be able to easily see the debugging of the kernel and what happened. This way, if you think it's a kernel bug, you will be able to contact OpenBSD core team that will help you finding and debugging the problem.
The last part is the vnops. The work is usually done in myfs_vnops.c. Unlike the vfsops part, we will begin by the implementation of the operations. All the functions have the same signature (int func (void *)) so be careful to cast the void * to the corresponding struct before using it. If you don't do that the kernel will remind you to do it. The above example is the standard coding style in the OpenBSD kernel. Take care to use ap-> and not v-> in future treatments.

int
myfs_foo(v)
	void *v;
{
	struct vop_foo_args /* {
		struct vnode *a_dvp;
                int a;
	} */ *ap = v;

        printf (``arg->a=%i'', ap->a);
...

I recommend that you simply make empty functions that always return EOPNOTSUPP and print "hello myfs_foo" for the moment. This way you will have a trace of what is done each time and you will be able to make the vnops vector quickly. It is done in two steps, the first step is to make the operations vector, while the second has the operation, vector and the double pointer needed to have a complete description.
/* Global vfs vnode operations for myfs. */
int (**myfs_vnodeop_p)(void *);
struct vnodeopv_entry_desc myfs_vnodeop_entries[] = {
	{ &vop_default_desc, vn_default_error },
	{ &vop_lookup_desc, myfs_lookup },	/* lookup */
	{ &vop_create_desc, myfs_create },	/* create */
	{ &vop_mknod_desc, myfs_mknod },		/* mknod */
	{ &vop_open_desc, myfs_open },		/* open */
	{ &vop_close_desc, myfs_close },			/* close */
	{ &vop_access_desc, myfs_access },	/* access */
	{ &vop_getattr_desc, myfs_getattr },	/* getattr */
	{ &vop_setattr_desc, myfs_setattr },	/* setattr */
	{ &vop_read_desc, myfs_read },		/* read */
	{ &vop_write_desc, myfs_write },		/* write */
...
	{ NULL, NULL }
};
struct vnodeopv_desc myfs_vnodeop_opv_desc =
	{ &myfs_vnodeop_p, myfs_vnodeop_entries };
This is the myfs_vnodeop_opv_desc that will be used by the vfs interface. For vnops if you do not want to support an operation you simply do not need to include it in the available entries. The vfs will only call entries put in the descs vector. Otherwise it will return a EINVALL error.