fast_symlink
Rev.6を表示中。最新版はこちら。
シンボリックリンク作成は、ext3ファイルシステム時、dirオペレーションコールバック関数ext3_symlink()がコールされます。ext3_new_inode()でシンボリックのinodeを取得し、symname(シンボリック名)が、sizeof (EXT3_I(inode)->i_data)より長ければ、ext3_symlink_inode_operations、短ければext3_fast_symlink_inode_operationsがinodeオペレーションコールバックとして設定されます。
sizeof (EXT3_I(inode)->i_data)は、indoe->vfs_inodeに設定してある、ファイルシステム依存のinodeが設定されています。そのinode->i_data[]は、ファイルの中身のデータブロックのインデックスを設定する領域です。従って実データとなるリンク先名が15×4=60バイト以下だとi_data[]に、そうでないと、通常ファイルと同じようにデータブロックに設定されます。
struct inode_operations ext3_dir_inode_operations = {
.create = ext3_create,
.lookup = ext3_lookup,
.link = ext3_link,
.unlink = ext3_unlink,
.symlink = ext3_symlink,
.mkdir = ext3_mkdir,
.rmdir = ext3_rmdir,
.mknod = ext3_mknod,
.rename = ext3_rename,
.setattr = ext3_setattr,
.setxattr = ext3_setxattr,
.getxattr = ext3_getxattr,
.listxattr = ext3_listxattr,
.removexattr = ext3_removexattr,
.permission = ext3_permission,
};
static int ext3_symlink (struct inode * dir,
struct dentry *dentry, const char * symname)
{
handle_t *handle;
struct inode * inode;
int l, err;
l = strlen(symname)+1;
if (l > dir->i_sb->s_blocksize)
return -ENAMETOOLONG;
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5);
if (IS_ERR(handle))
return PTR_ERR(handle);
if (IS_DIRSYNC(dir))
handle->h_sync = 1;
inode = ext3_new_inode (handle, dir, S_IFLNK|S_IRWXUGO);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out_stop;
if (l > sizeof (EXT3_I(inode)->i_data)) {
inode->i_op = &ext3_symlink_inode_operations;
ext3_set_aops(inode);
err = page_symlink(inode, symname, l);
if (err) {
ext3_dec_count(handle, inode);
ext3_mark_inode_dirty(handle, inode);
iput (inode);
goto out_stop;
}
} else {
inode->i_op = &ext3_fast_symlink_inode_operations;
memcpy((char*)&EXT3_I(inode)->i_data,symname,l);
inode->i_size = l-1;
}
EXT3_I(inode)->i_disksize = inode->i_size;
err = ext3_add_nondir(handle, dentry, inode);
out_stop:
ext3_journal_stop(handle);
return err;
}
static inline struct ext3_inode_info *EXT3_I(struct inode *inode)
{
return container_of(inode, struct ext3_inode_info, vfs_inode);
}
リンク先取得のreadlinkシステムコールは、inodeコールバック関数の.readlinがコールされ、ext3_fast_symlink_inode_operationsならext3_readlink()をコールし、inode->vfs_inode->i_dataをそのまま返しますが、ext3_symlink_inode_operationsならpage_readlink()はデータブロックを読み込みそれを返すことになります。
struct inode_operations ext3_fast_symlink_inode_operations = {
.readlink = ext3_readlink, /* BKL not held. Don't need */
.follow_link = ext3_follow_link, /* BKL not held. Don't need */
.setxattr = ext3_setxattr,
.getxattr = ext3_getxattr,
.listxattr = ext3_listxattr,
.removexattr = ext3_removexattr,
};
struct inode_operations ext3_symlink_inode_operations = {
.readlink = page_readlink,
.follow_link = page_follow_link,
.setxattr = ext3_setxattr,
.getxattr = ext3_getxattr,
.listxattr = ext3_listxattr,
.removexattr = ext3_removexattr,
};
static int
ext3_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
struct ext3_inode_info *ei = EXT3_I(dentry->d_inode);
return vfs_readlink(dentry, buffer, buflen, (char*)ei->i_data);
}
int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
struct page *page = NULL;
char *s = page_getlink(dentry, &page);
int res = vfs_readlink(dentry,buffer,buflen,s);
if (page) {
kunmap(page);
page_cache_release(page);
}
return res;
}
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;
}
struct ext3_inode_info {
__u32 i_data[15];
__u32 i_flags;
#ifdef EXT3_FRAGMENTS
__u32 i_faddr;
__u8 i_frag_no;
__u8 i_frag_size;
#endif
__u32 i_file_acl;
__u32 i_dir_acl;
__u32 i_dtime;
__u32 i_block_group;
__u32 i_state; /* Dynamic state flags for ext3 */
__u32 i_next_alloc_block;
__u32 i_next_alloc_goal;
#ifdef EXT3_PREALLOCATE
__u32 i_prealloc_block;
__u32 i_prealloc_count;
#endif
__u32 i_dir_start_lookup;
#ifdef CONFIG_EXT3_FS_XATTR
struct rw_semaphore xattr_sem;
#endif
#ifdef CONFIG_EXT3_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
struct list_head i_orphan; /* unlinked but open inodes */
loff_t i_disksize;
struct semaphore truncate_sem;
struct inode vfs_inode;
};





