SLAVEマウント
mount --bind mnt1 mnt2は、mnt1がSHAREなら、mnt1.mnt_shareにmnt2がリストされ、mnt1がSLAVEなら、mnt1.mnt_slaveにmnt2がリストされ、mnt2.mnt_master = mnt1.mnt_masterとなります。mnt1がクーロンmntでないならmnt1.mnt_master=mnt1で、mountのdentryのmnt.mnt_masterからmnt_shareリストのmntにもmountされます。
make-slaveするとmnt_shareにリストされているmntが削除され、mnt.mnt_master=mntとなり、従ってmnt1をSLAVEすると、全BINDしてるmntは無効となり、mnt2をSLAVすると、mnt2.mnt_masteからのmnt1.mnt_shareへ参照できず、mnt1へはmountされなくなります。
mntのmnt_shareにリストされているmntのmnt_shareにリストmntと階層的に反映していきますが、この時階層化のmnt_slaveも対象となります。(SLAVEでbindし、--make-shareで共有mntした後、それでrbindすると、先のSLAVEでbindしたmntも対象となります。--make-shareは((m)->mnt.mnt_flags & MNT_SHARED)とするだけで、--make-slaveと異なり掛かるbindリスト情報の変更はありません。
make-slaveするとmnt_shareにリストされているmntが削除され、mnt.mnt_master=mntとなり、従ってmnt1をSLAVEすると、全BINDしてるmntは無効となり、mnt2をSLAVすると、mnt2.mnt_masteからのmnt1.mnt_shareへ参照できず、mnt1へはmountされなくなります。
static int do_make_slave(struct mount *mnt) { struct mount *peer_mnt = mnt, *master = mnt->mnt_master; struct mount *slave_mnt; while ((peer_mnt = next_peer(peer_mnt)) != mnt && peer_mnt->mnt.mnt_root != mnt->mnt.mnt_root) ; if (peer_mnt == mnt) { peer_mnt = next_peer(mnt); if (peer_mnt == mnt) peer_mnt = NULL; } if (IS_MNT_SHARED(mnt) && list_empty(&mnt->mnt_share)) mnt_release_group_id(mnt); list_del_init(&mnt->mnt_share); mnt->mnt_group_id = 0; if (peer_mnt) master = peer_mnt; if (master) { list_for_each_entry(slave_mnt, &mnt->mnt_slave_list, mnt_slave) slave_mnt->mnt_master = master; list_move(&mnt->mnt_slave, &master->mnt_slave_list); list_splice(&mnt->mnt_slave_list, master->mnt_slave_list.prev); INIT_LIST_HEAD(&mnt->mnt_slave_list); } else { struct list_head *p = &mnt->mnt_slave_list; while (!list_empty(p)) { slave_mnt = list_first_entry(p, struct mount, mnt_slave); list_del_init(&slave_mnt->mnt_slave); slave_mnt->mnt_master = NULL; } } mnt->mnt_master = master; CLEAR_MNT_SHARED(mnt); return 0; }
クーロンmnt2をSLAVE: クーロンmnt1ソースへのmntは全クーロンmntに反映されます
[root@localhost loop]# mount --make-shared mnt1 [root@localhost loop]# mount --bind mnt1 mnt2 [root@localhost loop]# mount --bind mnt1 mnt3 [root@localhost loop]# mount --make-slave mnt2 [root@localhost loop]# mount -o loop loopfile4 mnt1 [root@localhost loop]# ls mnt1 lost+found mnt4.txt [root@localhost loop]# ls mnt2 lost+found mnt4.txt [root@localhost loop]# ls mnt3 lost+found mnt4.txt
クーロンmnt2をSLAVE: クーロンmnt2へのmntはクーロンmnt1ソース及び他のSLAVE mntに反映されません。
[root@localhost loop]# mount --make-shared mnt1 [root@localhost loop]# mount --bind mnt1 mnt2 [root@localhost loop]# mount --bind mnt1 mnt3 [root@localhost loop]# mount --make-slave mnt2 [root@localhost loop]# mount -o loop loopfile4 mnt2 [root@localhost loop]# ls mnt1 lost+found mnt1.txt [root@localhost loop]# ls mnt2 lost+found mnt4.txt [root@localhost loop]# ls mnt3 lost+found mnt1.txt
[root@localhost loop]# mount --make-shared mnt1 [root@localhost loop]# mount --bind mnt1 mnt2 [root@localhost loop]# mount --bind mnt1 mnt3 [root@localhost loop]# mount --make-slave mnt2 [root@localhost loop]# mount -o loop loopfile4 mnt3 [root@localhost loop]# ls mnt1 lost+found mnt4.txt [root@localhost loop]# ls mnt2 lost+found mnt4.txt [root@localhost loop]# ls mnt3 lost+found mnt4.txt
クーロンmnt1ソースをSLAVE: mnt1をソースとする他の全SLAVE mntに反映されません。
[root@localhost loop]# mount --make-shared mnt1 [root@localhost loop]# mount --bind mnt1 mnt2 [root@localhost loop]# mount --bind mnt1 mnt3 [root@localhost loop]# mount --make-slave mnt1 [root@localhost loop]# mount -o loop loopfile4 mnt1 [root@localhost loop]# ls mnt1 lost+found mnt4.txt [root@localhost loop]# ls mnt2 lost+found mnt1.txt [root@localhost loop]# ls mnt3 lost+found mnt1.txt
補足
mount bindのdo_loopback()からpropagate_mnt()がコールされ、mount対象となるmntをtree_listにリストします。mntのmnt_shareにリストされているmntのmnt_shareにリストmntと階層的に反映していきますが、この時階層化のmnt_slaveも対象となります。(SLAVEでbindし、--make-shareで共有mntした後、それでrbindすると、先のSLAVEでbindしたmntも対象となります。--make-shareは((m)->mnt.mnt_flags & MNT_SHARED)とするだけで、--make-slaveと異なり掛かるbindリスト情報の変更はありません。
int propagate_mnt(struct mount *dest_mnt, struct dentry *dest_dentry, struct mount *source_mnt, struct list_head *tree_list) { struct mount *m, *child; int ret = 0; struct mount *prev_dest_mnt = dest_mnt; struct mount *prev_src_mnt = source_mnt; LIST_HEAD(tmp_list); LIST_HEAD(umount_list); for (m = propagation_next(dest_mnt, dest_mnt); m; m = propagation_next(m, dest_mnt)) { int type; struct mount *source; if (IS_MNT_NEW(m)) continue; source = get_source(m, prev_dest_mnt, prev_src_mnt, &type); if (!(child = copy_tree(source, source->mnt.mnt_root, type))) { ret = -ENOMEM; list_splice(tree_list, tmp_list.prev); goto out; } if (is_subdir(dest_dentry, m->mnt.mnt_root)) { mnt_set_mountpoint(m, dest_dentry, child); list_add_tail(&child->mnt_hash, tree_list); } else { list_add_tail(&child->mnt_hash, &tmp_list); } prev_dest_mnt = m; prev_src_mnt = child; } out: br_write_lock(vfsmount_lock); while (!list_empty(&tmp_list)) { child = list_first_entry(&tmp_list, struct mount, mnt_hash); umount_tree(child, 0, &umount_list); } br_write_unlock(vfsmount_lock); release_mounts(&umount_list); return ret; } static struct mount *propagation_next(struct mount *m, struct mount *origin) { while (1) { struct mount *master = m->mnt_master; if (master == origin->mnt_master) { struct mount *next = next_peer(m); return (next == origin) ? NULL : next; } else if (m->mnt_slave.next != &master->mnt_slave_list) return next_slave(m); m = master; } } static inline struct mount *next_peer(struct mount *p) { return list_entry(p->mnt_share.next, struct mount, mnt_share); } static inline struct mount *next_slave(struct mount *p) { return list_entry(p->mnt_slave.next, struct mount, mnt_slave); }