All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] NFSD: add FREE_STATEID and TEST_STATEID operations
@ 2011-05-16 19:43 bjschuma
  2011-05-16 19:43 ` [PATCH v2 1/2] NFSD: added FREE_STATEID operation bjschuma
  2011-05-16 19:43 ` [PATCH v2 2/2] NFSD: Added TEST_STATEID opreation bjschuma
  0 siblings, 2 replies; 11+ messages in thread
From: bjschuma @ 2011-05-16 19:43 UTC (permalink / raw)
  To: bfields; +Cc: linux-nfs, Bryan Schumaker

From: Bryan Schumaker <bjschuma@netapp.com>

These patches add FREE_STATEID and TEST_STATEID to the NFS server.  I
appreciate any comments, especially if there is a better way of doing things.

v2:
- FREE_STATEID
  - Free open, lock, and delegation state ids correctly
  - Use the check_for_locks() function to check for locks

- TEST_STATEID
  - Remove openmode check
  - Sanity check on size of stateid list
  - Delay decoding stateid list until encoding the reply

Thanks!

- Bryan

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v2 1/2] NFSD: added FREE_STATEID operation
  2011-05-16 19:43 [PATCH v2 0/2] NFSD: add FREE_STATEID and TEST_STATEID operations bjschuma
@ 2011-05-16 19:43 ` bjschuma
  2011-05-18 22:56   ` J. Bruce Fields
  2011-05-16 19:43 ` [PATCH v2 2/2] NFSD: Added TEST_STATEID opreation bjschuma
  1 sibling, 1 reply; 11+ messages in thread
From: bjschuma @ 2011-05-16 19:43 UTC (permalink / raw)
  To: bfields; +Cc: linux-nfs, Bryan Schumaker

From: Bryan Schumaker <bjschuma@netapp.com>

This operation is used by the client to tell the server to free a
stateid.

Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
---
 fs/nfsd/nfs4proc.c  |    5 ++
 fs/nfsd/nfs4state.c |  106 +++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/nfsd/nfs4xdr.c   |   32 ++++++++++++++-
 fs/nfsd/xdr4.h      |    8 ++++
 4 files changed, 149 insertions(+), 2 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 3a6dbd7..7e00116 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1412,6 +1412,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
 		.op_flags = OP_HANDLES_WRONGSEC,
 		.op_name = "OP_SECINFO_NO_NAME",
 	},
+	[OP_FREE_STATEID] = {
+		.op_func = (nfsd4op_func)nfsd4_free_stateid,
+		.op_flags = ALLOWED_WITHOUT_FH,
+		.op_name = "OP_FREE_STATEID",
+	},
 };
 
 static const char *nfsd4_op_name(unsigned opnum)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a2ea14f..043baa0 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -60,9 +60,12 @@ static u64 current_sessionid = 1;
 
 /* forward declarations */
 static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags);
+static struct nfs4_stateid *search_for_stateid(stateid_t *stid);
+static struct nfs4_delegation *search_for_delegation(stateid_t *stid);
 static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid);
 static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
 static void nfs4_set_recdir(char *recdir);
+static int check_for_locks(struct nfs4_file *filp, struct nfs4_stateowner *lowner);
 
 /* Locking: */
 
@@ -3212,6 +3215,59 @@ out:
 	return status;
 }
 
+static __be32
+nfsd4_free_delegation_stateid(stateid_t *stateid)
+{
+	struct nfs4_delegation *dp = search_for_delegation(stateid);
+	__be32 ret = nfs_ok;
+
+	if (dp)
+		unhash_delegation(dp);
+	else
+		ret = nfserr_bad_stateid;
+	return ret;
+}
+
+static __be32
+nfsd4_free_file_stateid(stateid_t *stateid)
+{
+	struct nfs4_stateid *stp = search_for_stateid(stateid);
+	if (!stp)
+		return nfserr_bad_stateid;
+	if (stateid->si_generation != 0) {
+		if (stateid->si_generation < stp->st_stateid.si_generation)
+			return nfserr_old_stateid;
+		if (stateid->si_generation > stp->st_stateid.si_generation)
+			return nfserr_bad_stateid;
+	}
+
+	if (check_for_locks(stp->st_file, stp->st_stateowner))
+		return nfserr_locks_held;
+
+	if (stp->st_openstp)
+		release_lock_stateid(stp);
+	else
+		release_open_stateid(stp);
+	return nfs_ok;
+}
+
+/*
+ * Free a state id
+ */
+__be32
+nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+		   struct nfsd4_free_stateid *free_stateid)
+{
+	stateid_t *stateid = &free_stateid->fr_stateid;
+	__be32 ret;
+
+	if (is_delegation_stateid(stateid))
+		ret = nfsd4_free_delegation_stateid(stateid);
+	else
+		ret = nfsd4_free_file_stateid(stateid);
+	return ret;
+}
+
 static inline int
 setlkflg (int type)
 {
@@ -3590,6 +3646,18 @@ static struct list_head lock_ownerid_hashtbl[LOCK_HASH_SIZE];
 static struct list_head	lock_ownerstr_hashtbl[LOCK_HASH_SIZE];
 static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE];
 
+static int
+same_stateid(stateid_t *id_one, stateid_t *id_two)
+{
+	if (id_one->si_generation != id_two->si_generation)
+		return 0;
+	else if (id_one->si_boot != id_two->si_boot)
+		return 0;
+	else if (id_one->si_stateownerid != id_two->si_stateownerid)
+		return 0;
+	return id_one->si_fileid == id_two->si_fileid;
+}
+
 static struct nfs4_stateid *
 find_stateid(stateid_t *stid, int flags)
 {
@@ -3619,6 +3687,44 @@ find_stateid(stateid_t *stid, int flags)
 	return NULL;
 }
 
+static struct nfs4_stateid *
+search_for_stateid(stateid_t *stid)
+{
+	struct nfs4_stateid *local;
+	unsigned int hashval = stateid_hashval(stid->si_stateownerid, stid->si_fileid);
+
+	list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) {
+		if (same_stateid(&local->st_stateid, stid))
+			return local;
+	}
+
+	list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) {
+		if (same_stateid(&local->st_stateid, stid))
+			return local;
+	}
+	return NULL;
+}
+
+static struct nfs4_delegation *
+search_for_delegation(stateid_t *stid)
+{
+	struct nfs4_file *fp;
+	struct nfs4_delegation *dp;
+	struct list_head *pos;
+	int i;
+
+	for (i = 0; i < FILE_HASH_SIZE; i++) {
+		list_for_each_entry(fp, &file_hashtbl[i], fi_hash) {
+			list_for_each(pos, &fp->fi_delegations) {
+				dp = list_entry(pos, struct nfs4_delegation, dl_perfile);
+				if (same_stateid(&dp->dl_stateid, stid))
+					return dp;
+			}
+		}
+	}
+	return NULL;
+}
+
 static struct nfs4_delegation *
 find_delegation_stateid(struct inode *ino, stateid_t *stid)
 {
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 195a91d..5da6874 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1246,6 +1246,19 @@ nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
 }
 
 static __be32
+nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
+			  struct nfsd4_free_stateid *free_stateid)
+{
+	DECODE_HEAD;
+
+	READ_BUF(sizeof(stateid_t));
+	READ32(free_stateid->fr_stateid.si_generation);
+	COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t));
+
+	DECODE_TAIL;
+}
+
+static __be32
 nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
 		      struct nfsd4_sequence *seq)
 {
@@ -1370,7 +1383,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
 	[OP_EXCHANGE_ID]	= (nfsd4_dec)nfsd4_decode_exchange_id,
 	[OP_CREATE_SESSION]	= (nfsd4_dec)nfsd4_decode_create_session,
 	[OP_DESTROY_SESSION]	= (nfsd4_dec)nfsd4_decode_destroy_session,
-	[OP_FREE_STATEID]	= (nfsd4_dec)nfsd4_decode_notsupp,
+	[OP_FREE_STATEID]	= (nfsd4_dec)nfsd4_decode_free_stateid,
 	[OP_GET_DIR_DELEGATION]	= (nfsd4_dec)nfsd4_decode_notsupp,
 	[OP_GETDEVICEINFO]	= (nfsd4_dec)nfsd4_decode_notsupp,
 	[OP_GETDEVICELIST]	= (nfsd4_dec)nfsd4_decode_notsupp,
@@ -3115,6 +3128,21 @@ nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr,
 	return nfserr;
 }
 
+static __be32
+nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int nfserr,
+			  struct nfsd4_free_stateid *free_stateid)
+{
+	__be32 *p;
+
+	if (nfserr)
+		return nfserr;
+
+	RESERVE_SPACE(4);
+	WRITE32(nfserr);
+	ADJUST_ARGS();
+	return nfserr;
+}
+
 __be32
 nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
 		      struct nfsd4_sequence *seq)
@@ -3196,7 +3224,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
 	[OP_EXCHANGE_ID]	= (nfsd4_enc)nfsd4_encode_exchange_id,
 	[OP_CREATE_SESSION]	= (nfsd4_enc)nfsd4_encode_create_session,
 	[OP_DESTROY_SESSION]	= (nfsd4_enc)nfsd4_encode_destroy_session,
-	[OP_FREE_STATEID]	= (nfsd4_enc)nfsd4_encode_noop,
+	[OP_FREE_STATEID]	= (nfsd4_enc)nfsd4_encode_free_stateid,
 	[OP_GET_DIR_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
 	[OP_GETDEVICEINFO]	= (nfsd4_enc)nfsd4_encode_noop,
 	[OP_GETDEVICELIST]	= (nfsd4_enc)nfsd4_encode_noop,
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 366401e..ed1784d 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -342,6 +342,11 @@ struct nfsd4_setclientid_confirm {
 	nfs4_verifier	sc_confirm;
 };
 
+struct nfsd4_free_stateid {
+	stateid_t	fr_stateid;         /* request */
+	__be32		fr_status;          /* response */
+};
+
 /* also used for NVERIFY */
 struct nfsd4_verify {
 	u32		ve_bmval[3];        /* request */
@@ -432,6 +437,7 @@ struct nfsd4_op {
 		struct nfsd4_destroy_session	destroy_session;
 		struct nfsd4_sequence		sequence;
 		struct nfsd4_reclaim_complete	reclaim_complete;
+		struct nfsd4_free_stateid	free_stateid;
 	} u;
 	struct nfs4_replay *			replay;
 };
@@ -564,6 +570,8 @@ extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
 		struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
 extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
 			  struct nfsd4_compound_state *, clientid_t *clid);
+extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
+		struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
 #endif
 
 /*
-- 
1.7.5.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 2/2] NFSD: Added TEST_STATEID opreation
  2011-05-16 19:43 [PATCH v2 0/2] NFSD: add FREE_STATEID and TEST_STATEID operations bjschuma
  2011-05-16 19:43 ` [PATCH v2 1/2] NFSD: added FREE_STATEID operation bjschuma
