vfs: Add iomap_seek_hole and iomap_seek_data helpers
authorAndreas Gruenbacher <agruenba@redhat.com>
Thu, 29 Jun 2017 18:43:21 +0000 (11:43 -0700)
committerDarrick J. Wong <darrick.wong@oracle.com>
Mon, 3 Jul 2017 05:46:13 +0000 (22:46 -0700)
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>
fs/iomap.c
include/linux/iomap.h

index 4b10892967a5ae9a4ece5d8571e47c01c72f74da..432eed8f091f171d479c4c7174269c4a62d61e6b 100644 (file)
@@ -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:
index f753e788da3109cc85cd07ac44b2f7e47fcde9b6..8a03f5dcd89b38ae2803292588d13b4c63043885 100644 (file)
@@ -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: