From f3e5aa184cb8e31a8915d70c9e0a0700e8b9a483 Mon Sep 17 00:00:00 2001
From: Trond Myklebust <trond.myklebust@fys.uio.no>
Date: Sat, 10 Apr 2004 21:35:03 -0700
Subject: [PATCH] NFSv4: use the (more efficient) NFSv2/v3-like XDR scheme for
 generating PATHCONF RPC calls.

---
 fs/nfs/nfs4proc.c       |  43 ++++++--------
 fs/nfs/nfs4xdr.c        | 120 +++++++++++++++++++++++++++++++++++-----
 include/linux/nfs4.h    |   1 +
 include/linux/nfs_xdr.h |   6 +-
 4 files changed, 130 insertions(+), 40 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 2693f8bdbec5..682c9c2d1832 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -119,15 +119,13 @@ u32 nfs4_pathconf_bitmap[2] = {
 static inline void
 __nfs4_setup_getattr(struct nfs4_compound *cp, u32 *bitmap,
 		     struct nfs_fattr *fattr,
-		     struct nfs_fsstat *fsstat,
-		     struct nfs_pathconf *pathconf)
+		     struct nfs_fsstat *fsstat)
 {
         struct nfs4_getattr *getattr = GET_OP(cp, getattr);
 
-        getattr->gt_bmval = bitmap;
-        getattr->gt_attrs = fattr;
+	getattr->gt_bmval = bitmap;
+	getattr->gt_attrs = fattr;
 	getattr->gt_fsstat = fsstat;
-	getattr->gt_pathconf = pathconf;
 
         OPNUM(cp) = OP_GETATTR;
         cp->req_nops++;
@@ -137,8 +135,7 @@ static void
 nfs4_setup_getattr(struct nfs4_compound *cp,
 		struct nfs_fattr *fattr)
 {
-	__nfs4_setup_getattr(cp, nfs4_fattr_bitmap, fattr,
-			NULL, NULL);
+	__nfs4_setup_getattr(cp, nfs4_fattr_bitmap, fattr, NULL);
 }
 
 static void
@@ -146,15 +143,7 @@ nfs4_setup_statfs(struct nfs4_compound *cp,
 		struct nfs_fsstat *fsstat)
 {
 	__nfs4_setup_getattr(cp, nfs4_statfs_bitmap,
-			NULL, fsstat, NULL);
-}
-
-static void
-nfs4_setup_pathconf(struct nfs4_compound *cp,
-		struct nfs_pathconf *pathconf)
-{
-	__nfs4_setup_getattr(cp, nfs4_pathconf_bitmap,
-			NULL, NULL, pathconf);
+			NULL, fsstat);
 }
 
 static void
@@ -1380,17 +1369,21 @@ static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, s
 	return nfs4_map_errors(nfs4_do_fsinfo(server, fhandle, fsinfo));
 }
 
-static int
-nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
-		   struct nfs_pathconf *pathconf)
+static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
+		struct nfs_pathconf *pathconf)
 {
-	struct nfs4_compound compound;
-	struct nfs4_op ops[2];
+	struct nfs4_pathconf_arg args = {
+		.fh = fhandle,
+		.bitmask = nfs4_pathconf_bitmap,
+	};
+	struct rpc_message msg = {
+		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_PATHCONF],
+		.rpc_argp = &args,
+		.rpc_resp = pathconf,
+	};
 
-	nfs4_setup_compound(&compound, ops, server, "statfs");
-	nfs4_setup_putfh(&compound, fhandle);
-	nfs4_setup_pathconf(&compound, pathconf);
-	return nfs4_map_errors(nfs4_call_compound(&compound, NULL, 0));
+	pathconf->fattr->valid = 0;
+	return nfs4_map_errors(rpc_call_sync(server->client, &msg, 0));
 }
 
 static void
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 1ab60b1dbf90..50dbcfb82f34 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -319,6 +319,12 @@ static int nfs_stat_to_errno(int);
 				decode_create_maxsz + \
 				decode_getattr_maxsz + \
 				decode_getfh_maxsz)
+#define NFS4_enc_pathconf_sz	(compound_encode_hdr_maxsz + \
+				encode_putfh_maxsz + \
+				encode_getattr_maxsz)
+#define NFS4_dec_pathconf_sz	(compound_decode_hdr_maxsz + \
+				decode_putfh_maxsz + \
+				decode_getattr_maxsz)
 
 
 
@@ -1658,6 +1664,27 @@ nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, uint32_t *p, void *fhandle)
 	return status;
 }
 
+/*
+ * a PATHCONF request
+ */
+static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, uint32_t *p, const struct nfs4_pathconf_arg *args)
+{
+	extern u32 nfs4_pathconf_bitmap[2];
+	struct xdr_stream xdr;
+	struct compound_hdr hdr = {
+		.nops = 2,
+	};
+	int status;
+
+	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+	encode_compound_hdr(&xdr, &hdr);
+	status = encode_putfh(&xdr, args->fh);
+	if (!status)
+		status = encode_getattr_one(&xdr,
+				args->bitmask[0] & nfs4_pathconf_bitmap[0]);
+	return status;
+}
+
 /*
  * a RENEW request
  */
@@ -1911,6 +1938,40 @@ static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t
 	return 0;
 }
 
