理想未来ってなんやねん

娘可愛い。お父さん頑張る。

続:FreeBSD のファイルシステムのソースコードを読む

昨日の続き
modulesディレクトリの下にはMakefileしかなく、ソースコード本体はfsやufsディレクトリにあることがわかりましたので、fsディレクトリを見ていきたいと思います。

ソースコードが短いものを探す

できれば短いコードの方がわかりやすいので、サイズの小さいものを探します。

$ cd /usr/src/sys/fs
$ du | sort -n
8	./deadfs
22	./fifofs
26	./fdescfs
28	./portalfs
50	./nullfs
70	./pseudofs
76	./procfs
92	./devfs
94	./udf
108	./hpfs
110	./nwfs
110	./unionfs
120	./tmpfs
130	./ntfs
140	./cd9660
140	./smbfs
184	./coda
246	./msdosfs
318	./nfs
432	./nfsserver
552	./nfsclient
3058	.

deadfsが一番小さいようですね。

deadfs

どんなファイルシステムか知りませんが、とりあえず見てみます。

$ cd deadfs
$ ls -l
-rw-r--r--  1 root  wheel  5563 Oct 25  2009 dead_vnops.c

dead_vnops.cというファイルが一つだけありました。

中身を確認します。
長くなりますが始めなので敢えて全て載せます。

/*-
 * Copyright (c) 1989, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)dead_vnops.c	8.1 (Berkeley) 6/10/93
 * $FreeBSD: src/sys/fs/deadfs/dead_vnops.c,v 1.51.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/poll.h>
#include <sys/vnode.h>

/*
 * Prototypes for dead operations on vnodes.
 */
static vop_bmap_t	dead_bmap;
static vop_ioctl_t	dead_ioctl;
static vop_lookup_t	dead_lookup;
static vop_open_t	dead_open;
static vop_poll_t	dead_poll;
static vop_read_t	dead_read;
static vop_write_t	dead_write;
static vop_getwritemount_t dead_getwritemount;
static vop_rename_t	dead_rename;

struct vop_vector dead_vnodeops = {
	.vop_default =		&default_vnodeops,

	.vop_access =		VOP_EBADF,
	.vop_advlock =		VOP_EBADF,
	.vop_bmap =		dead_bmap,
	.vop_create =		VOP_PANIC,
	.vop_getattr =		VOP_EBADF,
	.vop_getwritemount =	dead_getwritemount,
	.vop_inactive =		VOP_NULL,
	.vop_ioctl =		dead_ioctl,
	.vop_link =		VOP_PANIC,
	.vop_lookup =		dead_lookup,
	.vop_mkdir =		VOP_PANIC,
	.vop_mknod =		VOP_PANIC,
	.vop_open =		dead_open,
	.vop_pathconf =		VOP_EBADF,	/* per pathconf(2) */
	.vop_poll =		dead_poll,
	.vop_read =		dead_read,
	.vop_readdir =		VOP_EBADF,
	.vop_readlink =		VOP_EBADF,
	.vop_reclaim =		VOP_NULL,
	.vop_remove =		VOP_PANIC,
	.vop_rename =		dead_rename,
	.vop_rmdir =		VOP_PANIC,
	.vop_setattr =		VOP_EBADF,
	.vop_symlink =		VOP_PANIC,
	.vop_vptocnp =		VOP_EBADF,
	.vop_write =		dead_write,
};

/* ARGSUSED */
static int
dead_getwritemount(ap)
	struct vop_getwritemount_args /* {
		struct vnode *a_vp;
		struct mount **a_mpp;
	} */ *ap;
{
	*(ap->a_mpp) = NULL;
	return (0);
}

/*
 * Trivial lookup routine that always fails.
 */
/* ARGSUSED */
static int
dead_lookup(ap)
	struct vop_lookup_args /* {
		struct vnode * a_dvp;
		struct vnode ** a_vpp;
		struct componentname * a_cnp;
	} */ *ap;
{

	*ap->a_vpp = NULL;
	return (ENOTDIR);
}

/*
 * Open always fails as if device did not exist.
 */
/* ARGSUSED */
static int
dead_open(ap)
	struct vop_open_args /* {
		struct vnode *a_vp;
		int  a_mode;
		struct ucred *a_cred;
		struct proc *a_p;
	} */ *ap;
{

	return (ENXIO);
}

