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