diff options
Diffstat (limited to 'fs/ocfs2/extent_map.c')
| -rw-r--r-- | fs/ocfs2/extent_map.c | 96 | 
1 files changed, 96 insertions, 0 deletions
diff --git a/fs/ocfs2/extent_map.c b/fs/ocfs2/extent_map.c index 23457b491e8..2f5b92ef0e5 100644 --- a/fs/ocfs2/extent_map.c +++ b/fs/ocfs2/extent_map.c @@ -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,  |