0から作るソフトウェア開発

日々勉強中。。。

Tips
リンク

ファイルシステム >

0から作るLinuxプログラム Ext2ファイルシステム ステップ4 マウントとスーパーブロックオブジェクト

Ext2ファイルシステム

Linuxのファイルシステムのうち基本的なファイルシステムであるExt2について
みていきます。

それでは、順番に見ていきます。

目次 Ext2ファイルシステム

  1. ファイルシステムの概要

  2. ファイルシステムへのアクセス

  3. カーネルサイドから見たシステムコール

  4. ファイルシステム関連のシステムコール

  5. 仮想ファイルシステム(VFS)

  6. ステップ0 Ext2ファイルシステムと簡単なモジュールの実装

  7. ステップ1 Ext2ファイルシステムタイプとマウントメソッド

  8. ステップ2 Ext2スーパーブロックの読み込み

  9. ステップ3 スーパーブロック管理情報

  10. (現在のページ) ステップ4 マウントとスーパーブロックオブジェクト

  11. ステップ5 スーパーブロックオブジェクトと管理情報のセットアップ

  12. ステップ6 ブロックグループディスクリプターの読み込み

  13. ステップ7 ルートディレクトリのinode読み込み

  14. ステップ8 簡単なディレクトリの読み込み

  15. ステップ9 ディレクトリ読み込みとページキャッシュとaddress_space

  16. ステップ10 inodeオブジェクトのlookupメソッド

  17. ステップ11 inodeオブジェクトのmkdirメソッドとper-cpuカウンター

  18. ステップ12 簡単なブロックの新規割り当て処理

  19. ステップ13 スーパーブロックオブジェクトのwrite_inodeメソッド

  20. ステップ14 Orlov方式による新規ディレクトリのinodeを割り当てるブロックグループ選択

  21. ステップ15 inodeオブジェクトのrmdirメソッドとunlinkメソッド

  22. ステップ16 inodeオブジェクトのrenameメソッド

  23. ステップ17 inodeオブジェクトのcreateメソッドとファイルオブジェクトの汎用メソッド

  24. ステップ18 inodeオブジェクトのlinkメソッド

  25. ステップ19 シンボリックとinodeオブジェクトのsymlinkメソッド

  26. ステップ20 inodeオブジェクトのmknodメソッドとtmpfileメソッド

  27. ステップ21 スーパーブロックオブジェクトのevict_inodeメソッド

  28. ステップ22 スーパーブロックオブジェクトのsync_fsメソッド

  29. ステップ23 スーパーブロックオブジェクトのstatfsメソッドとメモリーバリアー/フェンス

  30. ステップ24 スーパーブロックオブジェクトのremount_fsメソッドとマウントオプション解析

  31. ステップ25 スーパーブロックオブジェクトのshow_optionsメソッドとprocfs

  32. ステップ26 カーネルオブジェクトとsysfs

  33. ステップ27 スーパーブロックオブジェクトのfreeze_fs/unfreeze_fsメソッド

  34. ステップ28 ファイルオブジェクトのioctlメソッド

  35. ステップ29 ファイルオブジェクトのsetattrメソッド

  36. ステップ30 address_spaceのdirect_IOメソッド

  37. ステップ31 リザベーションウィンドウ

  38. ステップ32 拡張アトリビュートその1 ハンドラーの呼び出し

  39. ステップ33 拡張アトリビュートその2 コア処理

  40. ステップ34 POSIX ACLその1 ハンドラーの呼び出し

  41. ステップ35 POSIX ACLその2 コア処理

  42. ステップ36 ディスククォータ

仮想ファイルシステムのスーパーブロックオブジェクト

ステップ2で見てきましたようにfill_superコールバック関数の処理では

読みこんだスーパーブロックの値を仮想ファイルシステムのスーパーブロック

オブジェクトに格納する必要がありました。

今回の目標はマウントがとりあえず成功するまでを見ていきたいと思います。マウントが

成功するには仮想ファイルシステムのスーパーブロックオブジェクトを最低限セットアップ

する必要があります。

また、今回はアンマウント時に行うスーパーブロックオブジェクトの破棄を行う処理も

作っていきましょう。


まずは、仮想ファイルシステムのスーパーブロックオブジェクトの構造を見ていきます。

仮想ファイルシステムのスーパーブロックオブジェクトの構造

仮想ファイルシステムで次のように定義されています。結構長いです。。。


#include <linux/fs.h>

struct super_block {
    struct list_head        s_list;         /* Keep this first */
    dev_t                   s_dev;          /* search index; _not_ kdev_t */
    unsigned char           s_blocksize_bits;
    unsigned long           s_blocksize;
    loff_t                  s_maxbytes;     /* Max file size */
    struct file_system_type *s_type;
    const struct super_operations   *s_op;
    const struct dquot_operations   *dq_op;
    const struct quotactl_ops       *s_qcop;
    const struct export_operations  *s_export_op;
    unsigned long           s_flags;
    unsigned long           s_magic;
    struct dentry           *s_root;
    struct rw_semaphore     s_umount;
    int                     s_count;
    atomic_t                s_active;
#ifdef CONFIG_SECURITY
    void                    *s_security;
#endif
    const struct xattr_handler **s_xattr;

    struct list_head        s_inodes;       /* all inodes */
    struct hlist_bl_head    s_anon;         /* anonymous dentries for (nfs) 
                                               exporting */
    struct list_head        s_mounts;       /* list of mounts; _not_ for fs use */
    struct block_device     *s_bdev;
    struct backing_dev_info *s_bdi;
    struct mtd_info         *s_mtd;
    struct hlist_node       s_instances;
    struct quota_info       s_dquot;        /* Diskquota specific options */

    struct sb_writers       s_writers;

    char s_id[32];                          /* Informational name */
    u8 s_uuid[16];                          /* UUID */

    void                    *s_fs_info;     /* Filesystem private info */
    unsigned int            s_max_links;
    fmode_t                 s_mode;

    /* Granularity of c/m/atime in ns.
       Cannot be worse than a second */
    u32                     s_time_gran;

    /*
     * The next field is for VFS *only*. No filesystems have any business
     * even looking at it. You had been warned.
     */
    struct mutex           s_vfs_rename_mutex;        /* Kludge */

    /*
     * Filesystem subtype.  If non-empty the filesystem type field
     * in /proc/mounts will be "type.subtype"
     */
    char                   *s_subtype;

    /*
     * Saved mount options for lazy filesystems using
     * generic_show_options()
     */
    char __rcu             *s_options;
    const struct dentry_operations *s_d_op; /* default d_op for dentries */

    /*
     * Saved pool identifier for cleancache (-1 means none)
     */
    int                   cleancache_poolid;

    struct shrinker       s_shrink;       /* per-sb shrinker handle */

    /* Number of inodes with nlink == 0 but still referenced */
    atomic_long_t         s_remove_count;

    /* Being remounted read-only */
    int                   s_readonly_remount;

    /* AIO completions deferred from interrupt context */
    struct workqueue_struct *s_dio_done_wq;
    struct hlist_head      s_pins;

    /*
     * Keep the lru lists last in the structure so they always sit on their
     * own individual cachelines.
     */
    struct list_lru       s_dentry_lru ____cacheline_aligned_in_smp;
    struct list_lru       s_inode_lru ____cacheline_aligned_in_smp;
    struct rcu_head       rcu;

    /*
     * Indicates how deep in a filesystem stack this SB is
     */
    int                   s_stack_depth;
};



