共有マウント
SHAREはclone_mnt()で実装されるmount bind等で扱われます。(他にcloneでのスレッド) clone_mnt()は元ファイルシステムを雛形とし、mnt_rootを接続先dentryとする新規のmntを作成します。従って互いに独立したファイルシステムで、一方のディレクトリにmountする掛かる変更は、他方のmntへ反映されず、一方のmnt下でmountされたファイルシステムを参照できませんが、bind元mntをSHAREとすると、一方のmntにもmntされます。
検証サンプル
NO SHAREのbind元mnt1をmnt2にbind後、mnt1/submntをmountしても、mnt2/submntにはmountされません。
SHAREのbind元mnt1をmnt2にbind後、mnt1/submntをmountすると、mnt2/submntにもmountされます。
SHAREのbind元mnt1をmnt2にbind後、mnt2/submntをmountしても、mnt1/submntにもmountされます。
bind元mnt1をSHAREのmnt2にbind後、mnt1/submntまたmnt2/submntをmountしても、mnt2/submntまたmnt1/submntにもmountされません。
検証サンプル
[root@localhost north]# mount -o loop loopfile1 mnt1/ [root@localhost north]# mount -o loop loopfile2 mnt2/ [root@localhost north]# mount -o loop loopfile3 mnt3/ [root@localhost north]# ls mnt1 lost+found mnt1.txt submnt [root@localhost north]# ls mnt1/submnt/ submnt1.txt [root@localhost north]# ls mnt2 lost+found mnt2.txt submnt [root@localhost north]# ls mnt2/submnt/ submnt2.txt [root@localhost north]# ls mnt3 lost+found mnt3.txt submnt [root@localhost north]# ls mnt3/submnt/ submnt3.txt
NO SHAREのbind元mnt1をmnt2にbind後、mnt1/submntをmountしても、mnt2/submntにはmountされません。
[root@localhost north]# mount --bind mnt1 mnt2 [root@localhost north]# mount -o loop loopfile3 mnt1/submnt/ [root@localhost north]# ls mnt1/submnt/ lost+found mnt3.txt submnt [root@localhost north]# ls mnt2/submnt/ submnt1.txt
SHAREのbind元mnt1をmnt2にbind後、mnt1/submntをmountすると、mnt2/submntにもmountされます。
[root@localhost north]# mount --make-shared mnt1 [root@localhost north]# mount --bind mnt1 mnt2 [root@localhost north]# mount -o loop loopfile3 mnt1/submnt/ [root@localhost north]# ls mnt1/submnt/ lost+found mnt3.txt submnt [root@localhost north]# ls mnt2/submnt/ lost+found mnt3.txt submnt
SHAREのbind元mnt1をmnt2にbind後、mnt2/submntをmountしても、mnt1/submntにもmountされます。
[root@localhost north]# mount --make-shared mnt1 [root@localhost north]# mount --bind mnt1 mnt2 [root@localhost north]# mount -o loop loopfile3 mnt2/submnt/ [root@localhost north]# ls mnt1/submnt/ lost+found mnt3.txt submnt [root@localhost north]# ls mnt2/submnt/ lost+found mnt3.txt submnt
bind元mnt1をSHAREのmnt2にbind後、mnt1/submntまたmnt2/submntをmountしても、mnt2/submntまたmnt1/submntにもmountされません。
[root@localhost north]# mount --make-shared mnt2 [root@localhost north]# mount --bind mnt1 mnt2 [root@localhost north]# mount -o loop loopfile3 mnt1/submnt/ [root@localhost north]# ls mnt1/submnt/ lost+found mnt3.txt submnt [root@localhost north]# ls mnt2/submnt/ submnt1.txt [root@localhost north]# umount mnt1/submnt/ [root@localhost north]# mount -o loop loopfile3 mnt2/submnt/ [root@localhost north]# ls mnt1/submnt/ submnt1.txt [root@localhost north]# ls mnt2/submnt/ lost+found mnt3.txt submntoldはbind元mntで、oldを雛形とするバーチャルmntを作成します。SHAREならmnt->mnt_shareをold->mnt_shareにリストし、mnt->mnt_master = old->mnt_masterとします。通常のファイルシステムはmnt->mnt_master = mntで、oldがbindされてないならold->mnt_master=oldで、バーチャルmnt->mnt_master->mnt_shareから、SHAREされているmntを取得し、mntするdentryがそれらのmnt配下ならmountします。(mount bindではflagは0です。)
static struct mount *clone_mnt(struct mount *old, struct dentry *root,
int flag)
{
struct super_block *sb = old->mnt.mnt_sb;
struct mount *mnt = alloc_vfsmnt(old->mnt_devname);
if (mnt) {
if (flag & (CL_SLAVE | CL_PRIVATE))
mnt->mnt_group_id = 0;
else
mnt->mnt_group_id = old->mnt_group_id;
if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) {
int err = mnt_alloc_group_id(mnt);
if (err)
goto out_free;
}
mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD;
atomic_inc(&sb->s_active);
mnt->mnt.mnt_sb = sb;
mnt->mnt.mnt_root = dget(root);
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
mnt->mnt_parent = mnt;
br_write_lock(vfsmount_lock);
list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
br_write_unlock(vfsmount_lock);
if (flag & CL_SLAVE) {
list_add(&mnt->mnt_slave, &old->mnt_slave_list);
mnt->mnt_master = old;
CLEAR_MNT_SHARED(mnt);
} else if (!(flag & CL_PRIVATE)) {
if ((flag & CL_MAKE_SHARED) || IS_MNT_SHARED(old))
list_add(&mnt->mnt_share, &old->mnt_share);
if (IS_MNT_SLAVE(old))
list_add(&mnt->mnt_slave, &old->mnt_slave);
mnt->mnt_master = old->mnt_master;
}
if (flag & CL_MAKE_SHARED)
set_mnt_shared(mnt);
if (flag & CL_EXPIRE) {
if (!list_empty(&old->mnt_expire))
list_add(&mnt->mnt_expire, &old->mnt_expire);
}
}
return mnt;
out_free:
free_vfsmnt(mnt);
return NULL;
}
do_new_mount()でnameデバイスのmntをpathにmountします。
static int do_new_mount(struct path *path, char *type, int flags,
int mnt_flags, char *name, void *data)
{
struct vfsmount *mnt;
int err;
if (!type)
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
mnt = do_kern_mount(type, flags, name, data);
if (IS_ERR(mnt))
return PTR_ERR(mnt);
err = do_add_mount(real_mount(mnt), path, mnt_flags);
if (err)
mntput(mnt);
return err;
}
static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
{
int err;
mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL);
err = lock_mount(path);
if (err)
return err;
err = -EINVAL;
if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(real_mount(path->mnt)))
goto unlock;
err = -EBUSY;
if (path->mnt->mnt_sb == newmnt->mnt.mnt_sb &&
path->mnt->mnt_root == path->dentry)
goto unlock;
err = -EINVAL;
if (S_ISLNK(newmnt->mnt.mnt_root->d_inode->i_mode))
goto unlock;
newmnt->mnt.mnt_flags = mnt_flags;
err = graft_tree(newmnt, path);
unlock:
unlock_mount(path);
return err;
}
static int graft_tree(struct mount *mnt, struct path *path)
{
if (mnt->mnt.mnt_sb->s_flags & MS_NOUSER)
return -EINVAL;
if (S_ISDIR(path->dentry->d_inode->i_mode) !=
S_ISDIR(mnt->mnt.mnt_root->d_inode->i_mode))
return -ENOTDIR;
if (d_unlinked(path->dentry))
return -ENOENT;
return attach_recursive_mnt(mnt, path, NULL);
}
static int attach_recursive_mnt(struct mount *source_mnt,
struct path *path, struct path *parent_path)
{
LIST_HEAD(tree_list);
struct mount *dest_mnt = real_mount(path->mnt);
struct dentry *dest_dentry = path->dentry;
struct mount *child, *p;
int err;
if (IS_MNT_SHARED(dest_mnt)) {
err = invent_group_ids(source_mnt, true);
if (err)
goto out;
}
err = propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list);
if (err)
goto out_cleanup_ids;
br_write_lock(vfsmount_lock);
if (IS_MNT_SHARED(dest_mnt)) {
for (p = source_mnt; p; p = next_mnt(p, source_mnt))
set_mnt_shared(p);
}
if (parent_path) {
detach_mnt(source_mnt, parent_path);
attach_mnt(source_mnt, path);
touch_mnt_namespace(source_mnt->mnt_ns);
} else {
mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);
commit_tree(source_mnt);
}
list_for_each_entry_safe(child, p, &tree_list, mnt_hash) {
list_del_init(&child->mnt_hash);
commit_tree(child);
}
br_write_unlock(vfsmount_lock);
return 0;
out_cleanup_ids:
if (IS_MNT_SHARED(dest_mnt))
cleanup_group_ids(source_mnt, NULL);
out:
return err;
}
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)
{
if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
return first_slave(m);
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);
}







