BINDオプションでのmount
Rev.4を表示中。最新版はこちら。
通常マウントはファイルシステムのルートがマウント先にアタッチされますが、bindオプションをつけると、任意のディレクトリ以下を仮想の独自のファイルシステムとしてアタッチすることができます。mount --bind olddir newdirolddirはnewdirとなり、まさにシンボリックリンクと同じようになことが実現できます。どのように違うのでしょうか? chrootでルートを変更すると、シンボリックリンクでの環境外へのアクセスができなくなりますが、bindマウントはアクセスが可能です。シンボリックリンクはinode下で実現しているのに対して、mountはdentry下で実装しているからです。
dentry----dentry |--dentry |--dentry---dentry |-@dentry上のようなdentry構成で、例えば@dentryにシンボリックリンクしているとします。それは取得した@dentryのinodeをチェックすれば分かります。そしてシンボリックリンク先を以降の検索pathとして、@dentryへと差し替えて次へと検索します。このときシンボリック先を参照してもいいかどうかのチェックが行われるようになっており、従ってchrootでの参照できないわけです。
@dentryがbindマウントした場合だと、@dentryのd_mountedメンバーをチェックすることで、@dentryがマウントポイントのディレクトリということが分かります。するとマウントしているファイルシステム(vfsmount)を検索し、そのルートdentry(マウント元)を、@dentryに差し替えて、次へと検索しています。inodeにかかるチェックは介在していません。
通常のmountは、指定されたブロックデバイスファイルから、vfsmountを作成し、そこにルートのdentryを設定するものでした。bindマウントでは、newdirが属するvfsmountを取得して、それをベースに新たにvfsmountを作成、そこにnewdirのdentryをセットすることで実現しているようです。
bindオプション時のmountはdo_loopbackが呼ばれます。第一引数のndは、path_lookupでマウントポイントのnameidataです。old_nameはマウントするディレクトリです。
old_nameの属するvfsmountを参照するために、path_lookupでold_nameのnameidataを取得します。copy_treeでこのvfsmountをもとに、新規のvfsmountを作成し、ハッシュテーブル、リストに登録します。このときこのマウント先はold_nd.path.dentry
このnameidataからディレクトリの属するvfsmountを取得することができます。
static noinline int do_loopback(struct nameidata *nd, char *old_name, int recurse) { struct nameidata old_nd; struct vfsmount *mnt = NULL; ・・・・・ err = path_lookup(old_name, LOOKUP_FOLLOW, &old_nd); ・・・・・ mnt = copy_tree(old_nd.path.mnt, old_nd.path.dentry, 0); ・・・・・ err = graft_tree(mnt, &nd->path);path_lookupでマウント元のnameidataが作成されます。それをベースにして新たにvfsmount構造体を作成し、vfsmountのmnt_rootに通常ルートになるところを、old_nd.path.dentryを設定します。最後にgraft_treeマウントポイント側を処理しておしまいです。基本的な処理は通常マウントとほとんど変わりないようです。
追記
最初にvfsmntを作成する場合、デバイスファイルが必要で、bindオプションのmountは、そのホルダがあるデバイスのvfsmntから、新しいvfsmntを作成するようなっています。従ってbindでmountする場合、そのディレクトリーがあるデバイスが、あらかじめ通常にマウントされてなければならないと言うことです。(