Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
[firefly-linux-kernel-4.4.55.git] / net / sunrpc / auth_gss / auth_gss.c
index affa631ac1abe3d42f75635a30e1f066dccbfb44..d3ad81f8da5b79551c36b17a7d53007406946699 100644 (file)
@@ -81,7 +81,7 @@ struct gss_auth {
         * mechanism (for example, "krb5") and exists for
         * backwards-compatibility with older gssd's.
         */
-       struct dentry *dentry[2];
+       struct rpc_pipe *pipe[2];
 };
 
 /* pipe_version >= 0 if and only if someone has a pipe open. */
@@ -112,7 +112,7 @@ gss_put_ctx(struct gss_cl_ctx *ctx)
 /* gss_cred_set_ctx:
  * called by gss_upcall_callback and gss_create_upcall in order
  * to set the gss context. The actual exchange of an old context
- * and a new one is protected by the inode->i_lock.
+ * and a new one is protected by the pipe->lock.
  */
 static void
 gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)
@@ -251,7 +251,7 @@ struct gss_upcall_msg {
        struct rpc_pipe_msg msg;
        struct list_head list;
        struct gss_auth *auth;
-       struct rpc_inode *inode;
+       struct rpc_pipe *pipe;
        struct rpc_wait_queue rpc_waitqueue;
        wait_queue_head_t waitqueue;
        struct gss_cl_ctx *ctx;
@@ -294,10 +294,10 @@ gss_release_msg(struct gss_upcall_msg *gss_msg)
 }
 
 static struct gss_upcall_msg *
