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; }