forked from Minki/linux
ocfs2: Implement llseek()
ocfs2 implements its own llseek() to provide the SEEK_HOLE/SEEK_DATA functionality. SEEK_HOLE sets the file pointer to the start of either a hole or an unwritten (preallocated) extent, that is greater than or equal to the supplied offset. SEEK_DATA sets the file pointer to the start of an allocated extent (not unwritten) that is greater than or equal to the supplied offset. If the supplied offset is on a desired region, then the file pointer is set to it. Offsets greater than or equal to the file size return -ENXIO. Unwritten (preallocated) extents are considered holes because the file system treats reads to such regions in the same way as it does to holes. Signed-off-by: Sunil Mushran <sunil.mushran@oracle.com>
This commit is contained in:
parent
5cffff9e29
commit
93862d5e1a
@ -832,6 +832,102 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ocfs2_seek_data_hole_offset(struct file *file, loff_t *offset, int origin)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
int ret;
|
||||
unsigned int is_last = 0, is_data = 0;
|
||||
u16 cs_bits = OCFS2_SB(inode->i_sb)->s_clustersize_bits;
|
||||
u32 cpos, cend, clen, hole_size;
|
||||
u64 extoff, extlen;
|
||||
struct buffer_head *di_bh = NULL;
|
||||
struct ocfs2_extent_rec rec;
|
||||
|
||||
BUG_ON(origin != SEEK_DATA && origin != SEEK_HOLE);
|
||||
|
||||
ret = ocfs2_inode_lock(inode, &di_bh, 0);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
down_read(&OCFS2_I(inode)->ip_alloc_sem);
|
||||
|
||||
if (*offset >= inode->i_size) {
|
||||
ret = -ENXIO;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
|
||||
if (origin == SEEK_HOLE)
|
||||
*offset = inode->i_size;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
clen = 0;
|
||||
cpos = *offset >> cs_bits;
|
||||
cend = ocfs2_clusters_for_bytes(inode->i_sb, inode->i_size);
|
||||
|
||||
while (cpos < cend && !is_last) {
|
||||
ret = ocfs2_get_clusters_nocache(inode, di_bh, cpos, &hole_size,
|
||||
&rec, &is_last);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
extoff = cpos;
|
||||
extoff <<= cs_bits;
|
||||
|
||||
if (rec.e_blkno == 0ULL) {
|
||||
clen = hole_size;
|
||||
is_data = 0;
|
||||
} else {
|
||||
clen = le16_to_cpu(rec.e_leaf_clusters) -
|
||||
(cpos - le32_to_cpu(rec.e_cpos));
|
||||
is_data = (rec.e_flags & OCFS2_EXT_UNWRITTEN) ? 0 : 1;
|
||||
}
|
||||
|
||||
if ((!is_data && origin == SEEK_HOLE) ||
|
||||
(is_data && origin == SEEK_DATA)) {
|
||||
if (extoff > *offset)
|
||||
*offset = extoff;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (!is_last)
|
||||
cpos += clen;
|
||||
}
|
||||
|
||||
if (origin == SEEK_HOLE) {
|
||||
extoff = cpos;
|
||||
extoff <<= cs_bits;
|
||||
extlen = clen;
|
||||
extlen <<= cs_bits;
|
||||
|
||||
if ((extoff + extlen) > inode->i_size)
|
||||
extlen = inode->i_size - extoff;
|
||||
extoff += extlen;
|
||||
if (extoff > *offset)
|
||||
*offset = extoff;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = -ENXIO;
|
||||
|
||||
out_unlock:
|
||||
|
||||
brelse(di_bh);
|
||||
|
||||
up_read(&OCFS2_I(inode)->ip_alloc_sem);
|
||||
|
||||
ocfs2_inode_unlock(inode, 0);
|
||||
out:
|
||||
if (ret && ret != -ENXIO)
|
||||
ret = -ENXIO;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ocfs2_read_virt_blocks(struct inode *inode, u64 v_block, int nr,
|
||||
struct buffer_head *bhs[], int flags,
|
||||
int (*validate)(struct super_block *sb,
|
||||
|
@ -53,6 +53,8 @@ int ocfs2_extent_map_get_blocks(struct inode *inode, u64 v_blkno, u64 *p_blkno,
|
||||
int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
u64 map_start, u64 map_len);
|
||||
|
||||
int ocfs2_seek_data_hole_offset(struct file *file, loff_t *offset, int origin);
|
||||
|
||||
int ocfs2_xattr_get_clusters(struct inode *inode, u32 v_cluster,
|
||||
u32 *p_cluster, u32 *num_clusters,
|
||||
struct ocfs2_extent_list *el,
|
||||
|
@ -2591,6 +2591,57 @@ bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Refer generic_file_llseek_unlocked() */
|
||||
static loff_t ocfs2_file_llseek(struct file *file, loff_t offset, int origin)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
switch (origin) {
|
||||
case SEEK_SET:
|
||||
break;
|
||||
case SEEK_END:
|
||||
offset += inode->i_size;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
if (offset == 0) {
|
||||
offset = file->f_pos;
|
||||
goto out;
|
||||
}
|
||||
offset += file->f_pos;
|
||||
break;
|
||||
case SEEK_DATA:
|
||||
case SEEK_HOLE:
|
||||
ret = ocfs2_seek_data_hole_offset(file, &offset, origin);
|
||||
if (ret)
|
||||
goto out;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET))
|
||||
ret = -EINVAL;
|
||||
if (!ret && offset > inode->i_sb->s_maxbytes)
|
||||
ret = -EINVAL;
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (offset != file->f_pos) {
|
||||
file->f_pos = offset;
|
||||
file->f_version = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
return offset;
|
||||
}
|
||||
|
||||
const struct inode_operations ocfs2_file_iops = {
|
||||
.setattr = ocfs2_setattr,
|
||||
.getattr = ocfs2_getattr,
|
||||
@ -2615,7 +2666,7 @@ const struct inode_operations ocfs2_special_file_iops = {
|
||||
* ocfs2_fops_no_plocks and ocfs2_dops_no_plocks!
|
||||
*/
|
||||
const struct file_operations ocfs2_fops = {
|
||||
.llseek = generic_file_llseek,
|
||||
.llseek = ocfs2_file_llseek,
|
||||
.read = do_sync_read,
|
||||
.write = do_sync_write,
|
||||
.mmap = ocfs2_mmap,
|
||||
@ -2663,7 +2714,7 @@ const struct file_operations ocfs2_dops = {
|
||||
* the cluster.
|
||||
*/
|
||||
const struct file_operations ocfs2_fops_no_plocks = {
|
||||
.llseek = generic_file_llseek,
|
||||
.llseek = ocfs2_file_llseek,
|
||||
.read = do_sync_read,
|
||||
.write = do_sync_write,
|
||||
.mmap = ocfs2_mmap,
|
||||
|
Loading…
Reference in New Issue
Block a user