@ 2011-05-16 19:43 ` bjschuma
  2011-05-18 23:57   ` J. Bruce Fields
  1 sibling, 1 reply; 11+ messages in thread
From: bjschuma @ 2011-05-16 19:43 UTC (permalink / raw)
  To: bfields; +Cc: linux-nfs, Bryan Schumaker

From: Bryan Schumaker <bjschuma@netapp.com>

This operation is used by the client to check the validity of a list of
stateids.

Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
---
 fs/nfsd/nfs4proc.c  |    5 +++
 fs/nfsd/nfs4state.c |   53 +++++++++++++++++++++++++++++++
 fs/nfsd/nfs4xdr.c   |   85 +++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/nfsd/state.h     |    1 +
 fs/nfsd/xdr4.h      |   17 ++++++++++
 5 files changed, 158 insertions(+), 3 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 7e00116..a856f30 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1412,6 +1412,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
 		.op_flags = OP_HANDLES_WRONGSEC,
 		.op_name = "OP_SECINFO_NO_NAME",
 	},
+	[OP_TEST_STATEID] = {
+		.op_func = (nfsd4op_func)nfsd4_test_stateid,
+		.op_flags = ALLOWED_WITHOUT_FH,
+		.op_name = "OP_TEST_STATEID",
+	},
 	[OP_FREE_STATEID] = {
 		.op_func = (nfsd4op_func)nfsd4_free_stateid,
 		.op_flags = ALLOWED_WITHOUT_FH,
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 043baa0..18e0762 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -37,6 +37,7 @@
 #include <linux/slab.h>
 #include <linux/namei.h>
 #include <linux/swap.h>
+#include <linux/pagemap.h>
 #include <linux/sunrpc/svcauth_gss.h>
 #include <linux/sunrpc/clnt.h>
 #include "xdr4.h"
@@ -3136,6 +3137,32 @@ static int is_delegation_stateid(stateid_t *stateid)
 	return stateid->si_fileid == 0;
 }
 
+static __be32 nfs4_validate_stateid(stateid_t *stateid, int flags, struct file **filpp, struct svc_fh *current_fh)
+{
+	struct nfs4_stateid *stp = NULL;
+	__be32 status = nfserr_stale_stateid;
+
+	if (STALE_STATEID(stateid))
+		goto out;
+
+	status = nfserr_expired;
+	stp = search_for_stateid(stateid);
+	if (!stp)
+		goto out;
+	status = nfserr_bad_stateid;
+
+	if (!stp->st_stateowner->so_confirmed)
+		goto out;
+
+	status = check_stateid_generation(stateid, &stp->st_stateid, flags);
+	if (status)
+		goto out;
+
+	status = nfs_ok;
+out:
+	return status;
+}
+
 /*
 * Checks for stateid operations
 */
@@ -3252,6 +3279,32 @@ nfsd4_free_file_stateid(stateid_t *stateid)
 }
 
 /*
+ * Test if the stateid is valid
+ */
+__be32
+nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+		   struct nfsd4_test_stateid *test_stateid)
+{
+	test_stateid->ts_saved_state = cstate;
+	return nfs_ok;
+}
+
+__be32
+nfsd4_do_test_stateid(stateid_t *stateid, struct nfsd4_compound_state *cstate)
+{
+	__be32 ret;
+	unsigned int lock_flags = LOCK_STATE | RD_STATE | WR_STATE;
+	unsigned int open_flags = OPEN_STATE | RD_STATE | WR_STATE;
+	struct svc_fh *current_fh;
+
+	current_fh = &cstate->current_fh;
+	ret = nfs4_validate_stateid(stateid, lock_flags, NULL, current_fh);
+	if (!ret)
+		ret = nfs4_validate_stateid(stateid, open_flags, NULL, current_fh);
+	return ret;
+}
+
+/*
  * Free a state id
  */
 __be32
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 5da6874..2239114 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -44,13 +44,14 @@
 #include <linux/namei.h>
 #include <linux/statfs.h>
 #include <linux/utsname.h>
+#include <linux/pagemap.h>
 #include <linux/sunrpc/svcauth_gss.h>
 
 #include "idmap.h"
 #include "acl.h"
 #include "xdr4.h"
 #include "vfs.h"
-
+#include "state.h"
 
 #define NFSDDBG_FACILITY		NFSDDBG_XDR
 
@@ -131,6 +132,22 @@ xdr_error:					\
 	}					\
 } while (0)
 
+static void save_buf(struct nfsd4_compoundargs *argp, struct nfsd4_saved_compoundargs *savep)
+{
+	savep->p        = argp->p;
+	savep->end      = argp->end;
+	savep->pagelen  = argp->pagelen;
+	savep->pagelist = argp->pagelist;
+}
+
+static void restore_buf(struct nfsd4_compoundargs *argp, struct nfsd4_saved_compoundargs *savep)
+{
+	argp->p        = savep->p;
+	argp->end      = savep->end;
+	argp->pagelen  = savep->pagelen;
+	argp->pagelist = savep->pagelist;
+}
+
 static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
 {
 	/* We want more bytes than seem to be available.
@@ -1274,6 +1291,40 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
 	DECODE_TAIL;
 }
 
+static __be32
+nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid)
+{
+	unsigned int nbytes;
+	stateid_t si;
+	int i;
+	__be32 *p;
+	__be32 status;
+
+	READ_BUF(4);
+	test_stateid->ts_num_ids = ntohl(*p++);
+
+	nbytes = test_stateid->ts_num_ids * sizeof(stateid_t);
+	if (nbytes > (u32)((char *)argp->end - (char *)argp->p))
+		goto xdr_error;
+
+	test_stateid->ts_saved_args = argp;
+	save_buf(argp, &test_stateid->ts_savedp);
+
+	for (i = 0; i < test_stateid->ts_num_ids; i++) {
+		status = nfsd4_decode_stateid(argp, &si);
+		if (status)
+			return status;
+	}
+
+	status = 0;
+out:
+	return status;
+xdr_error:
+	dprintk("NFSD: xdr error (%s:%d)\n", __FILE__, __LINE__);
+	status = nfserr_bad_xdr;
+	goto out;
+}
+
 static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
 {
 	DECODE_HEAD;
@@ -1393,7 +1444,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
 	[OP_SECINFO_NO_NAME]	= (nfsd4_dec)nfsd4_decode_secinfo_no_name,
 	[OP_SEQUENCE]		= (nfsd4_dec)nfsd4_decode_sequence,
 	[OP_SET_SSV]		= (nfsd4_dec)nfsd4_decode_notsupp,
-	[OP_TEST_STATEID]	= (nfsd4_dec)nfsd4_decode_notsupp,
+	[OP_TEST_STATEID]	= (nfsd4_dec)nfsd4_decode_test_stateid,
 	[OP_WANT_DELEGATION]	= (nfsd4_dec)nfsd4_decode_notsupp,
 	[OP_DESTROY_CLIENTID]	= (nfsd4_dec)nfsd4_decode_notsupp,
 	[OP_RECLAIM_COMPLETE]	= (nfsd4_dec)nfsd4_decode_reclaim_complete,
@@ -3166,6 +3217,34 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
 	return 0;
 }
 
+__be32
+nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr,
+			  struct nfsd4_test_stateid *test_stateid)
+{
+	struct nfsd4_compoundargs *argp;
+	stateid_t si;
+	__be32 *p;
+	int i;
+	int valid;
+
+	restore_buf(test_stateid->ts_saved_args, &test_stateid->ts_savedp);
+	argp = test_stateid->ts_saved_args;
+
+	RESERVE_SPACE(4);
+	*p++ = htonl(test_stateid->ts_num_ids);
+	resp->p = p;
+
+	for (i = 0; i < test_stateid->ts_num_ids; i++) {
+		nfsd4_decode_stateid(argp, &si);
+		valid = nfsd4_do_test_stateid(&si, test_stateid->ts_saved_state);
+		RESERVE_SPACE(4);
+		*p++ = htonl(valid);
+		resp->p = p;
+	}
+
+	return nfserr;
+}
+
 static __be32
 nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
 {
@@ -3234,7 +3313,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
 	[OP_SECINFO_NO_NAME]	= (nfsd4_enc)nfsd4_encode_secinfo_no_name,
 	[OP_SEQUENCE]		= (nfsd4_enc)nfsd4_encode_sequence,
 	[OP_SET_SSV]		= (nfsd4_enc)nfsd4_encode_noop,
-	[OP_TEST_STATEID]	= (nfsd4_enc)nfsd4_encode_noop,
+	[OP_TEST_STATEID]	= (nfsd4_enc)nfsd4_encode_test_stateid,
 	[OP_WANT_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
 	[OP_DESTROY_CLIENTID]	= (nfsd4_enc)nfsd4_encode_noop,
 	[OP_RECLAIM_COMPLETE]	= (nfsd4_enc)nfsd4_encode_noop,
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 6bd2f3c..3447b04 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -482,6 +482,7 @@ extern void nfsd4_recdir_purge_old(void);
 extern int nfsd4_create_clid_dir(struct nfs4_client *clp);
 extern void nfsd4_remove_clid_dir(struct nfs4_client *clp);
 extern void release_session_client(struct nfsd4_session *);
+extern __be32 nfsd4_do_test_stateid(stateid_t *, struct nfsd4_compound_state *);
 
 static inline void
 nfs4_put_stateowner(struct nfs4_stateowner *so)
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index ed1784d..628ef97 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -342,6 +342,20 @@ struct nfsd4_setclientid_confirm {
 	nfs4_verifier	sc_confirm;
 };
 
+struct nfsd4_saved_compoundargs {
+	__be32 *p;
+	__be32 *end;
+	int pagelen;
+	struct page **pagelist;
+};
+
+struct nfsd4_test_stateid {
+	__be32		ts_num_ids;
+	struct nfsd4_compoundargs *ts_saved_args;
+	struct nfsd4_saved_compoundargs ts_savedp;
+	struct nfsd4_compound_state *ts_saved_state;
+};
+
 struct nfsd4_free_stateid {
 	stateid_t	fr_stateid;         /* request */
 	__be32		fr_status;          /* response */
@@ -437,6 +451,7 @@ struct nfsd4_op {
 		struct nfsd4_destroy_session	destroy_session;
 		struct nfsd4_sequence		sequence;
 		struct nfsd4_reclaim_complete	reclaim_complete;
+		struct nfsd4_test_stateid	test_stateid;
 		struct nfsd4_free_stateid	free_stateid;
 	} u;
 	struct nfs4_replay *			replay;
@@ -570,6 +585,8 @@ extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
 		struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
 extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
 			  struct nfsd4_compound_state *, clientid_t *clid);
+extern __be32 nfsd4_test_stateid(struct svc_rqst *rqstp,
+		struct nfsd4_compound_state *, struct nfsd4_test_stateid *test_stateid);
 extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
 		struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
 #endif
-- 
1.7.5.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 1/2] NFSD: added FREE_STATEID operation
  2011-05-16 19:43 ` [PATCH v2 1/2] NFSD: added FREE_STATEID operation bjschuma
@ 2011-05-18 22:56   ` J. Bruce Fields
  2011-05-19  1:12     ` J. Bruce Fields
  2011-05-19 15:33     ` Bryan Schumaker
  0 siblings, 2 replies; 11+ messages in thread
From: J. Bruce Fields @ 2011-05-18 22:56 UTC (permalink / raw)
  To: bjschuma; +Cc: linux-nfs