super_block構造体
名前 説明
struct list_heads_list 仮想ファイルシステムのスーパーブロックオブジェクトを連結するリストのノードです。
dev_ts_dev スーパーブロックオブジェクトに関連付けされたデバイスのデバイス番号です。
unsigned chars_blocksize_bits ファイルシステムのブロックサイズをビット(2の累乗の形式)で格納します。
unsigned longs_blocksize ファイルシステムのブロックサイズです。
loff_ts_maxbytes ファイルシステムの最大ファイルサイズです。
struct file_system_type *s_type このスーパーブロックオブジェクトに関連付けられたファイルシステムタイプです。
const struct super_operations *s_op スーパーブロックオブジェクトのメソッドです。
const struct dquot_operations *dq_op ディスククォータのメソッドです。
const struct quotactl_ops *s_qcop ディスククォータ制御メソッドです。
const struct export_operations *s_export_op NFSなどで使用するエクスポートメソッドです。
unsigned longs_flags マウントフラグです。
unsigned longs_magic ファイルシステムのマジックナンバーを格納します。
struct dentry *s_root ルートディレクトリのdエントリーオブジェクトです。
struct rw_semaphores_umount アンマウント時に使用するセマフォーです。
ints_count スーパーブロックオブジェクトの参照カウンターです。
atomic_ts_active アクティブ参照カウンターです。
void *s_security セキュリティサブシステムで使用するデータとなります。
const struct xattr_handler **s_xattr 拡張アトリビュート関連のメソッドです。
struct list_heads_inodes このファイルシステムの全てのinodeオブジェクトを連結したリストです。
struct hlist_bl_heads_anon NFSのエクスポートで使用する無名dエントリーの連結リストです。
struct list_heads_mounts マウントオブジェクトに連結されるリストノードです。
struct backing_dev_info *s_bdi デバイスにデータを書き戻すときの管理情報です。
struct mtd_info *s_mtd フラッシュデバイスなどのメモリーデバイスを管理するMTD (Memory Technology Device)オブジェクトです。
struct hlist_nodes_instances ファイルシステムタイプのハッシュリストに連結されるノードです。
struct quota_infos_dquot ディスククォータの管理情報です。
struct sb_writerss_writers スーパーブロックの書き込みを排他制御するための待ち行列などに使用します。
chars_id[32] スーパーブロックが格納されているデバイス名が格納されます。
u8s_uuid[16] ファイルシステムのUUID (Universally Unique Identifier)を格納します。
void *s_fs_info ファイルシステムのプライベートデータ(管理情報など)を格納します。
unsigned ints_max_links ファイルシステムの最大リンク数を格納します。。
fmode_ts_mode スーパーブロックオブジェクトが格納されているデバイスに対するファイルモードが格納されます。
u32s_time_gran タイムスタンプの時刻粒度がナノ秒単位で格納されます。
struct mutexs_vfs_rename_mutex リネーム時に使用するミューテックスです。仮想ファイルシステムレイヤーでのみ使用します。
char *s_subtype ファイルシステムのサブタイプが格納されます。
char __rcu *s_options マウントオプションが格納されます。
intcleancache_poolid クリーンキャッシュ用のプールIDが格納されます。-1の場合無しです。
struct shrinkers_shrink メモリーが枯渇したときに、ファイルシステムリソース(inodeオブジェクトやdエントリーオブジェクトなど)を縮小するシュリンカーとなります。
atomic_long_ts_remove_count リンク数が0ですがまだ参照されているinodeオブジェクトの数が格納されます。
ints_readonly_remount リードオンリーで再マウント中のフラグです。
struct workqueue_struct *s_dio_done_wq 割り込みコンテキストから遅延させて非同期I/Oを完了させるためのワークキューです。
struct hlist_heads_pins スーパーブロックオブジェクトがアンマウントされたときにピンに挿入されている処理を呼び出します。(mountオブジェクトのmnt_pinsと同じように使用します)。
struct list_lrus_dentry_lru このスーパーブロックオブジェクトに関連付けされているdエントリーオブジェクトのLRU (Least Recently Used) リスト(直近で最も使用されていないdエントリーオブジェクトの連結リストです)。
struct list_lrus_inode_lru このスーパーブロックオブジェクトに関連付けされているinodeオブジェクトのLRU (Least Recently Used) リスト(直近で最も使用されていないinodeオブジェクトの連結リストです)。
ints_stack_depth ファイルシステムスタックの深さを格納します。オーバーレイファイルシステムやeCryptファイルシステムで使用します。


スーパーブロックオブジェクトのすべてのメンバーについてファイルシステムで値を

入れる必要はありません。ほとんどは仮想ファイルシステムで使用するメンバー

となります。


それでは、読みこんだスーパーブロックの値を仮想ファイルシステムのスーパーブロック

オブジェクトに入れていきましょう。

スーパーブロックオブジェクトのセットアップ

今回のステップでマウントがただ成功するだけを目標に、最低限のスーパーブロック

オブジェクトをセットアップしていきます。最低限というのは、ファイルシステムのルート

dエントリーをスーパーブロックオブジェクトにセットすれば、とりあえずマウントできるように

なります。


今回はルートのdエントリーのセットと次のメンバーもセットアップしていきましょう。

また、スーパーブロックオブジェクトのメソッドはデバッグ出力を行うだけとなります。

  1. const struct super_operations *s_op
  2. スーパーブロックオブジェクトのメソッド


  3. void *s_fs_info
  4. ファイルシステム固有情報(前回のステップで出てきました)


  5. struct dentry *s_root(とルートのinodeオブジェクト)
  6. ルートのdエントリーオブジェクト




まずはスーパーブロックオブジェクトのメソッドs_opから見ていきましょう。

スーパーブロックオブジェクトのメソッドs_opのセットアップ

今回のステップでは特に何もしないメソッドを作成していきます。スーパーブロックオブジェクト

のメソッドが呼び出されたことが確認できるようにデバッグ情報を出力するだけの処理群と

しています。各メソッドは後のステップで中身を実装していきます。


スーパーブロックオブジェクトには次のメソッドがあります。

(今回は概略のみとなります)


struct super_operations {
    struct inode *(*alloc_inode)(struct super_block *sb);
    void (*destroy_inode)(struct inode *inode);

    void (*dirty_inode) (struct inode *inode, int flags);
    int (*write_inode) (struct inode *inode, struct writeback_control *wbc);
    int (*drop_inode) (struct inode *inode);
    void (*evict_inode) (struct inode *inode);
    void (*put_super) (struct super_block *sb);
    int (*sync_fs)(struct super_block *sb, int wait);
    int (*freeze_fs) (struct super_block *sb);
    int (*unfreeze_fs) (struct super_block *sb);
    int (*statfs) (struct dentry *dentry, struct kstatfs *buf);
    int (*remount_fs) (struct super_block *sb, int *flags, char *data);
    void (*umount_begin) (struct super_block *sb);

    int (*show_options)(struct seq_file *seq, struct dentry *root);
    int (*show_devname)(struct seq_file *seq, struct dentry *root);
    int (*show_path)(struct seq_file *seq, struct dentry *path);
    int (*show_stats)(struct seq_file *seq, struct dentry *root);
#ifdef CONFIG_QUOTA
    ssize_t (*quota_read)(struct super_block *sb, int type,
                          char *data, size_t len, loff_t off);
    ssize_t (*quota_write)(struct super_block *sb, int type,
                           const char *data, size_t len, loff_t off);
#endif
    int (*bdev_try_to_free_page)(struct super_block *sb,
                                 struct page *page, gfp_t wait);
    long (*nr_cached_objects)(struct super_block *sb, int nid);
    long (*free_cached_objects)(struct super_block *sb, long nr_to_scan, int nid);
};



