blob: 16556fa4effb1e989aecd7301d8ee234ba1a97d1 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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>
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000024#include <linux/nfsacl.h>
David Howellsf7b422b2006-06-09 09:34:33 -040025#include "internal.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
27#define NFSDBG_FACILITY NFSDBG_XDR
28
29/* Mapping from NFS error code to "errno" error code. */
30#define errno_NFSERR_IO EIO
31
Linus Torvalds1da177e2005-04-16 15:20:36 -070032/*
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)
Chuck Lever94a6d752006-08-22 20:06:23 -040059#define NFS3_symlinkargs_sz (NFS3_diropargs_sz+1+NFS3_sattr_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#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
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000082#define ACL3_getaclargs_sz (NFS3_fh_sz+1)
83#define ACL3_setaclargs_sz (NFS3_fh_sz+1+2*(2+5*3))
84#define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+2*(2+5*3))
85#define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz)
86
Linus Torvalds1da177e2005-04-16 15:20:36 -070087/*
88 * Map file type to S_IFMT bits
89 */
90static struct {
91 unsigned int mode;
92 unsigned int nfs2type;
93} nfs_type2fmt[] = {
94 { 0, NFNON },
95 { S_IFREG, NFREG },
96 { S_IFDIR, NFDIR },
97 { S_IFBLK, NFBLK },
98 { S_IFCHR, NFCHR },
99 { S_IFLNK, NFLNK },
100 { S_IFSOCK, NFSOCK },
101 { S_IFIFO, NFFIFO },
102 { 0, NFBAD }
103};
104
105/*
106 * Common NFS XDR functions as inlines
107 */
108static inline u32 *
109xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
110{
111 return xdr_encode_array(p, fh->data, fh->size);
112}
113
114static inline u32 *
115xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
116{
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 */
127static inline u32 *
128xdr_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
135static inline u32 *
136xdr_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
143static u32 *
144xdr_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
Trond Myklebust8b4bdcf2006-06-09 09:34:19 -0400168 p = xdr_decode_hyper(p, &fattr->fsid.major);
169 fattr->fsid.minor = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 p = xdr_decode_hyper(p, &fattr->fileid);
171 p = xdr_decode_time3(p, &fattr->atime);
172 p = xdr_decode_time3(p, &fattr->mtime);
173 p = xdr_decode_time3(p, &fattr->ctime);
174
175 /* Update the mode bits */
176 fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 return p;
178}
179
180static inline u32 *
181xdr_encode_sattr(u32 *p, struct iattr *attr)
182{
183 if (attr->ia_valid & ATTR_MODE) {
184 *p++ = xdr_one;
Trond Myklebustcf3fff52006-01-03 09:55:53 +0100185 *p++ = htonl(attr->ia_mode & S_IALLUGO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 } else {
187 *p++ = xdr_zero;
188 }
189 if (attr->ia_valid & ATTR_UID) {
190 *p++ = xdr_one;
191 *p++ = htonl(attr->ia_uid);
192 } else {
193 *p++ = xdr_zero;
194 }
195 if (attr->ia_valid & ATTR_GID) {
196 *p++ = xdr_one;
197 *p++ = htonl(attr->ia_gid);
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
226static inline u32 *
227xdr_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
236static inline u32 *
237xdr_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
244static inline u32 *
245xdr_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
253static inline u32 *
254xdr_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 */
267static int
268nfs3_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 */
278static int
279nfs3_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 */
293static int
294nfs3_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 */
305static int
306nfs3_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 */
319static int
320nfs3_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 */
341static int
342nfs3_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 */
362static int
363nfs3_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 */
382static int
383nfs3_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 */
395static int
396nfs3_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);
Chuck Lever94a6d752006-08-22 20:06:23 -0400401 *p++ = htonl(args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
Chuck Lever94a6d752006-08-22 20:06:23 -0400403
404 /* Copy the page */
405 xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 return 0;
407}
408
409/*
410 * Encode MKNOD arguments
411 */
412static int
413nfs3_xdr_mknodargs(struct rpc_rqst *req, u32 *p, struct nfs3_mknodargs *args)
414{
415 p = xdr_encode_fhandle(p, args->fh);
416 p = xdr_encode_array(p, args->name, args->len);
417 *p++ = htonl(args->type);
418 p = xdr_encode_sattr(p, args->sattr);
419 if (args->type == NF3CHR || args->type == NF3BLK) {
420 *p++ = htonl(MAJOR(args->rdev));
421 *p++ = htonl(MINOR(args->rdev));
422 }
423
424 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
425 return 0;
426}
427
428/*
429 * Encode RENAME arguments
430 */
431static int
432nfs3_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs3_renameargs *args)
433{
434 p = xdr_encode_fhandle(p, args->fromfh);
435 p = xdr_encode_array(p, args->fromname, args->fromlen);
436 p = xdr_encode_fhandle(p, args->tofh);
437 p = xdr_encode_array(p, args->toname, args->tolen);
438 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
439 return 0;
440}
441
442/*
443 * Encode LINK arguments
444 */
445static int
446nfs3_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs3_linkargs *args)
447{
448 p = xdr_encode_fhandle(p, args->fromfh);
449 p = xdr_encode_fhandle(p, args->tofh);
450 p = xdr_encode_array(p, args->toname, args->tolen);
451 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
452 return 0;
453}
454
455/*
456 * Encode arguments to readdir call
457 */
458static int
459nfs3_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_readdirargs *args)
460{
461 struct rpc_auth *auth = req->rq_task->tk_auth;
462 unsigned int replen;
463 u32 count = args->count;
464
465 p = xdr_encode_fhandle(p, args->fh);
466 p = xdr_encode_hyper(p, args->cookie);
467 *p++ = args->verf[0];
468 *p++ = args->verf[1];
469 if (args->plus) {
470 /* readdirplus: need dircount + buffer size.
471 * We just make sure we make dircount big enough */
472 *p++ = htonl(count >> 3);
473 }
474 *p++ = htonl(count);
475 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
476
477 /* Inline the page array */
478 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
479 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
480 return 0;
481}
482
483/*
484 * Decode the result of a readdir call.
485 * We just check for syntactical correctness.
486 */
487static int
488nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
489{
490 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
491 struct kvec *iov = rcvbuf->head;
492 struct page **page;
493 int hdrlen, recvd;
494 int status, nr;
495 unsigned int len, pglen;
496 u32 *entry, *end, *kaddr;
497
498 status = ntohl(*p++);
499 /* Decode post_op_attrs */
500 p = xdr_decode_post_op_attr(p, res->dir_attr);
501 if (status)
502 return -nfs_stat_to_errno(status);
503 /* Decode verifier cookie */
504 if (res->verf) {
505 res->verf[0] = *p++;
506 res->verf[1] = *p++;
507 } else {
508 p += 2;
509 }
510
511 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
512 if (iov->iov_len < hdrlen) {
513 printk(KERN_WARNING "NFS: READDIR reply header overflowed:"
514 "length %d > %Zu\n", hdrlen, iov->iov_len);
515 return -errno_NFSERR_IO;
516 } else if (iov->iov_len != hdrlen) {
517 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
518 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
519 }
520
521 pglen = rcvbuf->page_len;
522 recvd = rcvbuf->len - hdrlen;
523 if (pglen > recvd)
524 pglen = recvd;
525 page = rcvbuf->pages;
526 kaddr = p = (u32 *)kmap_atomic(*page, KM_USER0);
527 end = (u32 *)((char *)p + pglen);
528 entry = p;
529 for (nr = 0; *p++; nr++) {
530 if (p + 3 > end)
531 goto short_pkt;
532 p += 2; /* inode # */
533 len = ntohl(*p++); /* string length */
534 p += XDR_QUADLEN(len) + 2; /* name + cookie */
535 if (len > NFS3_MAXNAMLEN) {
536 printk(KERN_WARNING "NFS: giant filename in readdir (len %x)!\n",
537 len);
538 goto err_unmap;
539 }
540
541 if (res->plus) {
542 /* post_op_attr */
543 if (p + 2 > end)
544 goto short_pkt;
545 if (*p++) {
546 p += 21;
547 if (p + 1 > end)
548 goto short_pkt;
549 }
550 /* post_op_fh3 */
551 if (*p++) {
552 if (p + 1 > end)
553 goto short_pkt;
554 len = ntohl(*p++);
555 if (len > NFS3_FHSIZE) {
556 printk(KERN_WARNING "NFS: giant filehandle in "
557 "readdir (len %x)!\n", len);
558 goto err_unmap;
559 }
560 p += XDR_QUADLEN(len);
561 }
562 }
563
564 if (p + 2 > end)
565 goto short_pkt;
566 entry = p;
567 }
568 if (!nr && (entry[0] != 0 || entry[1] == 0))
569 goto short_pkt;
570 out:
571 kunmap_atomic(kaddr, KM_USER0);
572 return nr;
573 short_pkt:
574 entry[0] = entry[1] = 0;
575 /* truncate listing ? */
576 if (!nr) {
577 printk(KERN_NOTICE "NFS: readdir reply truncated!\n");
578 entry[1] = 1;
579 }
580 goto out;
581err_unmap:
582 nr = -errno_NFSERR_IO;
583 goto out;
584}
585
586u32 *
587nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
588{
589 struct nfs_entry old = *entry;
590
591 if (!*p++) {
592 if (!*p)
593 return ERR_PTR(-EAGAIN);
594 entry->eof = 1;
595 return ERR_PTR(-EBADCOOKIE);
596 }
597
598 p = xdr_decode_hyper(p, &entry->ino);
599 entry->len = ntohl(*p++);
600 entry->name = (const char *) p;
601 p += XDR_QUADLEN(entry->len);
602 entry->prev_cookie = entry->cookie;
603 p = xdr_decode_hyper(p, &entry->cookie);
604
605 if (plus) {
606 entry->fattr->valid = 0;
607 p = xdr_decode_post_op_attr(p, entry->fattr);
608 /* In fact, a post_op_fh3: */
609 if (*p++) {
610 p = xdr_decode_fhandle(p, entry->fh);
611 /* Ugh -- server reply was truncated */
612 if (p == NULL) {
613 dprintk("NFS: FH truncated\n");
614 *entry = old;
615 return ERR_PTR(-EAGAIN);
616 }
617 } else
618 memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
619 }
620
621 entry->eof = !p[0] && p[1];
622 return p;
623}
624
625/*
626 * Encode COMMIT arguments
627 */
628static int
629nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
630{
631 p = xdr_encode_fhandle(p, args->fh);
632 p = xdr_encode_hyper(p, args->offset);
633 *p++ = htonl(args->count);
634 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
635 return 0;
636}
637
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000638#ifdef CONFIG_NFS_V3_ACL
639/*
640 * Encode GETACL arguments
641 */
642static int
643nfs3_xdr_getaclargs(struct rpc_rqst *req, u32 *p,
644 struct nfs3_getaclargs *args)
645{
646 struct rpc_auth *auth = req->rq_task->tk_auth;
647 unsigned int replen;
648
649 p = xdr_encode_fhandle(p, args->fh);
650 *p++ = htonl(args->mask);
651 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
652
653 if (args->mask & (NFS_ACL | NFS_DFACL)) {
654 /* Inline the page array */
655 replen = (RPC_REPHDRSIZE + auth->au_rslack +
656 ACL3_getaclres_sz) << 2;
657 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
658 NFSACL_MAXPAGES << PAGE_SHIFT);
659 }
660 return 0;
661}
662
663/*
664 * Encode SETACL arguments
665 */
666static int
667nfs3_xdr_setaclargs(struct rpc_rqst *req, u32 *p,
668 struct nfs3_setaclargs *args)
669{
670 struct xdr_buf *buf = &req->rq_snd_buf;
671 unsigned int base, len_in_head, len = nfsacl_size(
672 (args->mask & NFS_ACL) ? args->acl_access : NULL,
673 (args->mask & NFS_DFACL) ? args->acl_default : NULL);
674 int count, err;
675
676 p = xdr_encode_fhandle(p, NFS_FH(args->inode));
677 *p++ = htonl(args->mask);
678 base = (char *)p - (char *)buf->head->iov_base;
679 /* put as much of the acls into head as possible. */
680 len_in_head = min_t(unsigned int, buf->head->iov_len - base, len);
681 len -= len_in_head;
Andreas Gruenbacher21348422005-06-22 17:16:28 +0000682 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p + (len_in_head >> 2));
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000683
684 for (count = 0; (count << PAGE_SHIFT) < len; count++) {
685 args->pages[count] = alloc_page(GFP_KERNEL);
686 if (!args->pages[count]) {
687 while (count)
688 __free_page(args->pages[--count]);
689 return -ENOMEM;
690 }
691 }
692 xdr_encode_pages(buf, args->pages, 0, len);
693
694 err = nfsacl_encode(buf, base, args->inode,
695 (args->mask & NFS_ACL) ?
696 args->acl_access : NULL, 1, 0);
697 if (err > 0)
698 err = nfsacl_encode(buf, base + err, args->inode,
699 (args->mask & NFS_DFACL) ?
700 args->acl_default : NULL, 1,
701 NFS_ACL_DEFAULT);
702 return (err > 0) ? 0 : err;
703}
704#endif /* CONFIG_NFS_V3_ACL */
705
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706/*
707 * NFS XDR decode functions
708 */
709
710/*
711 * Decode attrstat reply.
712 */
713static int
714nfs3_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
715{
716 int status;
717
718 if ((status = ntohl(*p++)))
719 return -nfs_stat_to_errno(status);
720 xdr_decode_fattr(p, fattr);
721 return 0;
722}
723
724/*
725 * Decode status+wcc_data reply
726 * SATTR, REMOVE, RMDIR
727 */
728static int
729nfs3_xdr_wccstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
730{
731 int status;
732
733 if ((status = ntohl(*p++)))
734 status = -nfs_stat_to_errno(status);
735 xdr_decode_wcc_data(p, fattr);
736 return status;
737}
738
739/*
740 * Decode LOOKUP reply
741 */
742static int
743nfs3_xdr_lookupres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
744{
745 int status;
746
747 if ((status = ntohl(*p++))) {
748 status = -nfs_stat_to_errno(status);
749 } else {
750 if (!(p = xdr_decode_fhandle(p, res->fh)))
751 return -errno_NFSERR_IO;
752 p = xdr_decode_post_op_attr(p, res->fattr);
753 }
754 xdr_decode_post_op_attr(p, res->dir_attr);
755 return status;
756}
757
758/*
759 * Decode ACCESS reply
760 */
761static int
762nfs3_xdr_accessres(struct rpc_rqst *req, u32 *p, struct nfs3_accessres *res)
763{
764 int status = ntohl(*p++);
765
766 p = xdr_decode_post_op_attr(p, res->fattr);
767 if (status)
768 return -nfs_stat_to_errno(status);
769 res->access = ntohl(*p++);
770 return 0;
771}
772
773static int
774nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *args)
775{
776 struct rpc_auth *auth = req->rq_task->tk_auth;
777 unsigned int replen;
778
779 p = xdr_encode_fhandle(p, args->fh);
780 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
781
782 /* Inline the page array */
783 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
784 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
785 return 0;
786}
787
788/*
789 * Decode READLINK reply
790 */
791static int
792nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
793{
794 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
795 struct kvec *iov = rcvbuf->head;
796 int hdrlen, len, recvd;
797 char *kaddr;
798 int status;
799
800 status = ntohl(*p++);
801 p = xdr_decode_post_op_attr(p, fattr);
802
803 if (status != 0)
804 return -nfs_stat_to_errno(status);
805
806 /* Convert length of symlink */
807 len = ntohl(*p++);
808 if (len >= rcvbuf->page_len || len <= 0) {
809 dprintk(KERN_WARNING "nfs: server returned giant symlink!\n");
810 return -ENAMETOOLONG;
811 }
812
813 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
814 if (iov->iov_len < hdrlen) {
815 printk(KERN_WARNING "NFS: READLINK reply header overflowed:"
816 "length %d > %Zu\n", hdrlen, iov->iov_len);
817 return -errno_NFSERR_IO;
818 } else if (iov->iov_len != hdrlen) {
819 dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
820 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
821 }
822 recvd = req->rq_rcv_buf.len - hdrlen;
823 if (recvd < len) {
824 printk(KERN_WARNING "NFS: server cheating in readlink reply: "
825 "count %u > recvd %u\n", len, recvd);
826 return -EIO;
827 }
828
829 /* NULL terminate the string we got */
830 kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
831 kaddr[len+rcvbuf->page_base] = '\0';
832 kunmap_atomic(kaddr, KM_USER0);
833 return 0;
834}
835
836/*
837 * Decode READ reply
838 */
839static int
840nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
841{
842 struct kvec *iov = req->rq_rcv_buf.head;
843 int status, count, ocount, recvd, hdrlen;
844
845 status = ntohl(*p++);
846 p = xdr_decode_post_op_attr(p, res->fattr);
847
848 if (status != 0)
849 return -nfs_stat_to_errno(status);
850
851 /* Decode reply could and EOF flag. NFSv3 is somewhat redundant
852 * in that it puts the count both in the res struct and in the
853 * opaque data count. */
854 count = ntohl(*p++);
855 res->eof = ntohl(*p++);
856 ocount = ntohl(*p++);
857
858 if (ocount != count) {
859 printk(KERN_WARNING "NFS: READ count doesn't match RPC opaque count.\n");
860 return -errno_NFSERR_IO;
861 }
862
863 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
864 if (iov->iov_len < hdrlen) {
865 printk(KERN_WARNING "NFS: READ reply header overflowed:"
866 "length %d > %Zu\n", hdrlen, iov->iov_len);
867 return -errno_NFSERR_IO;
868 } else if (iov->iov_len != hdrlen) {
869 dprintk("NFS: READ header is short. iovec will be shifted.\n");
870 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
871 }
872
873 recvd = req->rq_rcv_buf.len - hdrlen;
874 if (count > recvd) {
875 printk(KERN_WARNING "NFS: server cheating in read reply: "
876 "count %d > recvd %d\n", count, recvd);
877 count = recvd;
878 res->eof = 0;
879 }
880
881 if (count < res->count)
882 res->count = count;
883
884 return count;
885}
886
887/*
888 * Decode WRITE response
889 */
890static int
891nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
892{
893 int status;
894
895 status = ntohl(*p++);
896 p = xdr_decode_wcc_data(p, res->fattr);
897
898 if (status != 0)
899 return -nfs_stat_to_errno(status);
900
901 res->count = ntohl(*p++);
902 res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
903 res->verf->verifier[0] = *p++;
904 res->verf->verifier[1] = *p++;
905
906 return res->count;
907}
908
909/*
910 * Decode a CREATE response
911 */
912static int
913nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
914{
915 int status;
916
917 status = ntohl(*p++);
918 if (status == 0) {
919 if (*p++) {
920 if (!(p = xdr_decode_fhandle(p, res->fh)))
921 return -errno_NFSERR_IO;
922 p = xdr_decode_post_op_attr(p, res->fattr);
923 } else {
924 memset(res->fh, 0, sizeof(*res->fh));
925 /* Do decode post_op_attr but set it to NULL */
926 p = xdr_decode_post_op_attr(p, res->fattr);
927 res->fattr->valid = 0;
928 }
929 } else {
930 status = -nfs_stat_to_errno(status);
931 }
932 p = xdr_decode_wcc_data(p, res->dir_attr);
933 return status;
934}
935
936/*
937 * Decode RENAME reply
938 */
939static int
940nfs3_xdr_renameres(struct rpc_rqst *req, u32 *p, struct nfs3_renameres *res)
941{
942 int status;
943
944 if ((status = ntohl(*p++)) != 0)
945 status = -nfs_stat_to_errno(status);
946 p = xdr_decode_wcc_data(p, res->fromattr);
947 p = xdr_decode_wcc_data(p, res->toattr);
948 return status;
949}
950
951/*
952 * Decode LINK reply
953 */
954static int
955nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res)
956{
957 int status;
958
959 if ((status = ntohl(*p++)) != 0)
960 status = -nfs_stat_to_errno(status);
961 p = xdr_decode_post_op_attr(p, res->fattr);
962 p = xdr_decode_wcc_data(p, res->dir_attr);
963 return status;
964}
965
966/*
967 * Decode FSSTAT reply
968 */
969static int
970nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsstat *res)
971{
972 int status;
973
974 status = ntohl(*p++);
975
976 p = xdr_decode_post_op_attr(p, res->fattr);
977 if (status != 0)
978 return -nfs_stat_to_errno(status);
979
980 p = xdr_decode_hyper(p, &res->tbytes);
981 p = xdr_decode_hyper(p, &res->fbytes);
982 p = xdr_decode_hyper(p, &res->abytes);
983 p = xdr_decode_hyper(p, &res->tfiles);
984 p = xdr_decode_hyper(p, &res->ffiles);
985 p = xdr_decode_hyper(p, &res->afiles);
986
987 /* ignore invarsec */
988 return 0;
989}
990
991/*
992 * Decode FSINFO reply
993 */
994static int
995nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
996{
997 int status;
998
999 status = ntohl(*p++);
1000
1001 p = xdr_decode_post_op_attr(p, res->fattr);
1002 if (status != 0)
1003 return -nfs_stat_to_errno(status);
1004
1005 res->rtmax = ntohl(*p++);
1006 res->rtpref = ntohl(*p++);
1007 res->rtmult = ntohl(*p++);
1008 res->wtmax = ntohl(*p++);
1009 res->wtpref = ntohl(*p++);
1010 res->wtmult = ntohl(*p++);
1011 res->dtpref = ntohl(*p++);
1012 p = xdr_decode_hyper(p, &res->maxfilesize);
1013
1014 /* ignore time_delta and properties */
1015 res->lease_time = 0;
1016 return 0;
1017}
1018
1019/*
1020 * Decode PATHCONF reply
1021 */
1022static int
1023nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_pathconf *res)
1024{
1025 int status;
1026
1027 status = ntohl(*p++);
1028
1029 p = xdr_decode_post_op_attr(p, res->fattr);
1030 if (status != 0)
1031 return -nfs_stat_to_errno(status);
1032 res->max_link = ntohl(*p++);
1033 res->max_namelen = ntohl(*p++);
1034
1035 /* ignore remaining fields */
1036 return 0;
1037}
1038
1039/*
1040 * Decode COMMIT reply
1041 */
1042static int
1043nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
1044{
1045 int status;
1046
1047 status = ntohl(*p++);
1048 p = xdr_decode_wcc_data(p, res->fattr);
1049 if (status != 0)
1050 return -nfs_stat_to_errno(status);
1051
1052 res->verf->verifier[0] = *p++;
1053 res->verf->verifier[1] = *p++;
1054 return 0;
1055}
1056
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001057#ifdef CONFIG_NFS_V3_ACL
1058/*
1059 * Decode GETACL reply
1060 */
1061static int
1062nfs3_xdr_getaclres(struct rpc_rqst *req, u32 *p,
1063 struct nfs3_getaclres *res)
1064{
1065 struct xdr_buf *buf = &req->rq_rcv_buf;
1066 int status = ntohl(*p++);
1067 struct posix_acl **acl;
1068 unsigned int *aclcnt;
1069 int err, base;
1070
1071 if (status != 0)
1072 return -nfs_stat_to_errno(status);
1073 p = xdr_decode_post_op_attr(p, res->fattr);
1074 res->mask = ntohl(*p++);
1075 if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
1076 return -EINVAL;
1077 base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
1078
1079 acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
1080 aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
1081 err = nfsacl_decode(buf, base, aclcnt, acl);
1082
1083 acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
1084 aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
1085 if (err > 0)
1086 err = nfsacl_decode(buf, base + err, aclcnt, acl);
1087 return (err > 0) ? 0 : err;
1088}
1089
1090/*
1091 * Decode setacl reply.
1092 */
1093static int
1094nfs3_xdr_setaclres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
1095{
1096 int status = ntohl(*p++);
1097
1098 if (status)
1099 return -nfs_stat_to_errno(status);
1100 xdr_decode_post_op_attr(p, fattr);
1101 return 0;
1102}
1103#endif /* CONFIG_NFS_V3_ACL */
1104
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105#ifndef MAX
1106# define MAX(a, b) (((a) > (b))? (a) : (b))
1107#endif
1108
1109#define PROC(proc, argtype, restype, timer) \
1110[NFS3PROC_##proc] = { \
1111 .p_proc = NFS3PROC_##proc, \
1112 .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
1113 .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
1114 .p_bufsiz = MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2, \
Chuck Levercc0175c2006-03-20 13:44:22 -05001115 .p_timer = timer, \
1116 .p_statidx = NFS3PROC_##proc, \
1117 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 }
1119
1120struct rpc_procinfo nfs3_procedures[] = {
1121 PROC(GETATTR, fhandle, attrstat, 1),
1122 PROC(SETATTR, sattrargs, wccstat, 0),
1123 PROC(LOOKUP, diropargs, lookupres, 2),
1124 PROC(ACCESS, accessargs, accessres, 1),
1125 PROC(READLINK, readlinkargs, readlinkres, 3),
1126 PROC(READ, readargs, readres, 3),
1127 PROC(WRITE, writeargs, writeres, 4),
1128 PROC(CREATE, createargs, createres, 0),
1129 PROC(MKDIR, mkdirargs, createres, 0),
1130 PROC(SYMLINK, symlinkargs, createres, 0),
1131 PROC(MKNOD, mknodargs, createres, 0),
1132 PROC(REMOVE, diropargs, wccstat, 0),
1133 PROC(RMDIR, diropargs, wccstat, 0),
1134 PROC(RENAME, renameargs, renameres, 0),
1135 PROC(LINK, linkargs, linkres, 0),
1136 PROC(READDIR, readdirargs, readdirres, 3),
1137 PROC(READDIRPLUS, readdirargs, readdirres, 3),
1138 PROC(FSSTAT, fhandle, fsstatres, 0),
1139 PROC(FSINFO, fhandle, fsinfores, 0),
1140 PROC(PATHCONF, fhandle, pathconfres, 0),
1141 PROC(COMMIT, commitargs, commitres, 5),
1142};
1143
1144struct rpc_version nfs_version3 = {
1145 .number = 3,
Tobias Klausere8c96f82006-03-24 03:15:34 -08001146 .nrprocs = ARRAY_SIZE(nfs3_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 .procs = nfs3_procedures
1148};
1149
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001150#ifdef CONFIG_NFS_V3_ACL
1151static struct rpc_procinfo nfs3_acl_procedures[] = {
1152 [ACLPROC3_GETACL] = {
1153 .p_proc = ACLPROC3_GETACL,
1154 .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
1155 .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
1156 .p_bufsiz = MAX(ACL3_getaclargs_sz, ACL3_getaclres_sz) << 2,
1157 .p_timer = 1,
Chuck Levercc0175c2006-03-20 13:44:22 -05001158 .p_name = "GETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001159 },
1160 [ACLPROC3_SETACL] = {
1161 .p_proc = ACLPROC3_SETACL,
1162 .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
1163 .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
1164 .p_bufsiz = MAX(ACL3_setaclargs_sz, ACL3_setaclres_sz) << 2,
1165 .p_timer = 0,
Chuck Levercc0175c2006-03-20 13:44:22 -05001166 .p_name = "SETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001167 },
1168};
1169
1170struct rpc_version nfsacl_version3 = {
1171 .number = 3,
1172 .nrprocs = sizeof(nfs3_acl_procedures)/
1173 sizeof(nfs3_acl_procedures[0]),
1174 .procs = nfs3_acl_procedures,
1175};
1176#endif /* CONFIG_NFS_V3_ACL */