[PATCH] Add 32-bit compatibility for NFSv4 mount
[linux-3.10.git] / fs / compat.c
index a912bdf691cfca03e07eba1b5753d58779dabf66..67c0b94d11485fc4a0449aeb2d911636567daeea 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/smb.h>
 #include <linux/smb_mount.h>
 #include <linux/ncp_mount.h>
+#include <linux/nfs4_mount.h>
 #include <linux/smp_lock.h>
 #include <linux/syscalls.h>
 #include <linux/ctype.h>
@@ -806,10 +807,79 @@ static void *do_smb_super_data_conv(void *raw_data)
        return raw_data;
 }
 
+struct compat_nfs_string {
+       compat_uint_t len;
+       compat_uptr_t __user data;
+};
+
+static inline void compat_nfs_string(struct nfs_string *dst,
+                                    struct compat_nfs_string *src)
+{
+       dst->data = compat_ptr(src->data);
+       dst->len = src->len;
+}
+
+struct compat_nfs4_mount_data_v1 {
+       compat_int_t version;
+       compat_int_t flags;
+       compat_int_t rsize;
+       compat_int_t wsize;
+       compat_int_t timeo;
+       compat_int_t retrans;
+       compat_int_t acregmin;
+       compat_int_t acregmax;
+       compat_int_t acdirmin;
+       compat_int_t acdirmax;
+       struct compat_nfs_string client_addr;
+       struct compat_nfs_string mnt_path;
+       struct compat_nfs_string hostname;
+       compat_uint_t host_addrlen;
+       compat_uptr_t __user host_addr;
+       compat_int_t proto;
+       compat_int_t auth_flavourlen;
+       compat_uptr_t __user auth_flavours;
+};
+
+static int do_nfs4_super_data_conv(void *raw_data)
+{
+       int version = *(compat_uint_t *) raw_data;
+
+       if (version == 1) {
+               struct compat_nfs4_mount_data_v1 *raw = raw_data;
+               struct nfs4_mount_data *real = raw_data;
+
+               /* copy the fields backwards */
+               real->auth_flavours = compat_ptr(raw->auth_flavours);
+               real->auth_flavourlen = raw->auth_flavourlen;
+               real->proto = raw->proto;
+               real->host_addr = compat_ptr(raw->host_addr);
+               real->host_addrlen = raw->host_addrlen;
+               compat_nfs_string(&real->hostname, &raw->hostname);
+               compat_nfs_string(&real->mnt_path, &raw->mnt_path);
+               compat_nfs_string(&real->client_addr, &raw->client_addr);
+               real->acdirmax = raw->acdirmax;
+               real->acdirmin = raw->acdirmin;
+               real->acregmax = raw->acregmax;
+               real->acregmin = raw->acregmin;
+               real->retrans = raw->retrans;
+               real->timeo = raw->timeo;
+               real->wsize = raw->wsize;
+               real->rsize = raw->rsize;
+               real->flags = raw->flags;
+               real->version = raw->version;
+       }
+       else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 extern int copy_mount_options (const void __user *, unsigned long *);
 
 #define SMBFS_NAME      "smbfs"
 #define NCPFS_NAME      "ncpfs"
+#define NFS4_NAME      "nfs4"
 
 asmlinkage long compat_sys_mount(char __user * dev_name, char __user * dir_name,
                                 char __user * type, unsigned long flags,
@@ -845,6 +915,9 @@ asmlinkage long compat_sys_mount(char __user * dev_name, char __user * dir_name,
                        do_smb_super_data_conv((void *)data_page);
                } else if (!strcmp((char *)type_page, NCPFS_NAME)) {
                        do_ncp_super_data_conv((void *)data_page);
+               } else if (!strcmp((char *)type_page, NFS4_NAME)) {
+                       if (do_nfs4_super_data_conv((void *) data_page))
+                               goto out4;
                }
        }
 
@@ -853,6 +926,7 @@ asmlinkage long compat_sys_mount(char __user * dev_name, char __user * dir_name,
                        flags, (void*)data_page);
        unlock_kernel();
 
+ out4:
        free_page(data_page);
  out3:
        free_page(dev_page);