@@ -1427,6 +1427,7 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfs42_netaddr *naddr;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct nl4_server *ns;
+ struct nfs4_stid *stid = NULL;
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
&cn->cpn_src_stateid, RD_STATE, &src,
@@ -1440,7 +1441,19 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
* connections from the destination e.g. what is returned in cpn_src,
* to verify READ from dest server.
*/
-
+ /* mark the original open stateid special */
+ status = nfserr_bad_stateid;
+ stid = find_stateid_by_type(cstate->session->se_client,
+ &cn->cpn_src_stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|
+ NFS4_LOCK_STID);
+ if (stid) {
+ /* cnp_src_stateid is used for reply cnr_stateid */
+ stid->is_copy = 1;
+ nfs4_put_stid(stid);
+ } else {
+ dprintk("NFSD: %s can't find cpn_src_stateid\n", __func__);
+ goto out;
+ }
/**
* For now, only return one server address in cpn_src, the
* address used by the client to connect to this server.
@@ -1890,12 +1890,6 @@ same_verf(nfs4_verifier *v1, nfs4_verifier *v2)
return 0 == memcmp(v1->data, v2->data, sizeof(v1->data));
}
-static int
-same_clid(clientid_t *cl1, clientid_t *cl2)
-{
- return (cl1->cl_boot == cl2->cl_boot) && (cl1->cl_id == cl2->cl_id);
-}
-
static bool groups_equal(struct group_info *g1, struct group_info *g2)
{
int i;
@@ -2000,7 +1994,7 @@ find_stateid_locked(struct nfs4_client *cl, stateid_t *t)
return ret;
}
-static struct nfs4_stid *
+struct nfs4_stid *
find_stateid_by_type(struct nfs4_client *cl, stateid_t *t, char typemask)
{
struct nfs4_stid *s;
@@ -4824,6 +4818,36 @@ nfs4_check_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfs4_stid *s,
return 0;
}
+/**
+ * If lookup for the stateid fails, we need to look for the stateid under
+ * all other clientid structures and their corresponding stateid lists.
+ * Make sure the stateid was previously marked for use from a COPY operation.
+ */
+static __be32
+nfs4_find_copy_stateid(stateid_t *stateid, struct nfs4_stid **spp,
+ struct nfsd_net *nn)
+{
+ struct nfs4_client *clp = NULL;
+ struct nfs4_stid *s = NULL;
+ __be32 status = nfserr_bad_stateid;
+
+ spin_lock(&nn->client_lock);
+ list_for_each_entry(clp, &nn->client_lru, cl_lru) {
+ if (same_clid(&clp->cl_clientid, &stateid->si_opaque.so_clid)) {
+ s = find_stateid_by_type(clp, stateid,
+ NFS4_DELEG_STID|NFS4_OPEN_STID|
+ NFS4_LOCK_STID);
+ if (s && s->is_copy)
+ dprintk("%s found COPY stateid\n", __func__);
+ *spp = s;
+ status = nfs_ok;
+ break;
+ }
+ }
+ spin_unlock(&nn->client_lock);
+ return status;
+}
+
/*
* Checks for stateid operations
*/
@@ -4854,6 +4878,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 = nfs4_find_copy_stateid(stateid, &s, nn);
if (status)
return status;
status = check_stateid_generation(stateid, &s->sc_stateid,
@@ -94,6 +94,7 @@ struct nfs4_stid {
#define NFS4_REVOKED_DELEG_STID 16
#define NFS4_CLOSED_DELEG_STID 32
#define NFS4_LAYOUT_STID 64
+ bool is_copy;
unsigned char sc_type;
stateid_t sc_stateid;
spinlock_t sc_lock;
@@ -583,6 +584,13 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b)
struct nfsd4_compound_state;
struct nfsd_net;
+static inline int same_clid(clientid_t *cl1, clientid_t *cl2)
+{
+ return (cl1->cl_boot == cl2->cl_boot) && (cl1->cl_id == cl2->cl_id);
+}
+
+extern struct nfs4_stid *find_stateid_by_type(struct nfs4_client *cl,
+ stateid_t *t, char typemask);
extern __be32 nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate, struct svc_fh *fhp,
stateid_t *stateid, int flags, struct file **filp, bool *tmp_file);