diff --git a/fs/compat.c b/fs/compat.c index a912bdf691cfca03e07eba1b5753d58779dabf66..67c0b94d11485fc4a0449aeb2d911636567daeea 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -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);