vfs: Add iomap_seek_hole and iomap_seek_data helpers
Filesystems can use this for implementing lseek SEEK_HOLE / SEEK_DATA support via iomap. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> [hch: split functions, coding style cleanups] Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
This commit is contained in:
parent
334fd34d76
commit
0ed3b0d45f
2 changed files with 98 additions and 0 deletions
94
fs/iomap.c
94
fs/iomap.c
|
@ -584,6 +584,100 @@ int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fi,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iomap_fiemap);
|
||||
|
||||
static loff_t
|
||||
iomap_seek_hole_actor(struct inode *inode, loff_t offset, loff_t length,
|
||||
void *data, struct iomap *iomap)
|
||||
{
|
||||
switch (iomap->type) {
|
||||
case IOMAP_UNWRITTEN:
|
||||
offset = page_cache_seek_hole_data(inode, offset, length,
|
||||
SEEK_HOLE);
|
||||
if (offset < 0)
|
||||
return length;
|
||||
/* fall through */
|
||||
case IOMAP_HOLE:
|
||||
*(loff_t *)data = offset;
|
||||
return 0;
|
||||
default:
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
loff_t
|
||||
iomap_seek_hole(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
|
||||
{
|
||||
loff_t size = i_size_read(inode);
|
||||
loff_t length = size - offset;
|
||||
loff_t ret;
|
||||
|
||||
/* Nothing to be found beyond the end of the file. */
|
||||
if (offset >= size)
|
||||
return -ENXIO;
|
||||
|
||||
while (length > 0) {
|
||||
ret = iomap_apply(inode, offset, length, IOMAP_REPORT, ops,
|
||||
&offset, iomap_seek_hole_actor);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret == 0)
|
||||
break;
|
||||
|
||||
offset += ret;
|
||||
length -= ret;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iomap_seek_hole);
|
||||
|
||||
static loff_t
|
||||
iomap_seek_data_actor(struct inode *inode, loff_t offset, loff_t length,
|
||||
void *data, struct iomap *iomap)
|
||||
{
|
||||
switch (iomap->type) {
|
||||
case IOMAP_HOLE:
|
||||
return length;
|
||||
case IOMAP_UNWRITTEN:
|
||||
offset = page_cache_seek_hole_data(inode, offset, length,
|
||||
SEEK_DATA);
|
||||
if (offset < 0)
|
||||
return length;
|
||||
/*FALLTHRU*/
|
||||
default:
|
||||
*(loff_t *)data = offset;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
loff_t
|
||||
iomap_seek_data(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
|
||||
{
|
||||
loff_t size = i_size_read(inode);
|
||||
loff_t length = size - offset;
|
||||
loff_t ret;
|
||||
|
||||
/* Nothing to be found beyond the end of the file. */
|
||||
if (offset >= size)
|
||||
return -ENXIO;
|
||||
|
||||
while (length > 0) {
|
||||
ret = iomap_apply(inode, offset, length, IOMAP_REPORT, ops,
|
||||
&offset, iomap_seek_data_actor);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret == 0)
|
||||
break;
|
||||
|
||||
offset += ret;
|
||||
length -= ret;
|
||||
}
|
||||
|
||||
if (length <= 0)
|
||||
return -ENXIO;
|
||||
return offset;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iomap_seek_data);
|
||||
|
||||
/*
|
||||
* Private flags for iomap_dio, must not overlap with the public ones in
|
||||
* iomap.h:
|
||||
|
|
|
@ -83,6 +83,10 @@ int iomap_truncate_page(struct inode *inode, loff_t pos, bool *did_zero,
|
|||
int iomap_page_mkwrite(struct vm_fault *vmf, const struct iomap_ops *ops);
|
||||
int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
loff_t start, loff_t len, const struct iomap_ops *ops);
|
||||
loff_t iomap_seek_hole(struct inode *inode, loff_t offset,
|
||||
const struct iomap_ops *ops);
|
||||
loff_t iomap_seek_data(struct inode *inode, loff_t offset,
|
||||
const struct iomap_ops *ops);
|
||||
|
||||
/*
|
||||
* Flags for direct I/O ->end_io:
|
||||
|
|
Loading…
Reference in a new issue