BINDオプションでのmount
Rev.9を表示中。最新版はこちら。
mount --bindは、シンボリック/ハードリンクと同じで、リンク元をリンク先から参照するものです。シンボリック/ハードリンクはリンク先dentry/inodeに実装され、mount --bindは、bind元ディレクトリをmnt.mnt_rootとするバーチャルなデバイスファイルとし、そのファイルシステムをbind先ディレクトリにmountする事で実装します。dev_nameが接続元ディレクトリ、dir_nameが接続先ディレクトリです。
SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
char __user *, type, unsigned long, flags, void __user *, data)
{
int ret;
char *kernel_type;
char *kernel_dir;
char *kernel_dev;
unsigned long data_page;
ret = copy_mount_string(type, &kernel_type);
if (ret < 0)
goto out_type;
kernel_dir = getname(dir_name);
if (IS_ERR(kernel_dir)) {
ret = PTR_ERR(kernel_dir);
goto out_dir;
}
ret = copy_mount_string(dev_name, &kernel_dev);
if (ret < 0)
goto out_dev;
ret = copy_mount_options(data, &data_page);
if (ret < 0)
goto out_data;
ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags,
(void *) data_page);
free_page(data_page);
out_data:
kfree(kernel_dev);
out_dev:
putname(kernel_dir);
out_dir:
kfree(kernel_type);
out_type:
return ret;
}
mountコマンドの--bindは、flagsがMS_BINDで、--rbindはMS_BIND | MS_RECで、--bindはdo_loopback(&path, dev_name, FALSE);--rbindはdo_loopback(&path, dev_name, TRUE)コールされます。
long do_mount(char *dev_name, char *dir_name, char *type_page,
unsigned long flags, void *data_page)
{
struct path path;
int retval = 0;
int mnt_flags = 0;
if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
flags &= ~MS_MGC_MSK;
if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
return -EINVAL;
if (data_page)
((char *)data_page)[PAGE_SIZE - 1] = 0;
retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
if (retval)
return retval;
retval = security_sb_mount(dev_name, &path,
type_page, flags, data_page);
if (retval)
goto dput_out;
if (!(flags & MS_NOATIME))
mnt_flags |= MNT_RELATIME;
if (flags & MS_NOSUID)
mnt_flags |= MNT_NOSUID;
if (flags & MS_NODEV)
mnt_flags |= MNT_NODEV;
if (flags & MS_NOEXEC)
mnt_flags |= MNT_NOEXEC;
if (flags & MS_NOATIME)
mnt_flags |= MNT_NOATIME;
if (flags & MS_NODIRATIME)
mnt_flags |= MNT_NODIRATIME;
if (flags & MS_STRICTATIME)
mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
if (flags & MS_RDONLY)
mnt_flags |= MNT_READONLY;
flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN |
MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
MS_STRICTATIME);
if (flags & MS_REMOUNT)
retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
data_page);
else if (flags & MS_BIND)
retval = do_loopback(&path, dev_name, flags & MS_REC);
else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
retval = do_change_type(&path, flags);
else if (flags & MS_MOVE)
retval = do_move_mount(&path, dev_name);
else
retval = do_new_mount(&path, type_page, flags, mnt_flags,
dev_name, data_page);
dput_out:
path_put(&path);
return retval;
}
clone_mnt()はold_name(bind元)をmnt_rootとするstruct mount *mntを作成し、graft_tree()でそのmntをbind先pathにmountします。copy_tree()はrbindで配下にmountされてるファイルシステムもbindされます。
static int do_loopback(struct path *path, char *old_name,
int recurse)
{
LIST_HEAD(umount_list);
struct path old_path;
struct mount *mnt = NULL, *old;
int err = mount_is_safe(path);
if (err)
return err;
if (!old_name || !*old_name)
return -EINVAL;
err = kern_path(old_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
if (err)
return err;
err = lock_mount(path);
if (err)
goto out;
old = real_mount(old_path.mnt);
err = -EINVAL;
if (IS_MNT_UNBINDABLE(old))
goto out2;
if (!check_mnt(real_mount(path->mnt)) || !check_mnt(old))
goto out2;
err = -ENOMEM;
if (recurse)
mnt = copy_tree(old, old_path.dentry, 0);
else
mnt = clone_mnt(old, old_path.dentry, 0);
if (!mnt)
goto out2;
err = graft_tree(mnt, path);
if (err) {
br_write_lock(vfsmount_lock);
umount_tree(mnt, 0, &umount_list);
br_write_unlock(vfsmount_lock);
}
out2:
unlock_mount(path);
release_mounts(&umount_list);
out:
path_put(&old_path);
return err;
}
old[bind先]を雛形とするmntにmnt->mnt.mnt_root = dget(root)[bind元]のバーチャルファイルシステムを作成します。通常デバイスだとファイルシステム依存のデバイスのroot inodeからdentryを作成されmnt->mnt.mnt_rootに設定されます。
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; /* not a peer of original */
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;
}
clone_mnt()で作成された接続元バーチャルファイルシステムのchild_mnt->mnt_mountpointにbind先ディレクトリdget(dentry)を設定して、bind元バーチャルファイルシステムをbind先ディレクトリにmountします。
void mnt_set_mountpoint(struct mount *mnt, struct dentry *dentry,
struct mount *child_mnt)
{
mnt_add_count(mnt, 1); /* essentially, that's mntget */
child_mnt->mnt_mountpoint = dget(dentry);
child_mnt->mnt_parent = mnt;
spin_lock(&dentry->d_lock);
dentry->d_flags |= DCACHE_MOUNTED;
spin_unlock(&dentry->d_lock);
}
static void commit_tree(struct mount *mnt)
{
struct mount *parent = mnt->mnt_parent;
struct mount *m;
LIST_HEAD(head);
struct mnt_namespace *n = parent->mnt_ns;
BUG_ON(parent == mnt);
list_add_tail(&head, &mnt->mnt_list);
list_for_each_entry(m, &head, mnt_list) {
m->mnt_ns = n;
__mnt_make_longterm(m);
}
list_splice(&head, n->list.prev);
list_add_tail(&mnt->mnt_hash, mount_hashtable +
hash(&parent->mnt, mnt->mnt_mountpoint));
list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
touch_mnt_namespace(n);
}
サンプル
mountポイントの/mnt/loop2のファイルシステムにnoexec等のmountオプションを設定しての運用。
[root@localhost north]# ls /mnt/loop1
a.c a.out
[root@localhost north]# cat /mnt/loop1/a.c
#include <stdio.h>
void main()
{
printf("exec ok\n");
}
[root@localhost north]# mount --bind /mnt/loop1 /mnt/loop2 [root@localhost north]# ls /mnt/loop2 a.c a.out
[root@localhost north]# /mnt/loop1/a.out exec ok [root@localhost north]# /mnt/loop2/a.out exec ok
[root@localhost north]# mount -o remount,noexec /mnt/loop2 [root@localhost north]# /mnt/loop1/a.out exec ok [root@localhost north]# /mnt/loop2/a.out -bash: /mnt/loop2/a.out: 許可がありません





