Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[sfrench/cifs-2.6.git] / drivers / block / loop.c
index 68b0471ad5a64e52a2b53135ab58523507fb700d..0d4ccd4a09574fdfa3b72533b2fc333bfb191462 100644 (file)
@@ -66,6 +66,7 @@
 #include <linux/swap.h>
 #include <linux/slab.h>
 #include <linux/loop.h>
+#include <linux/compat.h>
 #include <linux/suspend.h>
 #include <linux/writeback.h>
 #include <linux/buffer_head.h>         /* for invalidate_bdev() */
@@ -294,7 +295,7 @@ fail:
  * and do_lo_send_write().
  */
 static int __do_lo_send_write(struct file *file,
-               u8 __user *buf, const int len, loff_t pos)
+               u8 *buf, const int len, loff_t pos)
 {
        ssize_t bw;
        mm_segment_t old_fs = get_fs();
@@ -323,7 +324,7 @@ static int do_lo_send_direct_write(struct loop_device *lo,
                struct bio_vec *bvec, int bsize, loff_t pos, struct page *page)
 {
        ssize_t bw = __do_lo_send_write(lo->lo_backing_file,
-                       (u8 __user *)kmap(bvec->bv_page) + bvec->bv_offset,
+                       kmap(bvec->bv_page) + bvec->bv_offset,
                        bvec->bv_len, pos);
        kunmap(bvec->bv_page);
        cond_resched();
@@ -350,7 +351,7 @@ static int do_lo_send_write(struct loop_device *lo, struct bio_vec *bvec,
                        bvec->bv_offset, bvec->bv_len, pos >> 9);
        if (likely(!ret))
                return __do_lo_send_write(lo->lo_backing_file,
-                               (u8 __user *)page_address(page), bvec->bv_len,
+                               page_address(page), bvec->bv_len,
                                pos);
        printk(KERN_ERR "loop: Transfer error at byte offset %llu, "
                        "length %i.\n", (unsigned long long)pos, bvec->bv_len);
@@ -832,7 +833,7 @@ out_clr:
        lo->lo_backing_file = NULL;
        lo->lo_flags = 0;
        set_capacity(disks[lo->lo_number], 0);
-       invalidate_bdev(bdev, 0);
+       invalidate_bdev(bdev);
        bd_set_size(bdev, 0);
        mapping_set_gfp_mask(mapping, lo->old_gfp_mask);
        lo->lo_state = Lo_unbound;
@@ -916,7 +917,7 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
        memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
        memset(lo->lo_crypt_name, 0, LO_NAME_SIZE);
        memset(lo->lo_file_name, 0, LO_NAME_SIZE);
-       invalidate_bdev(bdev, 0);
+       invalidate_bdev(bdev);
        set_capacity(disks[lo->lo_number], 0);
        bd_set_size(bdev, 0);
        mapping_set_gfp_mask(filp->f_mapping, gfp);
@@ -999,7 +1000,7 @@ loop_get_status(struct loop_device *lo, struct loop_info64 *info)
 
        if (lo->lo_state != Lo_bound)
                return -ENXIO;
-       error = vfs_getattr(file->f_vfsmnt, file->f_dentry, &stat);
+       error = vfs_getattr(file->f_path.mnt, file->f_path.dentry, &stat);
        if (error)
                return error;
        memset(info, 0, sizeof(*info));
@@ -1165,6 +1166,162 @@ static int lo_ioctl(struct inode * inode, struct file * file,
        return err;
 }
 
+#ifdef CONFIG_COMPAT
+struct compat_loop_info {
+       compat_int_t    lo_number;      /* ioctl r/o */
+       compat_dev_t    lo_device;      /* ioctl r/o */
+       compat_ulong_t  lo_inode;       /* ioctl r/o */
+       compat_dev_t    lo_rdevice;     /* ioctl r/o */
+       compat_int_t    lo_offset;
+       compat_int_t    lo_encrypt_type;
+       compat_int_t    lo_encrypt_key_size;    /* ioctl w/o */
+       compat_int_t    lo_flags;       /* ioctl r/o */
+       char            lo_name[LO_NAME_SIZE];
+       unsigned char   lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
+       compat_ulong_t  lo_init[2];
+       char            reserved[4];
+};
+
+/*
+ * Transfer 32-bit compatibility structure in userspace to 64-bit loop info
+ * - noinlined to reduce stack space usage in main part of driver
+ */
+static noinline int
+loop_info64_from_compat(const struct compat_loop_info __user *arg,
+                       struct loop_info64 *info64)
+{
+       struct compat_loop_info info;
+
+       if (copy_from_user(&info, arg, sizeof(info)))
+               return -EFAULT;
+
+       memset(info64, 0, sizeof(*info64));
+       info64->lo_number = info.lo_number;
+       info64->lo_device = info.lo_device;
+       info64->lo_inode = info.lo_inode;
+       info64->lo_rdevice = info.lo_rdevice;
+       info64->lo_offset = info.lo_offset;
+       info64->lo_sizelimit = 0;
+       info64->lo_encrypt_type = info.lo_encrypt_type;
+       info64->lo_encrypt_key_size = info.lo_encrypt_key_size;
+       info64->lo_flags = info.lo_flags;
+       info64->lo_init[0] = info.lo_init[0];
+       info64->lo_init[1] = info.lo_init[1];
+       if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI)
+               memcpy(info64->lo_crypt_name, info.lo_name, LO_NAME_SIZE);
+       else
+               memcpy(info64->lo_file_name, info.lo_name, LO_NAME_SIZE);
+       memcpy(info64->lo_encrypt_key, info.lo_encrypt_key, LO_KEY_SIZE);
+       return 0;
+}
+
+/*
+ * Transfer 64-bit loop info to 32-bit compatibility structure in userspace
+ * - noinlined to reduce stack space usage in main part of driver
+ */
+static noinline int
+loop_info64_to_compat(const struct loop_info64 *info64,
+                     struct compat_loop_info __user *arg)
+{
+       struct compat_loop_info info;
+
+       memset(&info, 0, sizeof(info));
+       info.lo_number = info64->lo_number;
+       info.lo_device = info64->lo_device;
+       info.lo_inode = info64->lo_inode;
+       info.lo_rdevice = info64->lo_rdevice;
+       info.lo_offset = info64->lo_offset;
+       info.lo_encrypt_type = info64->lo_encrypt_type;
+       info.lo_encrypt_key_size = info64->lo_encrypt_key_size;
+       info.lo_flags = info64->lo_flags;
+       info.lo_init[0] = info64->lo_init[0];
+       info.lo_init[1] = info64->lo_init[1];
+       if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI)
+               memcpy(info.lo_name, info64->lo_crypt_name, LO_NAME_SIZE);
+       else
+               memcpy(info.lo_name, info64->lo_file_name, LO_NAME_SIZE);
+       memcpy(info.lo_encrypt_key, info64->lo_encrypt_key, LO_KEY_SIZE);
+
+       /* error in case values were truncated */
+       if (info.lo_device != info64->lo_device ||
+           info.lo_rdevice != info64->lo_rdevice ||
+           info.lo_inode != info64->lo_inode ||
+           info.lo_offset != info64->lo_offset ||
+           info.lo_init[0] != info64->lo_init[0] ||
+           info.lo_init[1] != info64->lo_init[1])
+               return -EOVERFLOW;
+
+       if (copy_to_user(arg, &info, sizeof(info)))
+               return -EFAULT;
+       return 0;
+}
+
+static int
+loop_set_status_compat(struct loop_device *lo,
+                      const struct compat_loop_info __user *arg)
+{
+       struct loop_info64 info64;
+       int ret;
+
+       ret = loop_info64_from_compat(arg, &info64);
+       if (ret < 0)
+               return ret;
+       return loop_set_status(lo, &info64);
+}
+
+static int
+loop_get_status_compat(struct loop_device *lo,
+                      struct compat_loop_info __user *arg)
+{
+       struct loop_info64 info64;
+       int err = 0;
+
+       if (!arg)
+               err = -EINVAL;
+       if (!err)
+               err = loop_get_status(lo, &info64);
+       if (!err)
+               err = loop_info64_to_compat(&info64, arg);
+       return err;
+}
+
+static long lo_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct inode *inode = file->f_path.dentry->d_inode;
+       struct loop_device *lo = inode->i_bdev->bd_disk->private_data;
+       int err;
+
+       lock_kernel();
+       switch(cmd) {
+       case LOOP_SET_STATUS:
+               mutex_lock(&lo->lo_ctl_mutex);
+               err = loop_set_status_compat(
+                       lo, (const struct compat_loop_info __user *) arg);
+               mutex_unlock(&lo->lo_ctl_mutex);
+               break;
+       case LOOP_GET_STATUS:
+               mutex_lock(&lo->lo_ctl_mutex);
+               err = loop_get_status_compat(
+                       lo, (struct compat_loop_info __user *) arg);
+               mutex_unlock(&lo->lo_ctl_mutex);
+               break;
+       case LOOP_CLR_FD:
+       case LOOP_GET_STATUS64:
+       case LOOP_SET_STATUS64:
+               arg = (unsigned long) compat_ptr(arg);
+       case LOOP_SET_FD:
+       case LOOP_CHANGE_FD:
+               err = lo_ioctl(inode, file, cmd, arg);
+               break;
+       default:
+               err = -ENOIOCTLCMD;
+               break;
+       }
+       unlock_kernel();
+       return err;
+}
+#endif
+
 static int lo_open(struct inode *inode, struct file *file)
 {
        struct loop_device *lo = inode->i_bdev->bd_disk->private_data;
@@ -1192,6 +1349,9 @@ static struct block_device_operations lo_fops = {
        .open =         lo_open,
        .release =      lo_release,
        .ioctl =        lo_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = lo_compat_ioctl,
+#endif
 };
 
 /*