openatシステムコール
openシステムコールの相対パス検索は、ワーキングディレクトリから行いますが、openatシステムコールは、指定するファイルディスクリプタ(通常ファイルはダメです。)のディレクトリから検索します。その事以外はopenシステムコールと同じです。
また、マルチスレッドアプリケーションにおいて、ある処理を行うスレッド群ごとに、パス検索開始位置を指定できるという使い方もあるわけです。
open/openatシステムコールも、do_sys_open()をコールする事で実現していて、第一引数がAT_FDCWD/ディスクリプタかの違いだけです。
if (dfd == AT_FDCWD)は、openシステムコールのケースです。この場合プロセスのワーキングディレクトリが設定されています。
そして以降がopnatシステムコールのケースです。fget_light()で引数のディスクリプタに対応するfile構造体を取得し、 if (!S_ISDIR(dentry->d_inode->i_mode))で、それがディレクトリかどうかチェックします。ディレクトリならパーミッションをチェックした後、そのパスをnd->pathに設定しています。
#include <stdio.h>
#include <fcntl.h>
void main()
{
int fd1, fd2, len;
char buff[64];
fd1 = open("/proc/self", O_RDONLY);
fd2 = openat(fd1, "comm", O_RDONLY);
len = read(fd2, buff, sizeof(buff));
buff[len - 1] = 0;
printf("%s\n", buff);
close(fd1);
close(fd2);
}
[root@localhost kitamura]#./a.out
a.out
[root@localhost kitamura]#
検索開始ディレクトリを指定する事で、無駄な検索をスキップしようとするものです。パスはdentry(inode)として、キャッシュに蓄えられますが、場合にようってメモリー回収によりそのキャッシュが破棄される事もあります。そのような場合再度dentryの取得を行います。これはinodeの取得を意味し、物理デバイスへのアクセスをも意味します。また、マルチスレッドアプリケーションにおいて、ある処理を行うスレッド群ごとに、パス検索開始位置を指定できるという使い方もあるわけです。
open/openatシステムコールも、do_sys_open()をコールする事で実現していて、第一引数がAT_FDCWD/ディスクリプタかの違いだけです。
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}
SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,
int, mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(dfd, filename, flags, mode);
asmlinkage_protect(4, ret, dfd, filename, flags, mode);
return ret;
}
do_filp_open()で実際の処理が行われます。本内容にかかる処理は、ここでコールされるpath_init()です。path_init()でパスの検索の諸設定を行います。その設定はstruct nameidata ndに施され、以降の処理でこのndを参照して実際のパスの検索を行います。
struct file *do_filp_open(int dfd, const char *pathname,
int open_flag, int mode, int acc_mode)
{
struct nameidata nd;
:
reval:
error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);
:
}
if (*name=='/')はパスが絶対パスの場合です。set_root()でカレントプロセスのルートを取得し、nd->pathに設定します。if (dfd == AT_FDCWD)は、openシステムコールのケースです。この場合プロセスのワーキングディレクトリが設定されています。
そして以降がopnatシステムコールのケースです。fget_light()で引数のディスクリプタに対応するfile構造体を取得し、 if (!S_ISDIR(dentry->d_inode->i_mode))で、それがディレクトリかどうかチェックします。ディレクトリならパーミッションをチェックした後、そのパスをnd->pathに設定しています。
#define AT_FDCWD -100
static int path_init(int dfd, const char *name, unsigned int flags, struct nameidata *nd)
{
int retval = 0;
int fput_needed;
struct file *file;
nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags;
nd->depth = 0;
nd->root.mnt = NULL;
if (*name=='/') {
set_root(nd);
nd->path = nd->root;
path_get(&nd->root);
} else if (dfd == AT_FDCWD) {
struct fs_struct *fs = current->fs;
read_lock(&fs->lock);
nd->path = fs->pwd;
path_get(&fs->pwd);
read_unlock(&fs->lock);
} else {
struct dentry *dentry;
file = fget_light(dfd, &fput_needed);
retval = -EBADF;
if (!file)
goto out_fail;
dentry = file->f_path.dentry;
retval = -ENOTDIR;
if (!S_ISDIR(dentry->d_inode->i_mode))
goto fput_fail;
retval = file_permission(file, MAY_EXEC);
if (retval)
goto fput_fail;
nd->path = file->f_path;
path_get(&file->f_path);
fput_light(file, fput_needed);
}
return 0;
fput_fail:
fput_light(file, fput_needed);
out_fail:
return retval;
}






