無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

pivot_root


sys_pivot_rootはCD-ROM起動のシステムを作成するとの説明で、システムルートファイルシステムを差し替える。と理解していましたが、正しくはカレントプロセスのrootのファイルシステムを差し替える。と言うことです。chrootで変更しなければ、プロセスのrootは、システムルートファイルシステム故、先の解釈で実用上は問題なかった。と言うことです。

new_rootが差し替えるファイルシステムで、put_oldが差し替わる元のファイルシステムで、差し替わるファイルシステムは、current->fs->rootmntです。

detach_mnt()でcurrent->fs->rootmntとnew_rootをumountし、attach_mnt()でuser_nd.mntをput_oldに、new_nd.mntをcurrent->fs->rootにmountします。

pivot_rootが動作するには、以下の条件が必要です。
・new_root(差し替え先)は、カレントプロセスのネームスペース配下。
・カレントプロセスのrootは、マウントポイント。
 (マウントポイントでないパスをchrootすると、pivotできません。)
・new_roo/put_oldのファイルシステムは、current->fs->rootmntのファイルシステムでない。
 (同じファイルシステムを差し替える事で回帰となります。)
・new_rootはマウントポイント
・put_oldはnew_root配下で管理されている。
 (pivot後に、元のファイルシステムを参照できません。)
asmlinkage long sys_pivot_root(const char __user *new_root, const char __user *put_old)
{
       struct vfsmount *tmp;
       struct nameidata new_nd, old_nd, parent_nd, root_parent, user_nd;
       int error;

       if (!capable(CAP_SYS_ADMIN))
               return -EPERM;

       lock_kernel();

       error = __user_walk(new_root, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd);
       if (error)
               goto out0;
       error = -EINVAL;
       if (!check_mnt(new_nd.mnt))
               goto out1;

       error = __user_walk(put_old, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &old_nd);
       if (error)
               goto out1;

       error = security_sb_pivotroot(&old_nd, &new_nd);
       if (error) {
               path_release(&old_nd);
               goto out1;
       }

       read_lock(&current->fs->lock);
       user_nd.mnt = mntget(current->fs->rootmnt);
       user_nd.dentry = dget(current->fs->root);
       read_unlock(&current->fs->lock);
       down_write(&current->namespace->sem);
       down(&old_nd.dentry->d_inode->i_sem);
       error = -EINVAL;
       if (!check_mnt(user_nd.mnt))
               goto out2;
       error = -ENOENT;
       if (IS_DEADDIR(new_nd.dentry->d_inode))
               goto out2;
       if (d_unhashed(new_nd.dentry) && !IS_ROOT(new_nd.dentry))
               goto out2;
       if (d_unhashed(old_nd.dentry) && !IS_ROOT(old_nd.dentry))
               goto out2;
       error = -EBUSY;
       if (new_nd.mnt == user_nd.mnt || old_nd.mnt == user_nd.mnt)
               goto out2; /* loop */
       error = -EINVAL;
       if (user_nd.mnt->mnt_root != user_nd.dentry)
               goto out2;
       if (new_nd.mnt->mnt_root != new_nd.dentry)
               goto out2; /* not a mountpoint */
       tmp = old_nd.mnt; /* make sure we can reach put_old from new_root */
       spin_lock(&vfsmount_lock);
       if (tmp != new_nd.mnt) {
               for (;;) {
                       if (tmp->mnt_parent == tmp)
                               goto out3;
                       if (tmp->mnt_parent == new_nd.mnt)
                               break;
                       tmp = tmp->mnt_parent;
               }
               if (!is_subdir(tmp->mnt_mountpoint, new_nd.dentry))
                       goto out3;
       } else if (!is_subdir(old_nd.dentry, new_nd.dentry))
               goto out3;
       detach_mnt(new_nd.mnt, &parent_nd);
       detach_mnt(user_nd.mnt, &root_parent);
       attach_mnt(user_nd.mnt, &old_nd);
       attach_mnt(new_nd.mnt, &root_parent);
       spin_unlock(&vfsmount_lock);
       chroot_fs_refs(&user_nd, &new_nd);
       security_sb_post_pivotroot(&user_nd, &new_nd);
       error = 0;
       path_release(&root_parent);
       path_release(&parent_nd);
out2:
       up(&old_nd.dentry->d_inode->i_sem);
       up_write(&current->namespace->sem);
       path_release(&user_nd);
       path_release(&old_nd);
out1:
       path_release(&new_nd);
out0:
       unlock_kernel();
       return error;
out3:
       spin_unlock(&vfsmount_lock);
       goto out2;
}

static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd)
{
       old_nd->dentry = mnt->mnt_mountpoint;
       old_nd->mnt = mnt->mnt_parent;
       mnt->mnt_parent = mnt;
       mnt->mnt_mountpoint = mnt->mnt_root;
       list_del_init(&mnt->mnt_child);
       list_del_init(&mnt->mnt_hash);
       old_nd->dentry->d_mounted--;
}

