This commit was manufactured by cvs2svn to create tag
[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 count = args->count - 5;
706         unsigned int replen;
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                 dprintk(KERN_WARNING "nfs: server returned giant symlink!\n");
747                 kunmap_atomic(strlen, KM_USER0);
748                 return -ENAMETOOLONG;
749         }
750         *strlen = len;
751         /* NULL terminate the string we got */
752         string = (char *)(strlen + 1);
753         string[len] = '\0';
754         kunmap_atomic(strlen, KM_USER0);
755         return 0;
756 }
757
758 /*
759  * Decode READ reply
760  */
761 static int
762 nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
763 {
764         struct iovec *iov = req->rq_rcv_buf.head;
765         int     status, count, ocount, recvd, hdrlen;
766
767         status = ntohl(*p++);
768         p = xdr_decode_post_op_attr(p, res->fattr);
769
770         if (status != 0)
771                 return -nfs_stat_to_errno(status);
772
773         /* Decode reply could and EOF flag. NFSv3 is somewhat redundant
774          * in that it puts the count both in the res struct and in the
775          * opaque data count. */
776         count    = ntohl(*p++);
777         res->eof = ntohl(*p++);
778         ocount   = ntohl(*p++);
779
780         if (ocount != count) {
781                 printk(KERN_WARNING "NFS: READ count doesn't match RPC opaque count.\n");
782                 return -errno_NFSERR_IO;
783         }
784
785         hdrlen = (u8 *) p - (u8 *) iov->iov_base;
786         if (iov->iov_len < hdrlen) {
787                 printk(KERN_WARNING "NFS: READ reply header overflowed:"
788                                 "length %d > %Zu\n", hdrlen, iov->iov_len);
789                 return -errno_NFSERR_IO;
790         } else if (iov->iov_len != hdrlen) {
791                 dprintk("NFS: READ header is short. iovec will be shifted.\n");
792                 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
793         }
794
795         recvd = req->rq_rcv_buf.len - hdrlen;
796         if (count > recvd) {
797                 printk(KERN_WARNING "NFS: server cheating in read reply: "
798                         "count %d > recvd %d\n", count, recvd);
799                 count = recvd;
800                 res->eof = 0;
801         }
802
803         if (count < res->count)
804                 res->count = count;
805
806         return count;
807 }
808
809 /*
810  * Decode WRITE response
811  */
812 static int
813 nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
814 {
815         int     status;
816
817         status = ntohl(*p++);
818         p = xdr_decode_wcc_data(p, res->fattr);
819
820         if (status != 0)
821                 return -nfs_stat_to_errno(status);
822
823         res->count = ntohl(*p++);
824         res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
825         res->verf->verifier[0] = *p++;
826         res->verf->verifier[1] = *p++;
827
828         return res->count;
829 }
830
831 /*
832  * Decode a CREATE response
833  */
834 static int
835 nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
836 {
837         int     status;
838
839         status = ntohl(*p++);
840         if (status == 0) {
841                 if (*p++) {
842                         if (!(p = xdr_decode_fhandle(p, res->fh)))
843                                 return -errno_NFSERR_IO;
844                         p = xdr_decode_post_op_attr(p, res->fattr);
845                 } else {
846                         memset(res->fh, 0, sizeof(*res->fh));
847                         /* Do decode post_op_attr but set it to NULL */
848                         p = xdr_decode_post_op_attr(p, res->fattr);
849                         res->fattr->valid = 0;
850                 }
851         } else {
852                 status = -nfs_stat_to_errno(status);
853         }
854         p = xdr_decode_wcc_data(p, res->dir_attr);
855         return status;
856 }
857
858 /*
859  * Decode RENAME reply
860  */
861 static int
862 nfs3_xdr_renameres(struct rpc_rqst *req, u32 *p, struct nfs3_renameres *res)
863 {
864         int     status;
865
866         if ((status = ntohl(*p++)) != 0)
867                 status = -nfs_stat_to_errno(status);
868         p = xdr_decode_wcc_data(p, res->fromattr);
869         p = xdr_decode_wcc_data(p, res->toattr);
870         return status;
871 }
872
873 /*
874  * Decode LINK reply
875  */
876 static int
877 nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res)
878 {
879         int     status;
880
881         if ((status = ntohl(*p++)) != 0)
882                 status = -nfs_stat_to_errno(status);
883         p = xdr_decode_post_op_attr(p, res->fattr);
884         p = xdr_decode_wcc_data(p, res->dir_attr);
885         return status;
886 }
887
888 /*
889  * Decode FSSTAT reply
890  */
891 static int
892 nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsstat *res)
893 {
894         int             status;
895
896         status = ntohl(*p++);
897
898         p = xdr_decode_post_op_attr(p, res->fattr);
899         if (status != 0)
900                 return -nfs_stat_to_errno(status);
901
902         p = xdr_decode_hyper(p, &res->tbytes);
903         p = xdr_decode_hyper(p, &res->fbytes);
904         p = xdr_decode_hyper(p, &res->abytes);
905         p = xdr_decode_hyper(p, &res->tfiles);
906         p = xdr_decode_hyper(p, &res->ffiles);
907         p = xdr_decode_hyper(p, &res->afiles);
908
909         /* ignore invarsec */
910         return 0;
911 }
912
913 /*
914  * Decode FSINFO reply
915  */
916 static int
917 nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
918 {
919         int             status;
920
921         status = ntohl(*p++);
922
923         p = xdr_decode_post_op_attr(p, res->fattr);
924         if (status != 0)
925                 return -nfs_stat_to_errno(status);
926
927         res->rtmax  = ntohl(*p++);
928         res->rtpref = ntohl(*p++);
929         res->rtmult = ntohl(*p++);
930         res->wtmax  = ntohl(*p++);
931         res->wtpref = ntohl(*p++);
932         res->wtmult = ntohl(*p++);
933         res->dtpref = ntohl(*p++);
934         p = xdr_decode_hyper(p, &res->maxfilesize);
935
936         /* ignore time_delta and properties */
937         res->lease_time = 0;
938         return 0;
939 }
940
941 /*
942  * Decode PATHCONF reply
943  */
944 static int
945 nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_pathconf *res)
946 {
947         int             status;
948
949         status = ntohl(*p++);
950
951         p = xdr_decode_post_op_attr(p, res->fattr);
952         if (status != 0)
953                 return -nfs_stat_to_errno(status);
954         res->max_link = ntohl(*p++);
955         res->max_namelen = ntohl(*p++);
956
957         /* ignore remaining fields */
958         return 0;
959 }
960
961 /*
962  * Decode COMMIT reply
963  */
964 static int
965 nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
966 {
967         int             status;
968
969         status = ntohl(*p++);
970         p = xdr_decode_wcc_data(p, res->fattr);
971         if (status != 0)
972                 return -nfs_stat_to_errno(status);
973
974         res->verf->verifier[0] = *p++;
975         res->verf->verifier[1] = *p++;
976         return 0;
977 }
978
979 #ifndef MAX
980 # define MAX(a, b)      (((a) > (b))? (a) : (b))
981 #endif
982
983 #define PROC(proc, argtype, restype, timer)                             \
984 [NFS3PROC_##proc] = {                                                   \
985         .p_proc      = NFS3PROC_##proc,                                 \
986         .p_encode    = (kxdrproc_t) nfs3_xdr_##argtype,                 \
987         .p_decode    = (kxdrproc_t) nfs3_xdr_##restype,                 \
988         .p_bufsiz    = MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2,       \
989         .p_timer     = timer                                            \
990         }
991
992 struct rpc_procinfo     nfs3_procedures[] = {
993   PROC(GETATTR,         fhandle,        attrstat, 1),
994   PROC(SETATTR,         sattrargs,      wccstat, 0),
995   PROC(LOOKUP,          diropargs,      lookupres, 2),
996   PROC(ACCESS,          accessargs,     accessres, 1),
997   PROC(READLINK,        readlinkargs,   readlinkres, 3),
998   PROC(READ,            readargs,       readres, 3),
999   PROC(WRITE,           writeargs,      writeres, 4),
1000   PROC(CREATE,          createargs,     createres, 0),
1001   PROC(MKDIR,           mkdirargs,      createres, 0),
1002   PROC(SYMLINK,         symlinkargs,    createres, 0),
1003   PROC(MKNOD,           mknodargs,      createres, 0),
1004   PROC(REMOVE,          diropargs,      wccstat, 0),
1005   PROC(RMDIR,           diropargs,      wccstat, 0),
1006   PROC(RENAME,          renameargs,     renameres, 0),
1007   PROC(LINK,            linkargs,       linkres, 0),
1008   PROC(READDIR,         readdirargs,    readdirres, 3),
1009   PROC(READDIRPLUS,     readdirargs,    readdirres, 3),
1010   PROC(FSSTAT,          fhandle,        fsstatres, 0),
1011   PROC(FSINFO,          fhandle,        fsinfores, 0),
1012   PROC(PATHCONF,        fhandle,        pathconfres, 0),
1013   PROC(COMMIT,          commitargs,     commitres, 5),
1014 };
1015
1016 struct rpc_version              nfs_version3 = {
1017         .number                 = 3,
1018         .nrprocs                = sizeof(nfs3_procedures)/sizeof(nfs3_procedures[0]),
1019         .procs                  = nfs3_procedures
1020 };
1021