RBINDオプションでのmount
Rev.4を表示中。最新版はこちら。
BINDマウントは、マウントするファイルシステム下にマウントされているファイルシステムを参照する事はできません。/tmp/hogehogeに/をbindマウント(/sysはsysfsがマウントされていています。)
[root@localhost tmp]# mount --bind / hogehoge//は/tmp/hogehoge/にマウントされている
[root@localhost tmp]# ls / bin dev lib mnt proc run srv tmp var boot etc lost+found mnt1 pstore sbin sys usr debugfs home media opt root sd sysroot va [root@localhost tmp]# ls hogehoge/ bin dev lib mnt proc run srv tmp var boot etc lost+found mnt1 pstore sbin sys usr debugfs home media opt root sd sysroot va/hogehoge/sysはマウントされていない。
[root@localhost tmp]# ls /sys block class devices fs module bus dev firmware kernel power [root@localhost tmp]# ls hogehoge/sys [root@localhost tmp]#これは、path_lookup()のパス検索において、dentryがマウントされていると、follow_mount()からlookup_mnt()とコールされ、検索するmntとdentryのハッシュキーをインデックスとするmount_hashtable[]をヘッドするリストを走査することでmntを検索しているからです。/sysと/tmp/hogehoge/sysのdentryは別物で、/sys下のファイルシステムは、/tmp/hogehoge/sysをハッシュとするヘッドにリストされてないからです。
bindマウントにおいて、マウントするファイルシステム下にマウントされているファイルシステムも、改めてマウントし直すことで、新しい参照されるdentryでハッシュテーブルにリストされ、従ってサブマウントされているファイルシステム下のファイルも参照することが可能となります。これを実現するのがrbindです。下記のように/tmp/hogehogeに/をrbindマウントさせれば/sysもマウントされ、それを参照することが可能となります。
[root@localhost tmp]# mount --rbind / hogehoge/ [root@localhost tmp]# ls /sys block class devices fs module bus dev firmware kernel power [root@localhost tmp]# ls hogehoge/sys block class devices fs module bus dev firmware kernel powerrbindでmountすると、sys_mountシステムコールのフラグに、MS_BINDとMS_RECが設定され、do_mount()をコールされ、do_loopback()の第3パラメータが1でコールされます。
long do_mount(char * dev_name, char * dir_name, char *type_page, unsigned long flags, void *data_page) { : if (flags & MS_REMOUNT) retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags, data_page); else if (flags & MS_BIND) retval = do_loopback(&nd, dev_name, flags & MS_REC); else if (flags & MS_MOVE) retval = do_move_mount(&nd, dev_name); else retval = do_add_mount(&nd, type_page, flags, mnt_flags, dev_name, data_page); dput_out: path_release(&nd); return retval; }do_loopback()は、マウントされるパスのstruct nameidataのmntとdentryで、新規にvfsmntを作成し、graft_tree()で、このvfsmntをそれぞれにリスト化し、マウント先のdentryにバインドすることにあります。
ここで、RBIND時は recurse=1となります。この時copy_tree()によりvfsmntを取得します。
static int do_loopback(struct nameidata *nd, char *old_name, int recurse) { struct nameidata old_nd; struct vfsmount *mnt = NULL; int err = mount_is_safe(nd); if (err) return err; if (!old_name || !*old_name) return -EINVAL; err = path_lookup(old_name, LOOKUP_FOLLOW, &old_nd); if (err) return err; down_write(¤t->namespace->sem); err = -EINVAL; if (check_mnt(nd->mnt) && (!recurse || check_mnt(old_nd.mnt))) { err = -ENOMEM; if (recurse) mnt = copy_tree(old_nd.mnt, old_nd.dentry); else mnt = clone_mnt(old_nd.mnt, old_nd.dentry); } if (mnt) { err = graft_tree(mnt, nd); if (err) { spin_lock(&vfsmount_lock); umount_tree(mnt); spin_unlock(&vfsmount_lock); } else mntput(mnt); } up_write(¤t->namespace->sem); path_release(&old_nd); return err; }clone_mnt()は、マウントするパスの属するvfsmountをそのまま設定し、スーパブロックの参照カウンタをインクリメントているに過ぎません。なお、mnt_mountpointはgraft_tree()で、マウント先dentryに更新されます。
static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root) { struct super_block *sb = old->mnt_sb; struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); if (mnt) { mnt->mnt_flags = old->mnt_flags; atomic_inc(&sb->s_active); mnt->mnt_sb = sb; mnt->mnt_root = dget(root); mnt->mnt_mountpoint = mnt->mnt_root; mnt->mnt_parent = mnt; } return mnt; }copy_tree()は、ターゲットとなるファイルシステムにぶらさがる全てのファイルシステムを走査し、その1つ1つをまた、走査しながら、マウントされていないファイルシステムに行き着いて、それをclone_mnt()で新規にvfsmountを取得し、改めてマウントしているようです。これをマウント先のdentryをハッシュとしてリスト化され、このdentryからファイルシステムを検索する事が可能となるわけです。
static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry) { struct vfsmount *res, *p, *q, *r, *s; struct list_head *h; struct nameidata nd; res = q = clone_mnt(mnt, dentry); if (!q) goto Enomem; q->mnt_mountpoint = mnt->mnt_mountpoint; p = mnt; for (h = mnt->mnt_mounts.next; h != &mnt->mnt_mounts; h = h->next) { r = list_entry(h, struct vfsmount, mnt_child); if (!lives_below_in_same_fs(r->mnt_mountpoint, dentry)) continue; for (s = r; s; s = next_mnt(s, r)) { while (p != s->mnt_parent) { p = p->mnt_parent; q = q->mnt_parent; } p = s; nd.mnt = q; nd.dentry = p->mnt_mountpoint; q = clone_mnt(p, p->mnt_root); if (!q) goto Enomem; spin_lock(&vfsmount_lock); list_add_tail(&q->mnt_list, &res->mnt_list); attach_mnt(q, &nd); spin_unlock(&vfsmount_lock); } } return res; Enomem: if (res) { spin_lock(&vfsmount_lock); umount_tree(res); spin_unlock(&vfsmount_lock); } return NULL; }正直vfsmountのリスト管理の扱いが、今ひとつ読みきれていません。