[RFC PATCH 02/17] vfs: Implement a FIEMAP callback

David Howells dhowells at redhat.com
Wed Mar 4 06:03:09 PST 2026


Implement a callback in the internal kernel FIEMAP API so that kernel users
can make use of it as the filler function expects to write to userspace.
This allows the FIEMAP data to be captured and parsed.  This is useful for
cachefiles and also potentially for knfsd and ksmbd to implement their
equivalents of FIEMAP remotely rather than using SEEK_DATA/SEEK_HOLE.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Paulo Alcantara <pc at manguebit.org>
cc: Matthew Wilcox <willy at infradead.org>
cc: Christoph Hellwig <hch at infradead.org>
cc: Steve French <sfrench at samba.org>
cc: Namjae Jeon <linkinjeon at kernel.org>
cc: Tom Talpey <tom at talpey.com>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: linux-cifs at vger.kernel.org
cc: linux-nfs at vger.kernel.org
cc: netfs at lists.linux.dev
cc: linux-fsdevel at vger.kernel.org
---
 fs/ioctl.c             | 29 ++++++++++++++++++++---------
 include/linux/fiemap.h |  3 +++
 2 files changed, 23 insertions(+), 9 deletions(-)

diff --git a/fs/ioctl.c b/fs/ioctl.c
index 1c152c2b1b67..f0513e282eb7 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -93,6 +93,21 @@ static int ioctl_fibmap(struct file *filp, int __user *p)
 	return error;
 }
 
+static int fiemap_fill(struct fiemap_extent_info *fieinfo,
+		       const struct fiemap_extent *extent)
+{
+	struct fiemap_extent __user *dest = fieinfo->fi_extents_start;
+
+	dest += fieinfo->fi_extents_mapped;
+	if (copy_to_user(dest, extent, sizeof(*extent)))
+		return -EFAULT;
+
+	fieinfo->fi_extents_mapped++;
+	if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max)
+		return 1;
+	return 0;
+}
+
 /**
  * fiemap_fill_next_extent - Fiemap helper function
  * @fieinfo:	Fiemap context passed into ->fiemap
@@ -112,7 +127,7 @@ int fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical,
 			    u64 phys, u64 len, u32 flags)
 {
 	struct fiemap_extent extent;
-	struct fiemap_extent __user *dest = fieinfo->fi_extents_start;
+	int ret;
 
 	/* only count the extents */
 	if (fieinfo->fi_extents_max == 0) {
@@ -140,13 +155,9 @@ int fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical,
 	extent.fe_length = len;
 	extent.fe_flags = flags;
 
-	dest += fieinfo->fi_extents_mapped;
-	if (copy_to_user(dest, &extent, sizeof(extent)))
-		return -EFAULT;
-
-	fieinfo->fi_extents_mapped++;
-	if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max)
-		return 1;
+	ret = fieinfo->fi_fill(fieinfo, &extent);
+	if (ret != 0)
+		return ret; /* 1 to stop. */
 	return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
 }
 EXPORT_SYMBOL(fiemap_fill_next_extent);
@@ -199,7 +210,7 @@ EXPORT_SYMBOL(fiemap_prep);
 static int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap)
 {
 	struct fiemap fiemap;
-	struct fiemap_extent_info fieinfo = { 0, };
+	struct fiemap_extent_info fieinfo = { .fi_fill = fiemap_fill, };
 	struct inode *inode = file_inode(filp);
 	int error;
 
diff --git a/include/linux/fiemap.h b/include/linux/fiemap.h
index 966092ffa89a..01929ca4b834 100644
--- a/include/linux/fiemap.h
+++ b/include/linux/fiemap.h
@@ -11,12 +11,15 @@
  * @fi_extents_mapped:	Number of mapped extents
  * @fi_extents_max:	Size of fiemap_extent array
  * @fi_extents_start:	Start of fiemap_extent array
+ * @fi_fill:		Function to fill the extents array
  */
 struct fiemap_extent_info {
 	unsigned int fi_flags;
 	unsigned int fi_extents_mapped;
 	unsigned int fi_extents_max;
 	struct fiemap_extent __user *fi_extents_start;
+	int (*fi_fill)(struct fiemap_extent_info *fiefinfo,
+		       const struct fiemap_extent *extent);
 };
 
 int fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo,




More information about the linux-afs mailing list