擬似ファイルシステムを作ってみる(3)
説明
ファイル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がファイルなら、file用を、ディレクトリならディレクトリ用として設定され、従って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)ざっとこんな感じです。なお憶測で書いているところもあります。