Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 21 Jan 2016 20:14:24 +0000 (12:14 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 21 Jan 2016 20:14:24 +0000 (12:14 -0800)
Pull fuse updates from Miklos Szeredi:
 "This adds SEEK_HOLE and SEEK_DATA support in lseek"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: add support for SEEK_HOLE and SEEK_DATA in lseek

fs/fuse/file.c
fs/fuse/fuse_i.h
include/uapi/linux/fuse.h

index 570ca40..aa03aab 100644 (file)
@@ -2231,20 +2231,77 @@ static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
        return err ? 0 : outarg.block;
 }
 
+static loff_t fuse_lseek(struct file *file, loff_t offset, int whence)
+{
+       struct inode *inode = file->f_mapping->host;
+       struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_file *ff = file->private_data;
+       FUSE_ARGS(args);
+       struct fuse_lseek_in inarg = {
+               .fh = ff->fh,
+               .offset = offset,
+               .whence = whence
+       };
+       struct fuse_lseek_out outarg;
+       int err;
+
+       if (fc->no_lseek)
+               goto fallback;
+
+       args.in.h.opcode = FUSE_LSEEK;
+       args.in.h.nodeid = ff->nodeid;
+       args.in.numargs = 1;
+       args.in.args[0].size = sizeof(inarg);
+       args.in.args[0].value = &inarg;
+       args.out.numargs = 1;
+       args.out.args[0].size = sizeof(outarg);
+       args.out.args[0].value = &outarg;
+       err = fuse_simple_request(fc, &args);
+       if (err) {
+               if (err == -ENOSYS) {
+                       fc->no_lseek = 1;
+                       goto fallback;
+               }
+               return err;
+       }
+
+       return vfs_setpos(file, outarg.offset, inode->i_sb->s_maxbytes);
+
+fallback:
+       err = fuse_update_attributes(inode, NULL, file, NULL);
+       if (!err)
+               return generic_file_llseek(file, offset, whence);
+       else
+               return err;
+}
+
 static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence)
 {
        loff_t retval;
        struct inode *inode = file_inode(file);
 
-       /* No i_mutex protection necessary for SEEK_CUR and SEEK_SET */
-       if (whence == SEEK_CUR || whence == SEEK_SET)
-               return generic_file_llseek(file, offset, whence);
-
-       mutex_lock(&inode->i_mutex);
-       retval = fuse_update_attributes(inode, NULL, file, NULL);
-       if (!retval)
+       switch (whence) {
+       case SEEK_SET:
+       case SEEK_CUR:
+                /* No i_mutex protection necessary for SEEK_CUR and SEEK_SET */
                retval = generic_file_llseek(file, offset, whence);
-       mutex_unlock(&inode->i_mutex);
+               break;
+       case SEEK_END:
+               mutex_lock(&inode->i_mutex);
+               retval = fuse_update_attributes(inode, NULL, file, NULL);
+               if (!retval)
+                       retval = generic_file_llseek(file, offset, whence);
+               mutex_unlock(&inode->i_mutex);
+               break;
+       case SEEK_HOLE:
+       case SEEK_DATA:
+               mutex_lock(&inode->i_mutex);
+               retval = fuse_lseek(file, offset, whence);
+               mutex_unlock(&inode->i_mutex);
+               break;
+       default:
+               retval = -EINVAL;
+       }
 
        return retval;
 }
index 4051131..ce394b5 100644 (file)
@@ -605,6 +605,9 @@ struct fuse_conn {
        /** Does the filesystem support asynchronous direct-IO submission? */
        unsigned async_dio:1;
 
+       /** Is lseek not implemented by fs? */
+       unsigned no_lseek:1;
+
        /** The number of requests waiting for completion */
        atomic_t num_waiting;
 
index c9aca04..5974fae 100644 (file)
  *  - add ctime and ctimensec to fuse_setattr_in
  *  - add FUSE_RENAME2 request
  *  - add FUSE_NO_OPEN_SUPPORT flag
+ *
+ *  7.24
+ *  - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support
  */
 
 #ifndef _LINUX_FUSE_H
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 23
+#define FUSE_KERNEL_MINOR_VERSION 24
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -358,6 +361,7 @@ enum fuse_opcode {
        FUSE_FALLOCATE     = 43,
        FUSE_READDIRPLUS   = 44,
        FUSE_RENAME2       = 45,
+       FUSE_LSEEK         = 46,
 
        /* CUSE specific operations */
        CUSE_INIT          = 4096,
@@ -758,4 +762,15 @@ struct fuse_notify_retrieve_in {
 /* Device ioctls: */
 #define FUSE_DEV_IOC_CLONE     _IOR(229, 0, uint32_t)
 
+struct fuse_lseek_in {
+       uint64_t        fh;
+       uint64_t        offset;
+       uint32_t        whence;
+       uint32_t        padding;
+};
+
+struct fuse_lseek_out {
+       uint64_t        offset;
+};
+
 #endif /* _LINUX_FUSE_H */