On Mon, May 16, 2011 at 03:43:40PM -0400, bjschuma@netapp.com wrote:
> +static __be32
> +nfsd4_free_file_stateid(stateid_t *stateid)
> +{
> +	struct nfs4_stateid *stp = search_for_stateid(stateid);
> +	if (!stp)
> +		return nfserr_bad_stateid;
> +	if (stateid->si_generation != 0) {
> +		if (stateid->si_generation < stp->st_stateid.si_generation)
> +			return nfserr_old_stateid;
> +		if (stateid->si_generation > stp->st_stateid.si_generation)
> +			return nfserr_bad_stateid;
> +	}
> +
> +	if (check_for_locks(stp->st_file, stp->st_stateowner))
> +		return nfserr_locks_held;

I think this catches a lock stateid, but not an open stateid that has
associated lock stateid's that in turn hold locks.

Hm, also:

	"The FREE_STATEID operation is used to free a stateid that no
	longer has any associated locks (including opens, byte-range
	locks, delegations, and layouts)"

So an open stateid also shouldn't be freeable as long as there are opens
associated with it.

Also I guess a client shouldn't be permitted to free a delegation that
it still holds.  That means we'll always just return nfserr_locks for
delegation stateid's.  I assume free_stateid is only useful in this case
for the case where a server forcibly revokes part of the client's state
and leaves some "stub" stateid's around in place of the revoked ones.

--b.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 2/2] NFSD: Added TEST_STATEID opreation
  2011-05-16 19:43 ` [PATCH v2 2/2] NFSD: Added TEST_STATEID opreation bjschuma
@ 2011-05-18 23:57   ` J. Bruce Fields
  2011-05-19 15:21     ` Bryan Schumaker
  0 siblings, 1 reply; 11+ messages in thread
From: J. Bruce Fields @ 2011-05-18 23:57 UTC (permalink / raw)
  To: bjschuma; +Cc: linux-nfs

On Mon, May 16, 2011 at 03:43:41PM -0400, bjschuma@netapp.com wrote:
> From: Bryan Schumaker <bjschuma@netapp.com>
> 
> This operation is used by the client to check the validity of a list of
> stateids.
> 
> Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
> ---
>  fs/nfsd/nfs4proc.c  |    5 +++
>  fs/nfsd/nfs4state.c |   53 +++++++++++++++++++++++++++++++
>  fs/nfsd/nfs4xdr.c   |   85 +++++++++++++++++++++++++++++++++++++++++++++++++--
>  fs/nfsd/state.h     |    1 +
>  fs/nfsd/xdr4.h      |   17 ++++++++++
>  5 files changed, 158 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> index 7e00116..a856f30 100644
> --- a/fs/nfsd/nfs4proc.c
> +++ b/fs/nfsd/nfs4proc.c
> @@ -1412,6 +1412,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
>  		.op_flags = OP_HANDLES_WRONGSEC,
>  		.op_name = "OP_SECINFO_NO_NAME",
>  	},
> +	[OP_TEST_STATEID] = {
> +		.op_func = (nfsd4op_func)nfsd4_test_stateid,
> +		.op_flags = ALLOWED_WITHOUT_FH,
> +		.op_name = "OP_TEST_STATEID",
> +	},
>  	[OP_FREE_STATEID] = {
>  		.op_func = (nfsd4op_func)nfsd4_free_stateid,
>  		.op_flags = ALLOWED_WITHOUT_FH,
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 043baa0..18e0762 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -37,6 +37,7 @@
>  #include <linux/slab.h>
>  #include <linux/namei.h>
>  #include <linux/swap.h>
> +#include <linux/pagemap.h>
>  #include <linux/sunrpc/svcauth_gss.h>
>  #include <linux/sunrpc/clnt.h>
>  #include "xdr4.h"
> @@ -3136,6 +3137,32 @@ static int is_delegation_stateid(stateid_t *stateid)
>  	return stateid->si_fileid == 0;
>  }
>  
> +static __be32 nfs4_validate_stateid(stateid_t *stateid, int flags, struct file **filpp, struct svc_fh *current_fh)

You're not use current_fh (which makes sense, since test_stateid doesn't
seem to use the current filehandle).

Ditto for callers.

> +{
> +	struct nfs4_stateid *stp = NULL;
> +	__be32 status = nfserr_stale_stateid;
> +
> +	if (STALE_STATEID(stateid))
> +		goto out;
> +
> +	status = nfserr_expired;
> +	stp = search_for_stateid(stateid);
> +	if (!stp)
> +		goto out;
> +	status = nfserr_bad_stateid;
> +
> +	if (!stp->st_stateowner->so_confirmed)
> +		goto out;
> +
> +	status = check_stateid_generation(stateid, &stp->st_stateid, flags);
> +	if (status)
> +		goto out;
> +
> +	status = nfs_ok;
> +out:
> +	return status;
> +}
> +
>  /*
>  * Checks for stateid operations
>  */
> @@ -3252,6 +3279,32 @@ nfsd4_free_file_stateid(stateid_t *stateid)
>  }
>  
>  /*
> + * Test if the stateid is valid
> + */
> +__be32
> +nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> +		   struct nfsd4_test_stateid *test_stateid)
> +{
> +	test_stateid->ts_saved_state = cstate;
> +	return nfs_ok;
> +}
> +
> +__be32
> +nfsd4_do_test_stateid(stateid_t *stateid, struct nfsd4_compound_state *cstate)
> +{
> +	__be32 ret;
> +	unsigned int lock_flags = LOCK_STATE | RD_STATE | WR_STATE;
> +	unsigned int open_flags = OPEN_STATE | RD_STATE | WR_STATE;
> +	struct svc_fh *current_fh;
> +
> +	current_fh = &cstate->current_fh;
> +	ret = nfs4_validate_stateid(stateid, lock_flags, NULL, current_fh);
> +	if (!ret)
> +		ret = nfs4_validate_stateid(stateid, open_flags, NULL, current_fh);

Do we really need to do this once for each set of flags?

Seems we only use them in check_stateid_generation(), which only uses
them to check the HAS_SESSION case.  (Which actually should be set in
our case?)

> +	return ret;
> +}
> +
> +/*
>   * Free a state id
>   */
>  __be32
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 5da6874..2239114 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -44,13 +44,14 @@
>  #include <linux/namei.h>
>  #include <linux/statfs.h>
>  #include <linux/utsname.h>
> +#include <linux/pagemap.h>
>  #include <linux/sunrpc/svcauth_gss.h>
>  
>  #include "idmap.h"
>  #include "acl.h"
>  #include "xdr4.h"
>  #include "vfs.h"
> -
> +#include "state.h"
>  
>  #define NFSDDBG_FACILITY		NFSDDBG_XDR
>  
> @@ -131,6 +132,22 @@ xdr_error:					\
>  	}					\
>  } while (0)
>  
> +static void save_buf(struct nfsd4_compoundargs *argp, struct nfsd4_saved_compoundargs *savep)
> +{
> +	savep->p        = argp->p;
> +	savep->end      = argp->end;
> +	savep->pagelen  = argp->pagelen;
> +	savep->pagelist = argp->pagelist;
> +}
> +
> +static void restore_buf(struct nfsd4_compoundargs *argp, struct nfsd4_saved_compoundargs *savep)
> +{
> +	argp->p        = savep->p;
> +	argp->end      = savep->end;
> +	argp->pagelen  = savep->pagelen;
> +	argp->pagelist = savep->pagelist;
> +}
> +
>  static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
>  {
>  	/* We want more bytes than seem to be available.
> @@ -1274,6 +1291,40 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
>  	DECODE_TAIL;
>  }
>  
> +static __be32
> +nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid)
> +{
> +	unsigned int nbytes;
> +	stateid_t si;
> +	int i;
> +	__be32 *p;
> +	__be32 status;
> +
> +	READ_BUF(4);
> +	test_stateid->ts_num_ids = ntohl(*p++);
> +
> +	nbytes = test_stateid->ts_num_ids * sizeof(stateid_t);
> +	if (nbytes > (u32)((char *)argp->end - (char *)argp->p))
> +		goto xdr_error;
> +
> +	test_stateid->ts_saved_args = argp;
> +	save_buf(argp, &test_stateid->ts_savedp);

I guess that works.  It's a little strange.  I can't see an immediate
problem.

--b.

> +
> +	for (i = 0; i < test_stateid->ts_num_ids; i++) {
> +		status = nfsd4_decode_stateid(argp, &si);
> +		if (status)
> +			return status;
> +	}
> +
> +	status = 0;
> +out:
> +	return status;
> +xdr_error:
> +	dprintk("NFSD: xdr error (%s:%d)\n", __FILE__, __LINE__);
> +	status = nfserr_bad_xdr;
> +	goto out;
> +}
> +
>  static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
>  {
>  	DECODE_HEAD;
> @@ -1393,7 +1444,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
>  	[OP_SECINFO_NO_NAME]	= (nfsd4_dec)nfsd4_decode_secinfo_no_name,
>  	[OP_SEQUENCE]		= (nfsd4_dec)nfsd4_decode_sequence,
>  	[OP_SET_SSV]		= (nfsd4_dec)nfsd4_decode_notsupp,
> -	[OP_TEST_STATEID]	= (nfsd4_dec)nfsd4_decode_notsupp,
> +	[OP_TEST_STATEID]	= (nfsd4_dec)nfsd4_decode_test_stateid,
>  	[OP_WANT_DELEGATION]	= (nfsd4_dec)nfsd4_decode_notsupp,
>  	[OP_DESTROY_CLIENTID]	= (nfsd4_dec)nfsd4_decode_notsupp,
>  	[OP_RECLAIM_COMPLETE]	= (nfsd4_dec)nfsd4_decode_reclaim_complete,
> @@ -3166,6 +3217,34 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
>  	return 0;
>  }
>  
> +__be32
> +nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr,
> +			  struct nfsd4_test_stateid *test_stateid)
> +{
> +	struct nfsd4_compoundargs *argp;
> +	stateid_t si;
> +	__be32 *p;
> +	int i;
> +	int valid;
> +
> +	restore_buf(test_stateid->ts_saved_args, &test_stateid->ts_savedp);
> +	argp = test_stateid->ts_saved_args;
> +
> +	RESERVE_SPACE(4);
> +	*p++ = htonl(test_stateid->ts_num_ids);
> +	resp->p = p;
> +
> +	for (i = 0; i < test_stateid->ts_num_ids; i++) {
> +		nfsd4_decode_stateid(argp, &si);
> +		valid = nfsd4_do_test_stateid(&si, test_stateid->ts_saved_state);
> +		RESERVE_SPACE(4);
> +		*p++ = htonl(valid);
> +		resp->p = p;
> +	}
> +
> +	return nfserr;
> +}
> +
>  static __be32
>  nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
>  {
> @@ -3234,7 +3313,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
>  	[OP_SECINFO_NO_NAME]	= (nfsd4_enc)nfsd4_encode_secinfo_no_name,
>  	[OP_SEQUENCE]		= (nfsd4_enc)nfsd4_encode_sequence,
>  	[OP_SET_SSV]		= (nfsd4_enc)nfsd4_encode_noop,
> -	[OP_TEST_STATEID]	= (nfsd4_enc)nfsd4_encode_noop,
> +	[OP_TEST_STATEID]	= (nfsd4_enc)nfsd4_encode_test_stateid,
>  	[OP_WANT_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
>  	[OP_DESTROY_CLIENTID]	= (nfsd4_enc)nfsd4_encode_noop,
>  	[OP_RECLAIM_COMPLETE]	= (nfsd4_enc)nfsd4_encode_noop,
> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
> index 6bd2f3c..3447b04 100644
> --- a/fs/nfsd/state.h
> +++ b/fs/nfsd/state.h
> @@ -482,6 +482,7 @@ extern void nfsd4_recdir_purge_old(void);
>  extern int nfsd4_create_clid_dir(struct nfs4_client *clp);
>  extern void nfsd4_remove_clid_dir(struct nfs4_client *clp);
>  extern void release_session_client(struct nfsd4_session *);
> +extern __be32 nfsd4_do_test_stateid(stateid_t *, struct nfsd4_compound_state *);
>  
>  static inline void
>  nfs4_put_stateowner(struct nfs4_stateowner *so)
> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> index ed1784d..628ef97 100644
> --- a/fs/nfsd/xdr4.h
> +++ b/fs/nfsd/xdr4.h
> @@ -342,6 +342,20 @@ struct nfsd4_setclientid_confirm {
>  	nfs4_verifier	sc_confirm;
>  };
>  
> +struct nfsd4_saved_compoundargs {
> +	__be32 *p;
> +	__be32 *end;
> +	int pagelen;
> +	struct page **pagelist;
> +};
> +
> +struct nfsd4_test_stateid {
> +	__be32		ts_num_ids;
> +	struct nfsd4_compoundargs *ts_saved_args;
> +	struct nfsd4_saved_compoundargs ts_savedp;
> +	struct nfsd4_compound_state *ts_saved_state;
> +};
> +
>  struct nfsd4_free_stateid {
>  	stateid_t	fr_stateid;         /* request */
>  	__be32		fr_status;          /* response */
> @@ -437,6 +451,7 @@ struct nfsd4_op {
>  		struct nfsd4_destroy_session	destroy_session;
>  		struct nfsd4_sequence		sequence;
>  		struct nfsd4_reclaim_complete	reclaim_complete;
> +		struct nfsd4_test_stateid	test_stateid;
>  		struct nfsd4_free_stateid	free_stateid;
>  	} u;
>  	struct nfs4_replay *			replay;
> @@ -570,6 +585,8 @@ extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
>  		struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
>  extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
>  			  struct nfsd4_compound_state *, clientid_t *clid);
> +extern __be32 nfsd4_test_stateid(struct svc_rqst *rqstp,
> +		struct nfsd4_compound_state *, struct nfsd4_test_stateid *test_stateid);
>  extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
>  		struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
>  #endif
> -- 
> 1.7.5.1
> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 1/2] NFSD: added FREE_STATEID operation
  2011-05-18 22:56   ` J. Bruce Fields
