BINDオプションでのmount


mount --bindは、シンボリック/ハードリンクと同じで、リンク元をリンク先から参照するものです。シンボリック/ハードリンクはリンク先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されます。
(本関数は他から使用されておらず、第二引数のold_nameは、dev_nameとする方が、実装的に適合しているかと思います。)
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: 許可がありません

備考

chrootでルートを変更すると、シンボリックリンクではリンクパスでの走査となりchrootのルート配下でないと参照できませんが、bind mountだとchrootのルート配下のパスにmountのmnt_root配下として捜査し、、ハードリンクと同じでchrootのパスの走査が不要で、chrootによる制約がありません。

/ets/fstabでのbindオプションで、そのbind元ディレクトリがmount済みである必要があり、デバイスのあるディレクトリ下の構成のみをbind mountして使用する。と言った事はできません。

補足

ここでのbind元は/mnt/loop1、bind先は/mnt/loop2との意味合いです。


最終更新 2016/06/10 17:10:57 - north
(2010/03/05 18:30:23 作成)


検索

アクセス数
3689425
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。