RBINDオプションでのmount
rbindのmountはmountフラグのMS_BINDにMS_RECが付加されdo_loopback()をTRUEでコールする事で、bindのclone_mnt()をcopy_tree()でその配下のmountも実装されます。
mountは、そのdentryがdentry->d_flags | DCACHE_MOUNTEDで、dentryが属するmnt/dentryをハッシュキーとしてmount_hashtable[]に登録する事で実装されます。取得したmntからそのmnt->root_mntを次の走査元のdentryとして捜査します。結果的にシンボリックリンクと同じですが、シンボリックされたdentryのmntは元のmntですが、bindのmntは元のmntと違うという事で、配下のdentryはdentry->d_flags | DCACHE_MOUNTEDとなっているものの、そのmntはmount_hashtable[]に登録されていないため、mount先え参照できません。
__follow_mount_rcu()はpath走査時、各dentry毎にmountされているかチェックするためにコールされます。
mountは、そのdentryがdentry->d_flags | DCACHE_MOUNTEDで、dentryが属するmnt/dentryをハッシュキーとしてmount_hashtable[]に登録する事で実装されます。取得したmntからそのmnt->root_mntを次の走査元のdentryとして捜査します。結果的にシンボリックリンクと同じですが、シンボリックされたdentryのmntは元のmntですが、bindのmntは元のmntと違うという事で、配下のdentryはdentry->d_flags | DCACHE_MOUNTEDとなっているものの、そのmntはmount_hashtable[]に登録されていないため、mount先え参照できません。
__follow_mount_rcu()はpath走査時、各dentry毎にmountされているかチェックするためにコールされます。
static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
struct inode **inode)
{
for (;;) {
struct mount *mounted;
if (unlikely(managed_dentry_might_block(path->dentry)))
return false;
if (!d_mountpoint(path->dentry))
break;
mounted = __lookup_mnt(path->mnt, path->dentry, 1);
if (!mounted)
break;
path->mnt = &mounted->mnt;
path->dentry = mounted->mnt.mnt_root;
nd->flags |= LOOKUP_JUMPED;
nd->seq = read_seqcount_begin(&path->dentry->d_seq);
*inode = path->dentry->d_inode;
}
return true;
}
static inline bool d_mountpoint(struct dentry *dentry)
{
return dentry->d_flags & DCACHE_MOUNTED;
}
struct mount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry,
int dir)
{
struct list_head *head = mount_hashtable + hash(mnt, dentry);
struct list_head *tmp = head;
struct mount *p, *found = NULL;
for (;;) {
tmp = dir ? tmp->next : tmp->prev;
p = NULL;
if (tmp == head)
break;
p = list_entry(tmp, struct mount, mnt_hash);
if (&p->mnt_parent->mnt == mnt && p->mnt_mountpoint == dentry) {
found = p;
break;
}
}
return found;
}
copy_tree()はmnt配下(mnt->mnt_child)のdentry配下のmountもclone_mnt()でバーチャルファイルシステムのmntを作成し、attach_mnt()でバーチャルファイルシステムのmntとして、元のdentryにmountします。
struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
int flag)
{
struct mount *res, *p, *q, *r;
struct path path;
if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
return NULL;
res = q = clone_mnt(mnt, dentry, flag);
if (!q)
goto Enomem;
q->mnt_mountpoint = mnt->mnt_mountpoint;
p = mnt;
list_for_each_entry(r, &mnt->mnt_mounts, mnt_child) {
struct mount *s;
if (!is_subdir(r->mnt_mountpoint, dentry))
continue;
for (s = r; s; s = next_mnt(s, r)) {
if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) {
s = skip_mnt_tree(s);
continue;
}
while (p != s->mnt_parent) {
p = p->mnt_parent;
q = q->mnt_parent;
}
p = s;
path.mnt = &q->mnt;
path.dentry = p->mnt_mountpoint;
q = clone_mnt(p, p->mnt.mnt_root, flag);
if (!q)
goto Enomem;
br_write_lock(vfsmount_lock);
list_add_tail(&q->mnt_list, &res->mnt_list);
attach_mnt(q, &path);
br_write_unlock(vfsmount_lock);
}
}
return res;
Enomem:
if (res) {
LIST_HEAD(umount_list);
br_write_lock(vfsmount_lock);
umount_tree(res, 0, &umount_list);
br_write_unlock(vfsmount_lock);
release_mounts(&umount_list);
}
return NULL;
}
static void attach_mnt(struct mount *mnt, struct path *path)
{
mnt_set_mountpoint(real_mount(path->mnt), path->dentry, mnt);
list_add_tail(&mnt->mnt_hash, mount_hashtable +
hash(path->mnt, path->dentry));
list_add_tail(&mnt->mnt_child, &real_mount(path->mnt)->mnt_mounts);
}
サンプル[root@localhost loopdev]# ls /mnt/loop1/ submnt [root@localhost loopdev]# ls /mnt/loop1/submnt/ babakaka.txt [root@localhost loopdev]# mount -o loop loopfile1 /mnt/loop1/submnt/ [root@localhost loopdev]# ls /mnt/loop1/submnt/ lost+found
[root@localhost loopdev]# mount --bind /mnt/loop1 /mnt/loop2 [root@localhost loopdev]# ls /mnt/loop2 submnt [root@localhost loopdev]# ls /mnt/loop2/submnt/ babakaka.txt
[root@localhost loopdev]# mount --rbind /mnt/loop1 /mnt/loop3 [root@localhost loopdev]# ls /mnt/loop3 submnt [root@localhost loopdev]# ls /mnt/loop3/submnt/ lost+found





