NFSv4: Fix an atomicity problem in CLOSE
[linux-2.6-block.git] / fs / nfs / nfs4proc.c
index e7f8d5ff2581a98269a262998beb43ccaca23e3c..4863dec108652d8db8f1f3247c8d724160e53926 100644 (file)
@@ -1117,8 +1117,6 @@ static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
                return 0;
        if ((delegation->type & fmode) != fmode)
                return 0;
-       if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
-               return 0;
        if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
                return 0;
        nfs_mark_delegation_referenced(delegation);
@@ -2589,6 +2587,11 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                case -NFS4ERR_OLD_STATEID:
                case -NFS4ERR_BAD_STATEID:
                case -NFS4ERR_EXPIRED:
+                       if (!nfs4_stateid_match(&calldata->arg.stateid,
+                                               &state->stateid)) {
+                               rpc_restart_call_prepare(task);
+                               goto out_release;
+                       }
                        if (calldata->arg.fmode == 0)
                                break;
                default:
@@ -2621,6 +2624,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
        is_rdwr = test_bit(NFS_O_RDWR_STATE, &state->flags);
        is_rdonly = test_bit(NFS_O_RDONLY_STATE, &state->flags);
        is_wronly = test_bit(NFS_O_WRONLY_STATE, &state->flags);
+       nfs4_stateid_copy(&calldata->arg.stateid, &state->stateid);
        /* Calculate the change in open mode */
        calldata->arg.fmode = 0;
        if (state->n_rdwr == 0) {
@@ -2759,7 +2763,6 @@ int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait)
        calldata->inode = state->inode;
        calldata->state = state;
        calldata->arg.fh = NFS_FH(state->inode);
-       calldata->arg.stateid = &state->open_stateid;
        /* Serialization for the sequence id */
        calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid, gfp_mask);
        if (calldata->arg.seqid == NULL)
@@ -4917,11 +4920,14 @@ static void nfs4_init_boot_verifier(const struct nfs_client *clp,
 }
 
 static unsigned int
-nfs4_init_nonuniform_client_string(const struct nfs_client *clp,
+nfs4_init_nonuniform_client_string(struct nfs_client *clp,
                                   char *buf, size_t len)
 {
        unsigned int result;
 
+       if (clp->cl_owner_id != NULL)
+               return strlcpy(buf, clp->cl_owner_id, len);
+
        rcu_read_lock();
        result = scnprintf(buf, len, "Linux NFSv4.0 %s/%s %s",
                                clp->cl_ipaddr,
@@ -4930,24 +4936,32 @@ nfs4_init_nonuniform_client_string(const struct nfs_client *clp,
                                rpc_peeraddr2str(clp->cl_rpcclient,
                                                        RPC_DISPLAY_PROTO));
        rcu_read_unlock();
+       clp->cl_owner_id = kstrdup(buf, GFP_KERNEL);
        return result;
 }
 
 static unsigned int
-nfs4_init_uniform_client_string(const struct nfs_client *clp,
+nfs4_init_uniform_client_string(struct nfs_client *clp,
                                char *buf, size_t len)
 {
        const char *nodename = clp->cl_rpcclient->cl_nodename;
+       unsigned int result;
+
+       if (clp->cl_owner_id != NULL)
+               return strlcpy(buf, clp->cl_owner_id, len);
 
        if (nfs4_client_id_uniquifier[0] != '\0')
-               return scnprintf(buf, len, "Linux NFSv%u.%u %s/%s",
+               result = scnprintf(buf, len, "Linux NFSv%u.%u %s/%s",
                                clp->rpc_ops->version,
                                clp->cl_minorversion,
                                nfs4_client_id_uniquifier,
                                nodename);
-       return scnprintf(buf, len, "Linux NFSv%u.%u %s",
+       else
+               result = scnprintf(buf, len, "Linux NFSv%u.%u %s",
                                clp->rpc_ops->version, clp->cl_minorversion,
                                nodename);
+       clp->cl_owner_id = kstrdup(buf, GFP_KERNEL);
+       return result;
 }
 
 /*