OSDN Git Service

NFSD check stateids against copy stateids
authorOlga Kornievskaia <olga.kornievskaia@gmail.com>
Fri, 6 Sep 2019 19:17:21 +0000 (15:17 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Mon, 9 Dec 2019 16:42:14 +0000 (11:42 -0500)
Incoming stateid (used by a READ) could be a saved copy stateid.
Using the provided stateid, look it up in the list of copy_notify
stateids. If found, use the parent's stateid and parent's clid
to look up the parent's stid to do the appropriate checks.

Update the copy notify timestamp (cpntf_time) with current time
this making it 'active' so that laundromat thread will not delete
copy notify state.

Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
fs/nfsd/nfs4state.c

index ae3dff2..31b71ae 100644 (file)
@@ -4539,7 +4539,8 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4
 
 static __be32 lookup_clientid(clientid_t *clid,
                struct nfsd4_compound_state *cstate,
-               struct nfsd_net *nn)
+               struct nfsd_net *nn,
+               bool sessions)
 {
        struct nfs4_client *found;
 
@@ -4560,7 +4561,7 @@ static __be32 lookup_clientid(clientid_t *clid,
         */
        WARN_ON_ONCE(cstate->session);
        spin_lock(&nn->client_lock);
-       found = find_confirmed_client(clid, false, nn);
+       found = find_confirmed_client(clid, sessions, nn);
        if (!found) {
                spin_unlock(&nn->client_lock);
                return nfserr_expired;
@@ -4593,7 +4594,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
        if (open->op_file == NULL)
                return nfserr_jukebox;
 
-       status = lookup_clientid(clientid, cstate, nn);
+       status = lookup_clientid(clientid, cstate, nn, false);
        if (status)
                return status;
        clp = cstate->clp;
@@ -5182,7 +5183,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        dprintk("process_renew(%08x/%08x): starting\n", 
                        clid->cl_boot, clid->cl_id);
-       status = lookup_clientid(clid, cstate, nn);
+       status = lookup_clientid(clid, cstate, nn, false);
        if (status)
                goto out;
        clp = cstate->clp;
@@ -5584,7 +5585,8 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
        if (ZERO_STATEID(stateid) || ONE_STATEID(stateid) ||
                CLOSE_STATEID(stateid))
                return nfserr_bad_stateid;
-       status = lookup_clientid(&stateid->si_opaque.so_clid, cstate, nn);
+       status = lookup_clientid(&stateid->si_opaque.so_clid, cstate, nn,
+                                false);
        if (status == nfserr_stale_clientid) {
                if (cstate->session)
                        return nfserr_bad_stateid;
@@ -5674,6 +5676,59 @@ _free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps)
                   cps->cp_stateid.stid.si_opaque.so_id);
        kfree(cps);
 }
+/*
+ * A READ from an inter server to server COPY will have a
+ * copy stateid. Look up the copy notify stateid from the
+ * idr structure and take a reference on it.
+ */
+static __be32 _find_cpntf_state(struct nfsd_net *nn, stateid_t *st,
+                    struct nfs4_cpntf_state **cps)
+{
+       copy_stateid_t *cps_t;
+       struct nfs4_cpntf_state *state = NULL;
+
+       if (st->si_opaque.so_clid.cl_id != nn->s2s_cp_cl_id)
+               return nfserr_bad_stateid;
+       spin_lock(&nn->s2s_cp_lock);
+       cps_t = idr_find(&nn->s2s_cp_stateids, st->si_opaque.so_id);
+       if (cps_t) {
+               state = container_of(cps_t, struct nfs4_cpntf_state,
+                                    cp_stateid);
+               if (state->cp_stateid.sc_type != NFS4_COPYNOTIFY_STID)
+                       return nfserr_bad_stateid;
+               refcount_inc(&state->cp_stateid.sc_count);
+       }
+       spin_unlock(&nn->s2s_cp_lock);
+       if (!state)
+               return nfserr_bad_stateid;
+       *cps = state;
+       return 0;
+}
+
+static __be32 find_cpntf_state(struct nfsd_net *nn, stateid_t *st,
+                              struct nfs4_stid **stid)
+{
+       __be32 status;
+       struct nfs4_cpntf_state *cps = NULL;
+       struct nfsd4_compound_state cstate;
+
+       status = _find_cpntf_state(nn, st, &cps);
+       if (status)
+               return status;
+
+       cps->cpntf_time = get_seconds();
+       memset(&cstate, 0, sizeof(cstate));
+       status = lookup_clientid(&cps->cp_p_clid, &cstate, nn, true);
+       if (status)
+               goto out;
+       status = nfsd4_lookup_stateid(&cstate, &cps->cp_p_stateid,
+                               NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
+                               stid, nn);
+       put_client_renew(cstate.clp);
+out:
+       nfs4_put_cpntf_state(nn, cps);
+       return status;
+}
 
 void nfs4_put_cpntf_state(struct nfsd_net *nn, struct nfs4_cpntf_state *cps)
 {
@@ -5711,6 +5766,8 @@ nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
        status = nfsd4_lookup_stateid(cstate, stateid,
                                NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
                                &s, nn);
+       if (status == nfserr_bad_stateid)
+               status = find_cpntf_state(nn, stateid, &s);
        if (status)
                return status;
        status = nfsd4_stid_check_stateid_generation(stateid, s,
@@ -6743,7 +6800,8 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                 return nfserr_inval;
 
        if (!nfsd4_has_session(cstate)) {
-               status = lookup_clientid(&lockt->lt_clientid, cstate, nn);
+               status = lookup_clientid(&lockt->lt_clientid, cstate, nn,
+                                        false);
                if (status)
                        goto out;
        }
@@ -6927,7 +6985,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
        dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
                clid->cl_boot, clid->cl_id);
 
-       status = lookup_clientid(clid, cstate, nn);
+       status = lookup_clientid(clid, cstate, nn, false);
        if (status)
                return status;
 
@@ -7074,7 +7132,7 @@ nfs4_check_open_reclaim(clientid_t *clid,
        __be32 status;
 
        /* find clientid in conf_id_hashtbl */
-       status = lookup_clientid(clid, cstate, nn);
+       status = lookup_clientid(clid, cstate, nn, false);
        if (status)
                return nfserr_reclaim_bad;