@ 2011-05-19  1:12     ` J. Bruce Fields
  2011-05-19 15:33     ` Bryan Schumaker
  1 sibling, 0 replies; 11+ messages in thread
From: J. Bruce Fields @ 2011-05-19  1:12 UTC (permalink / raw)
  To: bjschuma; +Cc: linux-nfs

On Wed, May 18, 2011 at 06:56:09PM -0400, J. Bruce Fields wrote:
> Also I guess a client shouldn't be permitted to free a delegation that
> it still holds.  That means we'll always just return nfserr_locks for
> delegation stateid's.  I assume free_stateid is only useful in this case
> for the case where a server forcibly revokes part of the client's state
> and leaves some "stub" stateid's around in place of the revoked ones.

So I think what we really need to do here is this:

	http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues#SEQ4_STATUS_RECALLABLE_STATE_REVOKED

--b.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 2/2] NFSD: Added TEST_STATEID opreation
  2011-05-18 23:57   ` J. Bruce Fields
@ 2011-05-19 15:21     ` Bryan Schumaker
  0 siblings, 0 replies; 11+ messages in thread
From: Bryan Schumaker @ 2011-05-19 15:21 UTC (permalink / raw)
  To: J. Bruce Fields; +Cc: linux-nfs

On 05/18/2011 07:57 PM, J. Bruce Fields wrote:
> On Mon, May 16, 2011 at 03:43:41PM -0400, bjschuma@netapp.com wrote:
>> From: Bryan Schumaker <bjschuma@netapp.com>
>>
>> This operation is used by the client to check the validity of a list of
>> stateids.
>>
>> Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
>> ---
>>  fs/nfsd/nfs4proc.c  |    5 +++
>>  fs/nfsd/nfs4state.c |   53 +++++++++++++++++++++++++++++++
>>  fs/nfsd/nfs4xdr.c   |   85 +++++++++++++++++++++++++++++++++++++++++++++++++--
>>  fs/nfsd/state.h     |    1 +
>>  fs/nfsd/xdr4.h      |   17 ++++++++++
>>  5 files changed, 158 insertions(+), 3 deletions(-)
>>
>> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
>> index 7e00116..a856f30 100644
>> --- a/fs/nfsd/nfs4proc.c
>> +++ b/fs/nfsd/nfs4proc.c
>> @@ -1412,6 +1412,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
>>  		.op_flags = OP_HANDLES_WRONGSEC,
>>  		.op_name = "OP_SECINFO_NO_NAME",
>>  	},
>> +	[OP_TEST_STATEID] = {
>> +		.op_func = (nfsd4op_func)nfsd4_test_stateid,
>> +		.op_flags = ALLOWED_WITHOUT_FH,
>> +		.op_name = "OP_TEST_STATEID",
>> +	},
>>  	[OP_FREE_STATEID] = {
>>  		.op_func = (nfsd4op_func)nfsd4_free_stateid,
>>  		.op_flags = ALLOWED_WITHOUT_FH,
>> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
>> index 043baa0..18e0762 100644
>> --- a/fs/nfsd/nfs4state.c
>> +++ b/fs/nfsd/nfs4state.c
>> @@ -37,6 +37,7 @@
>>  #include <linux/slab.h>
>>  #include <linux/namei.h>
>>  #include <linux/swap.h>
>> +#include <linux/pagemap.h>
>>  #include <linux/sunrpc/svcauth_gss.h>
>>  #include <linux/sunrpc/clnt.h>
>>  #include "xdr4.h"
>> @@ -3136,6 +3137,32 @@ static int is_delegation_stateid(stateid_t *stateid)
>>  	return stateid->si_fileid == 0;
>>  }
>>  
>> +static __be32 nfs4_validate_stateid(stateid_t *stateid, int flags, struct file **filpp, struct svc_fh *current_fh)
> 
> You're not use current_fh (which makes sense, since test_stateid doesn't
> seem to use the current filehandle).