-__gss_find_upcall(struct rpc_inode *rpci, uid_t uid)
+__gss_find_upcall(struct rpc_pipe *pipe, uid_t uid)
 {
        struct gss_upcall_msg *pos;
-       list_for_each_entry(pos, &rpci->in_downcall, list) {
+       list_for_each_entry(pos, &pipe->in_downcall, list) {
                if (pos->uid != uid)
                        continue;
                atomic_inc(&pos->count);
@@ -315,18 +315,17 @@ __gss_find_upcall(struct rpc_inode *rpci, uid_t uid)
 static inline struct gss_upcall_msg *
 gss_add_msg(struct gss_upcall_msg *gss_msg)
 {
-       struct rpc_inode *rpci = gss_msg->inode;
-       struct inode *inode = &rpci->vfs_inode;
+       struct rpc_pipe *pipe = gss_msg->pipe;
        struct gss_upcall_msg *old;
 
-       spin_lock(&inode->i_lock);
-       old = __gss_find_upcall(rpci, gss_msg->uid);
+       spin_lock(&pipe->lock);
+       old = __gss_find_upcall(pipe, gss_msg->uid);
        if (old == NULL) {
                atomic_inc(&gss_msg->count);
-               list_add(&gss_msg->list, &rpci->in_downcall);
+               list_add(&gss_msg->list, &pipe->in_downcall);
        } else
                gss_msg = old;
-       spin_unlock(&inode->i_lock);
+       spin_unlock(&pipe->lock);
        return gss_msg;
 }
 
@@ -342,14 +341,14 @@ __gss_unhash_msg(struct gss_upcall_msg *gss_msg)
 static void
 gss_unhash_msg(struct gss_upcall_msg *gss_msg)
 {
-       struct inode *inode = &gss_msg->inode->vfs_inode;
+       struct rpc_pipe *pipe = gss_msg->pipe;
 
        if (list_empty(&gss_msg->list))
                return;
-       spin_lock(&inode->i_lock);
+       spin_lock(&pipe->lock);
        if (!list_empty(&gss_msg->list))
                __gss_unhash_msg(gss_msg);
-       spin_unlock(&inode->i_lock);
+       spin_unlock(&pipe->lock);
 }
 
 static void
@@ -376,11 +375,11 @@ gss_upcall_callback(struct rpc_task *task)
        struct gss_cred *gss_cred = container_of(task->tk_rqstp->rq_cred,
                        struct gss_cred, gc_base);
        struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall;
-       struct inode *inode = &gss_msg->inode->vfs_inode;
+       struct rpc_pipe *pipe = gss_msg->pipe;
 
-       spin_lock(&inode->i_lock);
+       spin_lock(&pipe->lock);
        gss_handle_downcall_result(gss_cred, gss_msg);
-       spin_unlock(&inode->i_lock);
+       spin_unlock(&pipe->lock);
        task->tk_status = gss_msg->msg.errno;
        gss_release_msg(gss_msg);
 }
@@ -450,7 +449,7 @@ gss_alloc_msg(struct gss_auth *gss_auth, struct rpc_clnt *clnt,
                kfree(gss_msg);
                return ERR_PTR(vers);
        }
-       gss_msg->inode = RPC_I(gss_auth->dentry[vers]->d_inode);
+       gss_msg->pipe = gss_auth->pipe[vers];
        INIT_LIST_HEAD(&gss_msg->list);
        rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
        init_waitqueue_head(&gss_msg->waitqueue);
@@ -474,8 +473,7 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr
                return gss_new;
        gss_msg = gss_add_msg(gss_new);
        if (gss_msg == gss_new) {
-               struct inode *inode = &gss_new->inode->vfs_inode;
-               int res = rpc_queue_upcall(inode, &gss_new->msg);
+               int res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);
                if (res) {
                        gss_unhash_msg(gss_new);
                        gss_msg = ERR_PTR(res);
@@ -506,7 +504,7 @@ gss_refresh_upcall(struct rpc_task *task)
        struct gss_cred *gss_cred = container_of(cred,
                        struct gss_cred, gc_base);
        struct gss_upcall_msg *gss_msg;
-       struct inode *inode;
+       struct rpc_pipe *pipe;
        int err = 0;
 
        dprintk("RPC: %5u gss_refresh_upcall for uid %u\n", task->tk_pid,
@@ -524,8 +522,8 @@ gss_refresh_upcall(struct rpc_task *task)
                err = PTR_ERR(gss_msg);
                goto out;
        }
-       inode = &gss_msg->inode->vfs_inode;
-       spin_lock(&inode->i_lock);
+       pipe = gss_msg->pipe;
+       spin_lock(&pipe->lock);
        if (gss_cred->gc_upcall != NULL)
                rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL);
        else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) {
@@ -538,7 +536,7 @@ gss_refresh_upcall(struct rpc_task *task)
                gss_handle_downcall_result(gss_cred, gss_msg);
                err = gss_msg->msg.errno;
        }
-       spin_unlock(&inode->i_lock);
+       spin_unlock(&pipe->lock);
        gss_release_msg(gss_msg);
 out:
        dprintk("RPC: %5u gss_refresh_upcall for uid %u result %d\n",
@@ -549,7 +547,7 @@ out:
 static inline int
 gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
 {
-       struct inode *inode;
+       struct rpc_pipe *pipe;
        struct rpc_cred *cred = &gss_cred->gc_base;
        struct gss_upcall_msg *gss_msg;
        DEFINE_WAIT(wait);
@@ -573,14 +571,14 @@ retry:
                err = PTR_ERR(gss_msg);
                goto out;
        }
-       inode = &gss_msg->inode->vfs_inode;
+       pipe = gss_msg->pipe;
        for (;;) {
                prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_KILLABLE);
-               spin_lock(&inode->i_lock);
+               spin_lock(&pipe->lock);
                if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) {
                        break;
                }
-               spin_unlock(&inode->i_lock);
+               spin_unlock(&pipe->lock);
                if (fatal_signal_pending(current)) {
                        err = -ERESTARTSYS;
                        goto out_intr;
@@ -591,7 +589,7 @@ retry:
                gss_cred_set_ctx(cred, gss_msg->ctx);
        else
                err = gss_msg->msg.errno;
-       spin_unlock(&inode->i_lock);
+       spin_unlock(&pipe->lock);
 out_intr:
        finish_wait(&gss_msg->waitqueue, &wait);
        gss_release_msg(gss_msg);
@@ -609,7 +607,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
        const void *p, *end;
        void *buf;
        struct gss_upcall_msg *gss_msg;
-       struct inode *inode = filp->f_path.dentry->d_inode;
+       struct rpc_pipe *pipe = RPC_I(filp->f_dentry->d_inode)->pipe;
        struct gss_cl_ctx *ctx;
        uid_t uid;
        ssize_t err = -EFBIG;
@@ -639,14 +637,14 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
 
        err = -ENOENT;
        /* Find a matching upcall */
-       spin_lock(&inode->i_lock);
-       gss_msg = __gss_find_upcall(RPC_I(inode), uid);
+       spin_lock(&pipe->lock);
+       gss_msg = __gss_find_upcall(pipe, uid);
        if (gss_msg == NULL) {
-               spin_unlock(&inode->i_lock);
+               spin_unlock(&pipe->lock);
                goto err_put_ctx;
        }
        list_del_init(&gss_msg->list);
-       spin_unlock(&inode->i_lock);
+       spin_unlock(&pipe->lock);
 
        p = gss_fill_context(p, end, ctx, gss_msg->auth->mech);
        if (IS_ERR(p)) {
@@ -674,9 +672,9 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
        err = mlen;
 
 err_release_msg:
-       spin_lock(&inode->i_lock);
+       spin_lock(&pipe->lock);
        __gss_unhash_msg(gss_msg);
-       spin_unlock(&inode->i_lock);
+       spin_unlock(&pipe->lock);
        gss_release_msg(gss_msg);
 err_put_ctx:
        gss_put_ctx(ctx);
@@ -722,23 +720,23 @@ static int gss_pipe_open_v1(struct inode *inode)
 static void
 gss_pipe_release(struct inode *inode)
 {
-       struct rpc_inode *rpci = RPC_I(inode);
+       struct rpc_pipe *pipe = RPC_I(inode)->pipe;
        struct gss_upcall_msg *gss_msg;
 
 restart:
-       spin_lock(&inode->i_lock);
-       list_for_each_entry(gss_msg, &rpci->in_downcall, list) {
+       spin_lock(&pipe->lock);
+       list_for_each_entry(gss_msg, &pipe->in_downcall, list) {
 
                if (!list_empty(&gss_msg->msg.list))
                        continue;
                gss_msg->msg.errno = -EPIPE;
                atomic_inc(&gss_msg->count);
                __gss_unhash_msg(gss_msg);
-               spin_unlock(&inode->i_lock);
+               spin_unlock(&pipe->lock);
                gss_release_msg(gss_msg);
                goto restart;
        }
-       spin_unlock(&inode->i_lock);
+       spin_unlock(&pipe->lock);
 
        put_pipe_version();
 }
@@ -759,6 +757,75 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
        }
 }
 
+static void gss_pipes_dentries_destroy(struct rpc_auth *auth)
+{
+       struct gss_auth *gss_auth;
+
+       gss_auth = container_of(auth, struct gss_auth, rpc_auth);
+       if (gss_auth->pipe[0]->dentry)
+               rpc_unlink(gss_auth->pipe[0]->dentry);
+       if (gss_auth->pipe[1]->dentry)
+               rpc_unlink(gss_auth->pipe[1]->dentry);
+}
+
+static int gss_pipes_dentries_create(struct rpc_auth *auth)
+{
+       int err;
+       struct gss_auth *gss_auth;
+       struct rpc_clnt *clnt;
+
+       gss_auth = container_of(auth, struct gss_auth, rpc_auth);
+       clnt = gss_auth->client;
+
+       gss_auth->pipe[1]->dentry = rpc_mkpipe_dentry(clnt->cl_dentry,
+                                                     "gssd",
+                                                     clnt, gss_auth->pipe[1]);
+       if (IS_ERR(gss_auth->pipe[1]->dentry))
+               return PTR_ERR(gss_auth->pipe[1]->dentry);
+       gss_auth->pipe[0]->dentry = rpc_mkpipe_dentry(clnt->cl_dentry,
+                                                     gss_auth->mech->gm_name,
+                                                     clnt, gss_auth->pipe[0]);
+       if (IS_ERR(gss_auth->pipe[0]->dentry)) {
+               err = PTR_ERR(gss_auth->pipe[0]->dentry);
+               goto err_unlink_pipe_1;
+       }
+       return 0;
+
+err_unlink_pipe_1:
+       rpc_unlink(gss_auth->pipe[1]->dentry);
+       return err;
+}
+
+static void gss_pipes_dentries_destroy_net(struct rpc_clnt *clnt,
+                                          struct rpc_auth *auth)
+{
+       struct net *net = rpc_net_ns(clnt);
+       struct super_block *sb;
+
+       sb = rpc_get_sb_net(net);
+       if (sb) {
+               if (clnt->cl_dentry)
+                       gss_pipes_dentries_destroy(auth);
+               rpc_put_sb_net(net);
+       }
+}
+
+static int gss_pipes_dentries_create_net(struct rpc_clnt *clnt,
+                                        struct rpc_auth *auth)
+{
+       struct net *net = rpc_net_ns(clnt);
+       struct super_block *sb;
+       int err = 0;
+
+       sb = rpc_get_sb_net(net);
+       if (sb) {
+               if (clnt->cl_dentry)
+                       err = gss_pipes_dentries_create(auth);
+               rpc_put_sb_net(net);
+       }
+       return err;
+}
+
 /*
  * NOTE: we have the opportunity to use different
  * parameters based on the input flavor (which must be a pseudoflavor)
@@ -801,32 +868,33 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
         * that we supported only the old pipe.  So we instead create
         * the new pipe first.
         */
-       gss_auth->dentry[1] = rpc_mkpipe(clnt->cl_path.dentry,
-                                        "gssd",
-                                        clnt, &gss_upcall_ops_v1,
-                                        RPC_PIPE_WAIT_FOR_OPEN);
-       if (IS_ERR(gss_auth->dentry[1])) {
-               err = PTR_ERR(gss_auth->dentry[1]);
+       gss_auth->pipe[1] = rpc_mkpipe_data(&gss_upcall_ops_v1,
+                                           RPC_PIPE_WAIT_FOR_OPEN);
+       if (IS_ERR(gss_auth->pipe[1])) {
+               err = PTR_ERR(gss_auth->pipe[1]);
                goto err_put_mech;
        }
 
-       gss_auth->dentry[0] = rpc_mkpipe(clnt->cl_path.dentry,
-                                        gss_auth->mech->gm_name,
-                                        clnt, &gss_upcall_ops_v0,
-                                        RPC_PIPE_WAIT_FOR_OPEN);
-       if (IS_ERR(gss_auth->dentry[0])) {
-               err = PTR_ERR(gss_auth->dentry[0]);
-               goto err_unlink_pipe_1;
+       gss_auth->pipe[0] = rpc_mkpipe_data(&gss_upcall_ops_v0,
+                                           RPC_PIPE_WAIT_FOR_OPEN);
+       if (IS_ERR(gss_auth->pipe[0])) {
+               err = PTR_ERR(gss_auth->pipe[0]);
+               goto err_destroy_pipe_1;
        }
+       err = gss_pipes_dentries_create_net(clnt, auth);
+       if (err)
+               goto err_destroy_pipe_0;
        err = rpcauth_init_credcache(auth);
        if (err)
-               goto err_unlink_pipe_0;
+               goto err_unlink_pipes;
 
        return auth;
-err_unlink_pipe_0:
-       rpc_unlink(gss_auth->dentry[0]);
-err_unlink_pipe_1:
-       rpc_unlink(gss_auth->dentry[1]);
+err_unlink_pipes:
+       gss_pipes_dentries_destroy_net(clnt, auth);
+err_destroy_pipe_0:
+       rpc_destroy_pipe_data(gss_auth->pipe[0]);
+err_destroy_pipe_1:
+       rpc_destroy_pipe_data(gss_auth->pipe[1]);
 err_put_mech:
        gss_mech_put(gss_auth->mech);
 err_free:
@@ -839,8 +907,9 @@ out_dec:
 static void
 gss_free(struct gss_auth *gss_auth)
 {
-       rpc_unlink(gss_auth->dentry[1]);
-       rpc_unlink(gss_auth->dentry[0]);
+       gss_pipes_dentries_destroy_net(gss_auth->client, &gss_auth->rpc_auth);
+       rpc_destroy_pipe_data(gss_auth->pipe[0]);
+       rpc_destroy_pipe_data(gss_auth->pipe[1]);
        gss_mech_put(gss_auth->mech);
 
        kfree(gss_auth);
@@ -1547,7 +1616,9 @@ static const struct rpc_authops authgss_ops = {
        .create         = gss_create,
        .destroy        = gss_destroy,
        .lookup_cred    = gss_lookup_cred,
-       .crcreate       = gss_create_cred
+       .crcreate       = gss_create_cred,
+       .pipes_create   = gss_pipes_dentries_create,
+       .pipes_destroy  = gss_pipes_dentries_destroy,
 };
 
 static const struct rpc_credops gss_credops = {
@@ -1591,6 +1662,21 @@ static const struct rpc_pipe_ops gss_upcall_ops_v1 = {
        .release_pipe   = gss_pipe_release,
 };
 
+static __net_init int rpcsec_gss_init_net(struct net *net)
+{
+       return gss_svc_init_net(net);
+}
+
+static __net_exit void rpcsec_gss_exit_net(struct net *net)
+{
+       gss_svc_shutdown_net(net);
+}
+
+static struct pernet_operations rpcsec_gss_net_ops = {
+       .init = rpcsec_gss_init_net,
+       .exit = rpcsec_gss_exit_net,
+};
+
 /*
  * Initialize RPCSEC_GSS module
  */
@@ -1604,8 +1690,13 @@ static int __init init_rpcsec_gss(void)
        err = gss_svc_init();
        if (err)
                goto out_unregister;
+       err = register_pernet_subsys(&rpcsec_gss_net_ops);
+       if (err)
+               goto out_svc_exit;
        rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version");
        return 0;
+out_svc_exit:
+       gss_svc_shutdown();
 out_unregister:
        rpcauth_unregister(&authgss_ops);
 out:
@@ -1614,6 +1705,7 @@ out:
 
 static void __exit exit_rpcsec_gss(void)
 {
+       unregister_pernet_subsys(&rpcsec_gss_net_ops);
        gss_svc_shutdown();
        rpcauth_unregister(&authgss_ops);
        rcu_barrier(); /* Wait for completion of call_rcu()'s */