| From: |
| Josef Bacik <jbacik@redhat.com> |
| To: |
| linux-fsdevel@vger.kernel.org |
| Subject: |
| [RFC][PATCH] Implement SEEK_HOLE/SEEK_DATA |
| Date: |
| Wed, 28 Nov 2007 15:02:07 -0500 |
| Message-ID: |
| <20071128200206.GB3977@dhcp243-37.rdu.redhat.com> |
| Archive‑link: | |
Article |
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&...
http://blogs.sun.com/roller/page/bonwick?entry=seek_hole_...
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@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html