I wasn't using filpp either.  I've removed both from the patch.

> 
> Ditto for callers.
> 
>> +{
>> +	struct nfs4_stateid *stp = NULL;
>> +	__be32 status = nfserr_stale_stateid;
>> +
>> +	if (STALE_STATEID(stateid))
>> +		goto out;
>> +
>> +	status = nfserr_expired;
>> +	stp = search_for_stateid(stateid);
>> +	if (!stp)
>> +		goto out;
>> +	status = nfserr_bad_stateid;
>> +
>> +	if (!stp->st_stateowner->so_confirmed)
>> +		goto out;
>> +
>> +	status = check_stateid_generation(stateid, &stp->st_stateid, flags);
>> +	if (status)
>> +		goto out;
>> +
>> +	status = nfs_ok;
>> +out:
>> +	return status;
>> +}
>> +
>>  /*
>>  * Checks for stateid operations
>>  */
>> @@ -3252,6 +3279,32 @@ nfsd4_free_file_stateid(stateid_t *stateid)
>>  }
>>  
>>  /*
>> + * Test if the stateid is valid
>> + */
>> +__be32
>> +nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
>> +		   struct nfsd4_test_stateid *test_stateid)
>> +{
>> +	test_stateid->ts_saved_state = cstate;
>> +	return nfs_ok;
>> +}
>> +
>> +__be32
>> +nfsd4_do_test_stateid(stateid_t *stateid, struct nfsd4_compound_state *cstate)
>> +{
>> +	__be32 ret;
>> +	unsigned int lock_flags = LOCK_STATE | RD_STATE | WR_STATE;
>> +	unsigned int open_flags = OPEN_STATE | RD_STATE | WR_STATE;
>> +	struct svc_fh *current_fh;
>> +
>> +	current_fh = &cstate->current_fh;
>> +	ret = nfs4_validate_stateid(stateid, lock_flags, NULL, current_fh);
>> +	if (!ret)
>> +		ret = nfs4_validate_stateid(stateid, open_flags, NULL, current_fh);
> 
> Do we really need to do this once for each set of flags?
> 
> Seems we only use them in check_stateid_generation(), which only uses
> them to check the HAS_SESSION case.  (Which actually should be set in
> our case?)

I can check for HAS_SESSION using cstate, and then pass the result to nfs4_validate_stateid().
> 
>> +	return ret;
>> +}
>> +
>> +/*
>>   * Free a state id
>>   */
>>  __be32
>> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
>> index 5da6874..2239114 100644
>> --- a/fs/nfsd/nfs4xdr.c
>> +++ b/fs/nfsd/nfs4xdr.c
>> @@ -44,13 +44,14 @@
>>  #include <linux/namei.h>
>>  #include <linux/statfs.h>
>>  #include <linux/utsname.h>
>> +#include <linux/pagemap.h>
>>  #include <linux/sunrpc/svcauth_gss.h>
>>  
>>  #include "idmap.h"
>>  #include "acl.h"
>>  #include "xdr4.h"
>>  #include "vfs.h"
>> -
>> +#include "state.h"
>>  
>>  #define NFSDDBG_FACILITY		NFSDDBG_XDR
>>  
>> @@ -131,6 +132,22 @@ xdr_error:					\
>>  	}					\
>>  } while (0)
>>  
>> +static void save_buf(struct nfsd4_compoundargs *argp, struct nfsd4_saved_compoundargs *savep)
>> +{
>> +	savep->p        = argp->p;
>> +	savep->end      = argp->end;
>> +	savep->pagelen  = argp->pagelen;
>> +	savep->pagelist = argp->pagelist;
>> +}
>> +
>> +static void restore_buf(struct nfsd4_compoundargs *argp, struct nfsd4_saved_compoundargs *savep)
>> +{
>> +	argp->p        = savep->p;
>> +	argp->end      = savep->end;
>> +	argp->pagelen  = savep->pagelen;
>> +	argp->pagelist = savep->pagelist;
>> +}
>> +
>>  static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
>>  {
>>  	/* We want more bytes than seem to be available.
>> @@ -1274,6 +1291,40 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
>>  	DECODE_TAIL;
>>  }
>>  
>> +static __be32
>> +nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid)
>> +{
>> +	unsigned int nbytes;
>> +	stateid_t si;
>> +	int i;
>> +	__be32 *p;
>> +	__be32 status;
>> +
>> +	READ_BUF(4);
>> +	test_stateid->ts_num_ids = ntohl(*p++);
>> +
>> +	nbytes = test_stateid->ts_num_ids * sizeof(stateid_t);
>> +	if (nbytes > (u32)((char *)argp->end - (char *)argp->p))
>> +		goto xdr_error;
>> +
>> +	test_stateid->ts_saved_args = argp;
>> +	save_buf(argp, &test_stateid->ts_savedp);
> 
> I guess that works.  It's a little strange.  I can't see an immediate
> problem.
> 
> --b.
> 
>> +
>> +	for (i = 0; i < test_stateid->ts_num_ids; i++) {
>> +		status = nfsd4_decode_stateid(argp, &si);
>> +		if (status)
>> +			return status;
>> +	}
>> +
>> +	status = 0;
>> +out:
>> +	return status;
>> +xdr_error:
>> +	dprintk("NFSD: xdr error (%s:%d)\n", __FILE__, __LINE__);
>> +	status = nfserr_bad_xdr;
>> +	goto out;
>> +}
>> +
>>  static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
>>  {
>>  	DECODE_HEAD;
>> @@ -1393,7 +1444,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
>>  	[OP_SECINFO_NO_NAME]	= (nfsd4_dec)nfsd4_decode_secinfo_no_name,
>>  	[OP_SEQUENCE]		= (nfsd4_dec)nfsd4_decode_sequence,
>>  	[OP_SET_SSV]		= (nfsd4_dec)nfsd4_decode_notsupp,
>> -	[OP_TEST_STATEID]	= (nfsd4_dec)nfsd4_decode_notsupp,
>> +	[OP_TEST_STATEID]	= (nfsd4_dec)nfsd4_decode_test_stateid,
>>  	[OP_WANT_DELEGATION]	= (nfsd4_dec)nfsd4_decode_notsupp,
>>  	[OP_DESTROY_CLIENTID]	= (nfsd4_dec)nfsd4_decode_notsupp,
>>  	[OP_RECLAIM_COMPLETE]	= (nfsd4_dec)nfsd4_decode_reclaim_complete,
>> @@ -3166,6 +3217,34 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
>>  	return 0;
>>  }
>>  
>> +__be32
>> +nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr,
>> +			  struct nfsd4_test_stateid *test_stateid)
>> +{
>> +	struct nfsd4_compoundargs *argp;
>> +	stateid_t si;
>> +	__be32 *p;
>> +	int i;
>> +	int valid;
>> +
>> +	restore_buf(test_stateid->ts_saved_args, &test_stateid->ts_savedp);
>> +	argp = test_stateid->ts_saved_args;
>> +
>> +	RESERVE_SPACE(4);
>> +	*p++ = htonl(test_stateid->ts_num_ids);
>> +	resp->p = p;
>> +
>> +	for (i = 0; i < test_stateid->ts_num_ids; i++) {
>> +		nfsd4_decode_stateid(argp, &si);
>> +		valid = nfsd4_do_test_stateid(&si, test_stateid->ts_saved_state);
>> +		RESERVE_SPACE(4);
>> +		*p++ = htonl(valid);
>> +		resp->p = p;
>> +	}
>> +
>> +	return nfserr;
>> +}
>> +
>>  static __be32
>>  nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
>>  {
>> @@ -3234,7 +3313,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
>>  	[OP_SECINFO_NO_NAME]	= (nfsd4_enc)nfsd4_encode_secinfo_no_name,
>>  	[OP_SEQUENCE]		= (nfsd4_enc)nfsd4_encode_sequence,
>>  	[OP_SET_SSV]		= (nfsd4_enc)nfsd4_encode_noop,
>> -	[OP_TEST_STATEID]	= (nfsd4_enc)nfsd4_encode_noop,
>> +	[OP_TEST_STATEID]	= (nfsd4_enc)nfsd4_encode_test_stateid,
>>  	[OP_WANT_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
>>  	[OP_DESTROY_CLIENTID]	= (nfsd4_enc)nfsd4_encode_noop,
>>  	[OP_RECLAIM_COMPLETE]	= (nfsd4_enc)nfsd4_encode_noop,
>> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
>> index 6bd2f3c..3447b04 100644
>> --- a/fs/nfsd/state.h
>> +++ b/fs/nfsd/state.h
>> @@ -482,6 +482,7 @@ extern void nfsd4_recdir_purge_old(void);
>>  extern int nfsd4_create_clid_dir(struct nfs4_client *clp);
>>  extern void nfsd4_remove_clid_dir(struct nfs4_client *clp);
>>  extern void release_session_client(struct nfsd4_session *);
>> +extern __be32 nfsd4_do_test_stateid(stateid_t *, struct nfsd4_compound_state *);
>>  
>>  static inline void
>>  nfs4_put_stateowner(struct nfs4_stateowner *so)
>> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
>> index ed1784d..628ef97 100644
>> --- a/fs/nfsd/xdr4.h
>> +++ b/fs/nfsd/xdr4.h
>> @@ -342,6 +342,20 @@ struct nfsd4_setclientid_confirm {
>>  	nfs4_verifier	sc_confirm;
>>  };
>>  
>> +struct nfsd4_saved_compoundargs {
>> +	__be32 *p;
>> +	__be32 *end;
>> +	int pagelen;
>> +	struct page **pagelist;
>> +};
>> +
>> +struct nfsd4_test_stateid {
>> +	__be32		ts_num_ids;
>> +	struct nfsd4_compoundargs *ts_saved_args;
>> +	struct nfsd4_saved_compoundargs ts_savedp;
>> +	struct nfsd4_compound_state *ts_saved_state;
>> +};
>> +
>>  struct nfsd4_free_stateid {
>>  	stateid_t	fr_stateid;         /* request */
>>  	__be32		fr_status;          /* response */
>> @@ -437,6 +451,7 @@ struct nfsd4_op {
>>  		struct nfsd4_destroy_session	destroy_session;
>>  		struct nfsd4_sequence		sequence;
>>  		struct nfsd4_reclaim_complete	reclaim_complete;
>> +		struct nfsd4_test_stateid	test_stateid;
>>  		struct nfsd4_free_stateid	free_stateid;
>>  	} u;
>>  	struct nfs4_replay *			replay;
>> @@ -570,6 +585,8 @@ extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
>>  		struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
>>  extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
>>  			  struct nfsd4_compound_state *, clientid_t *clid);
>> +extern __be32 nfsd4_test_stateid(struct svc_rqst *rqstp,
>> +		struct nfsd4_compound_state *, struct nfsd4_test_stateid *test_stateid);
>>  extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
>>  		struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
>>  #endif
>> -- 
>> 1.7.5.1
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 1/2] NFSD: added FREE_STATEID operation
  2011-05-18 22:56   ` J. Bruce Fields
  2011-05-19  1:12     ` J. Bruce Fields