/*
 * Vnode op for read
 */
/* ARGSUSED */
static int
dead_read(ap)
	struct vop_read_args /* {
		struct vnode *a_vp;
		struct uio *a_uio;
		int  a_ioflag;
		struct ucred *a_cred;
	} */ *ap;
{
	/*
	 * Return EOF for tty devices, EIO for others
	 */
	if ((ap->a_vp->v_vflag & VV_ISTTY) == 0)
		return (EIO);
	return (0);
}

/*
 * Vnode op for write
 */
/* ARGSUSED */
static int
dead_write(ap)
	struct vop_write_args /* {
		struct vnode *a_vp;
		struct uio *a_uio;
		int  a_ioflag;
		struct ucred *a_cred;
	} */ *ap;
{
	return (EIO);
}

/*
 * Device ioctl operation.
 */
/* ARGSUSED */
static int
dead_ioctl(ap)
	struct vop_ioctl_args /* {
		struct vnode *a_vp;
		u_long  a_command;
		caddr_t  a_data;
		int  a_fflag;
		struct ucred *a_cred;
		struct proc *a_p;
	} */ *ap;
{
	/* XXX: Doesn't this just recurse back here ? */
	return (VOP_IOCTL_AP(ap));
}

/*
 * Wait until the vnode has finished changing state.
 */
static int
dead_bmap(ap)
	struct vop_bmap_args /* {
		struct vnode *a_vp;
		daddr_t  a_bn;
		struct bufobj **a_bop;
		daddr_t *a_bnp;
		int *a_runp;
		int *a_runb;
	} */ *ap;
{

	return (VOP_BMAP(ap->a_vp, ap->a_bn, ap->a_bop, ap->a_bnp, ap->a_runp, ap->a_runb));
}

/*
 * Trivial poll routine that always returns POLLHUP.
 * This is necessary so that a process which is polling a file
 * gets notified when that file is revoke()d.
 */
static int
dead_poll(ap)
	struct vop_poll_args *ap;
{
	return (POLLHUP);
}

static int
dead_rename(ap)
	struct vop_rename_args  /* {
		struct vnode *a_fdvp;
		struct vnode *a_fvp;
		struct componentname *a_fcnp;
		struct vnode *a_tdvp;
		struct vnode *a_tvp;
		struct componentname *a_tcnp;
	} */ *ap;
{
	if (ap->a_tvp)
		vput(ap->a_tvp);
	if (ap->a_tdvp == ap->a_tvp)
		vrele(ap->a_tdvp);
	else
		vput(ap->a_tdvp);
	vrele(ap->a_fdvp);
	vrele(ap->a_fvp);
	return (EXDEV);
}

deadfsという名前の通り、中身はないようです。

このファイルから次のことが分かります。

  • includeされているヘッダファイル
  • 関数のプロトタイプ宣言
  • 関数テーブル(vop_vector)
  • 関数定義(実体)
  • 使われている定数、マクロ

ヘッダファイル

deadfsは最も小さいファイルシステムであると思いますので、ここで読み込んでいるヘッダファイルが、ファイルシステムを作る際に最低限必要になるヘッダファイルということになると思います。

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/poll.h>
#include <sys/vnode.h>

vop_vector構造体

興味を引くvop_vector構造体です。
おそらくファイルシステム共通の関数テーブルになっていると想像します。
ファイルシステムのモジュールを作成する場合は、この構造体変数を定義する必要があるのだと思います。

struct vop_vector dead_vnodeops = {
	.vop_default =		&default_vnodeops,

	.vop_access =		VOP_EBADF,
	.vop_advlock =		VOP_EBADF,
	.vop_bmap =		dead_bmap,
	.vop_create =		VOP_PANIC,
	.vop_getattr =		VOP_EBADF,
	.vop_getwritemount =	dead_getwritemount,
	.vop_inactive =		VOP_NULL,
	.vop_ioctl =		dead_ioctl,
	.vop_link =		VOP_PANIC,
	.vop_lookup =		dead_lookup,
	.vop_mkdir =		VOP_PANIC,
	.vop_mknod =		VOP_PANIC,
	.vop_open =		dead_open,
	.vop_pathconf =		VOP_EBADF,	/* per pathconf(2) */
	.vop_poll =		dead_poll,
	.vop_read =		dead_read,
	.vop_readdir =		VOP_EBADF,
	.vop_readlink =		VOP_EBADF,
	.vop_reclaim =		VOP_NULL,
	.vop_remove =		VOP_PANIC,
	.vop_rename =		dead_rename,
	.vop_rmdir =		VOP_PANIC,
	.vop_setattr =		VOP_EBADF,
	.vop_symlink =		VOP_PANIC,
	.vop_vptocnp =		VOP_EBADF,
	.vop_write =		dead_write,
};

