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