スーパーブロックオブジェクトのメソッドsuper_operations
メソッド名 説明 呼び出しトリガーの例
alloc_inode 新しいinodeオブジェクトを割り当てます。 creatopenmknodmkdirsymlinkなどのファイル新規作成時。パス名解決のファイル名ルックアップ(検索)。
destroy_inode 仮想ファイルシステムのinodeオブジェクトを削除します。 unlinkrmdirでファイル削除したとき。
dirty_inode inodeオブジェクトが更新されたときの処理を行います。(Ext2ファイルシステムでは使用していません)。 truncatewriteutimeutimensatchownchmodなどでinode情報を更新した後。
write_inode inodeをディスクに書き戻します。 syncfsfsyncsync_file_rangeやデーモンによる定期書き戻し時。
drop_inode 仮想ファイルシステムのinodeオブジェクトがハッシュリストから外されたときの処理を行います。(Ext2ファイルシステムでは使用していません)。 unlinkrmdirでファイル削除したとき。
evict_inode ファイルシステムのinodeを削除します。 unlinkrmdirでファイル削除したとき。
put_super スーパーブロックオブジェクトを削除します。 umount時。
sync_fs ディスクキャッシュとファイルシステムの同期を行います。 syncsyncfs時。
freeze_fs ファイルシステムをフリーズ(凍結)します。 ioctlのFIFREEZEリクエストやデバイスをスリープにしたときなど。
unfreeze_fs ファイルシステムをアンフリーズ(解凍)します。 ioctlのFITHAWリクエストやデバイスをスリープからウェイクアップさせたときなど。
statfs ファイルシステム統計情報を取得します。 statfs呼び出し時。
remount_fs ファイルシステムを再マウントします。 mountでremountオプションを指定したとき。
umount_begin アンマウント開始前の処理を行います。(Ext2ファイルシステムでは使用していません)。 umount時。
show_options ファイルシステムのマウントオプションを出力します。 mount時やprocfs読み出し時。
show_devname マウントしているデバイス名を出力します。(Ext2ファイルシステムでは使用していません)。 procfs読み出し時。
show_path パス情報を出力します。(Ext2ファイルシステムでは使用していません)。 procfs読み出し時。
show_stats マウントポイントについての統計情報を出力します。(Ext2ファイルシステムでは使用していません)。 procfs読み出し時。
quota_read クォータファイルからデータを読み込みます。 quotactl呼び出し時。
quota_write クォータファイルにデータを書き込みます。 quotactl呼び出し時。
bdev_try_to_free_page ページキャッシュを解放するときの処理を行います。(Ext2ファイルシステムでは使用していません)。 デーモンからの呼び出し。
nr_cached_objects シュリンカーから現在のオブジェクトを走査するときに呼び出されます。(Ext2ファイルシステムでは使用していません)。 シュリンカーからの呼び出し。
free_cached_objects シュリンカーからオブジェクトを解放するときに呼び出されます。(Ext2ファイルシステムでは使用していません)。 シュリンカーからの呼び出し。


それぞれのメソッドが呼び出されたときにデバッグ情報が出力されるように実装して

みます。次の例のようにスーパーブロックオブジェクトのメソッドをme2fs_super.cに

定義していきます。(今回Ext2ファイルシステムで実装の無いものは例では定義して

いません)。

[me2fs_super.c]

/*
==================================================================================

    Management

==================================================================================
*/
/*
---------------------------------------------------------------------------------
    Super Block Operations
---------------------------------------------------------------------------------
*/
static int me2fs_write_inode( struct inode* inode, struct writeback_control *wbc )
{ DBGPRINT( "<ME2FS>write_inode\n" ); return 0; }
static void me2fs_destroy_inode( struct inode *inode )
{ DBGPRINT( "<ME2FS>destroy_inode\n" ); }
//static int me2fs_drop_inode( struct inode *inode )
//{ DBGPRINT( "<ME2FS>drop_inode\n" ); return 0; }
static void me2fs_evict_inode( struct inode *inode )
{ DBGPRINT( "<ME2FS>evict_inode\n" ); }
static int me2fs_sync_fs( struct super_block *sb, int wait )
{ DBGPRINT( "<ME2FS>sync_fs\n" ); return 0; }
static int me2fs_freeze_fs( struct super_block *sb )
{ DBGPRINT( "<ME2FS>freeze_fs\n" ); return 0; }
static int me2fs_unfreeze_fs( struct super_block *sb )
{ DBGPRINT( "<ME2FS>unfreeze_fs\n" ); return 0; }
static int me2fs_statfs( struct dentry *dentry, struct kstatfs *buf )
{ DBGPRINT( "<ME2FS>unfreeze_fs\n" ); return 0; }
static int me2fs_remount_fs( struct super_block *sb, int *len, char *buf )
{ DBGPRINT( "<ME2FS>remount_fs\n" ); return 0; }
static int me2fs_show_options( struct seq_file *seq_file, struct dentry *dentry )
{ DBGPRINT( "<ME2FS>show_options\n" ); return 0; }


static struct super_operations me2fs_super_ops = {
//  .dirty_inode  = me2fs_dirty_inode,
    .write_inode  = me2fs_write_inode,
//  .drop_inode   = me2fs_drop_inode,
    .evict_inode  = me2fs_evict_inode,
    .sync_fs      = me2fs_sync_fs,
    .freeze_fs    = me2fs_freeze_fs,
    .unfreeze_fs  = me2fs_unfreeze_fs,
    .statfs       = me2fs_statfs,
    .remount_fs   = me2fs_remount_fs,
//  .umount_begin = me2fs_umount_begin,
    .show_options = me2fs_show_options,
};



そして、me2fsFillSuperBlock関数でメソッドをスーパーブロックにセットします。

[me2fs_super.c/me2fsFillSuperBlock]

    /* ------------------------------------------------------------------------ */
    /* set up vfs super block                                                   */
    /* ------------------------------------------------------------------------ */
    sb->s_op        = &me2fs_super_ops;



注意
今回は次に説明する「ルートディレクトリのdエントリーのセットアップ」で

仮想ファイルシステムのinodeオブジェクトキャッシュからinodeオブジェクト

を割り当てるため、alloc_inodeメソッドを定義していません。

また、put_superは後でこのステップで実装していきますので、

ここには出てきていません。



これで、スーパーブロックオブジェクトが呼び出されたときにデバッグ情報が出力

されるようになりました。次に、マウントに必要となるルートディレクトリのdエントリーを

セットアップしていきましょう。

ルートディレクトリのdエントリーのセットアップ

fill_superコールバック関数に登録するme2fsFillSuperBlock関数の

の戻り値としてdエントリーオブジェクトのポインターを返す必要があります。

これは、仮想ファイルシステムで見てきましたvfsmountオブジェクト

mnt_rootに格納されて、マウントしたファイルシステムが仮想ファイル

システムで管理されてるようになります。このため、me2fsFillSuperBlock関数

で返すdエントリーオブジェクトには対応するinodeが必要となります。

対応するinodeが無いネガティブdエントリーは、ルートディレクトリが

存在していない状態となってしまいます。


ルートディレクトリのdエントリーをセットアップするには、まずルートディレクトリの

inodeオブジェクトが必要となります。


まずルートdエントリーオブジェクトに対応するinodeオブジェクトの割り当て

について見ていきましょう。

inodeオブジェクトの割り当て

inodeオブジェクトの割り当て処理は仮想ファイルシステムで共通処理が

あります。 ファイルシステムでは基本的に次の関数を使用します。


static struct inode *alloc_inode(struct super_block *sb);
struct inode *new_inode_pseudo(struct super_block *sb);
struct inode *new_inode(struct super_block *sb);
struct inode *iget_locked(struct super_block *sb, unsigned long ino);
struct inode *iget5_locked(struct super_block *sb, unsigned long hashval,
                int (*test)(struct inode *, void *),
                int (*set)(struct inode *, void *), void *data);



仮想ファイルシステムのinodeオブジェクト割り当て処理
関数名 説明
alloc_inode 新しいinodeオブジェクトを割り当てます。引数で指定したスーパーブロックオブジェクトにalloc_inodeメソッドが定義されていればファイルシステム固有のalloc_inodeを呼び出します。定義されていなければ、仮想ファイルシステム層のinodeオブジェクトキャッシュ(スラブアロケーターのキャッシュです)から割り当てます。
new_inode_pseudo 疑似ファイルシステム用の新しいinodeオブジェクトを割り当てます。alloc_inodeの呼び出しを行います。この関数では新規割り当てしたinodeオブジェクトはスーパーブロックオブジェクトのs_inodesリストに連結しません。マウント不可のファイルシステムなどで使用します。
new_inode 疑似ファイルシステム用の新しいinodeオブジェクトを割り当てます。new_inode_pseudoの呼び出しを行い、新規割り当てしたinodeオブジェクトはスーパーブロックオブジェクトのs_inodesリストに連結します。マウントできる疑似ファイルシステムで使用します。
iget_locked inodeオブジェクトを割り当てる前に引数で指定したinode番号に対応するinodeオブジェクトがハッシュリストにあるか探索します。あれば、ハッシュリストのinodeオブジェクトを返します。無ければ、alloc_inodeでinodeオブジェクトを割り当て、その状態(i_state)を新規作成(i_NEW)にしてロックします。新規割り当て時に、ハッシュリストにinodeオブジェクトを登録します。
iget5_locked iget_lockedと同じですが、ハッシュリスト探索時に指定したtestコールバック関数でinodeオブジェクトが一致しているか判定します。また、inodeオブジェクトが新規割り当てされた場合、指定したsetコールバック関数で初期化を行います。