vop_vector構造体の定義がありませんでした。
sys/vnode.hで定義されているのかと思いましたが見当たりませんでした。
どうやらsys/kern/vnode_if.srcを元に、sys/tools/vnode_if.awkで生成されているようです。


sys/tools/vnode_if.awkは、sys/conf/kmod.mkおよびsys/conf/kern.post.mkで呼ばれています。
sys/conf/kmod.mkは、/usr/share/mk/bsd.kmod.mkでincludeされています。


bsd.kmod.mkは、sys/modules以下のモジュールのMakefile内でインクルードされていますので、makeする際に生成されるのではないかと推察します。


deadfsはsys/modulesディレクトリの下に無いため、代わりにtmpfsで確かめてみたところ、

# make
Warning: Object directory not changed from original /usr/src/sys/modules/tmpfs
@ -> /usr/src/sys
machine -> /usr/src/sys/amd64/include
awk -f @/tools/vnode_if.awk @/kern/vnode_if.src -p
awk -f @/tools/vnode_if.awk @/kern/vnode_if.src -q
awk -f @/tools/vnode_if.awk @/kern/vnode_if.src -h
cc -O2 -pipe -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc   -I. -I@ -I@/contrib/altq -finline-limit=8000 --param inline-unit-growth=100 --param large-function-growth=1000 -fno-common  -fno-omit-frame-pointer -mcmodel=kernel -mno-red-zone  -mfpmath=387 -mno-sse -mno-sse2 -mno-sse3 -mno-mmx -mno-3dnow  -msoft-float -fno-asynchronous-unwind-tables -ffreestanding -fstack-protector -std=iso9899:1999 -fstack-protector -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes  -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual  -Wundef -Wno-pointer-sign -fformat-extensions -c /usr/src/sys/modules/tmpfs/../../fs/tmpfs/tmpfs_vnops.c
cc -O2 -pipe -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc   -I. -I@ -I@/contrib/altq -finline-limit=8000 --param inline-unit-growth=100 --param large-function-growth=1000 -fno-common  -fno-omit-frame-pointer -mcmodel=kernel -mno-red-zone  -mfpmath=387 -mno-sse -mno-sse2 -mno-sse3 -mno-mmx -mno-3dnow  -msoft-float -fno-asynchronous-unwind-tables -ffreestanding -fstack-protector -std=iso9899:1999 -fstack-protector -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes  -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual  -Wundef -Wno-pointer-sign -fformat-extensions -c /usr/src/sys/modules/tmpfs/../../fs/tmpfs/tmpfs_fifoops.c
cc -O2 -pipe -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc   -I. -I@ -I@/contrib/altq -finline-limit=8000 --param inline-unit-growth=100 --param large-function-growth=1000 -fno-common  -fno-omit-frame-pointer -mcmodel=kernel -mno-red-zone  -mfpmath=387 -mno-sse -mno-sse2 -mno-sse3 -mno-mmx -mno-3dnow  -msoft-float -fno-asynchronous-unwind-tables -ffreestanding -fstack-protector -std=iso9899:1999 -fstack-protector -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes  -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual  -Wundef -Wno-pointer-sign -fformat-extensions -c /usr/src/sys/modules/tmpfs/../../fs/tmpfs/tmpfs_vfsops.c
cc -O2 -pipe -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc   -I. -I@ -I@/contrib/altq -finline-limit=8000 --param inline-unit-growth=100 --param large-function-growth=1000 -fno-common  -fno-omit-frame-pointer -mcmodel=kernel -mno-red-zone  -mfpmath=387 -mno-sse -mno-sse2 -mno-sse3 -mno-mmx -mno-3dnow  -msoft-float -fno-asynchronous-unwind-tables -ffreestanding -fstack-protector -std=iso9899:1999 -fstack-protector -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes  -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual  -Wundef -Wno-pointer-sign -fformat-extensions -c /usr/src/sys/modules/tmpfs/../../fs/tmpfs/tmpfs_subr.c
ld  -d -warn-common -r -d -o tmpfs.ko tmpfs_vnops.o tmpfs_fifoops.o tmpfs_vfsops.o tmpfs_subr.o
:> export_syms
awk -f /usr/src/sys/modules/tmpfs/../../conf/kmod_syms.awk tmpfs.ko  export_syms | xargs -J% objcopy % tmpfs.ko
objcopy --strip-debug tmpfs.ko


