nfsd4: fix delegation cleanup on error
authorJ. Bruce Fields <bfields@redhat.com>
Mon, 3 Mar 2014 17:19:18 +0000 (12:19 -0500)
committerJ. Bruce Fields <bfields@redhat.com>
Wed, 21 May 2014 16:17:17 +0000 (12:17 -0400)
We're not cleaning up everything we need to on error.  In particular,
we're not removing our lease.  Among other problems this can cause the
struct nfs4_file used as fl_owner to be referenced after it has been
destroyed.

Signed-off-by: J. Bruce Fields <bfields@redhat.com>
fs/nfsd/nfs4state.c

index fac26836e4ac295a4ed2d29c50da13d24d843b8d..c6ded9feaef967e5fcaa466e168565aff5764c7c 100644 (file)
@@ -418,6 +418,8 @@ nfs4_put_delegation(struct nfs4_delegation *dp)
 
 static void nfs4_put_deleg_lease(struct nfs4_file *fp)
 {
+       if (!fp->fi_lease)
+               return;
        if (atomic_dec_and_test(&fp->fi_delegees)) {
                vfs_setlease(fp->fi_deleg_file, F_UNLCK, &fp->fi_lease);
                fp->fi_lease = NULL;
@@ -440,9 +442,11 @@ unhash_delegation(struct nfs4_delegation *dp)
        list_del_init(&dp->dl_perfile);
        list_del_init(&dp->dl_recall_lru);
        spin_unlock(&recall_lock);
-       nfs4_put_deleg_lease(dp->dl_file);
-       put_nfs4_file(dp->dl_file);
-       dp->dl_file = NULL;
+       if (dp->dl_file) {
+               nfs4_put_deleg_lease(dp->dl_file);
+               put_nfs4_file(dp->dl_file);
+               dp->dl_file = NULL;
+       }
 }
 
 
@@ -3060,33 +3064,22 @@ out_free:
 
 static int nfs4_set_delegation(struct nfs4_delegation *dp, struct nfs4_file *fp)
 {
-       int status;
-
        if (fp->fi_had_conflict)
                return -EAGAIN;
        get_nfs4_file(fp);
        dp->dl_file = fp;
-       if (!fp->fi_lease) {
-               status = nfs4_setlease(dp);
-               if (status)
-                       goto out_free;
-               return 0;
-       }
+       if (!fp->fi_lease)
+               return nfs4_setlease(dp);
        spin_lock(&recall_lock);
+       atomic_inc(&fp->fi_delegees);
        if (fp->fi_had_conflict) {
                spin_unlock(&recall_lock);
-               status = -EAGAIN;
-               goto out_free;
+               return -EAGAIN;
        }
-       atomic_inc(&fp->fi_delegees);
        list_add(&dp->dl_perfile, &fp->fi_delegations);
        spin_unlock(&recall_lock);
        list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
        return 0;
-out_free:
-       put_nfs4_file(fp);
-       dp->dl_file = fp;
-       return status;
 }
 
 static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status)
@@ -3173,8 +3166,7 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh,
        open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
        return;
 out_free:
-       remove_stid(&dp->dl_stid);
-       nfs4_put_delegation(dp);
+       destroy_delegation(dp);
 out_no_deleg:
        open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE;
        if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS &&