openフラグのO_PATH


opeat/fchdir関数は、引数のファイルIDのディレクトリをカレントディレクトリとして、以降のパスを検索する。

openシステムコールの処理は、ファイルのdentryを検索し、配下のinodeのコールバック関数等の種々の情報を、このファイルID下のFILE構造体に設定するものだ。

しかし、opeat/fchdir関数の引数とするファイルIDは、コールバック関数等の処理は必要ない。そこで導入されたのが、O_PATHフラグである。

実装

openシステムコールがコールされると、パスのdentryを取得して、__dentry_open()がコールされる。ここでFILE構造体が設定される。

まずf->f_modeに、デフォルトとなるモードを設定するが、f->f_flags & O_PATHなら、f->f_mode = FMODE_PATHとなる。そしてFILE構造体のf_mapping/f_path.dentry/f_path.mnt/f_posを設定し、スーパブロックオブジェクトに、このFILEディスクリプタを登録する。そして、FILEコールバック関数として、empty_fops[]を設定することで終了している。opeat/fchdir関数で必要となる情報は、f_path.dentryだけよい。

ちなみに、通常の流れは、fops_get()でinodeのコールバック関数をFILEコールバック関数に設定し、security_dentry_open()で、selinuxでのdentryオープンチェック。OKならbreak_lease()でファイルがロックされていないかのチェックをする。場合によっては、ここでスリープする事もありえる。そしてopenコールバック関数をコールする。i_readcount_inc()はIMAというセキュリティにかかるチェック、file_ra_state_init()は先読みの処理で、そしてFILEディスクリプタの取得となる。

したがってO_PATHオプションは、セキュリティ/ファイルのロックに関係なく、ファイルIDが取得できるように導入されてものと思われる。コールバック関数は、empty_fopsのため、仮にこのファイルIDでread/writeされても問題ない。
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
                                       struct file *f,
                                       int (*open)(struct inode *, struct file *),
                                       const struct cred *cred)
{
       static const struct file_operations empty_fops = {};
       struct inode *inode;
       int error;

       f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK |
                               FMODE_PREAD | FMODE_PWRITE;

       if (unlikely(f->f_flags & O_PATH))
               f->f_mode = FMODE_PATH;

       inode = dentry->d_inode;
       if (f->f_mode & FMODE_WRITE) {
               error = __get_file_write_access(inode, mnt);
               if (error)
                       goto cleanup_file;
               if (!special_file(inode->i_mode))
                       file_take_write(f);
       }

       f->f_mapping = inode->i_mapping;
       f->f_path.dentry = dentry;
       f->f_path.mnt = mnt;
       f->f_pos = 0;
       file_sb_list_add(f, inode->i_sb);

       if (unlikely(f->f_mode & FMODE_PATH)) {
               f->f_op = &empty_fops;
               return f;
       }

       f->f_op = fops_get(inode->i_fop);

       error = security_dentry_open(f, cred);
       if (error)
               goto cleanup_all;

       error = break_lease(inode, f->f_flags);
       if (error)
               goto cleanup_all;

       if (!open && f->f_op)
               open = f->f_op->open;
       if (open) {
               error = open(inode, f);
               if (error)
                       goto cleanup_all;
       }
       if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
               i_readcount_inc(inode);

       f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);

       file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);

       if (f->f_flags & O_DIRECT) {
               if (!f->f_mapping->a_ops ||
                   ((!f->f_mapping->a_ops->direct_IO) &&
                   (!f->f_mapping->a_ops->get_xip_mem))) {
                       fput(f);
                       f = ERR_PTR(-EINVAL);
               }
       }

       return f;
    :
    :
}

補足

O_PATHのファイルIDのfcntlシステムコールは、F_DUPFD/F_DUPFD_CLOEXEC/F_GETFD/ F_SETFD/ F_GETFLのみ有効となる。
static int check_fcntl_cmd(unsigned cmd)
{
       switch (cmd) {
       case F_DUPFD:
       case F_DUPFD_CLOEXEC:
       case F_GETFD:
       case F_SETFD:
       case F_GETFL:
               return 1;
       }
       return 0;
}

SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
{       
       struct file *filp;
       long err = -EBADF;

       filp = fget_raw(fd);
       if (!filp)
               goto out;

       if (unlikely(filp->f_mode & FMODE_PATH)) {
               if (!check_fcntl_cmd(cmd)) {
                       fput(filp);
                       goto out;
               }
       }

       err = security_file_fcntl(filp, cmd, arg);
       if (err) {
               fput(filp);
               return err;
       }

       err = do_fcntl(fd, cmd, arg, filp);

       fput(filp);
out:
       return err;
}
また、カーネル内でファイルIDからFILEオブジェクトを取得する場合、通常fget()がコールされる。これはO_PATH(file->f_mode & FMODE_PATH)でオープンされたFILEオブジェクトは取得することはできないようになっている。
struct file *fget(unsigned int fd)
{
       struct file *file;
       struct files_struct *files = current->files;

       rcu_read_lock();
       file = fcheck_files(files, fd);
       if (file) {
               if (file->f_mode & FMODE_PATH ||
                   !atomic_long_inc_not_zero(&file->f_count))
                       file = NULL;
       }
       rcu_read_unlock();

       return file;
}
ただし、fcntlシステムコールのように、O_PATHのFILEオブジェクトを取得する必要がある場合、fget_raw()がコールされる。
struct file *fget_raw(unsigned int fd)
{
       struct file *file;
       struct files_struct *files = current->files;

       rcu_read_lock();
       file = fcheck_files(files, fd);
       if (file) {
               if (!atomic_long_inc_not_zero(&file->f_count))
                       file = NULL;
       }
       rcu_read_unlock();

       return file;
}

追記

FILE構造体にはdentryが設定されており、従ってそこからinodeへ、そしてread/writeのコールバック関数を参照することが可能であって、fileオペレーションとして、わざわざそれを再設定し、そのコールバック関数を呼び出すことでread/writeすることもないのでは。と漠然と思っていました。

しかし、inodeのコールバック関数をFILE構造体にあえて設定することで、おなじファイルをオープンしても、その処理を異なるものにできる。と言う設計をしていたんだな。と思うと、Linux開発者の方々には頭の下がる思いです。



最終更新 2012/10/15 17:44:07 - north
(2012/10/10 18:00:56 作成)


検索

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