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後に、元のファイルシステムを参照できません。)
カレントパスはpivot前の同じですが、rootパスは/ramdiskに更新され、/old-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(¤t->fs->lock); user_nd.mnt = mntget(current->fs->rootmnt); user_nd.dentry = dget(current->fs->root); read_unlock(¤t->fs->lock); down_write(¤t->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(¤t->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