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