となります。最終段でalloc_inode関数を呼びスーパーブロックオブジェクトの

alloc_inodeメソッドが呼び出されることになります。ここではiget_locked

関数を使用します。仮想ファイルシステムのinode割り当て処理を呼び出すのは

新規に割り当てられたinodeオブジェクトをハッシュリストに登録する必要があるため

となります。


alloc_inode関数

alloc_inode関数の引数と戻り値は次のようになっています。

alloc_inode関数の引数
引数名 説明
struct super_block *sb 仮想ファイルシステムのスーパーブロックオブジェクトを指定します。ファイルシステム固有のinode割り当て処理を行うにはalloc_inodeメソッドに関数をセットアップしておく必要があります。


alloc_inode関数の戻り値
説明
inode * 取得できた仮想ファイルシステムのinodeオブジェクトを返します。取得できなかった場合はNULLを返します。


new_inode_pseudo関数

new_inode_pseudo関数の引数と戻り値は次のようになっています。

new_inode_pseudo関数の引数
引数名 説明
struct super_block *sb 仮想ファイルシステムのスーパーブロックオブジェクトを指定します。


new_inode_pseudo関数の戻り値
説明
inode * 取得できた仮想ファイルシステムのinodeオブジェクトを返します。取得できなかった場合はNULLを返します。


new_inode関数

new_inode関数の引数と戻り値は次のようになっています。

new_inode関数の引数
引数名 説明
struct super_block *sb 仮想ファイルシステムのスーパーブロックオブジェクトを指定します。


new_inode_pseudo関数の戻り値
説明
inode * 取得できた仮想ファイルシステムのinodeオブジェクトを返します。取得できなかった場合はNULLを返します。


iget_locked関数

iget_locked関数の引数と戻り値は次のようになっています。

iget_locked関数の引数
引数名 説明
struct super_block *sb 仮想ファイルシステムのスーパーブロックオブジェクトを指定します。alloc_inode関数と同じです。
unsigned longino 割り当てるinodeオブジェクトのinode番号を指定します。inode番号とスーパーブロックオブジェクトからハッシュリストを探索します。


iget_locked関数の戻り値
説明
inode * ハッシュリストから取得できたinodeオブジェクトまたは新規に割り当てられたinodeオブジェクトを返します。取得できなかった場合、NULLを返します。


iget5_locked関数

iget5_locked関数の引数と戻り値は次のようになっています。

iget5_locked関数の引数
引数名 説明
struct super_block *sb 仮想ファイルシステムのスーパーブロックオブジェクトを指定します。alloc_inode関数と同じです。
unsigned longhashval ハッシュリストを探索するためのハッシュ値を指定します。
unsigned longino 割り当てるinodeオブジェクトのinode番号を指定します。inode番号とハッシュ値からハッシュリストを探索します。
int (*)(struct inode *, void * )test ハッシュリスト探索のinodeオブジェクト比較に使用するコールバック関数を指定します。
int (*)(struct inode *, void * )set 新規inodeオブジェクト割り当て時にinodeオブジェクトを初期化する関数をコールバック関数として指定します。
void *data testとsetコールバック関数の引数として使用します。


iget5_locked関数の戻り値
説明
inode * ハッシュリストから取得できたinodeオブジェクトまたは新規に割り当てられたinodeオブジェクトを返します。取得できなかった場合、NULLを返します。


iget_lockedとiget5_lockedは新規作成したinodeオブジェクトの

i_stateメンバーにI_NEWフラグをセットすることでinodeオブジェクトの

ロックを行います。ロック中にdiskに格納されているファイルシステムのinode

を読み込み、inodeオブジェクトをセットアップしていきます。inodeオブジェクト

セットアップが終わった後はunlock_new_inode関数でロック解除を行い

ます。


unlock_new_inode関数

unlock_new_inode関数は次のようになっています。


void unlock_new_inode(struct inode *inode);



unlock_new_inode関数の引数
引数名 説明
struct inode *inode iget_locked関数またはiget5_locked関数でロックしたinodeオブジェクトのロックを解除します。


unlock_new_inode関数に戻り値はありません。


また、ステップ7 ルートディレクトリのinode読み込み に出てきますiget_failed

という関数があります。

iget_failed関数

iget_failed関数はinodeオブジェクトの取得には成功しているが、ディスクから

inodeが読み込みできなかったなどの後などのエラー処理時に使用します。取得

したinodeオブジェクトを不良inode(バッドinode)として初期化しなおします。


iget_failed関数は次のようになっています。


void iget_failed(struct inode *inode);



iget_failed関数の引数
引数名 説明
struct inode *inode 解放対象のinodeオブジェクトを指定します。


iget_failed関数に戻り値はありません。

ルートディレクトリのinodeオブジェクト割り当て

今回はiget_locked関数でルートディレクトリのinodeオブジェクトを

疑似的に割り当ててマウントを行っていきたいと思います。


ここでiget_locked関数を見てみるとinode番号が必要となってきます。

Ext2ファイルシステムではルートディレクトリのinode番号は決まっていて2と

なります。また、その他にもブートローダーinodeなど特別なinode番号が

決められていますので、me2fs.hに定義していきましょう。

次の例のように定義しておきます。

[me2fs.h]

/*
----------------------------------------------------------------------------------
    Special Inode Number
----------------------------------------------------------------------------------
*/
#define ME2FS_EXT2_BAD_INO      1           /* bad blocks inode                 */
#define ME2FS_EXT2_ROOT_INO     2
#define ME2FS_EXT2_BL_INO       5           /* boot loader inode                */
#define ME2FS_EXT2_UNDEL_DIR_NO 6           /* undelete directory inode         */



特別なinode番号
inode番号 説明
1 不良セクターなどで使用できないブロックのinode番号となります。
2 ルートディレクトリのinode番号となります。
5 ブートローダーのinode番号となります。
6 まだ削除できていないディレクトリのinode番号となります。


ルートディレクトリのinode番号は2でME2FS_EXT2_ROOT_INOを

iget_locked関数の引数として指定します。次のような実装例となります。

[me2fs_super.c/me2fsFillSuperBlock]

    struct inode            *root;

    root = iget_locked( sb, ME2FS_EXT2_ROOT_INO );
    
    /* iget_lockedした後にinodeオブジェクトのセットアップ処理を行います。今回はしていません。 */

    if( IS_ERR( root ) )
    {
        DBGPRINT( "<ME2FS>error : failed to get root inode\n" );
        ret = PTR_ERR( root );
        goto error_mount;
    }
    
    unlock_new_inode( root );
    inc_nlink( root );

    root->i_mode = S_IFDIR;

    if( !S_ISDIR( root->i_mode ) )
    {
        DBGPRINT( "<ME2FS>root is not directory!!!!\n" );
    }



この実装例ではIS_ERRマクロを使用しています。この段階では必要ないマクロと

なりますが、この後ファイルシステム固有のinodeオブジェクト取得関数を作って

いきます。inodeオブジェクト取得関数ではエラー時に戻り値としてNULL以外の

値を返します。エラー時の戻り値は負の値をポインターとして返しますので、これを

IS_ERRマクロでエラー値に変換して判断します。具体的にはinodeオブジェクト

取得関数の戻り値がMAX_ERRNO(4095と定義されています)の負の値より小さい

場合エラーとしています。興味があれば次の定義を眺めてみてください。


#define MAX_ERRNO       4095

#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)

static inline bool __must_check IS_ERR(__force const void *ptr)
{
    return IS_ERR_VALUE((unsigned long)ptr);
}