予想通りtools/vnode_if.awkが実行され、下記のヘッダファイルが生成されました。

$ ls -l *.h
-rw-r--r--  1 root  wheel  32302 May  1 20:40 vnode_if.h
-rw-r--r--  1 root  wheel   1877 May  1 20:40 vnode_if_newproto.h
-rw-r--r--  1 root  wheel   4563 May  1 20:40 vnode_if_typedef.h

vop_vectorは、上記ヘッダファイル内のvnode_if_newproto.hで定義されています。
定義は下記の通りです。

struct vop_vector {
	struct vop_vector	*vop_default;
	vop_bypass_t	*vop_bypass;
	vop_islocked_t	*vop_islocked;
	vop_lookup_t	*vop_lookup;
	vop_cachedlookup_t	*vop_cachedlookup;
	vop_create_t	*vop_create;
	vop_whiteout_t	*vop_whiteout;
	vop_mknod_t	*vop_mknod;
	vop_open_t	*vop_open;
	vop_close_t	*vop_close;
	vop_access_t	*vop_access;
	vop_accessx_t	*vop_accessx;
	vop_getattr_t	*vop_getattr;
	vop_setattr_t	*vop_setattr;
	vop_markatime_t	*vop_markatime;
	vop_read_t	*vop_read;
	vop_write_t	*vop_write;
	vop_ioctl_t	*vop_ioctl;
	vop_poll_t	*vop_poll;
	vop_kqfilter_t	*vop_kqfilter;
	vop_revoke_t	*vop_revoke;
	vop_fsync_t	*vop_fsync;
	vop_remove_t	*vop_remove;
	vop_link_t	*vop_link;
	vop_rename_t	*vop_rename;
	vop_mkdir_t	*vop_mkdir;
	vop_rmdir_t	*vop_rmdir;
	vop_symlink_t	*vop_symlink;
	vop_readdir_t	*vop_readdir;
	vop_readlink_t	*vop_readlink;
	vop_inactive_t	*vop_inactive;
	vop_reclaim_t	*vop_reclaim;
	vop_lock1_t	*vop_lock1;
	vop_unlock_t	*vop_unlock;
	vop_bmap_t	*vop_bmap;
	vop_strategy_t	*vop_strategy;
	vop_getwritemount_t	*vop_getwritemount;
	vop_print_t	*vop_print;
	vop_pathconf_t	*vop_pathconf;
	vop_advlock_t	*vop_advlock;
	vop_advlockasync_t	*vop_advlockasync;
	vop_reallocblks_t	*vop_reallocblks;
	vop_getpages_t	*vop_getpages;
	vop_putpages_t	*vop_putpages;
	vop_getacl_t	*vop_getacl;
	vop_setacl_t	*vop_setacl;
	vop_aclcheck_t	*vop_aclcheck;
	vop_closeextattr_t	*vop_closeextattr;
	vop_getextattr_t	*vop_getextattr;
	vop_listextattr_t	*vop_listextattr;
	vop_openextattr_t	*vop_openextattr;
	vop_deleteextattr_t	*vop_deleteextattr;
	vop_setextattr_t	*vop_setextattr;
	vop_setlabel_t	*vop_setlabel;
	vop_vptofh_t	*vop_vptofh;
	vop_vptocnp_t	*vop_vptocnp;
};

ファイルシステムモジュールを作成する場合は、上記の構造体で定義されている関数を書いていけば良いのだと思います。
vop_vector構造体のメンバ変数の名前ですが、プレフィックスを取り除くと半数ぐらいはシステムコールに存在する関数の名前になっています。
おそらく該当するシステムコールが呼ばれた際に、この構造体にセットした関数が呼び出されるのではないかと思います。


次回に続く