SUNRPC: Introduce rpc_clone_client_set_auth()
authorChuck Lever <chuck.lever@oracle.com>
Fri, 14 Sep 2012 21:24:02 +0000 (17:24 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Mon, 1 Oct 2012 22:33:33 +0000 (15:33 -0700)
An ULP is supposed to be able to replace a GSS rpc_auth object with
another GSS rpc_auth object using rpcauth_create().  However,
rpcauth_create() in 3.5 reliably fails with -EEXIST in this case.
This is because when gss_create() attempts to create the upcall pipes,
sometimes they are already there.  For example if a pipe FS mount
event occurs, or a previous GSS flavor was in use for this rpc_clnt.

It turns out that's not the only problem here.  While working on a
fix for the above problem, we noticed that replacing an rpc_clnt's
rpc_auth is not safe, since dereferencing the cl_auth field is not
protected in any way.

So we're deprecating the ability of rpcauth_create() to switch an
rpc_clnt's security flavor during normal operation.  Instead, let's
add a fresh API that clones an rpc_clnt and gives the clone a new
flavor before it's used.

This makes immediate use of the new __rpc_clone_client() helper.

This can be used in a similar fashion to rpcauth_create() when a
client is hunting for the correct security flavor.  Instead of
replacing an rpc_clnt's security flavor in a loop, the ULP replaces
the whole rpc_clnt.

To fix the -EEXIST problem, any ULP logic that relies on replacing
an rpc_clnt's rpc_auth with rpcauth_create() must be changed to use
this API instead.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/client.c
fs/nfs/nfs4namespace.c
include/linux/sunrpc/clnt.h
net/sunrpc/clnt.c

index 99694442b93f6d64f5f433d8cf3cc8fdc08de52e..143149db3440e7ec33906c443e6694bc4fbaf282 100644 (file)
@@ -668,7 +668,8 @@ int nfs_init_server_rpcclient(struct nfs_server *server,
 {
        struct nfs_client *clp = server->nfs_client;
 
-       server->client = rpc_clone_client(clp->cl_rpcclient);
+       server->client = rpc_clone_client_set_auth(clp->cl_rpcclient,
+                                                       pseudoflavour);
        if (IS_ERR(server->client)) {
                dprintk("%s: couldn't create rpc_client!\n", __func__);
                return PTR_ERR(server->client);
@@ -678,16 +679,6 @@ int nfs_init_server_rpcclient(struct nfs_server *server,
                        timeo,
                        sizeof(server->client->cl_timeout_default));
        server->client->cl_timeout = &server->client->cl_timeout_default;
-
-       if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) {
-               struct rpc_auth *auth;
-
-               auth = rpcauth_create(pseudoflavour, server->client);
-               if (IS_ERR(auth)) {
-                       dprintk("%s: couldn't create credcache!\n", __func__);
-                       return PTR_ERR(auth);
-               }
-       }
        server->client->cl_softrtry = 0;
        if (server->flags & NFS_MOUNT_SOFT)
                server->client->cl_softrtry = 1;
index 4fdeb1b7042eb94378f7448275d4ee4ef4ec13bc..79fbb61ce202bcb90df2c8ad0f7f3ecf3bba7aac 100644 (file)
@@ -192,25 +192,13 @@ out:
 struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *clnt, struct inode *inode,
                                        struct qstr *name)
 {
-       struct rpc_clnt *clone;
-       struct rpc_auth *auth;
        rpc_authflavor_t flavor;
 
        flavor = nfs4_negotiate_security(inode, name);
        if ((int)flavor < 0)
                return ERR_PTR((int)flavor);
 
-       clone = rpc_clone_client(clnt);
-       if (IS_ERR(clone))
-               return clone;
-
-       auth = rpcauth_create(flavor, clone);
-       if (IS_ERR(auth)) {
-               rpc_shutdown_client(clone);
-               clone = ERR_PTR(-EIO);
-       }
-
-       return clone;
+       return rpc_clone_client_set_auth(clnt, flavor);
 }
 
 static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
index 523547ecfee2812c0f8d7fa069f7c8ac3f66ab2d..34206b84d8dac9208401c48e92713c1d8ca11a1f 100644 (file)
@@ -130,6 +130,8 @@ struct rpc_clnt     *rpc_bind_new_program(struct rpc_clnt *,
                                const struct rpc_program *, u32);
 void rpc_task_reset_client(struct rpc_task *task, struct rpc_clnt *clnt);
 struct rpc_clnt *rpc_clone_client(struct rpc_clnt *);
+struct rpc_clnt *rpc_clone_client_set_auth(struct rpc_clnt *,
+                               rpc_authflavor_t);
 void           rpc_shutdown_client(struct rpc_clnt *);
 void           rpc_release_client(struct rpc_clnt *);
 void           rpc_task_release_client(struct rpc_task *);
index afbeefab6600e06f234636218c6893f82f17207c..cdc7564b4512d7f79606bc87fb7726a668a9b9a0 100644 (file)
@@ -548,6 +548,28 @@ struct rpc_clnt *rpc_clone_client(struct rpc_clnt *clnt)
 }
 EXPORT_SYMBOL_GPL(rpc_clone_client);
 
+/**
+ * rpc_clone_client_set_auth - Clone an RPC client structure and set its auth
+ *
+ * @clnt: RPC client whose parameters are copied
+ * @auth: security flavor for new client
+ *
+ * Returns a fresh RPC client or an ERR_PTR.
+ */
+struct rpc_clnt *
+rpc_clone_client_set_auth(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
+{
+       struct rpc_create_args args = {
+               .program        = clnt->cl_program,
+               .prognumber     = clnt->cl_prog,
+               .version        = clnt->cl_vers,
+               .authflavor     = flavor,
+               .client_name    = clnt->cl_principal,
+       };
+       return __rpc_clone_client(&args, clnt);
+}
+EXPORT_SYMBOL_GPL(rpc_clone_client_set_auth);
+
 /*
  * Kill all tasks for the given client.
  * XXX: kill their descendants as well?