blob: 2da9824d432adc3d8f082d861e4e2e0aaba89918 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/fs/nfs/nfs2xdr.c
3 *
4 * XDR functions to encode/decode NFS RPC arguments and results.
5 *
6 * Copyright (C) 1992, 1993, 1994 Rick Sladkey
7 * Copyright (C) 1996 Olaf Kirch
8 * 04 Aug 1998 Ion Badulescu <ionut@cs.columbia.edu>
9 * FIFO's need special handling in NFSv2
10 */
11
12#include <linux/param.h>
13#include <linux/time.h>
14#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/errno.h>
16#include <linux/string.h>
17#include <linux/in.h>
18#include <linux/pagemap.h>
19#include <linux/proc_fs.h>
20#include <linux/sunrpc/clnt.h>
21#include <linux/nfs.h>
22#include <linux/nfs2.h>
23#include <linux/nfs_fs.h>
Trond Myklebust816724e2006-06-24 08:41:41 -040024#include "internal.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
26#define NFSDBG_FACILITY NFSDBG_XDR
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
Linus Torvalds1da177e2005-04-16 15:20:36 -070028/* Mapping from NFS error code to "errno" error code. */
29#define errno_NFSERR_IO EIO
30
31/*
32 * Declare the space requirements for NFS arguments and replies as
33 * number of 32bit-words
34 */
35#define NFS_fhandle_sz (8)
36#define NFS_sattr_sz (8)
37#define NFS_filename_sz (1+(NFS2_MAXNAMLEN>>2))
38#define NFS_path_sz (1+(NFS2_MAXPATHLEN>>2))
39#define NFS_fattr_sz (17)
40#define NFS_info_sz (5)
41#define NFS_entry_sz (NFS_filename_sz+3)
42
43#define NFS_diropargs_sz (NFS_fhandle_sz+NFS_filename_sz)
Trond Myklebust4fdc17b2007-07-14 15:39:57 -040044#define NFS_removeargs_sz (NFS_fhandle_sz+NFS_filename_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#define NFS_sattrargs_sz (NFS_fhandle_sz+NFS_sattr_sz)
46#define NFS_readlinkargs_sz (NFS_fhandle_sz)
47#define NFS_readargs_sz (NFS_fhandle_sz+3)
48#define NFS_writeargs_sz (NFS_fhandle_sz+4)
49#define NFS_createargs_sz (NFS_diropargs_sz+NFS_sattr_sz)
50#define NFS_renameargs_sz (NFS_diropargs_sz+NFS_diropargs_sz)
51#define NFS_linkargs_sz (NFS_fhandle_sz+NFS_diropargs_sz)
Chuck Lever94a6d752006-08-22 20:06:23 -040052#define NFS_symlinkargs_sz (NFS_diropargs_sz+1+NFS_sattr_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#define NFS_readdirargs_sz (NFS_fhandle_sz+2)
54
55#define NFS_attrstat_sz (1+NFS_fattr_sz)
56#define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz)
57#define NFS_readlinkres_sz (2)
58#define NFS_readres_sz (1+NFS_fattr_sz+1)
59#define NFS_writeres_sz (NFS_attrstat_sz)
60#define NFS_stat_sz (1)
61#define NFS_readdirres_sz (1)
62#define NFS_statfsres_sz (1+NFS_info_sz)
63
Chuck Lever25a08662010-12-14 14:54:30 +000064
65/*
66 * While encoding arguments, set up the reply buffer in advance to
67 * receive reply data directly into the page cache.
68 */
69static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
70 unsigned int base, unsigned int len,
71 unsigned int bufsize)
72{
73 struct rpc_auth *auth = req->rq_cred->cr_auth;
74 unsigned int replen;
75
76 replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
77 xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
78}
79
80
Linus Torvalds1da177e2005-04-16 15:20:36 -070081/*
82 * Common NFS XDR functions as inlines
83 */
Al Viro9d787a72006-10-19 23:28:47 -070084static inline __be32 *
Al Viro9d787a72006-10-19 23:28:47 -070085xdr_decode_fhandle(__be32 *p, struct nfs_fh *fhandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -070086{
87 /* NFSv2 handles have a fixed length */
88 fhandle->size = NFS2_FHSIZE;
89 memcpy(fhandle->data, p, NFS2_FHSIZE);
90 return p + XDR_QUADLEN(NFS2_FHSIZE);
91}
92
Al Viro9d787a72006-10-19 23:28:47 -070093static inline __be32*
Al Viro9d787a72006-10-19 23:28:47 -070094xdr_decode_time(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070095{
96 timep->tv_sec = ntohl(*p++);
97 /* Convert microseconds into nanoseconds */
98 timep->tv_nsec = ntohl(*p++) * 1000;
99 return p;
100}
101
Al Viro9d787a72006-10-19 23:28:47 -0700102static __be32 *
103xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104{
Trond Myklebustbca79472009-03-11 14:10:26 -0400105 u32 rdev, type;
106 type = ntohl(*p++);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 fattr->mode = ntohl(*p++);
108 fattr->nlink = ntohl(*p++);
109 fattr->uid = ntohl(*p++);
110 fattr->gid = ntohl(*p++);
111 fattr->size = ntohl(*p++);
112 fattr->du.nfs2.blocksize = ntohl(*p++);
113 rdev = ntohl(*p++);
114 fattr->du.nfs2.blocks = ntohl(*p++);
Trond Myklebust8b4bdcf2006-06-09 09:34:19 -0400115 fattr->fsid.major = ntohl(*p++);
116 fattr->fsid.minor = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 fattr->fileid = ntohl(*p++);
118 p = xdr_decode_time(p, &fattr->atime);
119 p = xdr_decode_time(p, &fattr->mtime);
120 p = xdr_decode_time(p, &fattr->ctime);
Trond Myklebust9e6e70f2009-03-11 14:10:24 -0400121 fattr->valid |= NFS_ATTR_FATTR_V2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 fattr->rdev = new_decode_dev(rdev);
Trond Myklebustbca79472009-03-11 14:10:26 -0400123 if (type == NFCHR && rdev == NFS2_FIFO_DEV) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
125 fattr->rdev = 0;
126 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 return p;
128}
129
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130/*
Chuck Lever25a08662010-12-14 14:54:30 +0000131 * Encode/decode NFSv2 basic data types
132 *
133 * Basic NFSv2 data types are defined in section 2.3 of RFC 1094:
134 * "NFS: Network File System Protocol Specification".
135 *
136 * Not all basic data types have their own encoding and decoding
137 * functions. For run-time efficiency, some data types are encoded
138 * or decoded inline.
139 */
140
141/*
142 * 2.3.3. fhandle
143 *
144 * typedef opaque fhandle[FHSIZE];
145 */
146static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
147{
148 __be32 *p;
149
150 BUG_ON(fh->size != NFS2_FHSIZE);
151 p = xdr_reserve_space(xdr, NFS2_FHSIZE);
152 memcpy(p, fh->data, NFS2_FHSIZE);
153}
154
155/*
Chuck Lever282ac2a2010-12-14 14:54:50 +0000156 * 2.3.4. timeval
157 *
158 * struct timeval {
159 * unsigned int seconds;
160 * unsigned int useconds;
161 * };
162 */
163static __be32 *xdr_encode_time(__be32 *p, const struct timespec *timep)
164{
165 *p++ = cpu_to_be32(timep->tv_sec);
166 if (timep->tv_nsec != 0)
167 *p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC);
168 else
169 *p++ = cpu_to_be32(0);
170 return p;
171}
172
173/*
174 * Passing the invalid value useconds=1000000 is a Sun convention for
175 * "set to current server time". It's needed to make permissions checks
176 * for the "touch" program across v2 mounts to Solaris and Irix servers
177 * work correctly. See description of sattr in section 6.1 of "NFS
178 * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5.
179 */
180static __be32 *xdr_encode_current_server_time(__be32 *p,
181 const struct timespec *timep)
182{
183 *p++ = cpu_to_be32(timep->tv_sec);
184 *p++ = cpu_to_be32(1000000);
185 return p;
186}
187
188/*
Chuck Lever25a08662010-12-14 14:54:30 +0000189 * 2.3.6. sattr
190 *
191 * struct sattr {
192 * unsigned int mode;
193 * unsigned int uid;
194 * unsigned int gid;
195 * unsigned int size;
196 * timeval atime;
197 * timeval mtime;
198 * };
199 */
200
201#define NFS2_SATTR_NOT_SET (0xffffffff)
202
203static __be32 *xdr_time_not_set(__be32 *p)
204{
205 *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
206 *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
207 return p;
208}
209
210static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr)
211{
212 __be32 *p;
213
214 p = xdr_reserve_space(xdr, NFS_sattr_sz << 2);
215
216 if (attr->ia_valid & ATTR_MODE)
217 *p++ = cpu_to_be32(attr->ia_mode);
218 else
219 *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
220 if (attr->ia_valid & ATTR_UID)
221 *p++ = cpu_to_be32(attr->ia_uid);
222 else
223 *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
224 if (attr->ia_valid & ATTR_GID)
225 *p++ = cpu_to_be32(attr->ia_gid);
226 else
227 *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
228 if (attr->ia_valid & ATTR_SIZE)
229 *p++ = cpu_to_be32((u32)attr->ia_size);
230 else
231 *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
232
233 if (attr->ia_valid & ATTR_ATIME_SET)
234 p = xdr_encode_time(p, &attr->ia_atime);
235 else if (attr->ia_valid & ATTR_ATIME)
236 p = xdr_encode_current_server_time(p, &attr->ia_atime);
237 else
238 p = xdr_time_not_set(p);
239 if (attr->ia_valid & ATTR_MTIME_SET)
240 xdr_encode_time(p, &attr->ia_mtime);
241 else if (attr->ia_valid & ATTR_MTIME)
242 xdr_encode_current_server_time(p, &attr->ia_mtime);
243 else
244 xdr_time_not_set(p);
245}
246
247/*
248 * 2.3.7. filename
249 *
250 * typedef string filename<MAXNAMLEN>;
251 */
252static void encode_filename(struct xdr_stream *xdr,
253 const char *name, u32 length)
254{
255 __be32 *p;
256
257 BUG_ON(length > NFS2_MAXNAMLEN);
258 p = xdr_reserve_space(xdr, 4 + length);
259 xdr_encode_opaque(p, name, length);
260}
261
262/*
263 * 2.3.8. path
264 *
265 * typedef string path<MAXPATHLEN>;
266 */
267static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
268{
269 __be32 *p;
270
271 BUG_ON(length > NFS2_MAXPATHLEN);
272 p = xdr_reserve_space(xdr, 4);
273 *p = cpu_to_be32(length);
274 xdr_write_pages(xdr, pages, 0, length);
275}
276
277/*
278 * 2.3.10. diropargs
279 *
280 * struct diropargs {
281 * fhandle dir;
282 * filename name;
283 * };
284 */
285static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,
286 const char *name, u32 length)
287{
288 encode_fhandle(xdr, fh);
289 encode_filename(xdr, name, length);
290}
291
292
293/*
Chuck Lever2d70f532010-12-14 14:54:40 +0000294 * NFSv2 XDR encode functions
295 *
296 * NFSv2 argument types are defined in section 2.2 of RFC 1094:
297 * "NFS: Network File System Protocol Specification".
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299
Chuck Lever25a08662010-12-14 14:54:30 +0000300static int nfs2_xdr_enc_fhandle(struct rpc_rqst *req, __be32 *p,
301 const struct nfs_fh *fh)
302{
303 struct xdr_stream xdr;
304
305 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
306 encode_fhandle(&xdr, fh);
307 return 0;
308}
309
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310/*
Chuck Lever25a08662010-12-14 14:54:30 +0000311 * 2.2.3. sattrargs
312 *
313 * struct sattrargs {
314 * fhandle file;
315 * sattr attributes;
316 * };
317 */
318static int nfs2_xdr_enc_sattrargs(struct rpc_rqst *req, __be32 *p,
319 const struct nfs_sattrargs *args)
320{
321 struct xdr_stream xdr;
322
323 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
324 encode_fhandle(&xdr, args->fh);
325 encode_sattr(&xdr, args->sattr);
326 return 0;
327}
328
Chuck Lever25a08662010-12-14 14:54:30 +0000329static int nfs2_xdr_enc_diropargs(struct rpc_rqst *req, __be32 *p,
330 const struct nfs_diropargs *args)
331{
332 struct xdr_stream xdr;
333
334 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
335 encode_diropargs(&xdr, args->fh, args->name, args->len);
336 return 0;
337}
338
Chuck Lever25a08662010-12-14 14:54:30 +0000339static int nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req, __be32 *p,
340 const struct nfs_readlinkargs *args)
341{
342 struct xdr_stream xdr;
343
344 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
345 encode_fhandle(&xdr, args->fh);
346 prepare_reply_buffer(req, args->pages, args->pgbase,
347 args->pglen, NFS_readlinkres_sz);
348 return 0;
349}
350
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400351/*
Chuck Lever25a08662010-12-14 14:54:30 +0000352 * 2.2.7. readargs
353 *
354 * struct readargs {
355 * fhandle file;
356 * unsigned offset;
357 * unsigned count;
358 * unsigned totalcount;
359 * };
360 */
361static void encode_readargs(struct xdr_stream *xdr,
362 const struct nfs_readargs *args)
363{
364 u32 offset = args->offset;
365 u32 count = args->count;
366 __be32 *p;
367
368 encode_fhandle(xdr, args->fh);
369
370 p = xdr_reserve_space(xdr, 4 + 4 + 4);
371 *p++ = cpu_to_be32(offset);
372 *p++ = cpu_to_be32(count);
373 *p = cpu_to_be32(count);
374}
375
376static int nfs2_xdr_enc_readargs(struct rpc_rqst *req, __be32 *p,
377 const struct nfs_readargs *args)
378{
379 struct xdr_stream xdr;
380
381 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
382 encode_readargs(&xdr, args);
383 prepare_reply_buffer(req, args->pages, args->pgbase,
384 args->count, NFS_readres_sz);
385 req->rq_rcv_buf.flags |= XDRBUF_READ;
386 return 0;
387}
388
389/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 * Decode READ reply
391 */
392static int
Al Viro9d787a72006-10-19 23:28:47 -0700393nfs_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394{
395 struct kvec *iov = req->rq_rcv_buf.head;
Chuck Lever6232dbb2007-10-26 13:31:52 -0400396 size_t hdrlen;
397 u32 count, recvd;
398 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399
400 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300401 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 p = xdr_decode_fattr(p, res->fattr);
403
404 count = ntohl(*p++);
405 res->eof = 0;
406 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
407 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400408 dprintk("NFS: READ reply header overflowed:"
Chuck Lever6232dbb2007-10-26 13:31:52 -0400409 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 return -errno_NFSERR_IO;
411 } else if (iov->iov_len != hdrlen) {
412 dprintk("NFS: READ header is short. iovec will be shifted.\n");
413 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
414 }
415
416 recvd = req->rq_rcv_buf.len - hdrlen;
417 if (count > recvd) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400418 dprintk("NFS: server cheating in read reply: "
Chuck Lever6232dbb2007-10-26 13:31:52 -0400419 "count %u > recvd %u\n", count, recvd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 count = recvd;
421 }
422
Chuck Lever6232dbb2007-10-26 13:31:52 -0400423 dprintk("RPC: readres OK count %u\n", count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 if (count < res->count)
425 res->count = count;
426
427 return count;
428}
429
430
431/*
Chuck Lever25a08662010-12-14 14:54:30 +0000432 * 2.2.9. writeargs
433 *
434 * struct writeargs {
435 * fhandle file;
436 * unsigned beginoffset;
437 * unsigned offset;
438 * unsigned totalcount;
439 * nfsdata data;
440 * };
441 */
442static void encode_writeargs(struct xdr_stream *xdr,
443 const struct nfs_writeargs *args)
444{
445 u32 offset = args->offset;
446 u32 count = args->count;
447 __be32 *p;
448
449 encode_fhandle(xdr, args->fh);
450
451 p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
452 *p++ = cpu_to_be32(offset);
453 *p++ = cpu_to_be32(offset);
454 *p++ = cpu_to_be32(count);
455
456 /* nfsdata */
457 *p = cpu_to_be32(count);
458 xdr_write_pages(xdr, args->pages, args->pgbase, count);
459}
460
461static int nfs2_xdr_enc_writeargs(struct rpc_rqst *req, __be32 *p,
462 const struct nfs_writeargs *args)
463{
464 struct xdr_stream xdr;
465
466 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
467 encode_writeargs(&xdr, args);
468 xdr.buf->flags |= XDRBUF_WRITE;
469 return 0;
470}
471
472/*
Chuck Lever25a08662010-12-14 14:54:30 +0000473 * 2.2.10. createargs
474 *
475 * struct createargs {
476 * diropargs where;
477 * sattr attributes;
478 * };
479 */
480static int nfs2_xdr_enc_createargs(struct rpc_rqst *req, __be32 *p,
481 const struct nfs_createargs *args)
482{
483 struct xdr_stream xdr;
484
485 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
486 encode_diropargs(&xdr, args->fh, args->name, args->len);
487 encode_sattr(&xdr, args->sattr);
488 return 0;
489}
490
491static int nfs2_xdr_enc_removeargs(struct rpc_rqst *req, __be32 *p,
492 const struct nfs_removeargs *args)
493{
494 struct xdr_stream xdr;
495
496 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
497 encode_diropargs(&xdr, args->fh, args->name.name, args->name.len);
498 return 0;
499}
500
501/*
Chuck Lever25a08662010-12-14 14:54:30 +0000502 * 2.2.12. renameargs
503 *
504 * struct renameargs {
505 * diropargs from;
506 * diropargs to;
507 * };
508 */
509static int nfs2_xdr_enc_renameargs(struct rpc_rqst *req, __be32 *p,
510 const struct nfs_renameargs *args)
511{
512 const struct qstr *old = args->old_name;
513 const struct qstr *new = args->new_name;
514 struct xdr_stream xdr;
515
516 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
517 encode_diropargs(&xdr, args->old_dir, old->name, old->len);
518 encode_diropargs(&xdr, args->new_dir, new->name, new->len);
519 return 0;
520}
521
522/*
Chuck Lever25a08662010-12-14 14:54:30 +0000523 * 2.2.13. linkargs
524 *
525 * struct linkargs {
526 * fhandle from;
527 * diropargs to;
528 * };
529 */
530static int nfs2_xdr_enc_linkargs(struct rpc_rqst *req, __be32 *p,
531 const struct nfs_linkargs *args)
532{
533 struct xdr_stream xdr;
534
535 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
536 encode_fhandle(&xdr, args->fromfh);
537 encode_diropargs(&xdr, args->tofh, args->toname, args->tolen);
538 return 0;
539}
540
541/*
Chuck Lever25a08662010-12-14 14:54:30 +0000542 * 2.2.14. symlinkargs
543 *
544 * struct symlinkargs {
545 * diropargs from;
546 * path to;
547 * sattr attributes;
548 * };
549 */
550static int nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req, __be32 *p,
551 const struct nfs_symlinkargs *args)
552{
553 struct xdr_stream xdr;
554
555 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
556 encode_diropargs(&xdr, args->fromfh, args->fromname, args->fromlen);
557 encode_path(&xdr, args->pages, args->pathlen);
558 encode_sattr(&xdr, args->sattr);
559 return 0;
560}
561
562/*
Chuck Lever25a08662010-12-14 14:54:30 +0000563 * 2.2.17. readdirargs
564 *
565 * struct readdirargs {
566 * fhandle dir;
567 * nfscookie cookie;
568 * unsigned count;
569 * };
570 */
571static void encode_readdirargs(struct xdr_stream *xdr,
572 const struct nfs_readdirargs *args)
573{
574 __be32 *p;
575
576 encode_fhandle(xdr, args->fh);
577
578 p = xdr_reserve_space(xdr, 4 + 4);
579 *p++ = cpu_to_be32(args->cookie);
580 *p = cpu_to_be32(args->count);
581}
582
583static int nfs2_xdr_enc_readdirargs(struct rpc_rqst *req, __be32 *p,
584 const struct nfs_readdirargs *args)
585{
586 struct xdr_stream xdr;
587
588 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
589 encode_readdirargs(&xdr, args);
590 prepare_reply_buffer(req, args->pages, 0,
591 args->count, NFS_readdirres_sz);
592 return 0;
593}
594
595/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 * Decode the result of a readdir call.
597 * We're not really decoding anymore, we just leave the buffer untouched
598 * and only check that it is syntactically correct.
599 * The real decoding happens in nfs_decode_entry below, called directly
600 * from nfs_readdir for each entry.
601 */
602static int
Al Viro9d787a72006-10-19 23:28:47 -0700603nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604{
605 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
606 struct kvec *iov = rcvbuf->head;
607 struct page **page;
Chuck Lever6232dbb2007-10-26 13:31:52 -0400608 size_t hdrlen;
609 unsigned int pglen, recvd;
Trond Myklebustac396122010-11-15 20:26:22 -0500610 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
612 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300613 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
615 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
616 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400617 dprintk("NFS: READDIR reply header overflowed:"
Chuck Lever6232dbb2007-10-26 13:31:52 -0400618 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 return -errno_NFSERR_IO;
620 } else if (iov->iov_len != hdrlen) {
621 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
622 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
623 }
624
625 pglen = rcvbuf->page_len;
626 recvd = rcvbuf->len - hdrlen;
627 if (pglen > recvd)
628 pglen = recvd;
629 page = rcvbuf->pages;
Trond Myklebustac396122010-11-15 20:26:22 -0500630 return pglen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631}
632
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400633static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634{
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400635 dprintk("nfs: %s: prematurely hit end of receive buffer. "
636 "Remaining buffer length is %tu words.\n",
637 func, xdr->end - xdr->p);
638}
639
640__be32 *
Bryan Schumaker82f2e542010-10-21 16:33:18 -0400641nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus)
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400642{
643 __be32 *p;
644 p = xdr_inline_decode(xdr, 4);
645 if (unlikely(!p))
646 goto out_overflow;
647 if (!ntohl(*p++)) {
648 p = xdr_inline_decode(xdr, 4);
649 if (unlikely(!p))
650 goto out_overflow;
651 if (!ntohl(*p++))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 return ERR_PTR(-EAGAIN);
653 entry->eof = 1;
654 return ERR_PTR(-EBADCOOKIE);
655 }
656
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400657 p = xdr_inline_decode(xdr, 8);
658 if (unlikely(!p))
659 goto out_overflow;
660
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 entry->ino = ntohl(*p++);
662 entry->len = ntohl(*p++);
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400663
664 p = xdr_inline_decode(xdr, entry->len + 4);
665 if (unlikely(!p))
666 goto out_overflow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 entry->name = (const char *) p;
668 p += XDR_QUADLEN(entry->len);
669 entry->prev_cookie = entry->cookie;
670 entry->cookie = ntohl(*p++);
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400671
Trond Myklebust0b26a0b2010-11-20 14:26:44 -0500672 entry->d_type = DT_UNKNOWN;
673
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400674 p = xdr_inline_peek(xdr, 8);
675 if (p != NULL)
676 entry->eof = !p[0] && p[1];
677 else
678 entry->eof = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679
680 return p;
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400681
682out_overflow:
683 print_overflow_msg(__func__, xdr);
Trond Myklebust463a3762010-11-20 12:22:20 -0500684 return ERR_PTR(-EAGAIN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685}
686
687/*
688 * NFS XDR decode functions
689 */
690/*
691 * Decode simple status reply
692 */
693static int
Al Viro9d787a72006-10-19 23:28:47 -0700694nfs_xdr_stat(struct rpc_rqst *req, __be32 *p, void *dummy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695{
696 int status;
697
698 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300699 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 return status;
701}
702
703/*
704 * Decode attrstat reply
705 * GETATTR, SETATTR, WRITE
706 */
707static int
Al Viro9d787a72006-10-19 23:28:47 -0700708nfs_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709{
710 int status;
711
712 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300713 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 xdr_decode_fattr(p, fattr);
715 return 0;
716}
717
718/*
719 * Decode diropres reply
720 * LOOKUP, CREATE, MKDIR
721 */
722static int
Al Viro9d787a72006-10-19 23:28:47 -0700723nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724{
725 int status;
726
727 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300728 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 p = xdr_decode_fhandle(p, res->fh);
730 xdr_decode_fattr(p, res->fattr);
731 return 0;
732}
733
734/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 * Decode READLINK reply
736 */
737static int
Al Viro9d787a72006-10-19 23:28:47 -0700738nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739{
740 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
741 struct kvec *iov = rcvbuf->head;
Chuck Lever6232dbb2007-10-26 13:31:52 -0400742 size_t hdrlen;
743 u32 len, recvd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 int status;
745
746 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300747 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 /* Convert length of symlink */
749 len = ntohl(*p++);
Chuck Lever6232dbb2007-10-26 13:31:52 -0400750 if (len >= rcvbuf->page_len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400751 dprintk("nfs: server returned giant symlink!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 return -ENAMETOOLONG;
753 }
754 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
755 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400756 dprintk("NFS: READLINK reply header overflowed:"
Chuck Lever6232dbb2007-10-26 13:31:52 -0400757 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 return -errno_NFSERR_IO;
759 } else if (iov->iov_len != hdrlen) {
760 dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
761 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
762 }
763 recvd = req->rq_rcv_buf.len - hdrlen;
764 if (recvd < len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400765 dprintk("NFS: server cheating in readlink reply: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 "count %u > recvd %u\n", len, recvd);
767 return -EIO;
768 }
769
Chuck Leverb4687da2010-09-21 16:55:48 -0400770 xdr_terminate_string(rcvbuf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 return 0;
772}
773
774/*
775 * Decode WRITE reply
776 */
777static int
Al Viro9d787a72006-10-19 23:28:47 -0700778nfs_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779{
780 res->verf->committed = NFS_FILE_SYNC;
781 return nfs_xdr_attrstat(req, p, res->fattr);
782}
783
784/*
785 * Decode STATFS reply
786 */
787static int
Al Viro9d787a72006-10-19 23:28:47 -0700788nfs_xdr_statfsres(struct rpc_rqst *req, __be32 *p, struct nfs2_fsstat *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789{
790 int status;
791
792 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300793 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
795 res->tsize = ntohl(*p++);
796 res->bsize = ntohl(*p++);
797 res->blocks = ntohl(*p++);
798 res->bfree = ntohl(*p++);
799 res->bavail = ntohl(*p++);
800 return 0;
801}
802
803/*
804 * We need to translate between nfs status return values and
805 * the local errno values which may not be the same.
806 */
Chuck Lever85828492010-12-14 14:55:00 +0000807static const struct {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 int stat;
809 int errno;
810} nfs_errtbl[] = {
811 { NFS_OK, 0 },
Benny Halevy856dff32008-03-31 17:39:06 +0300812 { NFSERR_PERM, -EPERM },
813 { NFSERR_NOENT, -ENOENT },
814 { NFSERR_IO, -errno_NFSERR_IO},
815 { NFSERR_NXIO, -ENXIO },
816/* { NFSERR_EAGAIN, -EAGAIN }, */
817 { NFSERR_ACCES, -EACCES },
818 { NFSERR_EXIST, -EEXIST },
819 { NFSERR_XDEV, -EXDEV },
820 { NFSERR_NODEV, -ENODEV },
821 { NFSERR_NOTDIR, -ENOTDIR },
822 { NFSERR_ISDIR, -EISDIR },
823 { NFSERR_INVAL, -EINVAL },
824 { NFSERR_FBIG, -EFBIG },
825 { NFSERR_NOSPC, -ENOSPC },
826 { NFSERR_ROFS, -EROFS },
827 { NFSERR_MLINK, -EMLINK },
828 { NFSERR_NAMETOOLONG, -ENAMETOOLONG },
829 { NFSERR_NOTEMPTY, -ENOTEMPTY },
830 { NFSERR_DQUOT, -EDQUOT },
831 { NFSERR_STALE, -ESTALE },
832 { NFSERR_REMOTE, -EREMOTE },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833#ifdef EWFLUSH
Benny Halevy856dff32008-03-31 17:39:06 +0300834 { NFSERR_WFLUSH, -EWFLUSH },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835#endif
Benny Halevy856dff32008-03-31 17:39:06 +0300836 { NFSERR_BADHANDLE, -EBADHANDLE },
837 { NFSERR_NOT_SYNC, -ENOTSYNC },
838 { NFSERR_BAD_COOKIE, -EBADCOOKIE },
839 { NFSERR_NOTSUPP, -ENOTSUPP },
840 { NFSERR_TOOSMALL, -ETOOSMALL },
Trond Myklebustfdcb4572010-02-08 09:32:40 -0500841 { NFSERR_SERVERFAULT, -EREMOTEIO },
Benny Halevy856dff32008-03-31 17:39:06 +0300842 { NFSERR_BADTYPE, -EBADTYPE },
843 { NFSERR_JUKEBOX, -EJUKEBOX },
844 { -1, -EIO }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845};
846
Chuck Lever85828492010-12-14 14:55:00 +0000847/**
848 * nfs_stat_to_errno - convert an NFS status code to a local errno
849 * @status: NFS status code to convert
850 *
851 * Returns a local errno value, or -EIO if the NFS status code is
852 * not recognized. This function is used jointly by NFSv2 and NFSv3.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 */
Chuck Lever85828492010-12-14 14:55:00 +0000854int nfs_stat_to_errno(enum nfs_stat status)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855{
856 int i;
857
858 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
Chuck Lever85828492010-12-14 14:55:00 +0000859 if (nfs_errtbl[i].stat == (int)status)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 return nfs_errtbl[i].errno;
861 }
Chuck Lever85828492010-12-14 14:55:00 +0000862 dprintk("NFS: Unrecognized nfs status value: %u\n", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 return nfs_errtbl[i].errno;
864}
865
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866#define PROC(proc, argtype, restype, timer) \
867[NFSPROC_##proc] = { \
868 .p_proc = NFSPROC_##proc, \
Chuck Lever25a08662010-12-14 14:54:30 +0000869 .p_encode = (kxdrproc_t)nfs2_xdr_enc_##argtype, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 .p_decode = (kxdrproc_t) nfs_xdr_##restype, \
Chuck Lever2bea90d2007-03-29 16:47:53 -0400871 .p_arglen = NFS_##argtype##_sz, \
872 .p_replen = NFS_##restype##_sz, \
Chuck Levercc0175c2006-03-20 13:44:22 -0500873 .p_timer = timer, \
874 .p_statidx = NFSPROC_##proc, \
875 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 }
877struct rpc_procinfo nfs_procedures[] = {
878 PROC(GETATTR, fhandle, attrstat, 1),
879 PROC(SETATTR, sattrargs, attrstat, 0),
880 PROC(LOOKUP, diropargs, diropres, 2),
881 PROC(READLINK, readlinkargs, readlinkres, 3),
882 PROC(READ, readargs, readres, 3),
883 PROC(WRITE, writeargs, writeres, 4),
884 PROC(CREATE, createargs, diropres, 0),
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400885 PROC(REMOVE, removeargs, stat, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 PROC(RENAME, renameargs, stat, 0),
887 PROC(LINK, linkargs, stat, 0),
888 PROC(SYMLINK, symlinkargs, stat, 0),
889 PROC(MKDIR, createargs, diropres, 0),
890 PROC(RMDIR, diropargs, stat, 0),
891 PROC(READDIR, readdirargs, readdirres, 3),
892 PROC(STATFS, fhandle, statfsres, 0),
893};
894
895struct rpc_version nfs_version2 = {
896 .number = 2,
Tobias Klausere8c96f82006-03-24 03:15:34 -0800897 .nrprocs = ARRAY_SIZE(nfs_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 .procs = nfs_procedures
899};