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を設定します。
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(&current->fs->lock);
       oldmnt = current->fs->altrootmnt;
       olddentry = current->fs->altroot;
       current->fs->altrootmnt = mnt;
       current->fs->altroot = dentry;
       write_unlock(&current->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(&current->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(&current->fs->lock);
                       if (__emul_lookup_dentry(name,nd))
                               return 0;
                       read_lock(&current->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(&current->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(&current->fs->lock);
               nd_root.mnt = mntget(current->fs->rootmnt);
               nd_root.dentry = dget(current->fs->root);
               read_unlock(&current->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

最終更新 2013/06/07 19:13:18 - north
(2013/06/07 19:05:01 作成)


検索

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