blob: 3be4e72a0227e71030709b019dd35db28897a34e [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>
15#include <linux/slab.h>
16#include <linux/utsname.h>
17#include <linux/errno.h>
18#include <linux/string.h>
19#include <linux/in.h>
20#include <linux/pagemap.h>
21#include <linux/proc_fs.h>
22#include <linux/sunrpc/clnt.h>
23#include <linux/nfs.h>
24#include <linux/nfs2.h>
25#include <linux/nfs_fs.h>
Trond Myklebust816724e2006-06-24 08:41:41 -040026#include "internal.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
28#define NFSDBG_FACILITY NFSDBG_XDR
29/* #define NFS_PARANOIA 1 */
30
Linus Torvalds1da177e2005-04-16 15:20:36 -070031/* Mapping from NFS error code to "errno" error code. */
32#define errno_NFSERR_IO EIO
33
34/*
35 * Declare the space requirements for NFS arguments and replies as
36 * number of 32bit-words
37 */
38#define NFS_fhandle_sz (8)
39#define NFS_sattr_sz (8)
40#define NFS_filename_sz (1+(NFS2_MAXNAMLEN>>2))
41#define NFS_path_sz (1+(NFS2_MAXPATHLEN>>2))
42#define NFS_fattr_sz (17)
43#define NFS_info_sz (5)
44#define NFS_entry_sz (NFS_filename_sz+3)
45
46#define NFS_diropargs_sz (NFS_fhandle_sz+NFS_filename_sz)
47#define NFS_sattrargs_sz (NFS_fhandle_sz+NFS_sattr_sz)
48#define NFS_readlinkargs_sz (NFS_fhandle_sz)
49#define NFS_readargs_sz (NFS_fhandle_sz+3)
50#define NFS_writeargs_sz (NFS_fhandle_sz+4)
51#define NFS_createargs_sz (NFS_diropargs_sz+NFS_sattr_sz)
52#define NFS_renameargs_sz (NFS_diropargs_sz+NFS_diropargs_sz)
53#define NFS_linkargs_sz (NFS_fhandle_sz+NFS_diropargs_sz)
Chuck Lever94a6d752006-08-22 20:06:23 -040054#define NFS_symlinkargs_sz (NFS_diropargs_sz+1+NFS_sattr_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#define NFS_readdirargs_sz (NFS_fhandle_sz+2)
56
57#define NFS_attrstat_sz (1+NFS_fattr_sz)
58#define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz)
59#define NFS_readlinkres_sz (2)
60#define NFS_readres_sz (1+NFS_fattr_sz+1)
61#define NFS_writeres_sz (NFS_attrstat_sz)
62#define NFS_stat_sz (1)
63#define NFS_readdirres_sz (1)
64#define NFS_statfsres_sz (1+NFS_info_sz)
65
66/*
67 * Common NFS XDR functions as inlines
68 */
Al Viro9d787a72006-10-19 23:28:47 -070069static inline __be32 *
70xdr_encode_fhandle(__be32 *p, struct nfs_fh *fhandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -070071{
72 memcpy(p, fhandle->data, NFS2_FHSIZE);
73 return p + XDR_QUADLEN(NFS2_FHSIZE);
74}
75
Al Viro9d787a72006-10-19 23:28:47 -070076static inline __be32 *
77xdr_decode_fhandle(__be32 *p, struct nfs_fh *fhandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -070078{
79 /* NFSv2 handles have a fixed length */
80 fhandle->size = NFS2_FHSIZE;
81 memcpy(fhandle->data, p, NFS2_FHSIZE);
82 return p + XDR_QUADLEN(NFS2_FHSIZE);
83}
84
Al Viro9d787a72006-10-19 23:28:47 -070085static inline __be32*
86xdr_encode_time(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070087{
88 *p++ = htonl(timep->tv_sec);
89 /* Convert nanoseconds into microseconds */
90 *p++ = htonl(timep->tv_nsec ? timep->tv_nsec / 1000 : 0);
91 return p;
92}
93
Al Viro9d787a72006-10-19 23:28:47 -070094static inline __be32*
95xdr_encode_current_server_time(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070096{
97 /*
98 * Passing the invalid value useconds=1000000 is a
99 * Sun convention for "set to current server time".
100 * It's needed to make permissions checks for the
101 * "touch" program across v2 mounts to Solaris and
102 * Irix boxes work correctly. See description of
103 * sattr in section 6.1 of "NFS Illustrated" by
104 * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
105 */
106 *p++ = htonl(timep->tv_sec);
107 *p++ = htonl(1000000);
108 return p;
109}
110
Al Viro9d787a72006-10-19 23:28:47 -0700111static inline __be32*
112xdr_decode_time(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113{
114 timep->tv_sec = ntohl(*p++);
115 /* Convert microseconds into nanoseconds */
116 timep->tv_nsec = ntohl(*p++) * 1000;
117 return p;
118}
119
Al Viro9d787a72006-10-19 23:28:47 -0700120static __be32 *
121xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122{
123 u32 rdev;
124 fattr->type = (enum nfs_ftype) ntohl(*p++);
125 fattr->mode = ntohl(*p++);
126 fattr->nlink = ntohl(*p++);
127 fattr->uid = ntohl(*p++);
128 fattr->gid = ntohl(*p++);
129 fattr->size = ntohl(*p++);
130 fattr->du.nfs2.blocksize = ntohl(*p++);
131 rdev = ntohl(*p++);
132 fattr->du.nfs2.blocks = ntohl(*p++);
Trond Myklebust8b4bdcf2006-06-09 09:34:19 -0400133 fattr->fsid.major = ntohl(*p++);
134 fattr->fsid.minor = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 fattr->fileid = ntohl(*p++);
136 p = xdr_decode_time(p, &fattr->atime);
137 p = xdr_decode_time(p, &fattr->mtime);
138 p = xdr_decode_time(p, &fattr->ctime);
139 fattr->valid |= NFS_ATTR_FATTR;
140 fattr->rdev = new_decode_dev(rdev);
141 if (fattr->type == NFCHR && rdev == NFS2_FIFO_DEV) {
142 fattr->type = NFFIFO;
143 fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
144 fattr->rdev = 0;
145 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 return p;
147}
148
Al Viro9d787a72006-10-19 23:28:47 -0700149static inline __be32 *
150xdr_encode_sattr(__be32 *p, struct iattr *attr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151{
Al Viro9d787a72006-10-19 23:28:47 -0700152 const __be32 not_set = __constant_htonl(0xFFFFFFFF);
Trond Myklebusteadb8c12006-01-03 09:55:54 +0100153
154 *p++ = (attr->ia_valid & ATTR_MODE) ? htonl(attr->ia_mode) : not_set;
155 *p++ = (attr->ia_valid & ATTR_UID) ? htonl(attr->ia_uid) : not_set;
156 *p++ = (attr->ia_valid & ATTR_GID) ? htonl(attr->ia_gid) : not_set;
157 *p++ = (attr->ia_valid & ATTR_SIZE) ? htonl(attr->ia_size) : not_set;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
159 if (attr->ia_valid & ATTR_ATIME_SET) {
160 p = xdr_encode_time(p, &attr->ia_atime);
161 } else if (attr->ia_valid & ATTR_ATIME) {
162 p = xdr_encode_current_server_time(p, &attr->ia_atime);
163 } else {
Trond Myklebusteadb8c12006-01-03 09:55:54 +0100164 *p++ = not_set;
165 *p++ = not_set;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 }
167
168 if (attr->ia_valid & ATTR_MTIME_SET) {
169 p = xdr_encode_time(p, &attr->ia_mtime);
170 } else if (attr->ia_valid & ATTR_MTIME) {
171 p = xdr_encode_current_server_time(p, &attr->ia_mtime);
172 } else {
Trond Myklebusteadb8c12006-01-03 09:55:54 +0100173 *p++ = not_set;
174 *p++ = not_set;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 }
176 return p;
177}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178
179/*
180 * NFS encode functions
181 */
182/*
183 * Encode file handle argument
184 * GETATTR, READLINK, STATFS
185 */
186static int
Al Viro9d787a72006-10-19 23:28:47 -0700187nfs_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188{
189 p = xdr_encode_fhandle(p, fh);
190 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
191 return 0;
192}
193
194/*
195 * Encode SETATTR arguments
196 */
197static int
Al Viro9d787a72006-10-19 23:28:47 -0700198nfs_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs_sattrargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199{
200 p = xdr_encode_fhandle(p, args->fh);
201 p = xdr_encode_sattr(p, args->sattr);
202 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
203 return 0;
204}
205
206/*
207 * Encode directory ops argument
208 * LOOKUP, REMOVE, RMDIR
209 */
210static int
Al Viro9d787a72006-10-19 23:28:47 -0700211nfs_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs_diropargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212{
213 p = xdr_encode_fhandle(p, args->fh);
214 p = xdr_encode_array(p, args->name, args->len);
215 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
216 return 0;
217}
218
219/*
220 * Arguments to a READ call. Since we read data directly into the page
221 * cache, we also set up the reply iovec here so that iov[1] points
222 * exactly to the page we want to fetch.
223 */
224static int
Al Viro9d787a72006-10-19 23:28:47 -0700225nfs_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226{
227 struct rpc_auth *auth = req->rq_task->tk_auth;
228 unsigned int replen;
229 u32 offset = (u32)args->offset;
230 u32 count = args->count;
231
232 p = xdr_encode_fhandle(p, args->fh);
233 *p++ = htonl(offset);
234 *p++ = htonl(count);
235 *p++ = htonl(count);
236 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
237
238 /* Inline the page array */
239 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
240 xdr_inline_pages(&req->rq_rcv_buf, replen,
241 args->pages, args->pgbase, count);
242 return 0;
243}
244
245/*
246 * Decode READ reply
247 */
248static int
Al Viro9d787a72006-10-19 23:28:47 -0700249nfs_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250{
251 struct kvec *iov = req->rq_rcv_buf.head;
252 int status, count, recvd, hdrlen;
253
254 if ((status = ntohl(*p++)))
255 return -nfs_stat_to_errno(status);
256 p = xdr_decode_fattr(p, res->fattr);
257
258 count = ntohl(*p++);
259 res->eof = 0;
260 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
261 if (iov->iov_len < hdrlen) {
262 printk(KERN_WARNING "NFS: READ reply header overflowed:"
263 "length %d > %Zu\n", hdrlen, iov->iov_len);
264 return -errno_NFSERR_IO;
265 } else if (iov->iov_len != hdrlen) {
266 dprintk("NFS: READ header is short. iovec will be shifted.\n");
267 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
268 }
269
270 recvd = req->rq_rcv_buf.len - hdrlen;
271 if (count > recvd) {
272 printk(KERN_WARNING "NFS: server cheating in read reply: "
273 "count %d > recvd %d\n", count, recvd);
274 count = recvd;
275 }
276
277 dprintk("RPC: readres OK count %d\n", count);
278 if (count < res->count)
279 res->count = count;
280
281 return count;
282}
283
284
285/*
286 * Write arguments. Splice the buffer to be written into the iovec.
287 */
288static int
Al Viro9d787a72006-10-19 23:28:47 -0700289nfs_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290{
291 struct xdr_buf *sndbuf = &req->rq_snd_buf;
292 u32 offset = (u32)args->offset;
293 u32 count = args->count;
294
295 p = xdr_encode_fhandle(p, args->fh);
296 *p++ = htonl(offset);
297 *p++ = htonl(offset);
298 *p++ = htonl(count);
299 *p++ = htonl(count);
300 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
301
302 /* Copy the page array */
303 xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
304 return 0;
305}
306
307/*
308 * Encode create arguments
309 * CREATE, MKDIR
310 */
311static int
Al Viro9d787a72006-10-19 23:28:47 -0700312nfs_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs_createargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313{
314 p = xdr_encode_fhandle(p, args->fh);
315 p = xdr_encode_array(p, args->name, args->len);
316 p = xdr_encode_sattr(p, args->sattr);
317 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
318 return 0;
319}
320
321/*
322 * Encode RENAME arguments
323 */
324static int
Al Viro9d787a72006-10-19 23:28:47 -0700325nfs_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326{
327 p = xdr_encode_fhandle(p, args->fromfh);
328 p = xdr_encode_array(p, args->fromname, args->fromlen);
329 p = xdr_encode_fhandle(p, args->tofh);
330 p = xdr_encode_array(p, args->toname, args->tolen);
331 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
332 return 0;
333}
334
335/*
336 * Encode LINK arguments
337 */
338static int
Al Viro9d787a72006-10-19 23:28:47 -0700339nfs_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs_linkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340{
341 p = xdr_encode_fhandle(p, args->fromfh);
342 p = xdr_encode_fhandle(p, args->tofh);
343 p = xdr_encode_array(p, args->toname, args->tolen);
344 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
345 return 0;
346}
347
348/*
349 * Encode SYMLINK arguments
350 */
351static int
Al Viro9d787a72006-10-19 23:28:47 -0700352nfs_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_symlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353{
Chuck Lever94a6d752006-08-22 20:06:23 -0400354 struct xdr_buf *sndbuf = &req->rq_snd_buf;
355 size_t pad;
356
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 p = xdr_encode_fhandle(p, args->fromfh);
358 p = xdr_encode_array(p, args->fromname, args->fromlen);
Chuck Lever94a6d752006-08-22 20:06:23 -0400359 *p++ = htonl(args->pathlen);
360 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
361
362 xdr_encode_pages(sndbuf, args->pages, 0, args->pathlen);
363
364 /*
365 * xdr_encode_pages may have added a few bytes to ensure the
366 * pathname ends on a 4-byte boundary. Start encoding the
367 * attributes after the pad bytes.
368 */
369 pad = sndbuf->tail->iov_len;
370 if (pad > 0)
371 p++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 p = xdr_encode_sattr(p, args->sattr);
Chuck Lever94a6d752006-08-22 20:06:23 -0400373 sndbuf->len += xdr_adjust_iovec(sndbuf->tail, p) - pad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 return 0;
375}
376
377/*
378 * Encode arguments to readdir call
379 */
380static int
Al Viro9d787a72006-10-19 23:28:47 -0700381nfs_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs_readdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382{
383 struct rpc_task *task = req->rq_task;
384 struct rpc_auth *auth = task->tk_auth;
385 unsigned int replen;
386 u32 count = args->count;
387
388 p = xdr_encode_fhandle(p, args->fh);
389 *p++ = htonl(args->cookie);
390 *p++ = htonl(count); /* see above */
391 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
392
393 /* Inline the page array */
394 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
395 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
396 return 0;
397}
398
399/*
400 * Decode the result of a readdir call.
401 * We're not really decoding anymore, we just leave the buffer untouched
402 * and only check that it is syntactically correct.
403 * The real decoding happens in nfs_decode_entry below, called directly
404 * from nfs_readdir for each entry.
405 */
406static int
Al Viro9d787a72006-10-19 23:28:47 -0700407nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408{
409 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
410 struct kvec *iov = rcvbuf->head;
411 struct page **page;
412 int hdrlen, recvd;
413 int status, nr;
414 unsigned int len, pglen;
Al Viro9d787a72006-10-19 23:28:47 -0700415 __be32 *end, *entry, *kaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416
417 if ((status = ntohl(*p++)))
418 return -nfs_stat_to_errno(status);
419
420 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
421 if (iov->iov_len < hdrlen) {
422 printk(KERN_WARNING "NFS: READDIR reply header overflowed:"
423 "length %d > %Zu\n", hdrlen, iov->iov_len);
424 return -errno_NFSERR_IO;
425 } else if (iov->iov_len != hdrlen) {
426 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
427 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
428 }
429
430 pglen = rcvbuf->page_len;
431 recvd = rcvbuf->len - hdrlen;
432 if (pglen > recvd)
433 pglen = recvd;
434 page = rcvbuf->pages;
Al Viro9d787a72006-10-19 23:28:47 -0700435 kaddr = p = kmap_atomic(*page, KM_USER0);
436 end = (__be32 *)((char *)p + pglen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 entry = p;
438 for (nr = 0; *p++; nr++) {
439 if (p + 2 > end)
440 goto short_pkt;
441 p++; /* fileid */
442 len = ntohl(*p++);
443 p += XDR_QUADLEN(len) + 1; /* name plus cookie */
444 if (len > NFS2_MAXNAMLEN) {
445 printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)!\n",
446 len);
447 goto err_unmap;
448 }
449 if (p + 2 > end)
450 goto short_pkt;
451 entry = p;
452 }
453 if (!nr && (entry[0] != 0 || entry[1] == 0))
454 goto short_pkt;
455 out:
456 kunmap_atomic(kaddr, KM_USER0);
457 return nr;
458 short_pkt:
459 entry[0] = entry[1] = 0;
460 /* truncate listing ? */
461 if (!nr) {
462 printk(KERN_NOTICE "NFS: readdir reply truncated!\n");
463 entry[1] = 1;
464 }
465 goto out;
466err_unmap:
467 nr = -errno_NFSERR_IO;
468 goto out;
469}
470
Al Viro0dbb4c62006-10-19 23:28:49 -0700471__be32 *
472nfs_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473{
474 if (!*p++) {
475 if (!*p)
476 return ERR_PTR(-EAGAIN);
477 entry->eof = 1;
478 return ERR_PTR(-EBADCOOKIE);
479 }
480
481 entry->ino = ntohl(*p++);
482 entry->len = ntohl(*p++);
483 entry->name = (const char *) p;
484 p += XDR_QUADLEN(entry->len);
485 entry->prev_cookie = entry->cookie;
486 entry->cookie = ntohl(*p++);
487 entry->eof = !p[0] && p[1];
488
489 return p;
490}
491
492/*
493 * NFS XDR decode functions
494 */
495/*
496 * Decode simple status reply
497 */
498static int
Al Viro9d787a72006-10-19 23:28:47 -0700499nfs_xdr_stat(struct rpc_rqst *req, __be32 *p, void *dummy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500{
501 int status;
502
503 if ((status = ntohl(*p++)) != 0)
504 status = -nfs_stat_to_errno(status);
505 return status;
506}
507
508/*
509 * Decode attrstat reply
510 * GETATTR, SETATTR, WRITE
511 */
512static int
Al Viro9d787a72006-10-19 23:28:47 -0700513nfs_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514{
515 int status;
516
517 if ((status = ntohl(*p++)))
518 return -nfs_stat_to_errno(status);
519 xdr_decode_fattr(p, fattr);
520 return 0;
521}
522
523/*
524 * Decode diropres reply
525 * LOOKUP, CREATE, MKDIR
526 */
527static int
Al Viro9d787a72006-10-19 23:28:47 -0700528nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529{
530 int status;
531
532 if ((status = ntohl(*p++)))
533 return -nfs_stat_to_errno(status);
534 p = xdr_decode_fhandle(p, res->fh);
535 xdr_decode_fattr(p, res->fattr);
536 return 0;
537}
538
539/*
540 * Encode READLINK args
541 */
542static int
Al Viro9d787a72006-10-19 23:28:47 -0700543nfs_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_readlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544{
545 struct rpc_auth *auth = req->rq_task->tk_auth;
546 unsigned int replen;
547
548 p = xdr_encode_fhandle(p, args->fh);
549 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
550
551 /* Inline the page array */
552 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
553 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
554 return 0;
555}
556
557/*
558 * Decode READLINK reply
559 */
560static int
Al Viro9d787a72006-10-19 23:28:47 -0700561nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562{
563 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
564 struct kvec *iov = rcvbuf->head;
565 int hdrlen, len, recvd;
566 char *kaddr;
567 int status;
568
569 if ((status = ntohl(*p++)))
570 return -nfs_stat_to_errno(status);
571 /* Convert length of symlink */
572 len = ntohl(*p++);
573 if (len >= rcvbuf->page_len || len <= 0) {
574 dprintk(KERN_WARNING "nfs: server returned giant symlink!\n");
575 return -ENAMETOOLONG;
576 }
577 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
578 if (iov->iov_len < hdrlen) {
579 printk(KERN_WARNING "NFS: READLINK reply header overflowed:"
580 "length %d > %Zu\n", hdrlen, iov->iov_len);
581 return -errno_NFSERR_IO;
582 } else if (iov->iov_len != hdrlen) {
583 dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
584 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
585 }
586 recvd = req->rq_rcv_buf.len - hdrlen;
587 if (recvd < len) {
588 printk(KERN_WARNING "NFS: server cheating in readlink reply: "
589 "count %u > recvd %u\n", len, recvd);
590 return -EIO;
591 }
592
593 /* NULL terminate the string we got */
594 kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0);
595 kaddr[len+rcvbuf->page_base] = '\0';
596 kunmap_atomic(kaddr, KM_USER0);
597 return 0;
598}
599
600/*
601 * Decode WRITE reply
602 */
603static int
Al Viro9d787a72006-10-19 23:28:47 -0700604nfs_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605{
606 res->verf->committed = NFS_FILE_SYNC;
607 return nfs_xdr_attrstat(req, p, res->fattr);
608}
609
610/*
611 * Decode STATFS reply
612 */
613static int
Al Viro9d787a72006-10-19 23:28:47 -0700614nfs_xdr_statfsres(struct rpc_rqst *req, __be32 *p, struct nfs2_fsstat *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615{
616 int status;
617
618 if ((status = ntohl(*p++)))
619 return -nfs_stat_to_errno(status);
620
621 res->tsize = ntohl(*p++);
622 res->bsize = ntohl(*p++);
623 res->blocks = ntohl(*p++);
624 res->bfree = ntohl(*p++);
625 res->bavail = ntohl(*p++);
626 return 0;
627}
628
629/*
630 * We need to translate between nfs status return values and
631 * the local errno values which may not be the same.
632 */
633static struct {
634 int stat;
635 int errno;
636} nfs_errtbl[] = {
637 { NFS_OK, 0 },
638 { NFSERR_PERM, EPERM },
639 { NFSERR_NOENT, ENOENT },
640 { NFSERR_IO, errno_NFSERR_IO },
641 { NFSERR_NXIO, ENXIO },
642/* { NFSERR_EAGAIN, EAGAIN }, */
643 { NFSERR_ACCES, EACCES },
644 { NFSERR_EXIST, EEXIST },
645 { NFSERR_XDEV, EXDEV },
646 { NFSERR_NODEV, ENODEV },
647 { NFSERR_NOTDIR, ENOTDIR },
648 { NFSERR_ISDIR, EISDIR },
649 { NFSERR_INVAL, EINVAL },
650 { NFSERR_FBIG, EFBIG },
651 { NFSERR_NOSPC, ENOSPC },
652 { NFSERR_ROFS, EROFS },
653 { NFSERR_MLINK, EMLINK },
654 { NFSERR_NAMETOOLONG, ENAMETOOLONG },
655 { NFSERR_NOTEMPTY, ENOTEMPTY },
656 { NFSERR_DQUOT, EDQUOT },
657 { NFSERR_STALE, ESTALE },
658 { NFSERR_REMOTE, EREMOTE },
659#ifdef EWFLUSH
660 { NFSERR_WFLUSH, EWFLUSH },
661#endif
662 { NFSERR_BADHANDLE, EBADHANDLE },
663 { NFSERR_NOT_SYNC, ENOTSYNC },
664 { NFSERR_BAD_COOKIE, EBADCOOKIE },
665 { NFSERR_NOTSUPP, ENOTSUPP },
666 { NFSERR_TOOSMALL, ETOOSMALL },
667 { NFSERR_SERVERFAULT, ESERVERFAULT },
668 { NFSERR_BADTYPE, EBADTYPE },
669 { NFSERR_JUKEBOX, EJUKEBOX },
670 { -1, EIO }
671};
672
673/*
674 * Convert an NFS error code to a local one.
675 * This one is used jointly by NFSv2 and NFSv3.
676 */
677int
678nfs_stat_to_errno(int stat)
679{
680 int i;
681
682 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
683 if (nfs_errtbl[i].stat == stat)
684 return nfs_errtbl[i].errno;
685 }
686 printk(KERN_ERR "nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
687 return nfs_errtbl[i].errno;
688}
689
690#ifndef MAX
691# define MAX(a, b) (((a) > (b))? (a) : (b))
692#endif
693
694#define PROC(proc, argtype, restype, timer) \
695[NFSPROC_##proc] = { \
696 .p_proc = NFSPROC_##proc, \
697 .p_encode = (kxdrproc_t) nfs_xdr_##argtype, \
698 .p_decode = (kxdrproc_t) nfs_xdr_##restype, \
699 .p_bufsiz = MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2, \
Chuck Levercc0175c2006-03-20 13:44:22 -0500700 .p_timer = timer, \
701 .p_statidx = NFSPROC_##proc, \
702 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 }
704struct rpc_procinfo nfs_procedures[] = {
705 PROC(GETATTR, fhandle, attrstat, 1),
706 PROC(SETATTR, sattrargs, attrstat, 0),
707 PROC(LOOKUP, diropargs, diropres, 2),
708 PROC(READLINK, readlinkargs, readlinkres, 3),
709 PROC(READ, readargs, readres, 3),
710 PROC(WRITE, writeargs, writeres, 4),
711 PROC(CREATE, createargs, diropres, 0),
712 PROC(REMOVE, diropargs, stat, 0),
713 PROC(RENAME, renameargs, stat, 0),
714 PROC(LINK, linkargs, stat, 0),
715 PROC(SYMLINK, symlinkargs, stat, 0),
716 PROC(MKDIR, createargs, diropres, 0),
717 PROC(RMDIR, diropargs, stat, 0),
718 PROC(READDIR, readdirargs, readdirres, 3),
719 PROC(STATFS, fhandle, statfsres, 0),
720};
721
722struct rpc_version nfs_version2 = {
723 .number = 2,
Tobias Klausere8c96f82006-03-24 03:15:34 -0800724 .nrprocs = ARRAY_SIZE(nfs_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 .procs = nfs_procedures
726};