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