このマクロを使ってポインターが戻り値である関数内でNULLを返すだけでなく

エラーの場合はエラー値を返すことができるようになります。


また、このif文のなかでPTR_ERRマクロを使用しています。if文の中に

処理が入ってくれば、ポインターにエラー値が格納されていることになり

ますので、PTR_ERRでポインターをエラー値に変換しています。変換は

ただ、ポインターをlong型にキャストするだけとなります。


unlocknew_inode関数処理の後で、inc_nlinkを呼び出しています。

これは次のステップで見ていきますが、inodeオブジェクトのハードリンク数を

カウントしているi_nlinkをインクリメントする関数となります。ディレクトリは

"."エントリーがあります。"."エントリーはそのディレクトリ自身のハードリンク

となりますので、i_nlinkを1つインクリメントしておきましょう。また、今回の

ステップではルートディレクトリのinodeオブジェクトとなりますので、inode

オブジェクトのi_modeのファイルタイプとしてディレクトリS_IFDIRをセットして

います。これも次のステップで詳しく見ていきましょう。

補足

ここで出てきたunlikelyマクロについては後ほど詳しい説明を
likelyマクロとunlikelyマクロで見ていきます。

ルートディレクトリのdエントリー

割り当てたinodeオブジェクトとdエントリーを関連付ける必要があります。

関連付けを行うには仮想ファイルシステムからdエントリーオブジェクトを新規に

割り当ててから(スラブアロケーターからの割り当てとなります)、仮想ファイル

システムのd_instantiate関数を使用しますが、ルートディレクトリの

dエントリー用に共通処理が定義されています。d_make_root関数です。

d_make_root関数は名前が"/"であるdエントリーオブジェクトを新規生成

してd_instantiate関数を呼び出します。これらの関数について見ていきま

すが、その前にまずdエントリーの構造体について見てから関数を見ていきま

しょう。dエントリーの構造は特にファイルシステムでは使用しませんので、

流してみてください。


dエントリーオブジェクトを表現するdentry構造体は次のようになります。


/*
 * Try to keep struct dentry aligned on 64 byte cachelines (this will
 * give reasonable cacheline footprint with larger lines without the
 * large memory footprint increase).
 */
#ifdef CONFIG_64BIT
# define DNAME_INLINE_LEN 32 /* 192 bytes */
#else
# ifdef CONFIG_SMP
#  define DNAME_INLINE_LEN 36 /* 128 bytes */
# else
#  define DNAME_INLINE_LEN 40 /* 128 bytes */
# endif
#endif

#define d_lock  d_lockref.lock

struct dentry {
    /* RCU lookup touched fields */
    unsigned int d_flags;           /* protected by d_lock */
    seqcount_t d_seq;               /* per dentry seqlock */
    struct hlist_bl_node d_hash;    /* lookup hash list */
    struct dentry *d_parent;        /* parent directory */
    struct qstr d_name;
    struct inode *d_inode;          /* Where the name belongs to - NULL is
                                     * negative */
    unsigned char d_iname[DNAME_INLINE_LEN];        /* small names */

    /* Ref lookup also touches following */
    struct lockref d_lockref;       /* per-dentry lock and refcount */
    const struct dentry_operations *d_op;
    struct super_block *d_sb;       /* The root of the dentry tree */
    unsigned long d_time;           /* used by d_revalidate */
    void *d_fsdata;                 /* fs-specific data */

    struct list_head d_lru;         /* LRU list */
    /*
     * d_child and d_rcu can share memory
     */
    union {
            struct list_head d_child;       /* child of parent list */
            struct rcu_head d_rcu;
    } d_u;
    struct list_head d_subdirs;     /* our children */
    struct hlist_node d_alias;      /* inode alias list */
};



dentry構造体
名前 説明
unsigned intd_flags dエントリーのフラグです。次のようなフラグがあります。

d_flags
フラグ 説明
DCACHE_OP_HASH dエントリーオブジェクトにd_hashメソッドがセットされています。
DCACHE_OP_COMPARE dエントリーオブジェクトにd_compareメソッドがセットされています。
DCACHE_OP_REVALIDATE dエントリーオブジェクトにd_revalidateメソッドがセットされています。
DCACHE_OP_DELETE dエントリーオブジェクトにd_deleteメソッドがセットされています。
DCACHE_OP_PRUNE dエントリーオブジェクトにd_pruneメソッドがセットされています。
DCACHE_DISCONNECTED dエントリーオブジェクトはファイルシステムツリーから切り離された状態です。
DCACHE_REFERENCED 最近参照されたdエントリーオブジェクトです。
DCACHE_RCUACCESS RCUリストに登録されているdエントリーオブジェクトです。
DCACHE_CANT_MOUNT このdエントリーオブジェクトにマウントできません。
DCACHE_GENOCIDE 破棄対象のdエントリーオブジェクトです。
DCACHE_SHRINK_LIST 使用メモリーをシュリンク(縮小)するためのリストにつながれたdエントリーオブジェクトです。
DCACHE_OP_WEAK_REVALIDATE dエントリーオブジェクトにd_weak_revalidateメソッドがセットされています。
DCACHE_NFSFS_RENAMED NFS上のdエントリーオブジェクトでリネーム対象です。
DCACHE_COOKIE dcookieサブシステムで使用するdエントリーオブジェクトです。
DCACHE_FSNOTIFY_PARENT_WATCHED このdエントリーオブジェクトの親ディレクトリはfsnotifyで監視対象となっています。
DCACHE_DENTRY_KILLED 削除されたdエントリーオブジェクトです。
DCACHE_MOUNTED マウントポイントのdエントリーオブジェクトです。
DCACHE_NEED_AUTOMOUNT automountの対象となるdエントリーオブジェクトです。
DCACHE_MANAGE_TRANSIT manage移行中のdエントリーオブジェクトです。
DCACHE_MANAGE_TRANSIT manageに移行したdエントリーオブジェクトです。次の定義と同義です。
(DCACHE_MOUNTED | DCACHE_NEED_AUTOMOUNT | DCACHE_MANAGE_TRANSIT)
DCACHE_LRU_LIST LRUリストに連結されたdエントリーオブジェクトです。
DCACHE_ENTRY_TYPE dエントリータイプのマスクとして使用します。
DCACHE_MISS_TYPE dエントリータイプです。ネガティブdエントリーを表します。
DCACHE_DIRECTORY_TYPE dエントリータイプです。このdエントリーはディレクトリです。
DCACHE_AUTODIR_TYPE dエントリータイプです。このdエントリーはautomountで使用されます。
DCACHE_SYMLINK_TYPE dエントリータイプです。このdエントリーはシンボリックリンクです。
DCACHE_FILE_TYPE dエントリータイプです。このdエントリーは上記以外のタイプのファイルです。
DCACHE_MAY_FREE dエントリーオブジェクトを解放します。

seqcount_td_seq dエントリーオブジェクトのを変更するときのシーケンシャルロックとなります。
補足
シーケンシャルロックは次のような条件下のときに使用するロックとなります。
  • ロック対象に対してリーダーが多く、ライターが少ない場合。
  • ロック対象に対してリーダーよりもライターの優先度を上げたい場合。
  • ライターの優先度が高いため、リーダーが多くてもライターでロック解除待ちが発生しません。

struct hlist_bl_noded_hash dエントリーオブジェクトのハッシュリストに連結するノードです。
struct dentry *d_parent dエントリーオブジェクトの親ディレクトリです。
struct qstrd_name ファイル名となります。qstr構造体は次のようになっています。名前検索に使用するためのファイル名のハッシュ値と長さの情報も格納できる構造体となります。


/*
 * "quick string" -- eases parameter passing, but more importantly
 * saves "metadata" about the string (ie length and the hash).
 *
 * hash comes first so it snuggles against d_parent in the
 * dentry.
 */
struct qstr {
    union {
        struct {
                HASH_LEN_DECLARE;
        };
        u64 hash_len;
    };
    const unsigned char *name;
};