@ 2011-05-19 15:33     ` Bryan Schumaker
  2011-05-19 16:30       ` J. Bruce Fields
  1 sibling, 1 reply; 11+ messages in thread
From: Bryan Schumaker @ 2011-05-19 15:33 UTC (permalink / raw)
  To: J. Bruce Fields; +Cc: linux-nfs

On 05/18/2011 06:56 PM, J. Bruce Fields wrote:
> On Mon, May 16, 2011 at 03:43:40PM -0400, bjschuma@netapp.com wrote:
>> +static __be32
>> +nfsd4_free_file_stateid(stateid_t *stateid)
>> +{
>> +	struct nfs4_stateid *stp = search_for_stateid(stateid);
>> +	if (!stp)
>> +		return nfserr_bad_stateid;
>> +	if (stateid->si_generation != 0) {
>> +		if (stateid->si_generation < stp->st_stateid.si_generation)
>> +			return nfserr_old_stateid;
>> +		if (stateid->si_generation > stp->st_stateid.si_generation)
>> +			return nfserr_bad_stateid;
>> +	}
>> +
>> +	if (check_for_locks(stp->st_file, stp->st_stateowner))
>> +		return nfserr_locks_held;
> 
> I think this catches a lock stateid, but not an open stateid that has
> associated lock stateid's that in turn hold locks.
> 
> Hm, also:
> 
> 	"The FREE_STATEID operation is used to free a stateid that no
> 	longer has any associated locks (including opens, byte-range
> 	locks, delegations, and layouts)"
> 
> So an open stateid also shouldn't be freeable as long as there are opens
> associated with it.

So having an open stateid doesn't necessarily mean that the file is open?  and having a lock stateid doesn't mean that the file is locked?

I'll look at making a "check_for_opens()" function to help with this check.

> 
> Also I guess a client shouldn't be permitted to free a delegation that
> it still holds.  That means we'll always just return nfserr_locks for
> delegation stateid's.  I assume free_stateid is only useful in this case

Sounds simple enough.

> for the case where a server forcibly revokes part of the client's state
> and leaves some "stub" stateid's around in place of the revoked ones.
> 
> --b.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 1/2] NFSD: added FREE_STATEID operation
  2011-05-19 15:33     ` Bryan Schumaker
@ 2011-05-19 16:30       ` J. Bruce Fields
  2011-05-19 16:35         ` Bryan Schumaker
  0 siblings, 1 reply; 11+ messages in thread
From: J. Bruce Fields @ 2011-05-19 16:30 UTC (permalink / raw)
  To: Bryan Schumaker; +Cc: linux-nfs

On Thu, May 19, 2011 at 11:33:56AM -0400, Bryan Schumaker wrote:
> On 05/18/2011 06:56 PM, J. Bruce Fields wrote:
> > On Mon, May 16, 2011 at 03:43:40PM -0400, bjschuma@netapp.com wrote:
> >> +static __be32
> >> +nfsd4_free_file_stateid(stateid_t *stateid)
> >> +{
> >> +	struct nfs4_stateid *stp = search_for_stateid(stateid);
> >> +	if (!stp)
> >> +		return nfserr_bad_stateid;
> >> +	if (stateid->si_generation != 0) {
> >> +		if (stateid->si_generation < stp->st_stateid.si_generation)
> >> +			return nfserr_old_stateid;
> >> +		if (stateid->si_generation > stp->st_stateid.si_generation)
> >> +			return nfserr_bad_stateid;
> >> +	}
> >> +
> >> +	if (check_for_locks(stp->st_file, stp->st_stateowner))
> >> +		return nfserr_locks_held;
> > 
> > I think this catches a lock stateid, but not an open stateid that has
> > associated lock stateid's that in turn hold locks.
> > 
> > Hm, also:
> > 
> > 	"The FREE_STATEID operation is used to free a stateid that no
> > 	longer has any associated locks (including opens, byte-range
> > 	locks, delegations, and layouts)"
> > 
> > So an open stateid also shouldn't be freeable as long as there are opens
> > associated with it.
> 
> So having an open stateid doesn't necessarily mean that the file is open?

Looking back at it.... Sorry, you're right, open stateid's are destroyed
on close, so like delegation stateid's they should just never be
freeable.

> and having a lock stateid doesn't mean that the file is locked?

Here we don't know whether the file's locked or not, so we do have to
check.

> I'll look at making a "check_for_opens()" function to help with this check.

So actually I think it's really simple: always fail opens and
delegations, but check for locks.  (Except I'm not sure if
check_for_locks() does the right things, as it operates on a stateowner
not a stateid--I'm forgetting how those work....  If there's a unique
lock stateid per (stateowner,file) pair then check_for_locks() should do
what you want.)

--b.

> 
> > 
> > Also I guess a client shouldn't be permitted to free a delegation that
> > it still holds.  That means we'll always just return nfserr_locks for
> > delegation stateid's.  I assume free_stateid is only useful in this case
> 
> Sounds simple enough.
> 
> > for the case where a server forcibly revokes part of the client's state
> > and leaves some "stub" stateid's around in place of the revoked ones.
> > 
> > --b.
> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 1/2] NFSD: added FREE_STATEID operation
  2011-05-19 16:30       ` J. Bruce Fields
