readlinkシステムコール


シンボリックリンク名の取得は、inodeコールバックの.readlinkから.follow_linkがコールされ、シンボリックでないファイルはこのコールバックを有しない故エラーとなります。

user_path_at_empty()は、pathnameを走査しそのファイルをpathに設定します。pathname=""なら返値は-ENOENTですが、LOOKUP_EMPTYだとエラーにならず、empty=1としdfdのファイルをpathに設定します。

.readlinkコールバックの返値、readlinkシステムコールの返値となりますが、シンボリックでないファイルは、.readlinkの定義されておらず、readlinkシステムコールの返値として、empty ? -ENOENT : -EINVALとします。
SYSCALL_DEFINE3(readlink, const char __user *, path, char __user *, buf,
               int, bufsiz)
{
       return sys_readlinkat(AT_FDCWD, path, buf, bufsiz);
}

SYSCALL_DEFINE4(readlinkat, int, dfd, const char __user *, pathname,
               char __user *, buf, int, bufsiz)
{
       struct path path;
       int error;
       int empty = 0;

       if (bufsiz <= 0)
               return -EINVAL;

       error = user_path_at_empty(dfd, pathname, LOOKUP_EMPTY, &path, &empty);
       if (!error) {
               struct inode *inode = path.dentry->d_inode;

               error = empty ? -ENOENT : -EINVAL;
               if (inode->i_op->readlink) {
                       error = security_inode_readlink(path.dentry);
                       if (!error) {
                               touch_atime(path.mnt, path.dentry);
                               error = inode->i_op->readlink(path.dentry,
                                                             buf, bufsiz);
                       }
               }
               path_put(&path);
       }
       return error;
}
ext2ファイルシステムで、データブロックにシンボリック名称が設定されているならext2_symlink_inode_operations、inodeブロックに設定されているならext2_fast_symlink_inode_operationsがinodeコールバックとなります。
const struct inode_operations ext2_symlink_inode_operations = {
       .readlink       = generic_readlink,
       .follow_link    = page_follow_link_light,
       .put_link       = page_put_link,
       .setattr        = ext2_setattr,
#ifdef CONFIG_EXT2_FS_XATTR
       .setxattr       = generic_setxattr,
       .getxattr       = generic_getxattr,
       .listxattr      = ext2_listxattr,
       .removexattr    = generic_removexattr,
#endif
};

const struct inode_operations ext2_fast_symlink_inode_operations = {
       .readlink       = generic_readlink,
       .follow_link    = ext2_follow_link,
       .setattr        = ext2_setattr,
#ifdef CONFIG_EXT2_FS_XATTR
       .setxattr       = generic_setxattr,
       .getxattr       = generic_getxattr,
       .listxattr      = ext2_listxattr,
       .removexattr    = generic_removexattr,
#endif
};
.follow_linkコールバックで、シンボリック名称がnd->saved_names[nd->depth]に設定され、vfs_readlink()でchar __user *bufferのユーザ引数に複写します。

ext2_follow_link()の返値はNULL,page_follow_link_light()の返値はデータブロックをreadしたバッファのpageで、i_op->put_link()はデータブロックを読み込んだpageキャッシュとしてのpageを解放します。
int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
       struct nameidata nd;
       void *cookie;
       int res;

       nd.depth = 0;
       cookie = dentry->d_inode->i_op->follow_link(dentry, &nd);
       if (IS_ERR(cookie))
               return PTR_ERR(cookie);

       res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
       if (dentry->d_inode->i_op->put_link)
               dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
       return res;
}

static inline char *nd_get_link(struct nameidata *nd)
{
       return nd->saved_names[nd->depth];
}

int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link)
{
       int len;

       len = PTR_ERR(link);
       if (IS_ERR(link))
               goto out;

       len = strlen(link);
       if (len > (unsigned) buflen)
               len = buflen;
       if (copy_to_user(buffer, link, len))
               len = -EFAULT;
out:
       return len;
}
データブロックにシンボリックリンク名称が設定されている.follow_link
void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd)
{
       struct page *page = NULL;
       nd_set_link(nd, page_getlink(dentry, &page));
       return page;
}

static inline void nd_set_link(struct nameidata *nd, char *path)
{
       nd->saved_names[nd->depth] = path;
}

static char *page_getlink(struct dentry * dentry, struct page **ppage)
{
       char *kaddr;
       struct page *page;
       struct address_space *mapping = dentry->d_inode->i_mapping;
       page = read_mapping_page(mapping, 0, NULL);
       if (IS_ERR(page))
               return (char*)page;
       *ppage = page;
       kaddr = kmap(page);
       nd_terminate_link(kaddr, dentry->d_inode->i_size, PAGE_SIZE - 1);
       return kaddr;
}

static inline void nd_terminate_link(void *name, size_t len, size_t maxlen)
{
       ((char *) name)[min(len, maxlen)] = '\0';
}
inodeブロックにシンボリックリンク名称が設定されている.follow_link
static inline struct ext3_inode_info *EXT3_I(struct inode *inode)
{
       return container_of(inode, struct ext3_inode_info, vfs_inode);
}

static void *ext2_follow_link(struct dentry *dentry, struct nameidata *nd)
{
       struct ext2_inode_info *ei = EXT2_I(dentry->d_inode);
       nd_set_link(nd, (char *)ei->i_data);
       return NULL;
}

static inline void nd_set_link(struct nameidata *nd, char *path)
{
       nd->saved_names[nd->depth] = path;
}

補足

  • fオプションのreadlinkコマンドは、readlinkシステムコールでなくgetcwdシステムコールで実装されます。
[root@localhost north]# touch babakaka

[root@localhost north]# strace readlink babakaka
 :
readlink("babakaka", 0x9f9c890, 64)     = -1 EINVAL (Invalid argument)
close(1)                                = 0
close(2)                                = 0
exit_group(1)                           = ?

[root@localhost north]# strace readlink -f babakaka
 :
getcwd("/home/north", 4096)          = 11
lstat64("/home/north/babakaka", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7423000
write(1, "/home/north/babakaka\n", 21/home/north/babakaka) = 21
close(1)                                = 0
munmap(0xb7423000, 4096)                = 0
close(2)                                = 0
exit_group(0)                           = ?


最終更新 2016/02/08 12:00:55 - north
(2016/02/08 12:00:55 作成)


検索

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