struct inode *inode dエントリーオブジェクトに関連付けされたinodeオブジェクトです。NULLだとネガティブdエントリーとなります。
unsigned char[DNAME_INLINE_LEN]d_iname ファイル名です。短いファイル名の場合このメモリー領域を使用します。
struct lockrefd_lockref dエントリーオブジェクトの参照カウンターとなります。
const struct dentry_operations *d_op dエントリーオブジェクトのメソッドとなります。
struct super_block *d_sb dエントリーオブジェクトが所属するファイルシステムのスーパーブロックオブジェクトです。
unsigned longd_time d_revalidateメソッドで使用します。
void *d_fsdata ファイルシステムで使用することができるプライベートデータです。
struct list_headd_lru dエントリーオブジェクトのLRUリストに連結するノードです。
struct list_headd_child 同じ親ディレクトリを持つdエントリーオブジェクトのリストに連結するノードです。
struct rcu_headd_rcu RCUで使用する連結リストのノードです。
struct list_headd_subdirs 同じ親ディレクトリを持つdエントリーオブジェクトのリストです。
struct hlist_noded_alias inodeのエイリアスリストに連結するノードです。


dエントリーオブジェクトではディレクトリの親子関係とファイルのハードリンクは次のように

リストで連結して管理を行っています。

dentry

そして、ディレクトリの親子関係やハードリンクの関係をセットアップして、ファイル

システムツリーにdエントリーオブジェクトを組み込む処理を行うのがd_instantiate

関数やd_make_root関数となります。これらの関数のインターフェースは次のように

なっています。


void d_instantiate(struct dentry *entry, struct inode * inode);
struct dentry *d_make_root(struct inode *root_inode);



inodeオブジェクトとdエントリーオブジェクトの関連付け処理
関数名 説明
d_instantiate 指定したdエントリーオブジェクトと指定したinodeオブジェクトを関連付けします。dエントリーオブジェクトのd_aliasリストにinodeオブジェクトと挿入し、d_inodeにinodeオブジェクトのアドレスを格納します。
d_make_root ルートディレクトリ用の名前が"/"であるdエントリーを新規生成して、d_instantiate関数を呼び出し指定されたルートディレクトリのinodeオブジェクトと関連付けを行います。


d_instantiate関数

d_instantiate関数の引数と戻り値は次のようになっています。

d_instantiate関数の引数
引数名 説明
struct dentry *entry 関連付けされるdエントリーオブジェクトを指定します。
struct inode *inode 関連付けされるinodeオブジェクトを指定します。


d_instantiate関数に戻り値はありません。

d_make_root関数

d_make_root関数の引数と戻り値は次のようになっています。

d_make_root関数の引数
引数名 説明
struct inode *root_inode 関連付けされるroot_inodeオブジェクトを指定します。


d_make_root関数の戻り値
説明
struct dentry * 指定されたinodeオブジェクトと関連付けされたdエントリオブジェクトを返します。失敗時にはNULLを返します。


ルートディレクトリのdエントリーとinodeの関連付け実装

d_make_root関数で取得したdエントリオブジェクトをスーパーブロックオブジェクトの

s_rootに格納してスーパーブロックオブジェクトとルートディレクトリのdエントリーの関連

付けを行います。次のような実装例となります。

[me2fs_super.c/me2fsFillSuperBlock]

    sb->s_root = d_make_root( root );

    if( !sb->s_root )
    {
        DBGPRINT( "<ME2FS>error : failed to make root\n" );
        ret = -ENOMEM;
        goto error_mount;
    }



ここまでで、一応マウントが成功します。マウント処理を行う前にディスク上の

スーパーブロックにあるs_mnt_countをインクリメントしておきます。実際には

バッファーキャッシュに格納されているスーパーブロックの変更を行います。

リトルエンディアンの値をインクリメントするにはle16_add_cpu関数を使用

します。le16_add_cpu関数は次のような関数となります。あわせて関連する

マクロについてもついでに見ていきましょう。


void le16_add_cpu(__le16 *var, u16 val);
void le32_add_cpu(__le32 *var, u32 val);
void le64_add_cpu(__le64 *var, u64 val);
void be16_add_cpu(__be16 *var, u16 val);
void be32_add_cpu(__be32 *var, u32 val);
void be64_add_cpu(__be64 *var, u64 val);



それぞれ、引数にポインターvarを指定します。ポインターvarが指す値と

引数で指定したvalをそれぞれのエンディアンに変換してから足してポインター

varが指す場所に格納します。

リトル・ビッグエンディアンに変換して加算する関数
関数 説明
le16_add_cpu ポインターvalが指す16ビットリトルエンディアンのデータに値valを足してvarの場所に格納します。
le32_add_cpu ポインターvalが指す32ビットリトルエンディアンのデータに値valを足してvarの場所に格納します。
le64_add_cpu ポインターvalが指す64ビットリトルエンディアンのデータに値valを足してvarの場所に格納します。
be16_add_cpu ポインターvalが指す16ビットビッグエンディアンのデータに値valを足してvarの場所に格納します。
be32_add_cpu ポインターvalが指す32ビットビッグエンディアンのデータに値valを足してvarの場所に格納します。
be64_add_cpu ポインターvalが指す64ビットビッグエンディアンのデータに値valを足してvarの場所に格納します。


s_mnt_countはファイルシステムがマウントされた回数となります。

マウント時、つまりfill_superコールバック関数(me2fsFillSuper関数)

で次のように1増やします。

[me2fs_super.c/me2fsFillSuperBlock]

    le16_add_cpu( &esb->s_mnt_count, 1 );

    DBGPRINT( "<ME2FS> me2fs is mounted !\n" );

    return( 0 );



ここまでで、マウントが成功したときの処理となります。

エラー処理が少し出てきましたので、次にマウント失敗時の処理について見ていきたいと思います。

マウント失敗時の処理とスーパーブロックオブジェクトの破棄

マウント失敗時にはエラーを検出時点で確保していたメモリーを解放したり

バッファーキャッシュの参照カウンターをクリアーして解放できるようにしておきます。

マウント時の処理が進むとそれに従って確保していくメモリーも増えていきます。

このため、エラー検出時の各段階で解放するメモリーも増えてきますので、次の

ように段階を設けています。


[me2fs_super.c/me2fsFillSuperBlock]

    /* ------------------------------------------------------------------------ */
    /* release buffer cache for read super block                                */
    /* ------------------------------------------------------------------------ */
error_mount:
    brelse( bh );

    /* ------------------------------------------------------------------------ */
    /* release me2fs super block information                                    */
    /* ------------------------------------------------------------------------ */
error_read_sb:
    sb->s_fs_info = NULL;
    kfree( msi );

    return( ret );



まず、err_read_sbラベルでは前回のステップで確保していたスーパーブロック

管理情報me2fs_sb_infoを解放しています。error_mountでは、スーパー

ブロックを読み込んだバッファーキャッシュをbrelse関数で解放するようにしておきます。

(brelseについてはステップ2.のバッファーキャッシュの解放を参照してください。

brelseはbuffer_head構造体のb_countメンバーをディクリメントします)。


ここで、エラー時のメモリー解放処理を追加しました。この処理は基本的にアンマウント

したときに呼び出されるスーパーブロックオブジェクトを破棄する処理でも同じことを行い

ます。ついでにスーパーブロックオブジェクトを破棄する処理についても簡単に作って

見ましょう。

スーパーブロックオブジェクトを破棄する処理(kill_sb)

ステップ2.のファイルシステムタイプオブジェクトで少し触れましたが、スーパー

ブロックオブジェクトを破棄する処理としてkill_sbメソッドがありました。アンマウントを

行ったときにこのメソッドが呼び出されます。kill_sbメソッドに実装する処理は、基本的に

ブロックデバイスでは同じ処理となりますので、仮想ファイルシステムで共通処理として実装

されています。この共通処理は次のような関数となります。


void kill_block_super(struct super_block *sb);



kill_block_super関数の引数
引数名 説明
struct super_block *sb アンマウントするファイルシステムのスーパーブロックオブジェクトを指定します。


