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 [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to