@ 2011-05-19 16:35         ` Bryan Schumaker
  2011-05-20 18:29           ` Bryan Schumaker
  0 siblings, 1 reply; 11+ messages in thread
From: Bryan Schumaker @ 2011-05-19 16:35 UTC (permalink / raw)
  To: J. Bruce Fields; +Cc: linux-nfs

On 05/19/2011 12:30 PM, J. Bruce Fields wrote:
> On Thu, May 19, 2011 at 11:33:56AM -0400, Bryan Schumaker wrote:
>> On 05/18/2011 06:56 PM, J. Bruce Fields wrote:
>>> On Mon, May 16, 2011 at 03:43:40PM -0400, bjschuma@netapp.com wrote:
>>>> +static __be32
>>>> +nfsd4_free_file_stateid(stateid_t *stateid)
>>>> +{
>>>> +	struct nfs4_stateid *stp = search_for_stateid(stateid);
>>>> +	if (!stp)
>>>> +		return nfserr_bad_stateid;
>>>> +	if (stateid->si_generation != 0) {
>>>> +		if (stateid->si_generation < stp->st_stateid.si_generation)
>>>> +			return nfserr_old_stateid;
>>>> +		if (stateid->si_generation > stp->st_stateid.si_generation)
>>>> +			return nfserr_bad_stateid;
>>>> +	}
>>>> +
>>>> +	if (check_for_locks(stp->st_file, stp->st_stateowner))
>>>> +		return nfserr_locks_held;
>>>
>>> I think this catches a lock stateid, but not an open stateid that has
>>> associated lock stateid's that in turn hold locks.
>>>
>>> Hm, also:
>>>
>>> 	"The FREE_STATEID operation is used to free a stateid that no
>>> 	longer has any associated locks (including opens, byte-range
>>> 	locks, delegations, and layouts)"
>>>
>>> So an open stateid also shouldn't be freeable as long as there are opens
>>> associated with it.
>>
>> So having an open stateid doesn't necessarily mean that the file is open?
> 
> Looking back at it.... Sorry, you're right, open stateid's are destroyed
> on close, so like delegation stateid's they should just never be
> freeable.
> 
>> and having a lock stateid doesn't mean that the file is locked?
> 
> Here we don't know whether the file's locked or not, so we do have to
> check.
> 
>> I'll look at making a "check_for_opens()" function to help with this check.
> 
> So actually I think it's really simple: always fail opens and
> delegations, but check for locks.  (Except I'm not sure if

That is much simpler.  I'm glad I asked!

> check_for_locks() does the right things, as it operates on a stateowner
> not a stateid--I'm forgetting how those work....  If there's a unique
> lock stateid per (stateowner,file) pair then check_for_locks() should do
> what you want.)

I'm not sure how they work either.  I'll browse through the code to see what I can find.

Thanks!

> 
> --b.
> 
>>
>>>
>>> Also I guess a client shouldn't be permitted to free a delegation that
>>> it still holds.  That means we'll always just return nfserr_locks for
>>> delegation stateid's.  I assume free_stateid is only useful in this case
>>
>> Sounds simple enough.
>>
>>> for the case where a server forcibly revokes part of the client's state
>>> and leaves some "stub" stateid's around in place of the revoked ones.
>>>
>>> --b.
>>


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 1/2] NFSD: added FREE_STATEID operation
  2011-05-19 16:35         ` Bryan Schumaker
@ 2011-05-20 18:29           ` Bryan Schumaker
  0 siblings, 0 replies; 11+ messages in thread
From: Bryan Schumaker @ 2011-05-20 18:29 UTC (permalink / raw)
  To: J. Bruce Fields; +Cc: linux-nfs

On 05/19/2011 12:35 PM, Bryan Schumaker wrote:
> On 05/19/2011 12:30 PM, J. Bruce Fields wrote:
>> On Thu, May 19, 2011 at 11:33:56AM -0400, Bryan Schumaker wrote:
>>> On 05/18/2011 06:56 PM, J. Bruce Fields wrote:
>>>> On Mon, May 16, 2011 at 03:43:40PM -0400, bjschuma@netapp.com wrote:
>>>>> +static __be32
>>>>> +nfsd4_free_file_stateid(stateid_t *stateid)
>>>>> +{
>>>>> +	struct nfs4_stateid *stp = search_for_stateid(stateid);
>>>>> +	if (!stp)
>>>>> +		return nfserr_bad_stateid;
>>>>> +	if (stateid->si_generation != 0) {
>>>>> +		if (stateid->si_generation < stp->st_stateid.si_generation)
>>>>> +			return nfserr_old_stateid;
>>>>> +		if (stateid->si_generation > stp->st_stateid.si_generation)
>>>>> +			return nfserr_bad_stateid;
>>>>> +	}
>>>>> +
>>>>> +	if (check_for_locks(stp->st_file, stp->st_stateowner))
>>>>> +		return nfserr_locks_held;
>>>>
>>>> I think this catches a lock stateid, but not an open stateid that has
>>>> associated lock stateid's that in turn hold locks.
>>>>
>>>> Hm, also:
>>>>
>>>> 	"The FREE_STATEID operation is used to free a stateid that no
>>>> 	longer has any associated locks (including opens, byte-range
>>>> 	locks, delegations, and layouts)"
>>>>
>>>> So an open stateid also shouldn't be freeable as long as there are opens
>>>> associated with it.
>>>
>>> So having an open stateid doesn't necessarily mean that the file is open?
>>
>> Looking back at it.... Sorry, you're right, open stateid's are destroyed
>> on close, so like delegation stateid's they should just never be
>> freeable.
>>
>>> and having a lock stateid doesn't mean that the file is locked?
>>
>> Here we don't know whether the file's locked or not, so we do have to
>> check.
>>
>>> I'll look at making a "check_for_opens()" function to help with this check.
>>
>> So actually I think it's really simple: always fail opens and
>> delegations, but check for locks.  (Except I'm not sure if
> 
> That is much simpler.  I'm glad I asked!
> 
>> check_for_locks() does the right things, as it operates on a stateowner
>> not a stateid--I'm forgetting how those work....  If there's a unique
>> lock stateid per (stateowner,file) pair then check_for_locks() should do
>> what you want.)
> 
> I'm not sure how they work either.  I'll browse through the code to see what I can find.

It looks like a stateid only applies to a single (stateowner, file), but I don't know if multiple stateids can point to the same (stateowner, file).

It looks like lock set / test / unlock is all done through the vfs, so I'm not sure how to check if a specific stateid is locked without using check_for_locks().
> 
> Thanks!
> 
>>
>> --b.
>>
>>>
>>>>
>>>> Also I guess a client shouldn't be permitted to free a delegation that
>>>> it still holds.  That means we'll always just return nfserr_locks for
>>>> delegation stateid's.  I assume free_stateid is only useful in this case
>>>
>>> Sounds simple enough.
>>>
>>>> for the case where a server forcibly revokes part of the client's state
>>>> and leaves some "stub" stateid's around in place of the revoked ones.
>>>>
>>>> --b.
>>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2011-05-20 18:29 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-16 19:43 [PATCH v2 0/2] NFSD: add FREE_STATEID and TEST_STATEID operations bjschuma
2011-05-16 19:43 ` [PATCH v2 1/2] NFSD: added FREE_STATEID operation bjschuma
2011-05-18 22:56   ` J. Bruce Fields
2011-05-19  1:12     ` J. Bruce Fields
2011-05-19 15:33     ` Bryan Schumaker
2011-05-19 16:30       ` J. Bruce Fields
2011-05-19 16:35         ` Bryan Schumaker
2011-05-20 18:29           ` Bryan Schumaker
2011-05-16 19:43 ` [PATCH v2 2/2] NFSD: Added TEST_STATEID opreation bjschuma
2011-05-18 23:57   ` J. Bruce Fields
2011-05-19 15:21     ` Bryan Schumaker

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.