static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd)
{
       mnt->mnt_parent = mntget(nd->mnt);
       mnt->mnt_mountpoint = dget(nd->dentry);
       list_add(&mnt->mnt_hash, mount_hashtable+hash(nd->mnt, nd->dentry));
       list_add_tail(&mnt->mnt_child, &nd->mnt->mnt_mounts);
       nd->dentry->d_mounted++;
}
chroot_fs_refs()は、do_each_thread()でinit_taskをヘッドとしてリストされている全てのプロセスのcwd/rootが、pivotするマウント先パス(カレントプロセスのroot)と同じなら、差し替えるファイルシステムのマウント先パスに更新し、他のプロセスにもpivotが自然な流れで反映されます。そうでないなら他のプロセスのcwd/rootはそのままです。pivotはchrootと異なりdentry構造を変更しません。chrootはdentryの検索開始位置を変更しますが、pivotは検索開始位置を変更するのでなく、ファイルシステムそのものを変更するので、変更されたファイルシステムがどこにマウントされようと、そのファイルシステム内で相対パスで検索するには問題ありません。
static void chroot_fs_refs(struct nameidata *old_nd, struct nameidata *new_nd)
{
       struct task_struct *g, *p;
       struct fs_struct *fs;

       read_lock(&tasklist_lock);
       do_each_thread(g, p) {
               task_lock(p);
               fs = p->fs;
               if (fs) {
                       atomic_inc(&fs->count);
                       task_unlock(p);
                       if (fs->root==old_nd->dentry&&fs->rootmnt==old_nd->mnt)
                               set_fs_root(fs, new_nd->mnt, new_nd->dentry);
                       if (fs->pwd==old_nd->dentry&&fs->pwdmnt==old_nd->mnt)
                               set_fs_pwd(fs, new_nd->mnt, new_nd->dentry);
                       put_fs_struct(fs);
               } else
                       task_unlock(p);
       } while_each_thread(g, p);
       read_unlock(&tasklist_lock);
}

サンプル

ramfsとカレントプロセスのrootのファイルシステムを差し替えます。カレントプロセスのrootはシステムルートファイルシステム故、結果的にシステムルートファイルシステムを差し替えることになります。
[root@localhost kitamura]# mount -t ramfs none /ramdisk
[root@localhost kitamura]# mkdir /ramdisk/old-root
[root@localhost kitamura]# touch /ramdisk/this_is_ramdisk
[root@localhost kitamura]# ls /ramdisk/
old-root  this_is_ramdisk
[root@localhost kitamura]# pivot_root /ramdisk/ /ramdisk/old-root/
dllはld-linux.so.2でコマンドのdllがクリンクされるため、/old-root配下のパス構造で、実行するコマンドは、ld-linux.so.2の引数として起動する必要があり、ld-linux.so.2が参照するdllのパスをLD_LIBRARY_PATHに設定します。

カレントパスはpivot前の同じですが、rootパスは/ramdiskに更新され、/old-rootには、元のファイルシステムがマウントされています。
[root@localhost kitamura]# export LD_LIBRARY_PATH=/old-root/lib
[root@localhost kitamura]# /old-root/lib/ld-linux.so.2 /old-root/bin/ls
Desktop    Downloads  Pictures  Templates  lkm    mnt2      test
Documents  Mail       Public    Videos     media  mysqlcpy  umount2.c

[root@localhost kitamura]# /old-root/lib/ld-linux.so.2 /old-root/bin/ls /
old-root  this_is_ramdisk

[root@localhost kitamura]# /old-root/lib/ld-linux.so.2 /old-root/bin/ls /old-root/
apr-tmp.3BYsWv  apr-tmp.FIt3Qt  apr-tmp.oqMsLF  dev     lost+found  mnt2      opt      run   sysroot
apr-tmp.5Y7Kqs  apr-tmp.ThqTwH  apr-tmp.q7VcB2  etc     media       mnt3      proc     sbin  tmp
apr-tmp.5ykbkB  apr-tmp.ZvIc0t  bin             export  misc        mnt4      pstore   sd    usr
apr-tmp.8wM2lK  apr-tmp.k94eiT  boot            home    mnt         net       ramdisk  srv   va
apr-tmp.9UD0AA  apr-tmp.oISac7  debugfs         lib     mnt1        new-root  root     sys   var
元の復帰します。
[root@localhost kitamura]# /old-root/lib/ld-linux.so.2 /old-root/sbin/pivot_root /old-root /old-root/ramdisk
[root@localhost kitamura]# ls /
apr-tmp.3BYsWv  apr-tmp.FIt3Qt  apr-tmp.oqMsLF  dev     lost+found  mnt2      opt      run   sysroot
apr-tmp.5Y7Kqs  apr-tmp.ThqTwH  apr-tmp.q7VcB2  etc     media       mnt3      proc     sbin  tmp
apr-tmp.5ykbkB  apr-tmp.ZvIc0t  bin             export  misc        mnt4      pstore   sd    usr
apr-tmp.8wM2lK  apr-tmp.k94eiT  boot            home    mnt         net       ramdisk  srv   va
apr-tmp.9UD0AA  apr-tmp.oISac7  debugfs         lib     mnt1        new-root  root     sys   var


最終更新 2014/08/16 23:01:55 - north
(2012/01/20 18:33:49 作成)