kill_block_superでは大まかに書くと次の処理を行います。

  1. アンマウントするファイルシステム用に生成したdエントリオブジェクトを解放していきます。


  2. アンマウントするファイルシステムとディスクの同期を行ってスーパーブロックオブジェクトに
    非アクティブのフラグ(~MS_ACTIVE)をセットします。


  3. アンマウントするファイルシステム用に生成したinodeオブジェクトを解放していきます。


  4. スーパーブロックオブジェクトのput_superメソッドを呼び出します。ここで、ファイルシステム
    が独自に仮想ファイルシステムのスーパーブロックオブジェクトに関連付けしていた管理情報
    などのメモリーを解放します。


ここで、ほぼ最終段でスーパーブロックオブジェクトのput_superメソッドが呼び出される

ことになります。ですので、アンマウントの処理はファイルシステムタイプオブジェクトのkill_sb

メソッドとスーパーブロックオブジェクトのput_superメソッドの処理を実装していくことに

なります。


それでは、まずkill_sbメソッドについてです。

ファイルシステムタイプのkill_sbメソッド

仮想ファイルシステムの共通処理kill_block_superを呼び出すだけとなります。次のように

me2fs_main.cのfile_sytem_typeに実装していきます。

[me2fs_main.c/file_system_type構造体]

/*
---------------------------------------------------------------------------------
    File system type
---------------------------------------------------------------------------------
*/
static struct file_system_type me2fs_fstype =
{
    .name       = "me2fs",
    .mount      = me2fs_mount,
    .kill_sb    = kill_block_super,
    .fs_flags   = FS_REQUIRES_DEV,
};



kill_block_superからスーパーブロックオブジェクトのput_superが

呼び出されます。put_superについて実装例を見ていきましょう。

スーパーブロックオブジェクトのput_superメソッド

スーパーブロックオブジェクトのput_superメソッドはme2fs_super.cに

実装していきます。ここでは、関数名としてme2fsPutSuperBlockと

しました。まずは、super_operationsにメソッドの定義を行います。

(プロトタイプ宣言も行っています。)

[me2fs_super.c/super_operations構造体]

/*
=================================================================================

    Prototype Statement

=================================================================================
*/
/*
---------------------------------------------------------------------------------
    For Super Block Operations
---------------------------------------------------------------------------------
*/
static int me2fsFillSuperBlock( struct super_block *sb,
                                void *data,
                                int silent );
static void me2fsPutSuperBlock( struct super_block *sb );

// 省略しています。

static struct super_operations me2fs_super_ops = {
    .destroy_inode = me2fs_destroy_inode,
//  .dirty_inode = me2fs_dirty_inode,
    .write_inode = me2fs_write_inode,
//  .drop_inode = me2fs_drop_inode,
    .evict_inode = me2fs_evict_inode,
    .put_super = me2fsPutSuperBlock,
    .sync_fs = me2fs_sync_fs,
    .freeze_fs = me2fs_freeze_fs,
    .unfreeze_fs = me2fs_unfreeze_fs,
    .statfs = me2fs_statfs,
    .remount_fs = me2fs_remount_fs,
//  .umount_begin = me2fs_umount_begin,
    .show_options = me2fs_show_options,
};



me2fsPutSuperBlock関数で行うことはme2fsFillSuperBlockの

エラー時の処理と同じです。バッファーキャッシュの参照カウンターをディクリメント

して、管理情報のメモリーを解放します。次の例のように実装します。

[me2fs_super.c/me2fsPutSuperBlock]

/*
==================================================================================
    Function    :me2fsPutSuperBlock
    Input       :struct super_block *sb
                 < vfs super block >
    Output      :void
    Return      :void

    Description :put super block
==================================================================================
*/
static void me2fsPutSuperBlock( struct super_block *sb )
{
    struct me2fs_sb_info    *msi;

    msi = ME2FS_SB( sb );

    brelse( msi->s_sbh );
    sb->s_fs_info = NULL;
    kfree( msi );
}



ここで、ME2FS_SBが出てきました。これはただスーパーブロックオブジェクトの

s_fs_infoを参照するインライン関数となります。次のようにヘッダーに定義

しています。スーパーブロックオブジェクトのs_fs_infoはいろいろな関数内で

頻繁に参照しますので、これでコード量が減らせます。

注意
LinuxのExt2ファイルシステムのソースではインライン関数ではなく単純に

置換するだけの定義となっています。



[me2fs_super.c/me2fsPutSuperBlock]

/*
==================================================================================

    Management

==================================================================================
*/
/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Function    :ME2FS_SB
    Input       :struct super_block *sb
                 < vfs super block >
    Output      :void
    Return      :struct me2fs_inode_info *
                 < me2fs inode information

    Description :get me2fs_sb_info from vfs super block
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
static inline struct me2fs_sb_info *ME2FS_SB( struct super_block *sb )
{
    return( ( struct me2fs_sb_info* )sb->s_fs_info );
}



me2fsFillSuperBlock関数の実装まとめと実行例

このステップの最後に所々見てきましたme2fsFillSuperBlock関数のまとめと

実際に実行したときの様子を見ていきます。

me2fsFillSuperBlock関数の実装まとめ

最後にme2fsFillSuperBlock関数の実装全体を見てみましょう。

次のような実装例となります。

[me2fs_super.c/me2fsFillSuperBlock]

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Local Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
==================================================================================
    Function    :me2fsFillSuperBlock
    Input       :struct super_block *sb
                 < vfs super block object >
                 void *data
                 < user data >
                 int silent
                 < silent flag >
    Output      :void
    Return      :void

    Description :fill my ext2 super block object
==================================================================================
*/
static int me2fsFillSuperBlock( struct super_block *sb,
                                void *data,
                                int silent )
{
    struct buffer_head      *bh;
    struct ext2_super_block *esb;
    struct me2fs_sb_info    *msi;
    struct inode            *root;
    int                     block_size;
    int                     ret = -EINVAL;
    //unsigned long         sb_block = 1;

    /* ------------------------------------------------------------------------ */
    /* allocate memory to me2fs_sb_info                                         */
    /* ------------------------------------------------------------------------ */
    msi = kzalloc( sizeof( struct me2fs_sb_info ), GFP_KERNEL );

    if( !msi )
    {
        DBGPRINT( "<ME2FS>error: unable to alloc me2fs_sb_info\n" );
        ret = -ENOMEM;
        return( ret );
    }

    /* ------------------------------------------------------------------------ */
    /* set device's block size and size bits to super block                     */
    /* ------------------------------------------------------------------------ */
    block_size = sb_min_blocksize( sb, BLOCK_SIZE );

    DBGPRINT( "<ME2FS>Fill Super! block_size = %d\n", block_size );
    DBGPRINT( "<ME2FS>s_blocksize_bits = %d\n", sb->s_blocksize_bits );
    DBGPRINT( "<ME2FS>default block size is : %d\n", BLOCK_SIZE );
    DBGPRINT( "<ME2FS>device is : %s\n", sb->s_id );

    if( !block_size )
    {
        DBGPRINT( "<ME2FS>error: unable to set blocksize\n" );
        goto error_read_sb;
    }

    if( !( bh = sb_bread( sb, 1 ) ) )
    {
        DBGPRINT( "<ME2FS>failed to bread super block\n" );
        goto error_read_sb;
    }

    esb = ( struct ext2_super_block* )( bh->b_data );

    sb->s_magic = le16_to_cpu( esb->s_magic );

    if( sb->s_magic != ME2FS_SUPER_MAGIC )
    {
        DBGPRINT( "<ME2FS>error : magic of super block is %lu\n", sb->s_magic );
        goto error_mount;
    }

    msi->s_esb = esb;
    msi->s_sbh = bh;

    //dbgPrintExt2SB( esb );
    /* ------------------------------------------------------------------------ */
    /* set up vfs super block                                                   */
    /* ------------------------------------------------------------------------ */
    sb->s_op        = &me2fs_super_ops;

    DBGPRINT( "<ME2FS>max file size = %lu\n", ( unsigned long )sb->s_maxbytes );

    sb->s_fs_info   = ( void* )msi;

    root = iget_locked( sb, ME2FS_EXT2_ROOT_INO );

    if( IS_ERR( root ) )
    {
        DBGPRINT( "<ME2FS>error : failed to get root inode\n" );
        ret = PTR_ERR( root );
        goto error_mount;
    }
    
    unlock_new_inode( root );
    inc_nlink( root );

    root->i_mode = S_IFDIR;

    if( !S_ISDIR( root->i_mode ) )
    {
        DBGPRINT( "<ME2FS>root is not directory!!!!\n" );
    }

    sb->s_root = d_make_root( root );

    if( !sb->s_root )
    {
        DBGPRINT( "<ME2FS>error : failed to make root\n" );
        ret = -ENOMEM;
        goto error_mount;
    }

    le16_add_cpu( &esb->s_mnt_count, 1 );

    DBGPRINT( "<ME2FS> me2fs is mounted !\n" );

    return( 0 );

    /* ------------------------------------------------------------------------ */
    /* release buffer cache for read super block                                */
    /* ------------------------------------------------------------------------ */
error_mount:
    brelse( bh );

    /* ------------------------------------------------------------------------ */
    /* release me2fs super block information                                    */
    /* ------------------------------------------------------------------------ */
error_read_sb:
    sb->s_fs_info = NULL;
    kfree( msi );

    return( ret );
}



