original comment: +Wilson03172004,marked due to this pci host does not support MWI
[linux-2.4.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         if (!*p++) {
603                 if (!*p)
604                         return ERR_PTR(-EAGAIN);
605                 entry->eof = 1;
606                 return ERR_PTR(-EBADCOOKIE);
607         }
608
609         p = xdr_decode_hyper(p, &entry->ino);
610         entry->len  = ntohl(*p++);
611         entry->name = (const char *) p;
612         p += XDR_QUADLEN(entry->len);
613         entry->prev_cookie = entry->cookie;
614         p = xdr_decode_hyper(p, &entry->cookie);
615
616         if (plus) {
617                 struct nfs_fattr fattr;
618                 p = xdr_decode_post_op_attr(p, &fattr);
619                 /* In fact, a post_op_fh3: */
620                 if (*p++) {
621                         struct nfs_fh fh;
622                         p = xdr_decode_fhandle(p, &fh);
623                 }
624         }
625
626         entry->eof = !p[0] && p[1];
627         return p;
628 }
629
630 /*
631  * Encode COMMIT arguments
632  */
633 static int
634 nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
635 {
636         p = xdr_encode_fhandle(p, args->fh);
637         p = xdr_encode_hyper(p, args->offset);
638         *p++ = htonl(args->count);
639         req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
640         return 0;
641 }
642
643 /*
644  * NFS XDR decode functions
645  */
646 /*
647  * Decode void reply
648  */
649 static int
650 nfs3_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
651 {
652         return 0;
653 }
654
655 /*
656  * Decode attrstat reply.
657  */
658 static int
659 nfs3_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
660 {
661         int     status;
662
663         if ((status = ntohl(*p++)))
664                 return -nfs_stat_to_errno(status);
665         xdr_decode_fattr(p, fattr);
666         return 0;
667 }
668
669 /*
670  * Decode status+wcc_data reply
671  * SATTR, REMOVE, RMDIR
672  */
673 static int
674 nfs3_xdr_wccstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
675 {
676         int     status;
677
678         if ((status = ntohl(*p++)))
679                 status = -nfs_stat_to_errno(status);
680         xdr_decode_wcc_data(p, fattr);
681         return status;
682 }
683
684 /*
685  * Decode LOOKUP reply
686  */
687 static int
688 nfs3_xdr_lookupres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
689 {
690         int     status;
691
692         if ((status = ntohl(*p++))) {
693                 status = -nfs_stat_to_errno(status);
694         } else {
695                 if (!(p = xdr_decode_fhandle(p, res->fh)))
696                         return -errno_NFSERR_IO;
697                 p = xdr_decode_post_op_attr(p, res->fattr);
698         }
699         xdr_decode_post_op_attr(p, res->dir_attr);
700         return status;
701 }
702
703 /*
704  * Decode ACCESS reply
705  */
706 static int
707 nfs3_xdr_accessres(struct rpc_rqst *req, u32 *p, struct nfs3_accessres *res)
708 {
709         int     status = ntohl(*p++);
710
711         p = xdr_decode_post_op_attr(p, res->fattr);
712         if (status)
713                 return -nfs_stat_to_errno(status);
714         res->access = ntohl(*p++);
715         return 0;
716 }
717
718 static int
719 nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *args)
720 {
721         struct rpc_auth *auth = req->rq_task->tk_auth;
722         unsigned int replen;
723         u32 count = args->count - 4;
724
725         p = xdr_encode_fhandle(p, args->fh);
726         req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
727
728         /* Inline the page array */
729         replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
730         xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
731         return 0;
732 }
733
734 /*
735  * Decode READLINK reply
736  */
737 static int
738 nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
739 {
740         struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
741         struct iovec *iov = rcvbuf->head;
742         unsigned int hdrlen;
743         u32     *strlen, len;
744         char    *string;
745         int     status;
746
747         status = ntohl(*p++);
748         p = xdr_decode_post_op_attr(p, fattr);
749
750         if (status != 0)
751                 return -nfs_stat_to_errno(status);
752
753         hdrlen = (u8 *) p - (u8 *) iov->iov_base;
754         if (iov->iov_len > hdrlen) {
755                 dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
756                 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
757         }
758
759         strlen = (u32*)kmap(rcvbuf->pages[0]);
760         /* Convert length of symlink */
761         len = ntohl(*strlen);
762         if (len >= rcvbuf->page_len - sizeof(u32)) {
763                 dprintk("NFS: server returned giant symlink!\n");
764                 kunmap(rcvbuf->pages[0]);
765                 return -ENAMETOOLONG;
766         }
767         *strlen = len;
768         /* NULL terminate the string we got */
769         string = (char *)(strlen + 1);
770         string[len] = 0;
771         kunmap(rcvbuf->pages[0]);
772         return 0;
773 }
774
775 /*
776  * Decode READ reply
777  */
778 static int
779 nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
780 {
781         struct iovec *iov = req->rq_rvec;
782         int     status, count, ocount, recvd, hdrlen;
783
784         status = ntohl(*p++);
785         p = xdr_decode_post_op_attr(p, res->fattr);
786
787         if (status != 0)
788                 return -nfs_stat_to_errno(status);
789
790         /* Decode reply could and EOF flag. NFSv3 is somewhat redundant
791          * in that it puts the count both in the res struct and in the
792          * opaque data count. */
793         count    = ntohl(*p++);
794         res->eof = ntohl(*p++);
795         ocount   = ntohl(*p++);
796
797         if (ocount != count) {
798                 printk(KERN_WARNING "NFS: READ count doesn't match RPC opaque count.\n");
799                 return -errno_NFSERR_IO;
800         }
801
802         hdrlen = (u8 *) p - (u8 *) iov->iov_base;
803         if (iov->iov_len < hdrlen) {
804                 printk(KERN_WARNING "NFS: READ reply header overflowed:"
805                                 "length %d > %Zu\n", hdrlen, iov->iov_len);
806                 return -errno_NFSERR_IO;
807         } else if (iov->iov_len != hdrlen) {
808                 dprintk("NFS: READ header is short. iovec will be shifted.\n");
809                 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
810         }
811
812         recvd = req->rq_received - hdrlen;
813         if (count > recvd) {
814                 printk(KERN_WARNING "NFS: server cheating in read reply: "
815                         "count %d > recvd %d\n", count, recvd);
816                 count = recvd;
817                 res->eof = 0;
818         }
819
820         if (count < res->count)
821                 res->count = count;
822
823         return count;
824 }
825
826 /*
827  * Decode WRITE response
828  */
829 static int
830 nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
831 {
832         int     status;
833
834         status = ntohl(*p++);
835         p = xdr_decode_wcc_data(p, res->fattr);
836
837         if (status != 0)
838                 return -nfs_stat_to_errno(status);
839
840         res->count = ntohl(*p++);
841         res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
842         res->verf->verifier[0] = *p++;
843         res->verf->verifier[1] = *p++;
844
845         return res->count;
846 }
847
848 /*
849  * Decode a CREATE response
850  */
851 static int
852 nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
853 {
854         int     status;
855
856         status = ntohl(*p++);
857         if (status == 0) {
858                 if (*p++) {
859                         if (!(p = xdr_decode_fhandle(p, res->fh)))
860                                 return -errno_NFSERR_IO;
861                         p = xdr_decode_post_op_attr(p, res->fattr);
862                 } else {
863                         memset(res->fh, 0, sizeof(*res->fh));
864                         /* Do decode post_op_attr but set it to NULL */
865                         p = xdr_decode_post_op_attr(p, res->fattr);
866                         res->fattr->valid = 0;
867                 }
868         } else {
869                 status = -nfs_stat_to_errno(status);
870         }
871         p = xdr_decode_wcc_data(p, res->dir_attr);
872         return status;
873 }
874
875 /*
876  * Decode RENAME reply
877  */
878 static int
879 nfs3_xdr_renameres(struct rpc_rqst *req, u32 *p, struct nfs3_renameres *res)
880 {
881         int     status;
882
883         if ((status = ntohl(*p++)) != 0)
884                 status = -nfs_stat_to_errno(status);
885         p = xdr_decode_wcc_data(p, res->fromattr);
886         p = xdr_decode_wcc_data(p, res->toattr);
887         return status;
888 }
889
890 /*
891  * Decode LINK reply
892  */
893 static int
894 nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res)
895 {
896         int     status;
897
898         if ((status = ntohl(*p++)) != 0)
899                 status = -nfs_stat_to_errno(status);
900         p = xdr_decode_post_op_attr(p, res->fattr);
901         p = xdr_decode_wcc_data(p, res->dir_attr);
902         return status;
903 }
904
905 /*
906  * Decode FSSTAT reply
907  */
908 static int
909 nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
910 {
911         struct nfs_fattr dummy;
912         int             status;
913
914         status = ntohl(*p++);
915
916         p = xdr_decode_post_op_attr(p, &dummy);
917         if (status != 0)
918                 return -nfs_stat_to_errno(status);
919
920         p = xdr_decode_hyper(p, &res->tbytes);
921         p = xdr_decode_hyper(p, &res->fbytes);
922         p = xdr_decode_hyper(p, &res->abytes);
923         p = xdr_decode_hyper(p, &res->tfiles);
924         p = xdr_decode_hyper(p, &res->ffiles);
925         p = xdr_decode_hyper(p, &res->afiles);
926
927         /* ignore invarsec */
928         return 0;
929 }
930
931 /*
932  * Decode FSINFO reply
933  */
934 static int
935 nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
936 {
937         struct nfs_fattr dummy;
938         int             status;
939
940         status = ntohl(*p++);
941
942         p = xdr_decode_post_op_attr(p, &dummy);
943         if (status != 0)
944                 return -nfs_stat_to_errno(status);
945
946         res->rtmax  = ntohl(*p++);
947         res->rtpref = ntohl(*p++);
948         res->rtmult = ntohl(*p++);
949         res->wtmax  = ntohl(*p++);
950         res->wtpref = ntohl(*p++);
951         res->wtmult = ntohl(*p++);
952         res->dtpref = ntohl(*p++);
953         p = xdr_decode_hyper(p, &res->maxfilesize);
954
955         /* ignore time_delta and properties */
956         return 0;
957 }
958
959 /*
960  * Decode PATHCONF reply
961  */
962 static int
963 nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
964 {
965         struct nfs_fattr dummy;
966         int             status;
967
968         status = ntohl(*p++);
969
970         p = xdr_decode_post_op_attr(p, &dummy);
971         if (status != 0)
972                 return -nfs_stat_to_errno(status);
973         res->linkmax = ntohl(*p++);
974         res->namelen = ntohl(*p++);
975
976         /* ignore remaining fields */
977         return 0;
978 }
979
980 /*
981  * Decode COMMIT reply
982  */
983 static int
984 nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
985 {
986         int             status;
987
988         status = ntohl(*p++);
989         p = xdr_decode_wcc_data(p, res->fattr);
990         if (status != 0)
991                 return -nfs_stat_to_errno(status);
992
993         res->verf->verifier[0] = *p++;
994         res->verf->verifier[1] = *p++;
995         return 0;
996 }
997
998 #ifndef MAX
999 # define MAX(a, b)      (((a) > (b))? (a) : (b))
1000 #endif
1001
1002 #define PROC(proc, argtype, restype, timer)                             \
1003     { .p_procname  = "nfs3_" #proc,                                     \
1004       .p_encode    = (kxdrproc_t) nfs3_xdr_##argtype,                   \
1005       .p_decode    = (kxdrproc_t) nfs3_xdr_##restype,                   \
1006       .p_bufsiz    = MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2, \
1007       .p_timer     = timer                                              \
1008     }
1009
1010 static struct rpc_procinfo      nfs3_procedures[22] = {
1011   PROC(null,            enc_void,       dec_void, 0),
1012   PROC(getattr,         fhandle,        attrstat, 1),
1013   PROC(setattr,         sattrargs,      wccstat, 0),
1014   PROC(lookup,          diropargs,      lookupres, 2),
1015   PROC(access,          accessargs,     accessres, 1),
1016   PROC(readlink,        readlinkargs,   readlinkres, 3),
1017   PROC(read,            readargs,       readres, 3),
1018   PROC(write,           writeargs,      writeres, 4),
1019   PROC(create,          createargs,     createres, 0),
1020   PROC(mkdir,           mkdirargs,      createres, 0),
1021   PROC(symlink,         symlinkargs,    createres, 0),
1022   PROC(mknod,           mknodargs,      createres, 0),
1023   PROC(remove,          diropargs,      wccstat, 0),
1024   PROC(rmdir,           diropargs,      wccstat, 0),
1025   PROC(rename,          renameargs,     renameres, 0),
1026   PROC(link,            linkargs,       linkres, 0),
1027   PROC(readdir,         readdirargs,    readdirres, 3),
1028   PROC(readdirplus,     readdirargs,    readdirres, 3),
1029   PROC(fsstat,          fhandle,        fsstatres, 0),
1030   PROC(fsinfo,          fhandle,        fsinfores, 0),
1031   PROC(pathconf,        fhandle,        pathconfres, 0),
1032   PROC(commit,          commitargs,     commitres, 5),
1033 };
1034
1035 struct rpc_version              nfs_version3 = {
1036         3,
1037         sizeof(nfs3_procedures)/sizeof(nfs3_procedures[0]),
1038         nfs3_procedures
1039 };
1040