1/*
2 * linux/fs/nfs/nfs3xdr.c
3 *
4 * XDR functions to encode/decode NFSv3 RPC arguments and results.
5 *
6 * Copyright (C) 1996, 1997 Olaf Kirch
7 */
8
9#include <linux/param.h>
10#include <linux/time.h>
11#include <linux/mm.h>
12#include <linux/slab.h>
13#include <linux/utsname.h>
14#include <linux/errno.h>
15#include <linux/string.h>
16#include <linux/in.h>
17#include <linux/pagemap.h>
18#include <linux/proc_fs.h>
19#include <linux/kdev_t.h>
20#include <linux/sunrpc/clnt.h>
21#include <linux/nfs.h>
22#include <linux/nfs3.h>
23#include <linux/nfs_fs.h>
24#include <linux/nfsacl.h>
25#include "internal.h"
26
27#define NFSDBG_FACILITY		NFSDBG_XDR
28
29/* Mapping from NFS error code to "errno" error code. */
30#define errno_NFSERR_IO		EIO
31
32/*
33 * Declare the space requirements for NFS arguments and replies as
34 * number of 32bit-words
35 */
36#define NFS3_fhandle_sz		(1+16)
37#define NFS3_fh_sz		(NFS3_fhandle_sz)	/* shorthand */
38#define NFS3_sattr_sz		(15)
39#define NFS3_filename_sz	(1+(NFS3_MAXNAMLEN>>2))
40#define NFS3_path_sz		(1+(NFS3_MAXPATHLEN>>2))
41#define NFS3_fattr_sz		(21)
42#define NFS3_wcc_attr_sz		(6)
43#define NFS3_pre_op_attr_sz	(1+NFS3_wcc_attr_sz)
44#define NFS3_post_op_attr_sz	(1+NFS3_fattr_sz)
45#define NFS3_wcc_data_sz		(NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
46#define NFS3_fsstat_sz
47#define NFS3_fsinfo_sz
48#define NFS3_pathconf_sz
49#define NFS3_entry_sz		(NFS3_filename_sz+3)
50
51#define NFS3_sattrargs_sz	(NFS3_fh_sz+NFS3_sattr_sz+3)
52#define NFS3_diropargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
53#define NFS3_accessargs_sz	(NFS3_fh_sz+1)
54#define NFS3_readlinkargs_sz	(NFS3_fh_sz)
55#define NFS3_readargs_sz	(NFS3_fh_sz+3)
56#define NFS3_writeargs_sz	(NFS3_fh_sz+5)
57#define NFS3_createargs_sz	(NFS3_diropargs_sz+NFS3_sattr_sz)
58#define NFS3_mkdirargs_sz	(NFS3_diropargs_sz+NFS3_sattr_sz)
59#define NFS3_symlinkargs_sz	(NFS3_diropargs_sz+1+NFS3_sattr_sz)
60#define NFS3_mknodargs_sz	(NFS3_diropargs_sz+2+NFS3_sattr_sz)
61#define NFS3_renameargs_sz	(NFS3_diropargs_sz+NFS3_diropargs_sz)
62#define NFS3_linkargs_sz		(NFS3_fh_sz+NFS3_diropargs_sz)
63#define NFS3_readdirargs_sz	(NFS3_fh_sz+2)
64#define NFS3_commitargs_sz	(NFS3_fh_sz+3)
65
66#define NFS3_attrstat_sz	(1+NFS3_fattr_sz)
67#define NFS3_wccstat_sz		(1+NFS3_wcc_data_sz)
68#define NFS3_lookupres_sz	(1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
69#define NFS3_accessres_sz	(1+NFS3_post_op_attr_sz+1)
70#define NFS3_readlinkres_sz	(1+NFS3_post_op_attr_sz+1)
71#define NFS3_readres_sz		(1+NFS3_post_op_attr_sz+3)
72#define NFS3_writeres_sz	(1+NFS3_wcc_data_sz+4)
73#define NFS3_createres_sz	(1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
74#define NFS3_renameres_sz	(1+(2 * NFS3_wcc_data_sz))
75#define NFS3_linkres_sz		(1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
76#define NFS3_readdirres_sz	(1+NFS3_post_op_attr_sz+2)
77#define NFS3_fsstatres_sz	(1+NFS3_post_op_attr_sz+13)
78#define NFS3_fsinfores_sz	(1+NFS3_post_op_attr_sz+12)
79#define NFS3_pathconfres_sz	(1+NFS3_post_op_attr_sz+6)
80#define NFS3_commitres_sz	(1+NFS3_wcc_data_sz+2)
81
82#define ACL3_getaclargs_sz	(NFS3_fh_sz+1)
83#define ACL3_setaclargs_sz	(NFS3_fh_sz+1+2*(2+5*3))
84#define ACL3_getaclres_sz	(1+NFS3_post_op_attr_sz+1+2*(2+5*3))
85#define ACL3_setaclres_sz	(1+NFS3_post_op_attr_sz)
86
87/*
88 * Map file type to S_IFMT bits
89 */
90static struct {
91	unsigned int	mode;
92	unsigned int	nfs2type;
93} nfs_type2fmt[] = {
94      { 0,		NFNON	},
95      { S_IFREG,	NFREG	},
96      { S_IFDIR,	NFDIR	},
97      { S_IFBLK,	NFBLK	},
98      { S_IFCHR,	NFCHR	},
99      { S_IFLNK,	NFLNK	},
100      { S_IFSOCK,	NFSOCK	},
101      { S_IFIFO,	NFFIFO	},
102      { 0,		NFBAD	}
103};
104
105/*
106 * Common NFS XDR functions as inlines
107 */
108static inline __be32 *
109xdr_encode_fhandle(__be32 *p, struct nfs_fh *fh)
110{
111	return xdr_encode_array(p, fh->data, fh->size);
112}
113
114static inline __be32 *
115xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
116{
117	if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
118		memcpy(fh->data, p, fh->size);
119		return p + XDR_QUADLEN(fh->size);
120	}
121	return NULL;
122}
123
124/*
125 * Encode/decode time.
126 */
127static inline __be32 *
128xdr_encode_time3(__be32 *p, struct timespec *timep)
129{
130	*p++ = htonl(timep->tv_sec);
131	*p++ = htonl(timep->tv_nsec);
132	return p;
133}
134
135static inline __be32 *
136xdr_decode_time3(__be32 *p, struct timespec *timep)
137{
138	timep->tv_sec = ntohl(*p++);
139	timep->tv_nsec = ntohl(*p++);
140	return p;
141}
142
143static __be32 *
144xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
145{
146	unsigned int	type, major, minor;
147	int		fmode;
148
149	type = ntohl(*p++);
150	if (type >= NF3BAD)
151		type = NF3BAD;
152	fmode = nfs_type2fmt[type].mode;
153	fattr->type = nfs_type2fmt[type].nfs2type;
154	fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
155	fattr->nlink = ntohl(*p++);
156	fattr->uid = ntohl(*p++);
157	fattr->gid = ntohl(*p++);
158	p = xdr_decode_hyper(p, &fattr->size);
159	p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
160
161	/* Turn remote device info into Linux-specific dev_t */
162	major = ntohl(*p++);
163	minor = ntohl(*p++);
164	fattr->rdev = MKDEV(major, minor);
165	if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor)
166		fattr->rdev = 0;
167
168	p = xdr_decode_hyper(p, &fattr->fsid.major);
169	fattr->fsid.minor = 0;
170	p = xdr_decode_hyper(p, &fattr->fileid);
171	p = xdr_decode_time3(p, &fattr->atime);
172	p = xdr_decode_time3(p, &fattr->mtime);
173	p = xdr_decode_time3(p, &fattr->ctime);
174
175	/* Update the mode bits */
176	fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
177	return p;
178}
179
180static inline __be32 *
181xdr_encode_sattr(__be32 *p, struct iattr *attr)
182{
183	if (attr->ia_valid & ATTR_MODE) {
184		*p++ = xdr_one;
185		*p++ = htonl(attr->ia_mode & S_IALLUGO);
186	} else {
187		*p++ = xdr_zero;
188	}
189	if (attr->ia_valid & ATTR_UID) {
190		*p++ = xdr_one;
191		*p++ = htonl(attr->ia_uid);
192	} else {
193		*p++ = xdr_zero;
194	}
195	if (attr->ia_valid & ATTR_GID) {
196		*p++ = xdr_one;
197		*p++ = htonl(attr->ia_gid);
198	} else {
199		*p++ = xdr_zero;
200	}
201	if (attr->ia_valid & ATTR_SIZE) {
202		*p++ = xdr_one;
203		p = xdr_encode_hyper(p, (__u64) attr->ia_size);
204	} else {
205		*p++ = xdr_zero;
206	}
207	if (attr->ia_valid & ATTR_ATIME_SET) {
208		*p++ = xdr_two;
209		p = xdr_encode_time3(p, &attr->ia_atime);
210	} else if (attr->ia_valid & ATTR_ATIME) {
211		*p++ = xdr_one;
212	} else {
213		*p++ = xdr_zero;
214	}
215	if (attr->ia_valid & ATTR_MTIME_SET) {
216		*p++ = xdr_two;
217		p = xdr_encode_time3(p, &attr->ia_mtime);
218	} else if (attr->ia_valid & ATTR_MTIME) {
219		*p++ = xdr_one;
220	} else {
221		*p++ = xdr_zero;
222	}
223	return p;
224}
225
226static inline __be32 *
227xdr_decode_wcc_attr(__be32 *p, struct nfs_fattr *fattr)
228{
229	p = xdr_decode_hyper(p, &fattr->pre_size);
230	p = xdr_decode_time3(p, &fattr->pre_mtime);
231	p = xdr_decode_time3(p, &fattr->pre_ctime);
232	fattr->valid |= NFS_ATTR_WCC;
233	return p;
234}
235
236static inline __be32 *
237xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
238{
239	if (*p++)
240		p = xdr_decode_fattr(p, fattr);
241	return p;
242}
243
244static inline __be32 *
245xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
246{
247	if (*p++)
248		return xdr_decode_wcc_attr(p, fattr);
249	return p;
250}
251
252
253static inline __be32 *
254xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
255{
256	p = xdr_decode_pre_op_attr(p, fattr);
257	return xdr_decode_post_op_attr(p, fattr);
258}
259
260/*
261 * NFS encode functions
262 */
263
264/*
265 * Encode file handle argument
266 */
267static int
268nfs3_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
269{
270	p = xdr_encode_fhandle(p, fh);
271	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
272	return 0;
273}
274
275/*
276 * Encode SETATTR arguments
277 */
278static int
279nfs3_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs3_sattrargs *args)
280{
281	p = xdr_encode_fhandle(p, args->fh);
282	p = xdr_encode_sattr(p, args->sattr);
283	*p++ = htonl(args->guard);
284	if (args->guard)
285		p = xdr_encode_time3(p, &args->guardtime);
286	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
287	return 0;
288}
289
290/*
291 * Encode directory ops argument
292 */
293static int
294nfs3_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs3_diropargs *args)
295{
296	p = xdr_encode_fhandle(p, args->fh);
297	p = xdr_encode_array(p, args->name, args->len);
298	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
299	return 0;
300}
301
302/*
303 * Encode access() argument
304 */
305static int
306nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *args)
307{
308	p = xdr_encode_fhandle(p, args->fh);
309	*p++ = htonl(args->access);
310	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
311	return 0;
312}
313
314/*
315 * Arguments to a READ call. Since we read data directly into the page
316 * cache, we also set up the reply iovec here so that iov[1] points
317 * exactly to the page we want to fetch.
318 */
319static int
320nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
321{
322	struct rpc_auth	*auth = req->rq_task->tk_auth;
323	unsigned int replen;
324	u32 count = args->count;
325
326	p = xdr_encode_fhandle(p, args->fh);
327	p = xdr_encode_hyper(p, args->offset);
328	*p++ = htonl(count);
329	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
330
331	/* Inline the page array */
332	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
333	xdr_inline_pages(&req->rq_rcv_buf, replen,
334			 args->pages, args->pgbase, count);
335	return 0;
336}
337
338/*
339 * Write arguments. Splice the buffer to be written into the iovec.
340 */
341static int
342nfs3_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
343{
344	struct xdr_buf *sndbuf = &req->rq_snd_buf;
345	u32 count = args->count;
346
347	p = xdr_encode_fhandle(p, args->fh);
348	p = xdr_encode_hyper(p, args->offset);
349	*p++ = htonl(count);
350	*p++ = htonl(args->stable);
351	*p++ = htonl(count);
352	sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
353
354	/* Copy the page array */
355	xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
356	return 0;
357}
358
359/*
360 * Encode CREATE arguments
361 */
362static int
363nfs3_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs3_createargs *args)
364{
365	p = xdr_encode_fhandle(p, args->fh);
366	p = xdr_encode_array(p, args->name, args->len);
367
368	*p++ = htonl(args->createmode);
369	if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
370		*p++ = args->verifier[0];
371		*p++ = args->verifier[1];
372	} else
373		p = xdr_encode_sattr(p, args->sattr);
374
375	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
376	return 0;
377}
378
379/*
380 * Encode MKDIR arguments
381 */
382static int
383nfs3_xdr_mkdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mkdirargs *args)
384{
385	p = xdr_encode_fhandle(p, args->fh);
386	p = xdr_encode_array(p, args->name, args->len);
387	p = xdr_encode_sattr(p, args->sattr);
388	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
389	return 0;
390}
391
392/*
393 * Encode SYMLINK arguments
394 */
395static int
396nfs3_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_symlinkargs *args)
397{
398	p = xdr_encode_fhandle(p, args->fromfh);
399	p = xdr_encode_array(p, args->fromname, args->fromlen);
400	p = xdr_encode_sattr(p, args->sattr);
401	*p++ = htonl(args->pathlen);
402	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
403
404	/* Copy the page */
405	xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
406	return 0;
407}
408
409/*
410 * Encode MKNOD arguments
411 */
412static int
413nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args)
414{
415	p = xdr_encode_fhandle(p, args->fh);
416	p = xdr_encode_array(p, args->name, args->len);
417	*p++ = htonl(args->type);
418	p = xdr_encode_sattr(p, args->sattr);
419	if (args->type == NF3CHR || args->type == NF3BLK) {
420		*p++ = htonl(MAJOR(args->rdev));
421		*p++ = htonl(MINOR(args->rdev));
422	}
423
424	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
425	return 0;
426}
427
428/*
429 * Encode RENAME arguments
430 */
431static int
432nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs3_renameargs *args)
433{
434	p = xdr_encode_fhandle(p, args->fromfh);
435	p = xdr_encode_array(p, args->fromname, args->fromlen);
436	p = xdr_encode_fhandle(p, args->tofh);
437	p = xdr_encode_array(p, args->toname, args->tolen);
438	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
439	return 0;
440}
441
442/*
443 * Encode LINK arguments
444 */
445static int
446nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args)
447{
448	p = xdr_encode_fhandle(p, args->fromfh);
449	p = xdr_encode_fhandle(p, args->tofh);
450	p = xdr_encode_array(p, args->toname, args->tolen);
451	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
452	return 0;
453}
454
455/*
456 * Encode arguments to readdir call
457 */
458static int
459nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args)
460{
461	struct rpc_auth	*auth = req->rq_task->tk_auth;
462	unsigned int replen;
463	u32 count = args->count;
464
465	p = xdr_encode_fhandle(p, args->fh);
466	p = xdr_encode_hyper(p, args->cookie);
467	*p++ = args->verf[0];
468	*p++ = args->verf[1];
469	if (args->plus) {
470		/* readdirplus: need dircount + buffer size.
471		 * We just make sure we make dircount big enough */
472		*p++ = htonl(count >> 3);
473	}
474	*p++ = htonl(count);
475	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
476
477	/* Inline the page array */
478	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
479	xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
480	return 0;
481}
482
483/*
484 * Decode the result of a readdir call.
485 * We just check for syntactical correctness.
486 */
487static int
488nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
489{
490	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
491	struct kvec *iov = rcvbuf->head;
492	struct page **page;
493	int hdrlen, recvd;
494	int status, nr;
495	unsigned int len, pglen;
496	__be32 *entry, *end, *kaddr;
497
498	status = ntohl(*p++);
499	/* Decode post_op_attrs */
500	p = xdr_decode_post_op_attr(p, res->dir_attr);
501	if (status)
502		return -nfs_stat_to_errno(status);
503	/* Decode verifier cookie */
504	if (res->verf) {
505		res->verf[0] = *p++;
506		res->verf[1] = *p++;
507	} else {
508		p += 2;
509	}
510
511	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
512	if (iov->iov_len < hdrlen) {
513		printk(KERN_WARNING "NFS: READDIR reply header overflowed:"
514				"length %d > %Zu\n", hdrlen, iov->iov_len);
515		return -errno_NFSERR_IO;
516	} else if (iov->iov_len != hdrlen) {
517		dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
518		xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
519	}
520
521	pglen = rcvbuf->page_len;
522	recvd = rcvbuf->len - hdrlen;
523	if (pglen > recvd)
524		pglen = recvd;
525	page = rcvbuf->pages;
526	kaddr = p = kmap_atomic(*page, KM_USER0);
527	end = (__be32 *)((char *)p + pglen);
528	entry = p;
529	for (nr = 0; *p++; nr++) {
530		if (p + 3 > end)
531			goto short_pkt;
532		p += 2;				/* inode # */
533		len = ntohl(*p++);		/* string length */
534		p += XDR_QUADLEN(len) + 2;	/* name + cookie */
535		if (len > NFS3_MAXNAMLEN) {
536			printk(KERN_WARNING "NFS: giant filename in readdir (len %x)!\n",
537						len);
538			goto err_unmap;
539		}
540
541		if (res->plus) {
542			/* post_op_attr */
543			if (p + 2 > end)
544				goto short_pkt;
545			if (*p++) {
546				p += 21;
547				if (p + 1 > end)
548					goto short_pkt;
549			}
550			/* post_op_fh3 */
551			if (*p++) {
552				if (p + 1 > end)
553					goto short_pkt;
554				len = ntohl(*p++);
555				if (len > NFS3_FHSIZE) {
556					printk(KERN_WARNING "NFS: giant filehandle in "
557						"readdir (len %x)!\n", len);
558					goto err_unmap;
559				}
560				p += XDR_QUADLEN(len);
561			}
562		}
563
564		if (p + 2 > end)
565			goto short_pkt;
566		entry = p;
567	}
568	if (!nr && (entry[0] != 0 || entry[1] == 0))
569		goto short_pkt;
570 out:
571	kunmap_atomic(kaddr, KM_USER0);
572	return nr;
573 short_pkt:
574	entry[0] = entry[1] = 0;
575	/* truncate listing ? */
576	if (!nr) {
577		printk(KERN_NOTICE "NFS: readdir reply truncated!\n");
578		entry[1] = 1;
579	}
580	goto out;
581err_unmap:
582	nr = -errno_NFSERR_IO;
583	goto out;
584}
585
586__be32 *
587nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
588{
589	struct nfs_entry old = *entry;
590
591	if (!*p++) {
592		if (!*p)
593			return ERR_PTR(-EAGAIN);
594		entry->eof = 1;
595		return ERR_PTR(-EBADCOOKIE);
596	}
597
598	p = xdr_decode_hyper(p, &entry->ino);
599	entry->len  = ntohl(*p++);
600	entry->name = (const char *) p;
601	p += XDR_QUADLEN(entry->len);
602	entry->prev_cookie = entry->cookie;
603	p = xdr_decode_hyper(p, &entry->cookie);
604
605	if (plus) {
606		entry->fattr->valid = 0;
607		p = xdr_decode_post_op_attr(p, entry->fattr);
608		/* In fact, a post_op_fh3: */
609		if (*p++) {
610			p = xdr_decode_fhandle(p, entry->fh);
611			/* Ugh -- server reply was truncated */
612			if (p == NULL) {
613				dprintk("NFS: FH truncated\n");
614				*entry = old;
615				return ERR_PTR(-EAGAIN);
616			}
617		} else
618			memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
619	}
620
621	entry->eof = !p[0] && p[1];
622	return p;
623}
624
625/*
626 * Encode COMMIT arguments
627 */
628static int
629nfs3_xdr_commitargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
630{
631	p = xdr_encode_fhandle(p, args->fh);
632	p = xdr_encode_hyper(p, args->offset);
633	*p++ = htonl(args->count);
634	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
635	return 0;
636}
637
638#ifdef CONFIG_NFS_V3_ACL
639/*
640 * Encode GETACL arguments
641 */
642static int
643nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
644		    struct nfs3_getaclargs *args)
645{
646	struct rpc_auth *auth = req->rq_task->tk_auth;
647	unsigned int replen;
648
649	p = xdr_encode_fhandle(p, args->fh);
650	*p++ = htonl(args->mask);
651	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
652
653	if (args->mask & (NFS_ACL | NFS_DFACL)) {
654		/* Inline the page array */
655		replen = (RPC_REPHDRSIZE + auth->au_rslack +
656			  ACL3_getaclres_sz) << 2;
657		xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
658				 NFSACL_MAXPAGES << PAGE_SHIFT);
659	}
660	return 0;
661}
662
663/*
664 * Encode SETACL arguments
665 */
666static int
667nfs3_xdr_setaclargs(struct rpc_rqst *req, __be32 *p,
668                   struct nfs3_setaclargs *args)
669{
670	struct xdr_buf *buf = &req->rq_snd_buf;
671	unsigned int base, len_in_head, len = nfsacl_size(
672		(args->mask & NFS_ACL)   ? args->acl_access  : NULL,
673		(args->mask & NFS_DFACL) ? args->acl_default : NULL);
674	int count, err;
675
676	p = xdr_encode_fhandle(p, NFS_FH(args->inode));
677	*p++ = htonl(args->mask);
678	base = (char *)p - (char *)buf->head->iov_base;
679	/* put as much of the acls into head as possible. */
680	len_in_head = min_t(unsigned int, buf->head->iov_len - base, len);
681	len -= len_in_head;
682	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p + (len_in_head >> 2));
683
684	for (count = 0; (count << PAGE_SHIFT) < len; count++) {
685		args->pages[count] = alloc_page(GFP_KERNEL);
686		if (!args->pages[count]) {
687			while (count)
688				__free_page(args->pages[--count]);
689			return -ENOMEM;
690		}
691	}
692	xdr_encode_pages(buf, args->pages, 0, len);
693
694	err = nfsacl_encode(buf, base, args->inode,
695			    (args->mask & NFS_ACL) ?
696			    args->acl_access : NULL, 1, 0);
697	if (err > 0)
698		err = nfsacl_encode(buf, base + err, args->inode,
699				    (args->mask & NFS_DFACL) ?
700				    args->acl_default : NULL, 1,
701				    NFS_ACL_DEFAULT);
702	return (err > 0) ? 0 : err;
703}
704#endif  /* CONFIG_NFS_V3_ACL */
705
706/*
707 * NFS XDR decode functions
708 */
709
710/*
711 * Decode attrstat reply.
712 */
713static int
714nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
715{
716	int	status;
717
718	if ((status = ntohl(*p++)))
719		return -nfs_stat_to_errno(status);
720	xdr_decode_fattr(p, fattr);
721	return 0;
722}
723
724/*
725 * Decode status+wcc_data reply
726 * SATTR, REMOVE, RMDIR
727 */
728static int
729nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
730{
731	int	status;
732
733	if ((status = ntohl(*p++)))
734		status = -nfs_stat_to_errno(status);
735	xdr_decode_wcc_data(p, fattr);
736	return status;
737}
738
739/*
740 * Decode LOOKUP reply
741 */
742static int
743nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
744{
745	int	status;
746
747	if ((status = ntohl(*p++))) {
748		status = -nfs_stat_to_errno(status);
749	} else {
750		if (!(p = xdr_decode_fhandle(p, res->fh)))
751			return -errno_NFSERR_IO;
752		p = xdr_decode_post_op_attr(p, res->fattr);
753	}
754	xdr_decode_post_op_attr(p, res->dir_attr);
755	return status;
756}
757
758/*
759 * Decode ACCESS reply
760 */
761static int
762nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
763{
764	int	status = ntohl(*p++);
765
766	p = xdr_decode_post_op_attr(p, res->fattr);
767	if (status)
768		return -nfs_stat_to_errno(status);
769	res->access = ntohl(*p++);
770	return 0;
771}
772
773static int
774nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
775{
776	struct rpc_auth *auth = req->rq_task->tk_auth;
777	unsigned int replen;
778
779	p = xdr_encode_fhandle(p, args->fh);
780	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
781
782	/* Inline the page array */
783	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
784	xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
785	return 0;
786}
787
788/*
789 * Decode READLINK reply
790 */
791static int
792nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
793{
794	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
795	struct kvec *iov = rcvbuf->head;
796	int hdrlen, len, recvd;
797	char	*kaddr;
798	int	status;
799
800	status = ntohl(*p++);
801	p = xdr_decode_post_op_attr(p, fattr);
802
803	if (status != 0)
804		return -nfs_stat_to_errno(status);
805
806	/* Convert length of symlink */
807	len = ntohl(*p++);
808	if (len >= rcvbuf->page_len || len <= 0) {
809		dprintk(KERN_WARNING "nfs: server returned giant symlink!\n");
810		return -ENAMETOOLONG;
811	}
812
813	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
814	if (iov->iov_len < hdrlen) {
815		printk(KERN_WARNING "NFS: READLINK reply header overflowed:"
816				"length %d > %Zu\n", hdrlen, iov->iov_len);
817		return -errno_NFSERR_IO;
818	} else if (iov->iov_len != hdrlen) {
819		dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
820		xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
821	}
822	recvd = req->rq_rcv_buf.len - hdrlen;
823	if (recvd < len) {
824		printk(KERN_WARNING "NFS: server cheating in readlink reply: "
825				"count %u > recvd %u\n", len, recvd);
826		return -EIO;
827	}
828
829	/* NULL terminate the string we got */
830	kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
831	kaddr[len+rcvbuf->page_base] = '\0';
832	kunmap_atomic(kaddr, KM_USER0);
833	return 0;
834}
835
836/*
837 * Decode READ reply
838 */
839static int
840nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
841{
842	struct kvec *iov = req->rq_rcv_buf.head;
843	int	status, count, ocount, recvd, hdrlen;
844
845	status = ntohl(*p++);
846	p = xdr_decode_post_op_attr(p, res->fattr);
847
848	if (status != 0)
849		return -nfs_stat_to_errno(status);
850
851	/* Decode reply could and EOF flag. NFSv3 is somewhat redundant
852	 * in that it puts the count both in the res struct and in the
853	 * opaque data count. */
854	count    = ntohl(*p++);
855	res->eof = ntohl(*p++);
856	ocount   = ntohl(*p++);
857
858	if (ocount != count) {
859		printk(KERN_WARNING "NFS: READ count doesn't match RPC opaque count.\n");
860		return -errno_NFSERR_IO;
861	}
862
863	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
864	if (iov->iov_len < hdrlen) {
865		printk(KERN_WARNING "NFS: READ reply header overflowed:"
866				"length %d > %Zu\n", hdrlen, iov->iov_len);
867       		return -errno_NFSERR_IO;
868	} else if (iov->iov_len != hdrlen) {
869		dprintk("NFS: READ header is short. iovec will be shifted.\n");
870		xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
871	}
872
873	recvd = req->rq_rcv_buf.len - hdrlen;
874	if (count > recvd) {
875		printk(KERN_WARNING "NFS: server cheating in read reply: "
876			"count %d > recvd %d\n", count, recvd);
877		count = recvd;
878		res->eof = 0;
879	}
880
881	if (count < res->count)
882		res->count = count;
883
884	return count;
885}
886
887/*
888 * Decode WRITE response
889 */
890static int
891nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
892{
893	int	status;
894
895	status = ntohl(*p++);
896	p = xdr_decode_wcc_data(p, res->fattr);
897
898	if (status != 0)
899		return -nfs_stat_to_errno(status);
900
901	res->count = ntohl(*p++);
902	res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
903	res->verf->verifier[0] = *p++;
904	res->verf->verifier[1] = *p++;
905
906	return res->count;
907}
908
909/*
910 * Decode a CREATE response
911 */
912static int
913nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
914{
915	int	status;
916
917	status = ntohl(*p++);
918	if (status == 0) {
919		if (*p++) {
920			if (!(p = xdr_decode_fhandle(p, res->fh)))
921				return -errno_NFSERR_IO;
922			p = xdr_decode_post_op_attr(p, res->fattr);
923		} else {
924			memset(res->fh, 0, sizeof(*res->fh));
925			/* Do decode post_op_attr but set it to NULL */
926			p = xdr_decode_post_op_attr(p, res->fattr);
927			res->fattr->valid = 0;
928		}
929	} else {
930		status = -nfs_stat_to_errno(status);
931	}
932	p = xdr_decode_wcc_data(p, res->dir_attr);
933	return status;
934}
935
936/*
937 * Decode RENAME reply
938 */
939static int
940nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs3_renameres *res)
941{
942	int	status;
943
944	if ((status = ntohl(*p++)) != 0)
945		status = -nfs_stat_to_errno(status);
946	p = xdr_decode_wcc_data(p, res->fromattr);
947	p = xdr_decode_wcc_data(p, res->toattr);
948	return status;
949}
950
951/*
952 * Decode LINK reply
953 */
954static int
955nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
956{
957	int	status;
958
959	if ((status = ntohl(*p++)) != 0)
960		status = -nfs_stat_to_errno(status);
961	p = xdr_decode_post_op_attr(p, res->fattr);
962	p = xdr_decode_wcc_data(p, res->dir_attr);
963	return status;
964}
965
966/*
967 * Decode FSSTAT reply
968 */
969static int
970nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
971{
972	int		status;
973
974	status = ntohl(*p++);
975
976	p = xdr_decode_post_op_attr(p, res->fattr);
977	if (status != 0)
978		return -nfs_stat_to_errno(status);
979
980	p = xdr_decode_hyper(p, &res->tbytes);
981	p = xdr_decode_hyper(p, &res->fbytes);
982	p = xdr_decode_hyper(p, &res->abytes);
983	p = xdr_decode_hyper(p, &res->tfiles);
984	p = xdr_decode_hyper(p, &res->ffiles);
985	p = xdr_decode_hyper(p, &res->afiles);
986
987	/* ignore invarsec */
988	return 0;
989}
990
991/*
992 * Decode FSINFO reply
993 */
994static int
995nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
996{
997	int		status;
998
999	status = ntohl(*p++);
1000
1001	p = xdr_decode_post_op_attr(p, res->fattr);
1002	if (status != 0)
1003		return -nfs_stat_to_errno(status);
1004
1005	res->rtmax  = ntohl(*p++);
1006	res->rtpref = ntohl(*p++);
1007	res->rtmult = ntohl(*p++);
1008	res->wtmax  = ntohl(*p++);
1009	res->wtpref = ntohl(*p++);
1010	res->wtmult = ntohl(*p++);
1011	res->dtpref = ntohl(*p++);
1012	p = xdr_decode_hyper(p, &res->maxfilesize);
1013
1014	/* ignore time_delta and properties */
1015	res->lease_time = 0;
1016	return 0;
1017}
1018
1019/*
1020 * Decode PATHCONF reply
1021 */
1022static int
1023nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
1024{
1025	int		status;
1026
1027	status = ntohl(*p++);
1028
1029	p = xdr_decode_post_op_attr(p, res->fattr);
1030	if (status != 0)
1031		return -nfs_stat_to_errno(status);
1032	res->max_link = ntohl(*p++);
1033	res->max_namelen = ntohl(*p++);
1034
1035	/* ignore remaining fields */
1036	return 0;
1037}
1038
1039/*
1040 * Decode COMMIT reply
1041 */
1042static int
1043nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
1044{
1045	int		status;
1046
1047	status = ntohl(*p++);
1048	p = xdr_decode_wcc_data(p, res->fattr);
1049	if (status != 0)
1050		return -nfs_stat_to_errno(status);
1051
1052	res->verf->verifier[0] = *p++;
1053	res->verf->verifier[1] = *p++;
1054	return 0;
1055}
1056
1057#ifdef CONFIG_NFS_V3_ACL
1058/*
1059 * Decode GETACL reply
1060 */
1061static int
1062nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
1063		   struct nfs3_getaclres *res)
1064{
1065	struct xdr_buf *buf = &req->rq_rcv_buf;
1066	int status = ntohl(*p++);
1067	struct posix_acl **acl;
1068	unsigned int *aclcnt;
1069	int err, base;
1070
1071	if (status != 0)
1072		return -nfs_stat_to_errno(status);
1073	p = xdr_decode_post_op_attr(p, res->fattr);
1074	res->mask = ntohl(*p++);
1075	if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
1076		return -EINVAL;
1077	base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
1078
1079	acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
1080	aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
1081	err = nfsacl_decode(buf, base, aclcnt, acl);
1082
1083	acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
1084	aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
1085	if (err > 0)
1086		err = nfsacl_decode(buf, base + err, aclcnt, acl);
1087	return (err > 0) ? 0 : err;
1088}
1089
1090/*
1091 * Decode setacl reply.
1092 */
1093static int
1094nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
1095{
1096	int status = ntohl(*p++);
1097
1098	if (status)
1099		return -nfs_stat_to_errno(status);
1100	xdr_decode_post_op_attr(p, fattr);
1101	return 0;
1102}
1103#endif  /* CONFIG_NFS_V3_ACL */
1104
1105#define PROC(proc, argtype, restype, timer)				\
1106[NFS3PROC_##proc] = {							\
1107	.p_proc      = NFS3PROC_##proc,					\
1108	.p_encode    = (kxdrproc_t) nfs3_xdr_##argtype,			\
1109	.p_decode    = (kxdrproc_t) nfs3_xdr_##restype,			\
1110	.p_arglen    = NFS3_##argtype##_sz,				\
1111	.p_replen    = NFS3_##restype##_sz,				\
1112	.p_timer     = timer,						\
1113	.p_statidx   = NFS3PROC_##proc,					\
1114	.p_name      = #proc,						\
1115	}
1116
1117struct rpc_procinfo	nfs3_procedures[] = {
1118  PROC(GETATTR,		fhandle,	attrstat, 1),
1119  PROC(SETATTR, 	sattrargs,	wccstat, 0),
1120  PROC(LOOKUP,		diropargs,	lookupres, 2),
1121  PROC(ACCESS,		accessargs,	accessres, 1),
1122  PROC(READLINK,	readlinkargs,	readlinkres, 3),
1123  PROC(READ,		readargs,	readres, 3),
1124  PROC(WRITE,		writeargs,	writeres, 4),
1125  PROC(CREATE,		createargs,	createres, 0),
1126  PROC(MKDIR,		mkdirargs,	createres, 0),
1127  PROC(SYMLINK,		symlinkargs,	createres, 0),
1128  PROC(MKNOD,		mknodargs,	createres, 0),
1129  PROC(REMOVE,		diropargs,	wccstat, 0),
1130  PROC(RMDIR,		diropargs,	wccstat, 0),
1131  PROC(RENAME,		renameargs,	renameres, 0),
1132  PROC(LINK,		linkargs,	linkres, 0),
1133  PROC(READDIR,		readdirargs,	readdirres, 3),
1134  PROC(READDIRPLUS,	readdirargs,	readdirres, 3),
1135  PROC(FSSTAT,		fhandle,	fsstatres, 0),
1136  PROC(FSINFO,  	fhandle,	fsinfores, 0),
1137  PROC(PATHCONF,	fhandle,	pathconfres, 0),
1138  PROC(COMMIT,		commitargs,	commitres, 5),
1139};
1140
1141struct rpc_version		nfs_version3 = {
1142	.number			= 3,
1143	.nrprocs		= ARRAY_SIZE(nfs3_procedures),
1144	.procs			= nfs3_procedures
1145};
1146
1147#ifdef CONFIG_NFS_V3_ACL
1148static struct rpc_procinfo	nfs3_acl_procedures[] = {
1149	[ACLPROC3_GETACL] = {
1150		.p_proc = ACLPROC3_GETACL,
1151		.p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
1152		.p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
1153		.p_arglen = ACL3_getaclargs_sz,
1154		.p_replen = ACL3_getaclres_sz,
1155		.p_timer = 1,
1156		.p_name = "GETACL",
1157	},
1158	[ACLPROC3_SETACL] = {
1159		.p_proc = ACLPROC3_SETACL,
1160		.p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
1161		.p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
1162		.p_arglen = ACL3_setaclargs_sz,
1163		.p_replen = ACL3_setaclres_sz,
1164		.p_timer = 0,
1165		.p_name = "SETACL",
1166	},
1167};
1168
1169struct rpc_version		nfsacl_version3 = {
1170	.number			= 3,
1171	.nrprocs		= sizeof(nfs3_acl_procedures)/
1172				  sizeof(nfs3_acl_procedures[0]),
1173	.procs			= nfs3_acl_procedures,
1174};
1175#endif  /* CONFIG_NFS_V3_ACL */
1176