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