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