Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi...
[cascardo/linux.git] / fs / fuse / file.c
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;
 }