NFSv4.1: Fix an Oops in nfs41_walk_client_list
[firefly-linux-kernel-4.4.55.git] / fs / nfs / nfs4client.c
index d949d0f378ec884cbea5332763ff5d3eac8dcf71..706ad10b8186d4401eb4da48ebf0e7fba7686481 100644 (file)
@@ -228,6 +228,7 @@ static void nfs4_shutdown_client(struct nfs_client *clp)
        kfree(clp->cl_serverowner);
        kfree(clp->cl_serverscope);
        kfree(clp->cl_implid);
+       kfree(clp->cl_owner_id);
 }
 
 void nfs4_free_client(struct nfs_client *clp)
@@ -452,6 +453,14 @@ static void nfs4_swap_callback_idents(struct nfs_client *keep,
        spin_unlock(&nn->nfs_client_lock);
 }
 
+static bool nfs4_match_client_owner_id(const struct nfs_client *clp1,
+               const struct nfs_client *clp2)
+{
+       if (clp1->cl_owner_id == NULL || clp2->cl_owner_id == NULL)
+               return true;
+       return strcmp(clp1->cl_owner_id, clp2->cl_owner_id) == 0;
+}
+
 /**
  * nfs40_walk_client_list - Find server that recognizes a client ID
  *
@@ -483,9 +492,6 @@ int nfs40_walk_client_list(struct nfs_client *new,
                if (pos->rpc_ops != new->rpc_ops)
                        continue;
 
-               if (pos->cl_proto != new->cl_proto)
-                       continue;
-
                if (pos->cl_minorversion != new->cl_minorversion)
                        continue;
 
@@ -510,6 +516,9 @@ int nfs40_walk_client_list(struct nfs_client *new,
                if (pos->cl_clientid != new->cl_clientid)
                        continue;
 
+               if (!nfs4_match_client_owner_id(pos, new))
+                       continue;
+
                atomic_inc(&pos->cl_count);
                spin_unlock(&nn->nfs_client_lock);
 
@@ -615,9 +624,6 @@ int nfs41_walk_client_list(struct nfs_client *new,
                if (pos->rpc_ops != new->rpc_ops)
                        continue;
 
-               if (pos->cl_proto != new->cl_proto)
-                       continue;
-
                if (pos->cl_minorversion != new->cl_minorversion)
                        continue;
 
@@ -633,7 +639,7 @@ int nfs41_walk_client_list(struct nfs_client *new,
                        prev = pos;
 
                        status = nfs_wait_client_init_complete(pos);
-                       if (status == 0) {
+                       if (pos->cl_cons_state == NFS_CS_SESSION_INITING) {
                                nfs4_schedule_lease_recovery(pos);
                                status = nfs4_wait_clnt_recover(pos);
                        }
@@ -656,6 +662,13 @@ int nfs41_walk_client_list(struct nfs_client *new,
                if (!nfs4_check_clientid_trunking(pos, new))
                        continue;
 
+               /* Unlike NFSv4.0, we know that NFSv4.1 always uses the
+                * uniform string, however someone might switch the
+                * uniquifier string on us.
+                */
+               if (!nfs4_match_client_owner_id(pos, new))
+                       continue;
+
                atomic_inc(&pos->cl_count);
                *result = pos;
                status = 0;