実行例

それではマウントを行っていきます。コンパイルしてモジュールをロードしてマウントを

してみてください。動作がおかしい場合はリブートをしてから実行してみてください。


$ make rmmod
sudo rmmod me2fs.ko

// モジュールをロードしていないときはエラーが出ます
$ make insmod
sudo insmod me2fs.ko
$ make mount
sudo mount -t me2fs -o loop ../ext2.img ../mnt



そしてdmesgで出力を確認します。


[48351.238370] (Me2FS)Hello, world
[48353.714662] <ME2FS>Fill Super! block_size = 1024
[48353.714666] <ME2FS>s_blocksize_bits = 10
[48353.714667] <ME2FS>default block size is : 1024
[48353.714668] <ME2FS>device is : loop0
[48353.721200] <ME2FS>max file size = 2147483647
[48353.721210] <ME2FS> me2fs is mounted !
[48353.722436] <ME2FS>show_options
[48353.722503] <ME2FS>show_options
[48353.722590] <ME2FS>show_options
[48353.723201] <ME2FS>show_options
[48353.723514] <ME2FS>show_options
[48353.723800] <ME2FS>show_options
[48353.724022] <ME2FS>show_options
[48353.838128] <ME2FS>show_options
[48353.838290] <ME2FS>show_options
[48353.838646] <ME2FS>show_options



エラーが出ていません!これでマウント処理が一応完了しました!

マウントポイントをlsコマンドで見てみましょう。


$ ls ../mnt
ls: cannot open directory ../mnt: Not a directory



この段階ではマウントはとにかくできましたが、まともに動作していません。。。

これから実装を行っていくことでlsコマンドも動作していきます。

ついでにアンマウントも行ってみましょう。


$ make umount
sudo umount ../mnt
make: *** [umount] Error 139
$ dmesg
[48474.019780] <ME2FS>show_options
[48534.019781] <ME2FS>show_options
[48594.019374] <ME2FS>show_options
[48626.504100] <ME2FS>sync_fs
[48626.504106] <ME2FS>sync_fs
[48626.504109] <ME2FS>evict_inode
[48626.504115] ------------[ cut here ]------------
[48626.504117] kernel BUG at /build/buildd/linux-lts-utopic-3.16.0/fs/inode.c:565!
[48626.504118] invalid opcode: 0000 [#1] SMP 
[48626.504828] Modules linked in: me2fs(OE) isofs vboxsf(OE) joydev serio_raw vboxguest(OE) ppdev parport_pc lp parport psmouse ahci libahci e1000
[48626.504835] CPU: 0 PID: 4502 Comm: umount Tainted: G           OE 3.16.0-28-generic #38-Ubuntu
[48626.504836] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[48626.504837] task: ffff88005b600a30 ti: ffff8800654c0000 task.ti: ffff8800654c0000
[48626.504838] RIP: 0010:[<ffffffff811ee8d9>]  [<ffffffff811ee8d9>] evict+0x179/0x180
[48626.504844] RSP: 0000:ffff8800654c3e20  EFLAGS: 00010206
[48626.504845] RAX: ffff88007ffc5720 RBX: ffff880072f08490 RCX: 0000000000000034
[48626.504846] RDX: 0000000000000003 RSI: ffff880072f08530 RDI: ffff88007ffc5718
[48626.504847] RBP: ffff8800654c3e38 R08: 6018000000000000 R09: 0072f085300c0000
[48626.504847] R10: ff6f0f978c154c03 R11: ffff8800654c3b2e R12: ffff880072f08518
[48626.504848] R13: ffffffffc01c5040 R14: ffff8800654c3e60 R15: ffff880072f08518
[48626.504850] FS:  00002b2ab2102240(0000) GS:ffff88007fc00000(0000) knlGS:0000000000000000
[48626.504851] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[48626.504852] CR2: 00000000017c90b0 CR3: 00000000654d1000 CR4: 00000000000006f0
[48626.504855] Stack:
[48626.504856]  ffff8800654c3e60 ffff880072f08490 ffff88006e79d8a8 ffff8800654c3e50
[48626.504858]  ffffffff811ee919 ffff88006e79d7a0 ffff8800654c3e98 ffffffff811ef67c
[48626.504859]  ffff8800654c3e60 ffff8800654c3e60 ffff88006e79d800 ffff88006e79d8a8
[48626.504861] Call Trace:
[48626.504864]  [<ffffffff811ee919>] dispose_list+0x39/0x50
[48626.504866]  [<ffffffff811ef67c>] evict_inodes+0x11c/0x130



メッセージを確認するとどうやらevict_inodeでエラーが発生して

いるようです。こちらも後々実装していくことで正常に動作していきますが、

スーパーブロックオブジェクトのevict_inodeメソッドの定義を次のように

コメントアウトしていただければアンマウントは成功します。試してみてください。

アンマウント時にevict_inodeメソッドが呼び出されることを確認するために

今回のステップでは定義を行っています。気になる方は実装を行うまでコメント

アウトしておきましょう。


/*
=================================================================================

    Prototype Statement

=================================================================================
*/
/*
---------------------------------------------------------------------------------
    For Super Block Operations
---------------------------------------------------------------------------------
*/
static int me2fsFillSuperBlock( struct super_block *sb,
                                void *data,
                                int silent );
static void me2fsPutSuperBlock( struct super_block *sb );

// 省略しています。

static struct super_operations me2fs_super_ops = {
    .destroy_inode = me2fs_destroy_inode,
//  .dirty_inode = me2fs_dirty_inode,
    .write_inode = me2fs_write_inode,
//  .drop_inode = me2fs_drop_inode,
//    .evict_inode = me2fs_evict_inode,
    .put_super = me2fsPutSuperBlock,
    .sync_fs = me2fs_sync_fs,
    .freeze_fs = me2fs_freeze_fs,
    .unfreeze_fs = me2fs_unfreeze_fs,
    .statfs = me2fs_statfs,
    .remount_fs = me2fs_remount_fs,
//  .umount_begin = me2fs_umount_begin,
    .show_options = me2fs_show_options,
};




いかがでしたでしょうか。とりあえずマウントは行えるようになりました。マウントは

ファイルシステムを使用する上で必要な処理で、複雑な処理となりますが、

これだけの実装でとりあえずマウントが成功するようになりました。


次のステップではスーパーブロックオブジェクトのセットアップをとりあえず、今できる

限り実装していきます。

PR 説明

ディベロッパー・エクスペリエンス Linux Ext2ファイルシステム
0から作るLinuxプログラム Linux Ext2ファイルシステムの完全説明版(Kindleのみ)となります。実装するステップ数は36ステップあります。少しずつステップを実装して、実際に動かして学習していきます。ファイルシステムの機能を呼び出すシステムコールの実装から、仮想ファイルシステムが呼び出すExt2ファイルシステムの実装を行っていきます。ファイルシステムの開発体験を通すことで、実際にカーネルが行っているファイルシステムの基礎的な動作を理解することができます。ぜひお試しください!

inserted by FC2 system