Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93862d5e authored by Sunil Mushran's avatar Sunil Mushran
Browse files

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: default avatarSunil Mushran <sunil.mushran@oracle.com>
parent 5cffff9e
Loading
Loading
Loading
Loading
+96 −0
Original line number Diff line number Diff line
@@ -832,6 +832,102 @@ int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
	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,
+2 −0
Original line number Diff line number Diff line
@@ -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,
+53 −2
Original line number Diff line number Diff line
@@ -2591,6 +2591,57 @@ static ssize_t ocfs2_file_aio_read(struct kiocb *iocb,
	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,