Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[~shefty/rdma-dev.git] / fs / nfs / idmap.c
index 957134b4c0fd47c59977607aa27b05d096bd0e0c..bc3968fa81e53c1164b9ebb93c7f122303731143 100644 (file)
 static const struct cred *id_resolver_cache;
 static struct key_type key_type_id_resolver_legacy;
 
-struct idmap {
-       struct rpc_pipe         *idmap_pipe;
-       struct key_construction *idmap_key_cons;
-       struct mutex            idmap_mutex;
-};
-
 struct idmap_legacy_upcalldata {
        struct rpc_pipe_msg pipe_msg;
        struct idmap_msg idmap_msg;
+       struct key_construction *key_cons;
        struct idmap *idmap;
 };
 
+struct idmap {
+       struct rpc_pipe         *idmap_pipe;
+       struct idmap_legacy_upcalldata *idmap_upcall_data;
+       struct mutex            idmap_mutex;
+};
+
 /**
  * nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields
  * @fattr: fully initialised struct nfs_fattr
@@ -158,7 +159,7 @@ static int nfs_map_string_to_numeric(const char *name, size_t namelen, __u32 *re
                return 0;
        memcpy(buf, name, namelen);
        buf[namelen] = '\0';
-       if (strict_strtoul(buf, 0, &val) != 0)
+       if (kstrtoul(buf, 0, &val) != 0)
                return 0;
        *res = val;
        return 1;
@@ -326,7 +327,6 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
                ret = nfs_idmap_request_key(&key_type_id_resolver_legacy,
                                            name, namelen, type, data,
                                            data_size, idmap);
-               idmap->idmap_key_cons = NULL;
                mutex_unlock(&idmap->idmap_mutex);
        }
        return ret;
@@ -360,7 +360,7 @@ static int nfs_idmap_lookup_id(const char *name, size_t namelen, const char *typ
        if (data_size <= 0) {
                ret = -EINVAL;
        } else {
-               ret = strict_strtol(id_str, 10, &id_long);
+               ret = kstrtol(id_str, 10, &id_long);
                *id = (__u32)id_long;
        }
        return ret;
@@ -461,8 +461,6 @@ nfs_idmap_new(struct nfs_client *clp)
        struct rpc_pipe *pipe;
        int error;
 
-       BUG_ON(clp->cl_idmap != NULL);
-
        idmap = kzalloc(sizeof(*idmap), GFP_KERNEL);
        if (idmap == NULL)
                return -ENOMEM;
@@ -506,7 +504,6 @@ static int __rpc_pipefs_event(struct nfs_client *clp, unsigned long event,
 
        switch (event) {
        case RPC_PIPEFS_MOUNT:
-               BUG_ON(clp->cl_rpcclient->cl_dentry == NULL);
                err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry,
                                                clp->cl_idmap,
                                                clp->cl_idmap->idmap_pipe);
@@ -628,9 +625,6 @@ static int nfs_idmap_prepare_message(char *desc, struct idmap *idmap,
        substring_t substr;
        int token, ret;
 
-       memset(im,  0, sizeof(*im));
-       memset(msg, 0, sizeof(*msg));
-
        im->im_type = IDMAP_TYPE_GROUP;
        token = match_token(desc, nfs_idmap_tokens, &substr);
 
@@ -661,6 +655,35 @@ out:
        return ret;
 }
 
+static bool
+nfs_idmap_prepare_pipe_upcall(struct idmap *idmap,
+               struct idmap_legacy_upcalldata *data)
+{
+       if (idmap->idmap_upcall_data != NULL) {
+               WARN_ON_ONCE(1);
+               return false;
+       }
+       idmap->idmap_upcall_data = data;
+       return true;
+}
+
+static void
+nfs_idmap_complete_pipe_upcall_locked(struct idmap *idmap, int ret)
+{
+       struct key_construction *cons = idmap->idmap_upcall_data->key_cons;
+
+       kfree(idmap->idmap_upcall_data);
+       idmap->idmap_upcall_data = NULL;
+       complete_request_key(cons, ret);
+}
+
+static void
+nfs_idmap_abort_pipe_upcall(struct idmap *idmap, int ret)
+{
+       if (idmap->idmap_upcall_data != NULL)
+               nfs_idmap_complete_pipe_upcall_locked(idmap, ret);
+}
+
 static int nfs_idmap_legacy_upcall(struct key_construction *cons,
                                   const char *op,
                                   void *aux)
@@ -673,29 +696,28 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
        int ret = -ENOMEM;
 
        /* msg and im are freed in idmap_pipe_destroy_msg */
