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