擬似ファイルシステムを作ってみる(3)
Rev.1を表示中。最新版はこちら。
ファイルopenでファイル名からファイルディスクリプタを取得します。ファイルディスクリプタが分かればfile構造体が分かります。file構造体がわかればinode構造体がわかり、そこのread,write等のコールバック関数をコールすることで、ファイルシステムに依存した読み書き込みが可能となるわけです。モジュールがインストールされるとregister_filesystemが呼ばれます。これはfile_system_type構造体を引数とし、それにはファイルシステム名(ext2やext3)や、システムに依存するスーパブロック情報を設定するコールバック関数が定義されており、ファイルシステムリストに登録されます。
fs/filesystems.c static struct file_system_type *file_systems; int register_filesystem(struct file_system_type * fs) { int res = 0; struct file_system_type ** p; BUG_ON(strchr(fs->name, '.')); if (fs->next) return -EBUSY; INIT_LIST_HEAD(&fs->fs_supers); write_lock(&file_systems_lock); p = find_filesystem(fs->name, strlen(fs->name)); if (*p) res = -EBUSY; else *p = fs; write_unlock(&file_systems_lock); return res; }ファイルシステムリストのヘッドとしてfile_systems変数が静的に定義されています。register_filesystemはfind_filesystemでこのfile_systemsからファイルシステムにすでに登録されてないか、ファイルシステム名をキーとして検索します。もし登録されていればそのポインターが、そうでなければ最後のローケーションが返され、そこに登録するファイルシステムのポインターを設定することで、システムにファイルシステムリストとして登録しています。
mountが呼び出されると、そのファイルシステムタイプをキーにしてfile_system_typeを取得し、そこで定義されている.get_sbで設定しているmy_vfs_get_sbが呼び出され、get_sb_nodevにて本システムファイル属性(要はスーパブロック)がsuper_block構造体に設定していきます。内部処理的にsuper_block構造体はファイルシステムそのものとなるわけです。
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = 0x484f4745; /* 'HOGE' */ sb->s_op = &my_vfs_ops;なおsuper_block構造体にもsuper_operationsコールバックを設定します。これはファイルシステムの状態とかスーパブロックを操作する関数と思われます。
ディレクトリ操作に関する処理で、その読み出し書き込み等の処理をfile_operationsに、ディレクトリそのもの(たぶん削除、作成等)をinode_operationsに記述します。
static const struct file_operations my_vfs_dir_ops = { .readdir = my_vfs_dir_readdir, }; static const struct inode_operations my_vfs_dir_inode_ops = { .lookup = my_vfs_dir_lookup, };ファイル操作に関する処理で、その読み出し書き込み等の処理をfile_operationsに、ファイルそのもの(たぶん削除、作成等)をinode_operationsに記述します。ファイルに削除、作成等はサポートしていませんので、inode_operations は設定していません。
static const struct file_operations my_vfs_file_ops = { .read = my_vfs_file_read, .write = my_vfs_file_write, .fsync = my_vfs_file_fsync };
static const struct inode_operations my_vfs_file_inode_ops = { };注意することはこれらのオペレーションはinodeに設定される。ということです。ですからファイル名からinodeを取得する処理が必要です。それがmy_vfs_dir_lookupです。my_vfs_dir_lookupはdentryにセットされたファイル名をキーにしてそのinodeを取得します。my_vfs_internal_lookupになります。inode番後はa,b,c,d,eに100,101,102,103,104を割り当てています。この番号はrootが1として、あとはそれ以外の番号であればなんでもかまいません。理由はすべて同じファイルシステム上でコールバック処理が同じファイル(ディレクトリでない。)だからです。なお、.lookupコールバックはスーパブロック取得時にrootのinodeとして設定されます。
システムコールreadから.read = my_vfs_file_readが、writeから.write = my_vfs_file_write等々がコールされ、該当の処理がされます。readについて言えば、引数のfile構造体からdentryがそしてinode番号が取得し、該当するデータブロックを読み込めばいいわけです。なおサンプルでのファイル名で該当するファイルを検索し、そのデータをファイルとして読み込んでいます。
static ssize_t my_vfs_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)ざっとこんな感じです。なお憶測で書いているところもあります。