blob: 86a80b33ec82f47b37a8ed23f624f8e0b46a1ecb [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)
Trond Myklebust4fdc17b2007-07-14 15:39:57 -040046#define NFS_removeargs_sz (NFS_fhandle_sz+NFS_filename_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#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 *
Trond Myklebust4fdc17b2007-07-14 15:39:57 -040070xdr_encode_fhandle(__be32 *p, const 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
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400208 * LOOKUP, RMDIR
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 */
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/*
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400220 * Encode REMOVE argument
221 */
222static int
223nfs_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
224{
225 p = xdr_encode_fhandle(p, args->fh);
226 p = xdr_encode_array(p, args->name.name, args->name.len);
227 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
228 return 0;
229}
230
231/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 * Arguments to a READ call. Since we read data directly into the page
233 * cache, we also set up the reply iovec here so that iov[1] points
234 * exactly to the page we want to fetch.
235 */
236static int
Al Viro9d787a72006-10-19 23:28:47 -0700237nfs_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400239 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 unsigned int replen;
241 u32 offset = (u32)args->offset;
242 u32 count = args->count;
243
244 p = xdr_encode_fhandle(p, args->fh);
245 *p++ = htonl(offset);
246 *p++ = htonl(count);
247 *p++ = htonl(count);
248 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
249
250 /* Inline the page array */
251 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
252 xdr_inline_pages(&req->rq_rcv_buf, replen,
253 args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400254 req->rq_rcv_buf.flags |= XDRBUF_READ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 return 0;
256}
257
258/*
259 * Decode READ reply
260 */
261static int
Al Viro9d787a72006-10-19 23:28:47 -0700262nfs_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263{
264 struct kvec *iov = req->rq_rcv_buf.head;
Chuck Lever6232dbb2007-10-26 13:31:52 -0400265 size_t hdrlen;
266 u32 count, recvd;
267 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
269 if ((status = ntohl(*p++)))
270 return -nfs_stat_to_errno(status);
271 p = xdr_decode_fattr(p, res->fattr);
272
273 count = ntohl(*p++);
274 res->eof = 0;
275 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
276 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400277 dprintk("NFS: READ reply header overflowed:"
Chuck Lever6232dbb2007-10-26 13:31:52 -0400278 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 return -errno_NFSERR_IO;
280 } else if (iov->iov_len != hdrlen) {
281 dprintk("NFS: READ header is short. iovec will be shifted.\n");
282 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
283 }
284
285 recvd = req->rq_rcv_buf.len - hdrlen;
286 if (count > recvd) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400287 dprintk("NFS: server cheating in read reply: "
Chuck Lever6232dbb2007-10-26 13:31:52 -0400288 "count %u > recvd %u\n", count, recvd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 count = recvd;
290 }
291
Chuck Lever6232dbb2007-10-26 13:31:52 -0400292 dprintk("RPC: readres OK count %u\n", count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 if (count < res->count)
294 res->count = count;
295
296 return count;
297}
298
299
300/*
301 * Write arguments. Splice the buffer to be written into the iovec.
302 */
303static int
Al Viro9d787a72006-10-19 23:28:47 -0700304nfs_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305{
306 struct xdr_buf *sndbuf = &req->rq_snd_buf;
307 u32 offset = (u32)args->offset;
308 u32 count = args->count;
309
310 p = xdr_encode_fhandle(p, args->fh);
311 *p++ = htonl(offset);
312 *p++ = htonl(offset);
313 *p++ = htonl(count);
314 *p++ = htonl(count);
315 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
316
317 /* Copy the page array */
318 xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400319 sndbuf->flags |= XDRBUF_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 return 0;
321}
322
323/*
324 * Encode create arguments
325 * CREATE, MKDIR
326 */
327static int
Al Viro9d787a72006-10-19 23:28:47 -0700328nfs_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs_createargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329{
330 p = xdr_encode_fhandle(p, args->fh);
331 p = xdr_encode_array(p, args->name, args->len);
332 p = xdr_encode_sattr(p, args->sattr);
333 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
334 return 0;
335}
336
337/*
338 * Encode RENAME arguments
339 */
340static int
Al Viro9d787a72006-10-19 23:28:47 -0700341nfs_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342{
343 p = xdr_encode_fhandle(p, args->fromfh);
344 p = xdr_encode_array(p, args->fromname, args->fromlen);
345 p = xdr_encode_fhandle(p, args->tofh);
346 p = xdr_encode_array(p, args->toname, args->tolen);
347 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
348 return 0;
349}
350
351/*
352 * Encode LINK arguments
353 */
354static int
Al Viro9d787a72006-10-19 23:28:47 -0700355nfs_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs_linkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356{
357 p = xdr_encode_fhandle(p, args->fromfh);
358 p = xdr_encode_fhandle(p, args->tofh);
359 p = xdr_encode_array(p, args->toname, args->tolen);
360 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
361 return 0;
362}
363
364/*
365 * Encode SYMLINK arguments
366 */
367static int
Al Viro9d787a72006-10-19 23:28:47 -0700368nfs_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_symlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369{
Chuck Lever94a6d752006-08-22 20:06:23 -0400370 struct xdr_buf *sndbuf = &req->rq_snd_buf;
371 size_t pad;
372
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 p = xdr_encode_fhandle(p, args->fromfh);
374 p = xdr_encode_array(p, args->fromname, args->fromlen);
Chuck Lever94a6d752006-08-22 20:06:23 -0400375 *p++ = htonl(args->pathlen);
376 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
377
378 xdr_encode_pages(sndbuf, args->pages, 0, args->pathlen);
379
380 /*
381 * xdr_encode_pages may have added a few bytes to ensure the
382 * pathname ends on a 4-byte boundary. Start encoding the
383 * attributes after the pad bytes.
384 */
385 pad = sndbuf->tail->iov_len;
386 if (pad > 0)
387 p++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 p = xdr_encode_sattr(p, args->sattr);
Chuck Lever94a6d752006-08-22 20:06:23 -0400389 sndbuf->len += xdr_adjust_iovec(sndbuf->tail, p) - pad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 return 0;
391}
392
393/*
394 * Encode arguments to readdir call
395 */
396static int
Al Viro9d787a72006-10-19 23:28:47 -0700397nfs_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs_readdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398{
399 struct rpc_task *task = req->rq_task;
Trond Myklebust1be27f32007-06-27 14:29:04 -0400400 struct rpc_auth *auth = task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 unsigned int replen;
402 u32 count = args->count;
403
404 p = xdr_encode_fhandle(p, args->fh);
405 *p++ = htonl(args->cookie);
406 *p++ = htonl(count); /* see above */
407 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
408
409 /* Inline the page array */
410 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
411 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
412 return 0;
413}
414
415/*
416 * Decode the result of a readdir call.
417 * We're not really decoding anymore, we just leave the buffer untouched
418 * and only check that it is syntactically correct.
419 * The real decoding happens in nfs_decode_entry below, called directly
420 * from nfs_readdir for each entry.
421 */
422static int
Al Viro9d787a72006-10-19 23:28:47 -0700423nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424{
425 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
426 struct kvec *iov = rcvbuf->head;
427 struct page **page;
Chuck Lever6232dbb2007-10-26 13:31:52 -0400428 size_t hdrlen;
429 unsigned int pglen, recvd;
430 u32 len;
Jeff Laytoncaa02bd2008-02-22 14:49:59 -0500431 int status, nr = 0;
Al Viro9d787a72006-10-19 23:28:47 -0700432 __be32 *end, *entry, *kaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433
434 if ((status = ntohl(*p++)))
435 return -nfs_stat_to_errno(status);
436
437 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
438 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400439 dprintk("NFS: READDIR reply header overflowed:"
Chuck Lever6232dbb2007-10-26 13:31:52 -0400440 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 return -errno_NFSERR_IO;
442 } else if (iov->iov_len != hdrlen) {
443 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
444 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
445 }
446
447 pglen = rcvbuf->page_len;
448 recvd = rcvbuf->len - hdrlen;
449 if (pglen > recvd)
450 pglen = recvd;
451 page = rcvbuf->pages;
Al Viro9d787a72006-10-19 23:28:47 -0700452 kaddr = p = kmap_atomic(*page, KM_USER0);
453 end = (__be32 *)((char *)p + pglen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 entry = p;
Jeff Laytoncaa02bd2008-02-22 14:49:59 -0500455
456 /* Make sure the packet actually has a value_follows and EOF entry */
457 if ((entry + 1) > end)
458 goto short_pkt;
459
460 for (; *p++; nr++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 if (p + 2 > end)
462 goto short_pkt;
463 p++; /* fileid */
464 len = ntohl(*p++);
465 p += XDR_QUADLEN(len) + 1; /* name plus cookie */
466 if (len > NFS2_MAXNAMLEN) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400467 dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 len);
469 goto err_unmap;
470 }
471 if (p + 2 > end)
472 goto short_pkt;
473 entry = p;
474 }
Jeff Laytoncaa02bd2008-02-22 14:49:59 -0500475
476 /*
477 * Apparently some server sends responses that are a valid size, but
478 * contain no entries, and have value_follows==0 and EOF==0. For
479 * those, just set the EOF marker.
480 */
481 if (!nr && entry[1] == 0) {
482 dprintk("NFS: readdir reply truncated!\n");
483 entry[1] = 1;
484 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 out:
486 kunmap_atomic(kaddr, KM_USER0);
487 return nr;
488 short_pkt:
Jeff Laytoncaa02bd2008-02-22 14:49:59 -0500489 /*
490 * When we get a short packet there are 2 possibilities. We can
491 * return an error, or fix up the response to look like a valid
492 * response and return what we have so far. If there are no
493 * entries and the packet was short, then return -EIO. If there
494 * are valid entries in the response, return them and pretend that
495 * the call was successful, but incomplete. The caller can retry the
496 * readdir starting at the last cookie.
497 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 entry[0] = entry[1] = 0;
Jeff Laytoncaa02bd2008-02-22 14:49:59 -0500499 if (!nr)
500 nr = -errno_NFSERR_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 goto out;
502err_unmap:
503 nr = -errno_NFSERR_IO;
504 goto out;
505}
506
Al Viro0dbb4c62006-10-19 23:28:49 -0700507__be32 *
508nfs_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509{
510 if (!*p++) {
511 if (!*p)
512 return ERR_PTR(-EAGAIN);
513 entry->eof = 1;
514 return ERR_PTR(-EBADCOOKIE);
515 }
516
517 entry->ino = ntohl(*p++);
518 entry->len = ntohl(*p++);
519 entry->name = (const char *) p;
520 p += XDR_QUADLEN(entry->len);
521 entry->prev_cookie = entry->cookie;
522 entry->cookie = ntohl(*p++);
523 entry->eof = !p[0] && p[1];
524
525 return p;
526}
527
528/*
529 * NFS XDR decode functions
530 */
531/*
532 * Decode simple status reply
533 */
534static int
Al Viro9d787a72006-10-19 23:28:47 -0700535nfs_xdr_stat(struct rpc_rqst *req, __be32 *p, void *dummy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536{
537 int status;
538
539 if ((status = ntohl(*p++)) != 0)
540 status = -nfs_stat_to_errno(status);
541 return status;
542}
543
544/*
545 * Decode attrstat reply
546 * GETATTR, SETATTR, WRITE
547 */
548static int
Al Viro9d787a72006-10-19 23:28:47 -0700549nfs_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550{
551 int status;
552
553 if ((status = ntohl(*p++)))
554 return -nfs_stat_to_errno(status);
555 xdr_decode_fattr(p, fattr);
556 return 0;
557}
558
559/*
560 * Decode diropres reply
561 * LOOKUP, CREATE, MKDIR
562 */
563static int
Al Viro9d787a72006-10-19 23:28:47 -0700564nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
566 int status;
567
568 if ((status = ntohl(*p++)))
569 return -nfs_stat_to_errno(status);
570 p = xdr_decode_fhandle(p, res->fh);
571 xdr_decode_fattr(p, res->fattr);
572 return 0;
573}
574
575/*
576 * Encode READLINK args
577 */
578static int
Al Viro9d787a72006-10-19 23:28:47 -0700579nfs_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_readlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400581 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 unsigned int replen;
583
584 p = xdr_encode_fhandle(p, args->fh);
585 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
586
587 /* Inline the page array */
588 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
589 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
590 return 0;
591}
592
593/*
594 * Decode READLINK reply
595 */
596static int
Al Viro9d787a72006-10-19 23:28:47 -0700597nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598{
599 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
600 struct kvec *iov = rcvbuf->head;
Chuck Lever6232dbb2007-10-26 13:31:52 -0400601 size_t hdrlen;
602 u32 len, recvd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 char *kaddr;
604 int status;
605
606 if ((status = ntohl(*p++)))
607 return -nfs_stat_to_errno(status);
608 /* Convert length of symlink */
609 len = ntohl(*p++);
Chuck Lever6232dbb2007-10-26 13:31:52 -0400610 if (len >= rcvbuf->page_len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400611 dprintk("nfs: server returned giant symlink!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 return -ENAMETOOLONG;
613 }
614 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
615 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400616 dprintk("NFS: READLINK reply header overflowed:"
Chuck Lever6232dbb2007-10-26 13:31:52 -0400617 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 return -errno_NFSERR_IO;
619 } else if (iov->iov_len != hdrlen) {
620 dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
621 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
622 }
623 recvd = req->rq_rcv_buf.len - hdrlen;
624 if (recvd < len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400625 dprintk("NFS: server cheating in readlink reply: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 "count %u > recvd %u\n", len, recvd);
627 return -EIO;
628 }
629
630 /* NULL terminate the string we got */
631 kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0);
632 kaddr[len+rcvbuf->page_base] = '\0';
633 kunmap_atomic(kaddr, KM_USER0);
634 return 0;
635}
636
637/*
638 * Decode WRITE reply
639 */
640static int
Al Viro9d787a72006-10-19 23:28:47 -0700641nfs_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642{
643 res->verf->committed = NFS_FILE_SYNC;
644 return nfs_xdr_attrstat(req, p, res->fattr);
645}
646
647/*
648 * Decode STATFS reply
649 */
650static int
Al Viro9d787a72006-10-19 23:28:47 -0700651nfs_xdr_statfsres(struct rpc_rqst *req, __be32 *p, struct nfs2_fsstat *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652{
653 int status;
654
655 if ((status = ntohl(*p++)))
656 return -nfs_stat_to_errno(status);
657
658 res->tsize = ntohl(*p++);
659 res->bsize = ntohl(*p++);
660 res->blocks = ntohl(*p++);
661 res->bfree = ntohl(*p++);
662 res->bavail = ntohl(*p++);
663 return 0;
664}
665
666/*
667 * We need to translate between nfs status return values and
668 * the local errno values which may not be the same.
669 */
670static struct {
671 int stat;
672 int errno;
673} nfs_errtbl[] = {
674 { NFS_OK, 0 },
675 { NFSERR_PERM, EPERM },
676 { NFSERR_NOENT, ENOENT },
677 { NFSERR_IO, errno_NFSERR_IO },
678 { NFSERR_NXIO, ENXIO },
679/* { NFSERR_EAGAIN, EAGAIN }, */
680 { NFSERR_ACCES, EACCES },
681 { NFSERR_EXIST, EEXIST },
682 { NFSERR_XDEV, EXDEV },
683 { NFSERR_NODEV, ENODEV },
684 { NFSERR_NOTDIR, ENOTDIR },
685 { NFSERR_ISDIR, EISDIR },
686 { NFSERR_INVAL, EINVAL },
687 { NFSERR_FBIG, EFBIG },
688 { NFSERR_NOSPC, ENOSPC },
689 { NFSERR_ROFS, EROFS },
690 { NFSERR_MLINK, EMLINK },
691 { NFSERR_NAMETOOLONG, ENAMETOOLONG },
692 { NFSERR_NOTEMPTY, ENOTEMPTY },
693 { NFSERR_DQUOT, EDQUOT },
694 { NFSERR_STALE, ESTALE },
695 { NFSERR_REMOTE, EREMOTE },
696#ifdef EWFLUSH
697 { NFSERR_WFLUSH, EWFLUSH },
698#endif
699 { NFSERR_BADHANDLE, EBADHANDLE },
700 { NFSERR_NOT_SYNC, ENOTSYNC },
701 { NFSERR_BAD_COOKIE, EBADCOOKIE },
702 { NFSERR_NOTSUPP, ENOTSUPP },
703 { NFSERR_TOOSMALL, ETOOSMALL },
704 { NFSERR_SERVERFAULT, ESERVERFAULT },
705 { NFSERR_BADTYPE, EBADTYPE },
706 { NFSERR_JUKEBOX, EJUKEBOX },
707 { -1, EIO }
708};
709
710/*
711 * Convert an NFS error code to a local one.
712 * This one is used jointly by NFSv2 and NFSv3.
713 */
714int
715nfs_stat_to_errno(int stat)
716{
717 int i;
718
719 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
720 if (nfs_errtbl[i].stat == stat)
721 return nfs_errtbl[i].errno;
722 }
Chuck Leverfe82a182007-09-11 18:01:10 -0400723 dprintk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 return nfs_errtbl[i].errno;
725}
726
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727#define PROC(proc, argtype, restype, timer) \
728[NFSPROC_##proc] = { \
729 .p_proc = NFSPROC_##proc, \
730 .p_encode = (kxdrproc_t) nfs_xdr_##argtype, \
731 .p_decode = (kxdrproc_t) nfs_xdr_##restype, \
Chuck Lever2bea90d2007-03-29 16:47:53 -0400732 .p_arglen = NFS_##argtype##_sz, \
733 .p_replen = NFS_##restype##_sz, \
Chuck Levercc0175c2006-03-20 13:44:22 -0500734 .p_timer = timer, \
735 .p_statidx = NFSPROC_##proc, \
736 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 }
738struct rpc_procinfo nfs_procedures[] = {
739 PROC(GETATTR, fhandle, attrstat, 1),
740 PROC(SETATTR, sattrargs, attrstat, 0),
741 PROC(LOOKUP, diropargs, diropres, 2),
742 PROC(READLINK, readlinkargs, readlinkres, 3),
743 PROC(READ, readargs, readres, 3),
744 PROC(WRITE, writeargs, writeres, 4),
745 PROC(CREATE, createargs, diropres, 0),
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400746 PROC(REMOVE, removeargs, stat, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 PROC(RENAME, renameargs, stat, 0),
748 PROC(LINK, linkargs, stat, 0),
749 PROC(SYMLINK, symlinkargs, stat, 0),
750 PROC(MKDIR, createargs, diropres, 0),
751 PROC(RMDIR, diropargs, stat, 0),
752 PROC(READDIR, readdirargs, readdirres, 3),
753 PROC(STATFS, fhandle, statfsres, 0),
754};
755
756struct rpc_version nfs_version2 = {
757 .number = 2,
Tobias Klausere8c96f82006-03-24 03:15:34 -0800758 .nrprocs = ARRAY_SIZE(nfs_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 .procs = nfs_procedures
760};