make oldconfig will rebuild these...
[linux-2.4.21-pre4.git] / fs / nfs / nfs3xdr.c
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
30 extern 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  */
87 static 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  */
105 static inline u32 *
106 xdr_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
113 static inline u32 *
114 xdr_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  */
132 static inline u32 *
133 xdr_encode_time(u32 *p, time_t time)
134 {
135         *p++ = htonl(time);
136         *p++ = 0;
137         return p;
138 }
139
140 static inline u32 *
141 xdr_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
148 static inline u32 *
149 xdr_encode_time3(u32 *p, u64 time)
150 {
151         *p++ = htonl(time >> 32);
152         *p++ = htonl(time & 0xFFFFFFFF);
153         return p;
154 }
155
156 static u32 *
157 xdr_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
187 static inline u32 *
188 xdr_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
233 static inline u32 *
234 xdr_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
243 static inline u32 *
244 xdr_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
251 static inline u32 *
252 xdr_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
260 static inline u32 *
261 xdr_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  */
273 static int
274 nfs3_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  */
283 static int
284 nfs3_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  */
294 static int
295 nfs3_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  */
309 static int
310 nfs3_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  */
321 static int
322 nfs3_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  */
335 static int
336 nfs3_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  */
357 static int
358 nfs3_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  */
378 static int
379 nfs3_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  */
398 static int
399 nfs3_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  */
411 static int
412 nfs3_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  */
425 static int
426 nfs3_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  */
444 static int
445 nfs3_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  */
458 static int
459 nfs3_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  */
471 static int
472 nfs3_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  */
500 static int
501 nfs3_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;
594 err_unmap:
595         kunmap(*page);
596         return -errno_NFSERR_IO;
597 }
598
599 u32 *
600 nfs3_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  */
643 static int
644 nfs3_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  */
659 static int
660 nfs3_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
661 {
662         return 0;
663 }
664
665 /*
666  * Decode attrstat reply.
667  */
668 static int
669 nfs3_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  */
683 static int
684 nfs3_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  */
697 static int
698 nfs3_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  */
716 static int
717 nfs3_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
728 static int
729 nfs3_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  */
747 static int
748 nfs3_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  */
785 static int
786 nfs3_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  */
836 static int
837 nfs3_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  */
858 static int
859 nfs3_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  */
885 static int
886 nfs3_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  */
900 static int
901 nfs3_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  */
915 static int
916 nfs3_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  */
941 static int
942 nfs3_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  */
969 static int
970 nfs3_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  */
990 static int
991 nfs3_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
1017 static 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
1042 struct rpc_version              nfs_version3 = {
1043         3,
1044         sizeof(nfs3_procedures)/sizeof(nfs3_procedures[0]),
1045         nfs3_procedures
1046 };
1047