altroot
chrootでルートを設定すると、そのプロセス構造体のstruct fs_structのstruct dentry * root/struct vfsmount * rootmntを設定し、検索ファイルのパスが絶対パスの時、このrootから検索する事にあります。altrootはこのrootの代替となります。
IA64のような32/64ビット互換なシステムで、カーネルとしても、実行下において両方をサポートしようとして導入されたらしいですが・・・。漠然ですが、要は64ビットシステムで、32ビットプロセスを動作させる場合、32ビットプロセス用のルートファイルシステムをaltrootに用意して、32ビットプロセスが動作する場合、これをroot割り当てて動作させる事のようですが。定かでありません。
sys_chroot()は、 まず__user_walk()でrootとするパスのstruct nameidataを取得します。この時引数のフラグにLOOKUP_NOALTを指定して検索は真のrootからとなります。なおaltrootのパスは、コンパイル時固定で、chroot等で後で設定することはできません。
パーミッションのチェックをした後、set_fs_root()で真のrootを、set_fs_altroot()でaltrootを設定します。
まずpath_walk()で検索します。もし、検索先のd_inodeが削除された場合、又はそれがディレクトリの時、真のrootから検索しなおします。ここで取得できたら、真のrootのそれを、できなければaltrootのそれを結果としています。
ディレクトリにかかる処理(cdでパスを変えたりとか・・・)は、可能なら真のrootを優先させる。と言うことのようですが、何故の理由でなのか分かりません。
IA64のような32/64ビット互換なシステムで、カーネルとしても、実行下において両方をサポートしようとして導入されたらしいですが・・・。漠然ですが、要は64ビットシステムで、32ビットプロセスを動作させる場合、32ビットプロセス用のルートファイルシステムをaltrootに用意して、32ビットプロセスが動作する場合、これをroot割り当てて動作させる事のようですが。定かでありません。
sys_chroot()は、 まず__user_walk()でrootとするパスのstruct nameidataを取得します。この時引数のフラグにLOOKUP_NOALTを指定して検索は真のrootからとなります。なおaltrootのパスは、コンパイル時固定で、chroot等で後で設定することはできません。
パーミッションのチェックをした後、set_fs_root()で真のrootを、set_fs_altroot()でaltrootを設定します。
asmlinkage long sys_chroot(const char __user * filename)
{
struct nameidata nd;
int error;
error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
if (error)
goto out;
error = permission(nd.dentry->d_inode,MAY_EXEC,&nd);
if (error)
goto dput_and_out;
error = -EPERM;
if (!capable(CAP_SYS_CHROOT))
goto dput_and_out;
set_fs_root(current->fs, nd.mnt, nd.dentry);
set_fs_altroot();
error = 0;
dput_and_out:
path_release(&nd);
out:
return error;
}
set_fs_root()は、current->fs->root/current->fs->rootmntを更新し、更新前の内容を解放します。mntget()/dget()は、メンバーの参照カウンタをインクリメントして、かかるメンバを返値としているだけです。
void set_fs_root(struct fs_struct *fs, struct vfsmount *mnt,
struct dentry *dentry)
{
struct dentry *old_root;
struct vfsmount *old_rootmnt;
write_lock(&fs->lock);
old_root = fs->root;
old_rootmnt = fs->rootmnt;
fs->rootmnt = mntget(mnt);
fs->root = dget(dentry);
write_unlock(&fs->lock);
if (old_root) {
dput(old_root);
mntput(old_rootmnt);
}
}
set_fs_altroot()でcurrent->fs->altroot/current->fs->altrootmntを更新します。まず、真のrootから__emul_prefix()で取得したパスを検索します。__emul_prefix()はアーキテクチャおよびコンパイルオプションに依存します。IA64なら/emul/ia32-linux/となります。このパスをaltrootmnt/altrootに設定しています。
void set_fs_altroot(void)
{
char *emul = __emul_prefix();
struct nameidata nd;
struct vfsmount *mnt = NULL, *oldmnt;
struct dentry *dentry = NULL, *olddentry;
int err;
if (!emul)
goto set_it;
err = path_lookup(emul, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd);
if (!err) {
mnt = nd.mnt;
dentry = nd.dentry;
}
set_it:
write_lock(¤t->fs->lock);
oldmnt = current->fs->altrootmnt;
olddentry = current->fs->altroot;
current->fs->altrootmnt = mnt;
current->fs->altroot = dentry;
write_unlock(¤t->fs->lock);
if (olddentry) {
dput(olddentry);
mntput(oldmnt);
}
}
実際のaltrootの機能は、path_lookup()内に実装されています。システムコール通しての通常のファイル操作では、引数のflagsにはLOOKUP_NOALTは設定されずにコールされます。従って、絶対パスの場合、current->fs->altrootが設定されているなら、__emul_lookup_dentry()をコールすしてのaltrootからの検索となります。__emul_lookup_dentry()で取得できなかったら、真のrootから取得します。
int path_lookup(const char *name, unsigned int flags, struct nameidata *nd)
{
nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags;
read_lock(¤t->fs->lock);
if (*name=='/') {
if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
nd->mnt = mntget(current->fs->altrootmnt);
nd->dentry = dget(current->fs->altroot);
read_unlock(¤t->fs->lock);
if (__emul_lookup_dentry(name,nd))
return 0;
read_lock(¤t->fs->lock);
}
nd->mnt = mntget(current->fs->rootmnt);
nd->dentry = dget(current->fs->root);
}
else{
nd->mnt = mntget(current->fs->pwdmnt);
nd->dentry = dget(current->fs->pwd);
}
read_unlock(¤t->fs->lock);
current->total_link_count = 0;
return link_path_walk(name, nd);
}
__emul_lookup_dentry()はaltrootからの検索に特化した実装です。struct nameidata *ndはaltrootのが設定されています。まずpath_walk()で検索します。もし、検索先のd_inodeが削除された場合、又はそれがディレクトリの時、真のrootから検索しなおします。ここで取得できたら、真のrootのそれを、できなければaltrootのそれを結果としています。
ディレクトリにかかる処理(cdでパスを変えたりとか・・・)は、可能なら真のrootを優先させる。と言うことのようですが、何故の理由でなのか分かりません。
static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
{
if (path_walk(name, nd))
return 0; /* something went wrong... */
if (!nd->dentry->d_inode || S_ISDIR(nd->dentry->d_inode->i_mode)) {
struct nameidata nd_root;
nd_root.last_type = LAST_ROOT;
nd_root.flags = nd->flags;
memcpy(&nd_root.intent, &nd->intent, sizeof(nd_root.intent));
read_lock(¤t->fs->lock);
nd_root.mnt = mntget(current->fs->rootmnt);
nd_root.dentry = dget(current->fs->root);
read_unlock(¤t->fs->lock);
if (path_walk(name, &nd_root))
return 1;
if (nd_root.dentry->d_inode) {
path_release(nd);
nd->dentry = nd_root.dentry;
nd->mnt = nd_root.mnt;
nd->last = nd_root.last;
return 1;
}
path_release(&nd_root);
}
return 1;
}
altrootのディレクトリです。i386ではNULLです。いつか#define __emul_prefix() "/emul/i386-linux/"として、実際の動作を検証してみたいと思っています。#ifndef __I386_NAMEI_H #define __I386_NAMEI_H #define __emul_prefix() NULL #endif /* __I386_NAMEI_H */
#ifndef _ASM_IA64_NAMEI_H #define _ASM_IA64_NAMEI_H #include <asm/ptrace.h> #include <asm/system.h> #define EMUL_PREFIX_LINUX_IA32 "/emul/ia32-linux/" #endif





