nfsd: convert file cache to use over/underflow safe refcount
[linux-2.6-microblaze.git] / fs / ioctl.c
index fef3a6b..2f5e4e5 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/syscalls.h>
 #include <linux/mm.h>
 #include <linux/capability.h>
+#include <linux/compat.h>
 #include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/security.h>
@@ -174,10 +175,9 @@ static int fiemap_check_ranges(struct super_block *sb,
        return 0;
 }
 
-static int ioctl_fiemap(struct file *filp, unsigned long arg)
+static int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap)
 {
        struct fiemap fiemap;
-       struct fiemap __user *ufiemap = (struct fiemap __user *) arg;
        struct fiemap_extent_info fieinfo = { 0, };
        struct inode *inode = file_inode(filp);
        struct super_block *sb = inode->i_sb;
@@ -244,7 +244,8 @@ fdput:
        return ret;
 }
 
-static long ioctl_file_clone_range(struct file *file, void __user *argp)
+static long ioctl_file_clone_range(struct file *file,
+                                  struct file_clone_range __user *argp)
 {
        struct file_clone_range args;
 
@@ -466,7 +467,7 @@ EXPORT_SYMBOL(generic_block_fiemap);
  * Only the l_start, l_len and l_whence fields of the 'struct space_resv'
  * are used here, rest are ignored.
  */
-int ioctl_preallocate(struct file *filp, void __user *argp)
+int ioctl_preallocate(struct file *filp, int mode, void __user *argp)
 {
        struct inode *inode = file_inode(filp);
        struct space_resv sr;
@@ -487,9 +488,39 @@ int ioctl_preallocate(struct file *filp, void __user *argp)
                return -EINVAL;
        }
 
-       return vfs_fallocate(filp, FALLOC_FL_KEEP_SIZE, sr.l_start, sr.l_len);
+       return vfs_fallocate(filp, mode | FALLOC_FL_KEEP_SIZE, sr.l_start,
+                       sr.l_len);
 }
 
+/* on ia32 l_start is on a 32-bit boundary */
+#if defined CONFIG_COMPAT && defined(CONFIG_X86_64)
+/* just account for different alignment */
+int compat_ioctl_preallocate(struct file *file, int mode,
+                               struct space_resv_32 __user *argp)
+{
+       struct inode *inode = file_inode(file);
+       struct space_resv_32 sr;
+
+       if (copy_from_user(&sr, argp, sizeof(sr)))
+               return -EFAULT;
+
+       switch (sr.l_whence) {
+       case SEEK_SET:
+               break;
+       case SEEK_CUR:
+               sr.l_start += file->f_pos;
+               break;
+       case SEEK_END:
+               sr.l_start += i_size_read(inode);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return vfs_fallocate(file, mode | FALLOC_FL_KEEP_SIZE, sr.l_start, sr.l_len);
+}
+#endif
+
 static int file_ioctl(struct file *filp, unsigned int cmd,
                unsigned long arg)
 {
@@ -503,7 +534,12 @@ static int file_ioctl(struct file *filp, unsigned int cmd,
                return put_user(i_size_read(inode) - filp->f_pos, p);
        case FS_IOC_RESVSP:
        case FS_IOC_RESVSP64:
-               return ioctl_preallocate(filp, p);
+               return ioctl_preallocate(filp, 0, p);
+       case FS_IOC_UNRESVSP:
+       case FS_IOC_UNRESVSP64:
+               return ioctl_preallocate(filp, FALLOC_FL_PUNCH_HOLE, p);
+       case FS_IOC_ZERO_RANGE:
+               return ioctl_preallocate(filp, FALLOC_FL_ZERO_RANGE, p);
        }
 
        return vfs_ioctl(filp, cmd, arg);
@@ -584,9 +620,9 @@ static int ioctl_fsthaw(struct file *filp)
        return thaw_super(sb);
 }
 
-static int ioctl_file_dedupe_range(struct file *file, void __user *arg)
+static int ioctl_file_dedupe_range(struct file *file,
+                                  struct file_dedupe_range __user *argp)
 {
-       struct file_dedupe_range __user *argp = arg;
        struct file_dedupe_range *same = NULL;
        int ret;
        unsigned long size;
@@ -635,7 +671,7 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
             unsigned long arg)
 {
        int error = 0;
-       int __user *argp = (int __user *)arg;
+       void __user *argp = (void __user *)arg;
        struct inode *inode = file_inode(filp);
 
        switch (cmd) {
@@ -674,13 +710,13 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
                break;
 
        case FS_IOC_FIEMAP:
-               return ioctl_fiemap(filp, arg);
+               return ioctl_fiemap(filp, argp);
 
        case FIGETBSZ:
                /* anon_bdev filesystems may not have a block size */
                if (!inode->i_sb->s_blocksize)
                        return -EINVAL;
-               return put_user(inode->i_sb->s_blocksize, argp);
+               return put_user(inode->i_sb->s_blocksize, (int __user *)argp);
 
        case FICLONE:
                return ioctl_file_clone(filp, arg, 0, 0, 0);
@@ -719,3 +755,37 @@ SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
 {
        return ksys_ioctl(fd, cmd, arg);
 }
+
+#ifdef CONFIG_COMPAT
+/**
+ * compat_ptr_ioctl - generic implementation of .compat_ioctl file operation
+ *
+ * This is not normally called as a function, but instead set in struct
+ * file_operations as
+ *
+ *     .compat_ioctl = compat_ptr_ioctl,
+ *
+ * On most architectures, the compat_ptr_ioctl() just passes all arguments
+ * to the corresponding ->ioctl handler. The exception is arch/s390, where
+ * compat_ptr() clears the top bit of a 32-bit pointer value, so user space
+ * pointers to the second 2GB alias the first 2GB, as is the case for
+ * native 32-bit s390 user space.
+ *
+ * The compat_ptr_ioctl() function must therefore be used only with ioctl
+ * functions that either ignore the argument or pass a pointer to a
+ * compatible data type.
+ *
+ * If any ioctl command handled by fops->unlocked_ioctl passes a plain
+ * integer instead of a pointer, or any of the passed data types
+ * is incompatible between 32-bit and 64-bit architectures, a proper
+ * handler is required instead of compat_ptr_ioctl.
+ */
+long compat_ptr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       if (!file->f_op->unlocked_ioctl)
+               return -ENOIOCTLCMD;
+
+       return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+EXPORT_SYMBOL(compat_ptr_ioctl);
+#endif