-       data = kmalloc(sizeof(*data), GFP_KERNEL);
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (!data)
                goto out1;
 
        msg = &data->pipe_msg;
        im = &data->idmap_msg;
        data->idmap = idmap;
+       data->key_cons = cons;
 
        ret = nfs_idmap_prepare_message(key->description, idmap, im, msg);
        if (ret < 0)
                goto out2;
 
-       BUG_ON(idmap->idmap_key_cons != NULL);
-       idmap->idmap_key_cons = cons;
+       ret = -EAGAIN;
+       if (!nfs_idmap_prepare_pipe_upcall(idmap, data))
+               goto out2;
 
        ret = rpc_queue_upcall(idmap->idmap_pipe, msg);
        if (ret < 0)
-               goto out3;
+               nfs_idmap_abort_pipe_upcall(idmap, ret);
 
        return ret;
-
-out3:
-       idmap->idmap_key_cons = NULL;
 out2:
        kfree(data);
 out1:
@@ -710,21 +732,32 @@ static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *dat
                                        authkey);
 }
 
-static int nfs_idmap_read_message(struct idmap_msg *im, struct key *key, struct key *authkey)
+static int nfs_idmap_read_and_verify_message(struct idmap_msg *im,
+               struct idmap_msg *upcall,
+               struct key *key, struct key *authkey)
 {
        char id_str[NFS_UINT_MAXLEN];
-       int ret = -EINVAL;
+       int ret = -ENOKEY;
 
+       /* ret = -ENOKEY */
+       if (upcall->im_type != im->im_type || upcall->im_conv != im->im_conv)
+               goto out;
        switch (im->im_conv) {
        case IDMAP_CONV_NAMETOID:
+               if (strcmp(upcall->im_name, im->im_name) != 0)
+                       break;
                sprintf(id_str, "%d", im->im_id);
                ret = nfs_idmap_instantiate(key, authkey, id_str);
                break;
        case IDMAP_CONV_IDTONAME:
+               if (upcall->im_id != im->im_id)
+                       break;
                ret = nfs_idmap_instantiate(key, authkey, im->im_name);
                break;
+       default:
+               ret = -EINVAL;
        }
-
+out:
        return ret;
 }
 
@@ -736,14 +769,16 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
        struct key_construction *cons;
        struct idmap_msg im;
        size_t namelen_in;
-       int ret;
+       int ret = -ENOKEY;
 
        /* If instantiation is successful, anyone waiting for key construction
         * will have been woken up and someone else may now have used
         * idmap_key_cons - so after this point we may no longer touch it.
         */
-       cons = ACCESS_ONCE(idmap->idmap_key_cons);
-       idmap->idmap_key_cons = NULL;
+       if (idmap->idmap_upcall_data == NULL)
+               goto out_noupcall;
+
+       cons = idmap->idmap_upcall_data->key_cons;
 
        if (mlen != sizeof(im)) {
                ret = -ENOSPC;
@@ -764,16 +799,19 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
        if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) {
                ret = -EINVAL;
                goto out;
-       }
+}
 
-       ret = nfs_idmap_read_message(&im, cons->key, cons->authkey);
+       ret = nfs_idmap_read_and_verify_message(&im,
+                       &idmap->idmap_upcall_data->idmap_msg,
+                       cons->key, cons->authkey);
        if (ret >= 0) {
                key_set_timeout(cons->key, nfs_idmap_cache_timeout);
                ret = mlen;
        }
 
 out:
-       complete_request_key(cons, ret);
+       nfs_idmap_complete_pipe_upcall_locked(idmap, ret);
+out_noupcall:
        return ret;
 }
 
@@ -784,14 +822,9 @@ idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg)
                        struct idmap_legacy_upcalldata,
                        pipe_msg);
        struct idmap *idmap = data->idmap;
-       struct key_construction *cons;
-       if (msg->errno) {
-               cons = ACCESS_ONCE(idmap->idmap_key_cons);
-               idmap->idmap_key_cons = NULL;
-               complete_request_key(cons, msg->errno);
-       }
-       /* Free memory allocated in nfs_idmap_legacy_upcall() */
-       kfree(data);
+
+       if (msg->errno)
+               nfs_idmap_abort_pipe_upcall(idmap, msg->errno);
 }
 
 static void
@@ -799,7 +832,8 @@ idmap_release_pipe(struct inode *inode)
 {
        struct rpc_inode *rpci = RPC_I(inode);
        struct idmap *idmap = (struct idmap *)rpci->private;
-       idmap->idmap_key_cons = NULL;
+
+       nfs_idmap_abort_pipe_upcall(idmap, -EPIPE);
 }
 
 int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid)