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

Commit 3fe3e6b1 authored by Jeff Liu's avatar Jeff Liu Committed by Ben Myers
Browse files

xfs: introduce SEEK_DATA/SEEK_HOLE support



This patch adds lseek(2) SEEK_DATA/SEEK_HOLE functionality to xfs.

Signed-off-by: default avatarJie Liu <jeff.liu@oracle.com>
Reviewed-by: default avatarMark Tinguely <tinguely@sgi.com>
Signed-off-by: default avatarBen Myers <bpm@sgi.com>
parent e700a06c
Loading
Loading
Loading
Loading
+142 −1
Original line number Diff line number Diff line
@@ -963,8 +963,149 @@ xfs_vm_page_mkwrite(
	return block_page_mkwrite(vma, vmf, xfs_get_blocks);
}

STATIC loff_t
xfs_seek_data(
	struct file		*file,
	loff_t			start,
	u32			type)
{
	struct inode		*inode = file->f_mapping->host;
	struct xfs_inode	*ip = XFS_I(inode);
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_bmbt_irec	map[2];
	int			nmap = 2;
	loff_t			uninitialized_var(offset);
	xfs_fsize_t		isize;
	xfs_fileoff_t		fsbno;
	xfs_filblks_t		end;
	uint			lock;
	int			error;

	lock = xfs_ilock_map_shared(ip);

	isize = i_size_read(inode);
	if (start >= isize) {
		error = ENXIO;
		goto out_unlock;
	}

	fsbno = XFS_B_TO_FSBT(mp, start);

	/*
	 * Try to read extents from the first block indicated
	 * by fsbno to the end block of the file.
	 */
	end = XFS_B_TO_FSB(mp, isize);

	error = xfs_bmapi_read(ip, fsbno, end - fsbno, map, &nmap,
			       XFS_BMAPI_ENTIRE);
	if (error)
		goto out_unlock;

	/*
	 * Treat unwritten extent as data extent since it might
	 * contains dirty data in page cache.
	 */
	if (map[0].br_startblock != HOLESTARTBLOCK) {
		offset = max_t(loff_t, start,
			       XFS_FSB_TO_B(mp, map[0].br_startoff));
	} else {
		if (nmap == 1) {
			error = ENXIO;
			goto out_unlock;
		}

		offset = max_t(loff_t, start,
			       XFS_FSB_TO_B(mp, map[1].br_startoff));
	}

	if (offset != file->f_pos)
		file->f_pos = offset;

out_unlock:
	xfs_iunlock_map_shared(ip, lock);

	if (error)
		return -error;
	return offset;
}

STATIC loff_t
xfs_seek_hole(
	struct file		*file,
	loff_t			start,
	u32			type)
{
	struct inode		*inode = file->f_mapping->host;
	struct xfs_inode	*ip = XFS_I(inode);
	struct xfs_mount	*mp = ip->i_mount;
	loff_t			uninitialized_var(offset);
	loff_t			holeoff;
	xfs_fsize_t		isize;
	xfs_fileoff_t		fsbno;
	uint			lock;
	int			error;

	if (XFS_FORCED_SHUTDOWN(mp))
		return -XFS_ERROR(EIO);

	lock = xfs_ilock_map_shared(ip);

	isize = i_size_read(inode);
	if (start >= isize) {
		error = ENXIO;
		goto out_unlock;
	}

	fsbno = XFS_B_TO_FSBT(mp, start);
	error = xfs_bmap_first_unused(NULL, ip, 1, &fsbno, XFS_DATA_FORK);
	if (error)
		goto out_unlock;

	holeoff = XFS_FSB_TO_B(mp, fsbno);
	if (holeoff <= start)
		offset = start;
	else {
		/*
		 * xfs_bmap_first_unused() could return a value bigger than
		 * isize if there are no more holes past the supplied offset.
		 */
		offset = min_t(loff_t, holeoff, isize);
	}

	if (offset != file->f_pos)
		file->f_pos = offset;

out_unlock:
	xfs_iunlock_map_shared(ip, lock);

	if (error)
		return -error;
	return offset;
}

STATIC loff_t
xfs_file_llseek(
	struct file	*file,
	loff_t		offset,
	int		origin)
{
	switch (origin) {
	case SEEK_END:
	case SEEK_CUR:
	case SEEK_SET:
		return generic_file_llseek(file, offset, origin);
	case SEEK_DATA:
		return xfs_seek_data(file, offset, origin);
	case SEEK_HOLE:
		return xfs_seek_hole(file, offset, origin);
	default:
		return -EINVAL;
	}
}

const struct file_operations xfs_file_operations = {
	.llseek		= generic_file_llseek,
	.llseek		= xfs_file_llseek,
	.read		= do_sync_read,
	.write		= do_sync_write,
	.aio_read	= xfs_file_aio_read,