+static int decode_attr_maxlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxlink)
+{
+	uint32_t *p;
+	int status = 0;
+
+	*maxlink = 1;
+	if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXLINK - 1U)))
+		return -EIO;
+	if (likely(bitmap[0] & FATTR4_WORD0_MAXLINK)) {
+		READ_BUF(4);
+		READ32(*maxlink);
+		bitmap[0] &= ~FATTR4_WORD0_MAXLINK;
+	}
+	dprintk("%s: maxlink=%u\n", __FUNCTION__, *maxlink);
+	return status;
+}
+
+static int decode_attr_maxname(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxname)
+{
+	uint32_t *p;
+	int status = 0;
+
+	*maxname = 1024;
+	if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXNAME - 1U)))
+		return -EIO;
+	if (likely(bitmap[0] & FATTR4_WORD0_MAXNAME)) {
+		READ_BUF(4);
+		READ32(*maxname);
+		bitmap[0] &= ~FATTR4_WORD0_MAXNAME;
+	}
+	dprintk("%s: maxname=%u\n", __FUNCTION__, *maxname);
+	return status;
+}
+
 static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *mode)
 {
 	uint32_t *p;
@@ -2184,7 +2245,6 @@ static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
 }
 
 extern uint32_t nfs4_fsstat_bitmap[2];
-extern uint32_t nfs4_pathconf_bitmap[2];
 
 static int
 decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr,
@@ -2192,7 +2252,6 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr,
 {
 	struct nfs_fattr *nfp = getattr->gt_attrs;
 	struct nfs_fsstat *fsstat = getattr->gt_fsstat;
-	struct nfs_pathconf *pathconf = getattr->gt_pathconf;
 	uint32_t attrlen, dummy32, bmlen,
 		 bmval0 = 0,
 		 bmval1 = 0,
@@ -2294,18 +2353,6 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr,
                 READ64(fsstat->tfiles);
                 dprintk("read_attrs: files_tot=0x%Lx\n", (long long) fsstat->tfiles);
         }
-	if (bmval0 & FATTR4_WORD0_MAXLINK) {
-		READ_BUF(4);
-		len += 4;
-		READ32(pathconf->max_link);
-		dprintk("read_attrs: maxlink=%d\n", pathconf->max_link);
-	}
-        if (bmval0 & FATTR4_WORD0_MAXNAME) {
-                READ_BUF(4);
-                len += 4;
-                READ32(pathconf->max_namelen);
-                dprintk("read_attrs: maxname=%d\n", pathconf->max_namelen);
-        }
 	
         if (bmval1 & FATTR4_WORD1_MODE) {
                 READ_BUF(4);
@@ -2414,6 +2461,32 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr,
         DECODE_TAIL;
 }
 
+static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf)
+{
+	uint32_t *savep;
+	uint32_t attrlen, 
+		 bitmap[2] = {0};
+	int status;
+	
+	if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
+		goto xdr_error;
+	if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
+		goto xdr_error;
+	if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
+		goto xdr_error;
+
+	if ((status = decode_attr_maxlink(xdr, bitmap, &pathconf->max_link)) != 0)
+		goto xdr_error;
+	if ((status = decode_attr_maxname(xdr, bitmap, &pathconf->max_namelen)) != 0)
+		goto xdr_error;
+
+	status = verify_attr_len(xdr, savep, attrlen);
+xdr_error:
+	if (status != 0)
+		printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status);
+	return status;
+}
+
 static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, const struct nfs_server *server)
 {
 	uint32_t *savep;
@@ -3556,6 +3629,24 @@ nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, uint32_t *p, struct nfs_fsinfo *fsinfo
 	return status;
 }
 
+/*
+ * PATHCONF request
+ */
+static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, uint32_t *p, struct nfs_pathconf *pathconf)
+{
+	struct xdr_stream xdr;
+	struct compound_hdr hdr;
+	int status;
+
+	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
+	status = decode_compound_hdr(&xdr, &hdr);
+	if (!status)
+		status = decode_putfh(&xdr);
+	if (!status)
+		status = decode_pathconf(&xdr, pathconf);
+	return status;
+}
+
 /*
  * Decode RENEW response
  */
@@ -3758,6 +3849,7 @@ struct rpc_procinfo	nfs4_procedures[] = {
   PROC(RENAME,		enc_rename,	dec_rename),
   PROC(LINK,		enc_link,	dec_link),
   PROC(CREATE,		enc_create,	dec_create),
+  PROC(PATHCONF,	enc_pathconf,	dec_pathconf),
 };
 
 struct rpc_version		nfs_version4 = {
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 6137a19830f0..46bdbb10987a 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -311,6 +311,7 @@ enum {
 	NFSPROC4_CLNT_RENAME,
 	NFSPROC4_CLNT_LINK,
 	NFSPROC4_CLNT_CREATE,
+	NFSPROC4_CLNT_PATHCONF,
 };
 
 #endif
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index c423805bc7a0..b7a2c1b1d2ee 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -534,7 +534,6 @@ struct nfs4_getattr {
         u32 *				gt_bmval;          /* request */
         struct nfs_fattr *		gt_attrs;          /* response */
 	struct nfs_fsstat *		gt_fsstat;         /* response */
-	struct nfs_pathconf *		gt_pathconf;       /* response */
 };
 
 struct nfs4_getattr_arg {
@@ -590,6 +589,11 @@ struct nfs4_open_confirm {
 	char *				oc_stateid;       /* request */
 };
 
+struct nfs4_pathconf_arg {
+	const struct nfs_fh *		fh;
+	const u32 *			bitmask;
+};
+
 struct nfs4_putfh {
 	struct nfs_fh *			pf_fhandle;       /* request */
 };
-- 
2.30.9