[RFC][PATCH] Implement SEEK_HOLE/SEEK_DATA

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

This is my first pass at implementing SEEK_HOLE/SEEK_DATA.  This has been in
solaris for about a year now, and is described here

http://docs.sun.com/app/docs/doc/819-2241/lseek-2?l=en&a=view&q=SEEK_HOLE
http://blogs.sun.com/roller/page/bonwick?entry=seek_hole_and_seek_data

I've added a file operation to allow filesystems to override the default
seek_hole_data function, which just loops through bmap looking for either a hole
or data.  I've tested this and it seems to work well.  I ran my testcase on a
solaris box to make sure I got consistent results (I just ran my test script on
the solaris box, I haven't looked at any of their code in case thats a concern).
All comments welcome.  Thank you,

Josef

diff --git a/fs/read_write.c b/fs/read_write.c
index ea1f94c..cf61e1e 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -31,10 +31,32 @@ const struct file_operations generic_ro_fops = {
 
 EXPORT_SYMBOL(generic_ro_fops);
 
+static loff_t generic_seek_hole_data(struct file *file, loff_t offset,
+				     int origin)
+{
+	loff_t retval = -ENXIO;
+	struct inode *inode = file->f_mapping->host;
+	sector_t block, found_block;
+	sector_t last_block = (i_size_read(inode) - 1) >> inode->i_blkbits;
+	int want = (origin == SEEK_HOLE) ? 0 : 1;
+
+	for (block = offset >> inode->i_blkbits; block <= last_block; block++) {
+		found_block = bmap(inode, block);
+
+		if (!!found_block == want) {
+			retval = block << inode->i_blkbits;
+			break;
+		}
+	}
+
+	return retval;
+}
+
 loff_t generic_file_llseek(struct file *file, loff_t offset, int origin)
 {
 	long long retval;
 	struct inode *inode = file->f_mapping->host;
+	loff_t (*fn)(struct file *, loff_t, int);
 
 	mutex_lock(&inode->i_mutex);
 	switch (origin) {
@@ -43,15 +65,24 @@ loff_t generic_file_llseek(struct file *file, loff_t offset, int origin)
 			break;
 		case SEEK_CUR:
 			offset += file->f_pos;
+			break;
+		case SEEK_HOLE:
+		case SEEK_DATA:
+			fn = generic_seek_hole_data;
+			if (file->f_op->seek_hole_data)
+				fn = file->f_op->seek_hole_data;
+			offset = fn(file, offset, origin);
 	}
 	retval = -EINVAL;
 	if (offset>=0 && offset<=inode->i_sb->s_maxbytes) {
-		if (offset != file->f_pos) {
+		if (offset != file->f_pos && origin != SEEK_HOLE) {
 			file->f_pos = offset;
 			file->f_version = 0;
 		}
 		retval = offset;
-	}
+	} else if (offset < 0)
+		retval = offset;
+
 	mutex_unlock(&inode->i_mutex);
 	return retval;
 }
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b3ec4a4..a55d68e 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -30,7 +30,9 @@
 #define SEEK_SET	0	/* seek relative to beginning of file */
 #define SEEK_CUR	1	/* seek relative to current file position */
 #define SEEK_END	2	/* seek relative to end of file */
-#define SEEK_MAX	SEEK_END
+#define SEEK_HOLE	3	/* seek relative to the next hole */
+#define SEEK_DATA	4	/* seek relative to the next block with data */
+#define SEEK_MAX	SEEK_DATA
 
 /* And dynamically-tunable limits and defaults: */
 struct files_stat_struct {
@@ -1163,6 +1165,7 @@ typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, u
 struct file_operations {
 	struct module *owner;
 	loff_t (*llseek) (struct file *, loff_t, int);
+	loff_t (*seek_hole_data) (struct file *, loff_t, int);
 	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
 	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
 	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux