nilfs2: fix lock order reversal in chcp operation
[safe/jmp/linux-2.6] / fs / nfs / super.c
index 599e8c5..90be551 100644 (file)
@@ -73,7 +73,7 @@ enum {
        Opt_cto, Opt_nocto,
        Opt_ac, Opt_noac,
        Opt_lock, Opt_nolock,
-       Opt_v2, Opt_v3,
+       Opt_v2, Opt_v3, Opt_v4,
        Opt_udp, Opt_tcp, Opt_rdma,
        Opt_acl, Opt_noacl,
        Opt_rdirplus, Opt_nordirplus,
@@ -127,6 +127,7 @@ static const match_table_t nfs_mount_option_tokens = {
        { Opt_nolock, "nolock" },
        { Opt_v2, "v2" },
        { Opt_v3, "v3" },
+       { Opt_v4, "v4" },
        { Opt_udp, "udp" },
        { Opt_tcp, "tcp" },
        { Opt_rdma, "rdma" },
@@ -274,6 +275,8 @@ static const struct super_operations nfs_sops = {
 #ifdef CONFIG_NFS_V4
 static int nfs4_validate_text_mount_data(void *options,
        struct nfs_parsed_mount_data *args, const char *dev_name);
+static int nfs4_try_mount(int flags, const char *dev_name,
+       struct nfs_parsed_mount_data *data, struct vfsmount *mnt);
 static int nfs4_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
 static int nfs4_remote_get_sb(struct file_system_type *fs_type,
@@ -725,6 +728,29 @@ static void nfs_umount_begin(struct super_block *sb)
        unlock_kernel();
 }
 
+static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(unsigned int version)
+{
+       struct nfs_parsed_mount_data *data;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (data) {
+               data->rsize             = NFS_MAX_FILE_IO_SIZE;
+               data->wsize             = NFS_MAX_FILE_IO_SIZE;
+               data->acregmin          = NFS_DEF_ACREGMIN;
+               data->acregmax          = NFS_DEF_ACREGMAX;
+               data->acdirmin          = NFS_DEF_ACDIRMIN;
+               data->acdirmax          = NFS_DEF_ACDIRMAX;
+               data->mount_server.port = NFS_UNSPEC_PORT;
+               data->nfs_server.port   = NFS_UNSPEC_PORT;
+               data->nfs_server.protocol = XPRT_TRANSPORT_TCP;
+               data->auth_flavors[0]   = RPC_AUTH_UNIX;
+               data->auth_flavor_len   = 1;
+               data->version           = version;
+               data->minorversion      = 0;
+       }
+       return data;
+}
+
 /*
  * Sanity-check a server address provided by the mount command.
  *
@@ -752,15 +778,13 @@ static int nfs_verify_server_address(struct sockaddr *addr)
  * Select between a default port value and a user-specified port value.
  * If a zero value is set, then autobind will be used.
  */
-static void nfs_set_default_port(struct sockaddr *sap, const int parsed_port,
+static void nfs_set_port(struct sockaddr *sap, int *port,
                                 const unsigned short default_port)
 {
-       unsigned short port = default_port;
-
-       if (parsed_port != NFS_UNSPEC_PORT)
-               port = parsed_port;
+       if (*port == NFS_UNSPEC_PORT)
+               *port = default_port;
 
-       rpc_set_port(sap, port);
+       rpc_set_port(sap, *port);
 }
 
 /*
@@ -932,10 +956,18 @@ static int nfs_parse_mount_options(char *raw,
                        break;
                case Opt_v2:
                        mnt->flags &= ~NFS_MOUNT_VER3;
+                       mnt->version = 2;
                        break;
                case Opt_v3:
                        mnt->flags |= NFS_MOUNT_VER3;
+                       mnt->version = 3;
+                       break;
+#ifdef CONFIG_NFS_V4
+               case Opt_v4:
+                       mnt->flags &= ~NFS_MOUNT_VER3;
+                       mnt->version = 4;
                        break;
+#endif
                case Opt_udp:
                        mnt->flags &= ~NFS_MOUNT_TCP;
                        mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP;
@@ -1149,10 +1181,18 @@ static int nfs_parse_mount_options(char *raw,
                        switch (option) {
                        case NFS2_VERSION:
                                mnt->flags &= ~NFS_MOUNT_VER3;
+                               mnt->version = 2;
                                break;
                        case NFS3_VERSION:
                                mnt->flags |= NFS_MOUNT_VER3;
+                               mnt->version = 3;
+                               break;
+#ifdef CONFIG_NFS_V4
+                       case NFS4_VERSION:
+                               mnt->flags &= ~NFS_MOUNT_VER3;
+                               mnt->version = 4;
                                break;
+#endif
                        default:
                                goto out_invalid_value;
                        }
@@ -1213,6 +1253,7 @@ static int nfs_parse_mount_options(char *raw,
                        default:
                                dfprintk(MOUNT, "NFS:   unrecognized "
                                                "transport protocol\n");
+                               kfree(string);
                                return 0;
                        }
                        break;
@@ -1411,10 +1452,13 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
        int status;
 
        if (args->mount_server.version == 0) {
-               if (args->flags & NFS_MOUNT_VER3)
-                       args->mount_server.version = NFS_MNT3_VERSION;
-               else
-                       args->mount_server.version = NFS_MNT_VERSION;
+               switch (args->version) {
+                       default:
+                               args->mount_server.version = NFS_MNT3_VERSION;
+                               break;
+                       case 2:
+                               args->mount_server.version = NFS_MNT_VERSION;
+               }
        }
        request.version = args->mount_server.version;
 
@@ -1432,7 +1476,7 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
                args->mount_server.addrlen = args->nfs_server.addrlen;
        }
        request.salen = args->mount_server.addrlen;
-       nfs_set_default_port(request.sap, args->mount_server.port, 0);
+       nfs_set_port(request.sap, &args->mount_server.port, 0);
 
        /*
         * Now ask the mount server to map our export path
@@ -1615,19 +1659,6 @@ static int nfs_validate_mount_data(void *options,
        if (data == NULL)
                goto out_no_data;
 
-       args->flags             = (NFS_MOUNT_VER3 | NFS_MOUNT_TCP);
-       args->rsize             = NFS_MAX_FILE_IO_SIZE;
-       args->wsize             = NFS_MAX_FILE_IO_SIZE;
-       args->acregmin          = NFS_DEF_ACREGMIN;
-       args->acregmax          = NFS_DEF_ACREGMAX;
-       args->acdirmin          = NFS_DEF_ACDIRMIN;
-       args->acdirmax          = NFS_DEF_ACDIRMAX;
-       args->mount_server.port = NFS_UNSPEC_PORT;
-       args->nfs_server.port   = NFS_UNSPEC_PORT;
-       args->nfs_server.protocol = XPRT_TRANSPORT_TCP;
-       args->auth_flavors[0]   = RPC_AUTH_UNIX;
-       args->auth_flavor_len   = 1;
-
        switch (data->version) {
        case 1:
                data->namlen = 0;
@@ -1648,8 +1679,11 @@ static int nfs_validate_mount_data(void *options,
                        if (data->root.size > NFS3_FHSIZE || data->root.size == 0)
                                goto out_invalid_fh;
                        mntfh->size = data->root.size;
-               } else
+                       args->version = 3;
+               } else {
                        mntfh->size = NFS2_FHSIZE;
+                       args->version = 2;
+               }
 
 
                memcpy(mntfh->data, data->root.data, mntfh->size);
@@ -1724,7 +1758,15 @@ static int nfs_validate_mount_data(void *options,
                if (!nfs_verify_server_address(sap))
                        goto out_no_address;
 
-               nfs_set_default_port(sap, args->nfs_server.port, 0);
+               if (args->version == 4)
+#ifdef CONFIG_NFS_V4
+                       return nfs4_validate_text_mount_data(options,
+                                                            args, dev_name);
+#else
+                       goto out_v4_not_compiled;
+#endif
+
+               nfs_set_port(sap, &args->nfs_server.port, 0);
 
                nfs_set_mount_transport_protocol(args);
 
@@ -1747,7 +1789,7 @@ static int nfs_validate_mount_data(void *options,
        }
 
 #ifndef CONFIG_NFS_V3
-       if (args->flags & NFS_MOUNT_VER3)
+       if (args->version == 3)
                goto out_v3_not_compiled;
 #endif /* !CONFIG_NFS_V3 */
 
@@ -1772,6 +1814,12 @@ out_v3_not_compiled:
        return -EPROTONOSUPPORT;
 #endif /* !CONFIG_NFS_V3 */
 
+#ifndef CONFIG_NFS_V4
+out_v4_not_compiled:
+       dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n");
+       return -EPROTONOSUPPORT;
+#endif /* !CONFIG_NFS_V4 */
+
 out_nomem:
        dfprintk(MOUNT, "NFS: not enough memory to handle mount options\n");
        return -ENOMEM;
@@ -1799,9 +1847,10 @@ nfs_compare_remount_data(struct nfs_server *nfss,
            data->acdirmin != nfss->acdirmin / HZ ||
            data->acdirmax != nfss->acdirmax / HZ ||
            data->timeo != (10U * nfss->client->cl_timeout->to_initval / HZ) ||
+           data->nfs_server.port != nfss->port ||
            data->nfs_server.addrlen != nfss->nfs_client->cl_addrlen ||
-           memcmp(&data->nfs_server.address, &nfss->nfs_client->cl_addr,
-                  data->nfs_server.addrlen) != 0)
+           !rpc_cmp_addr((struct sockaddr *)&data->nfs_server.address,
+                         (struct sockaddr *)&nfss->nfs_client->cl_addr))
                return -EINVAL;
 
        return 0;
@@ -1844,6 +1893,7 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
        data->acdirmin = nfss->acdirmin / HZ;
        data->acdirmax = nfss->acdirmax / HZ;
        data->timeo = 10U * nfss->client->cl_timeout->to_initval / HZ;
+       data->nfs_server.port = nfss->port;
        data->nfs_server.addrlen = nfss->nfs_client->cl_addrlen;
        memcpy(&data->nfs_server.address, &nfss->nfs_client->cl_addr,
                data->nfs_server.addrlen);
@@ -1881,6 +1931,8 @@ static inline void nfs_initialise_sb(struct super_block *sb)
        if (server->flags & NFS_MOUNT_NOAC)
                sb->s_flags |= MS_SYNCHRONOUS;
 
+       sb->s_bdi = &server->backing_dev_info;
+
        nfs_super_set_maxbytes(sb, server->maxfilesize);
 }
 
@@ -1897,7 +1949,7 @@ static void nfs_fill_super(struct super_block *sb,
        if (data->bsize)
                sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
 
-       if (server->flags & NFS_MOUNT_VER3) {
+       if (server->nfs_client->rpc_ops->version == 3) {
                /* The VFS shouldn't apply the umask to mode bits. We will do
                 * so ourselves when necessary.
                 */
@@ -1921,7 +1973,7 @@ static void nfs_clone_super(struct super_block *sb,
        sb->s_blocksize = old_sb->s_blocksize;
        sb->s_maxbytes = old_sb->s_maxbytes;
 
-       if (server->flags & NFS_MOUNT_VER3) {
+       if (server->nfs_client->rpc_ops->version == 3) {
                /* The VFS shouldn't apply the umask to mode bits. We will do
                 * so ourselves when necessary.
                 */
@@ -2055,7 +2107,7 @@ static int nfs_get_sb(struct file_system_type *fs_type,
        };
        int error = -ENOMEM;
 
-       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       data = nfs_alloc_parsed_mount_data(3);
        mntfh = kzalloc(sizeof(*mntfh), GFP_KERNEL);
        if (data == NULL || mntfh == NULL)
                goto out_free_fh;
@@ -2067,6 +2119,14 @@ static int nfs_get_sb(struct file_system_type *fs_type,
        if (error < 0)
                goto out;
 
+#ifdef CONFIG_NFS_V4
+       if (data->version == 4) {
+               error = nfs4_try_mount(flags, dev_name, data, mnt);
+               kfree(data->client_address);
+               goto out;
+       }
+#endif /* CONFIG_NFS_V4 */
+
        /* Get a volume representation */
        server = nfs_create_server(data, mntfh);
        if (IS_ERR(server)) {
@@ -2097,7 +2157,8 @@ static int nfs_get_sb(struct file_system_type *fs_type,
        if (!s->s_root) {
                /* initial superblock/root creation */
                nfs_fill_super(s, data);
-               nfs_fscache_get_super_cookie(s, data);
+               nfs_fscache_get_super_cookie(
+                       s, data ? data->fscache_uniq : NULL, NULL);
        }
 
        mntroot = nfs_get_root(s, mntfh);
@@ -2143,8 +2204,8 @@ static void nfs_kill_super(struct super_block *s)
 {
        struct nfs_server *server = NFS_SB(s);
 
-       bdi_unregister(&server->backing_dev_info);
        kill_anon_super(s);
+       bdi_unregister(&server->backing_dev_info);
        nfs_fscache_release_super_cookie(s);
        nfs_free_server(server);
 }
@@ -2198,6 +2259,7 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags,
        if (!s->s_root) {
                /* initial superblock/root creation */
                nfs_clone_super(s, data->sb);
+               nfs_fscache_get_super_cookie(s, NULL, data);
        }
 
        mntroot = nfs_get_root(s, data->fh);
@@ -2270,12 +2332,18 @@ static int nfs4_validate_text_mount_data(void *options,
 {
        struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
 
-       nfs_set_default_port(sap, args->nfs_server.port, NFS_PORT);
+       nfs_set_port(sap, &args->nfs_server.port, NFS_PORT);
 
        nfs_validate_transport_protocol(args);
 
        nfs4_validate_mount_flags(args);
 
+       if (args->version != 4) {
+               dfprintk(MOUNT,
+                        "NFS4: Illegal mount version\n");
+               return -EINVAL;
+       }
+
        if (args->auth_flavor_len > 1) {
                dfprintk(MOUNT,
                         "NFS4: Too many RPC auth flavours specified\n");
@@ -2309,17 +2377,6 @@ static int nfs4_validate_mount_data(void *options,
        if (data == NULL)
                goto out_no_data;
 
-       args->rsize             = NFS_MAX_FILE_IO_SIZE;
-       args->wsize             = NFS_MAX_FILE_IO_SIZE;
-       args->acregmin          = NFS_DEF_ACREGMIN;
-       args->acregmax          = NFS_DEF_ACREGMAX;
-       args->acdirmin          = NFS_DEF_ACDIRMIN;
-       args->acdirmax          = NFS_DEF_ACDIRMAX;
-       args->nfs_server.port   = NFS_UNSPEC_PORT;
-       args->auth_flavors[0]   = RPC_AUTH_UNIX;
-       args->auth_flavor_len   = 1;
-       args->minorversion      = 0;
-
        switch (data->version) {
        case 1:
                if (data->host_addrlen > sizeof(args->nfs_server.address))
@@ -2454,7 +2511,8 @@ static int nfs4_remote_get_sb(struct file_system_type *fs_type,
        if (!s->s_root) {
                /* initial superblock/root creation */
                nfs4_fill_super(s);
-               nfs_fscache_get_super_cookie(s, data);
+               nfs_fscache_get_super_cookie(
+                       s, data ? data->fscache_uniq : NULL, NULL);
        }
 
        mntroot = nfs4_get_root(s, mntfh);
@@ -2565,6 +2623,34 @@ out_err:
        return ret;
 }
 
+static int nfs4_try_mount(int flags, const char *dev_name,
+                        struct nfs_parsed_mount_data *data,
+                        struct vfsmount *mnt)
+{
+       char *export_path;
+       struct vfsmount *root_mnt;
+       int error;
+
+       dfprintk(MOUNT, "--> nfs4_try_mount()\n");
+
+       export_path = data->nfs_server.export_path;
+       data->nfs_server.export_path = "/";
+       root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, data,
+                       data->nfs_server.hostname);
+       data->nfs_server.export_path = export_path;
+
+       error = PTR_ERR(root_mnt);
+       if (IS_ERR(root_mnt))
+               goto out;
+
+       error = nfs_follow_remote_path(root_mnt, export_path, mnt);
+
+out:
+       dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n", error,
+                       error != 0 ? " [error]" : "");
+       return error;
+}
+
 /*
  * Get the superblock for an NFS4 mountpoint
  */
@@ -2572,11 +2658,9 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
 {
        struct nfs_parsed_mount_data *data;
-       char *export_path;
-       struct vfsmount *root_mnt;
        int error = -ENOMEM;
 
-       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       data = nfs_alloc_parsed_mount_data(4);
        if (data == NULL)
                goto out_free_data;
 
@@ -2585,17 +2669,7 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
        if (error < 0)
                goto out;
 
-       export_path = data->nfs_server.export_path;
-       data->nfs_server.export_path = "/";
-       root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, data,
-                       data->nfs_server.hostname);
-       data->nfs_server.export_path = export_path;
-
-       error = PTR_ERR(root_mnt);
-       if (IS_ERR(root_mnt))
-               goto out;
-
-       error = nfs_follow_remote_path(root_mnt, export_path, mnt);
+       error = nfs4_try_mount(flags, dev_name, data, mnt);
 
 out:
        kfree(data->client_address);
@@ -2616,7 +2690,6 @@ static void nfs4_kill_super(struct super_block *sb)
        dprintk("--> %s\n", __func__);
        nfs_super_return_all_delegations(sb);
        kill_anon_super(sb);
-       nfs4_renewd_prepare_shutdown(server);
        nfs_fscache_release_super_cookie(sb);
        nfs_free_server(server);
        dprintk("<-- %s\n", __func__);
@@ -2671,6 +2744,7 @@ static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags,
        if (!s->s_root) {
                /* initial superblock/root creation */
                nfs4_clone_super(s, data->sb);
+               nfs_fscache_get_super_cookie(s, NULL, data);
        }
 
        mntroot = nfs4_get_root(s, data->fh);
@@ -2752,6 +2826,7 @@ static int nfs4_remote_referral_get_sb(struct file_system_type *fs_type,
        if (!s->s_root) {
                /* initial superblock/root creation */
                nfs4_fill_super(s);
+               nfs_fscache_get_super_cookie(s, NULL, data);
        }
 
        mntroot = nfs4_get_root(s, &mntfh);