blob: 6cdeacffde469143045588c6b20e6cff72cc586a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/fs/nfs/nfs3xdr.c
3 *
4 * XDR functions to encode/decode NFSv3 RPC arguments and results.
5 *
6 * Copyright (C) 1996, 1997 Olaf Kirch
7 */
8
9#include <linux/param.h>
10#include <linux/time.h>
11#include <linux/mm.h>
12#include <linux/slab.h>
13#include <linux/utsname.h>
14#include <linux/errno.h>
15#include <linux/string.h>
16#include <linux/in.h>
17#include <linux/pagemap.h>
18#include <linux/proc_fs.h>
19#include <linux/kdev_t.h>
20#include <linux/sunrpc/clnt.h>
21#include <linux/nfs.h>
22#include <linux/nfs3.h>
23#include <linux/nfs_fs.h>
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000024#include <linux/nfsacl.h>
David Howellsf7b422b2006-06-09 09:34:33 -040025#include "internal.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
27#define NFSDBG_FACILITY NFSDBG_XDR
28
29/* Mapping from NFS error code to "errno" error code. */
30#define errno_NFSERR_IO EIO
31
Linus Torvalds1da177e2005-04-16 15:20:36 -070032/*
33 * Declare the space requirements for NFS arguments and replies as
34 * number of 32bit-words
35 */
36#define NFS3_fhandle_sz (1+16)
37#define NFS3_fh_sz (NFS3_fhandle_sz) /* shorthand */
38#define NFS3_sattr_sz (15)
39#define NFS3_filename_sz (1+(NFS3_MAXNAMLEN>>2))
40#define NFS3_path_sz (1+(NFS3_MAXPATHLEN>>2))
41#define NFS3_fattr_sz (21)
42#define NFS3_wcc_attr_sz (6)
43#define NFS3_pre_op_attr_sz (1+NFS3_wcc_attr_sz)
44#define NFS3_post_op_attr_sz (1+NFS3_fattr_sz)
45#define NFS3_wcc_data_sz (NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
46#define NFS3_fsstat_sz
47#define NFS3_fsinfo_sz
48#define NFS3_pathconf_sz
49#define NFS3_entry_sz (NFS3_filename_sz+3)
50
51#define NFS3_sattrargs_sz (NFS3_fh_sz+NFS3_sattr_sz+3)
52#define NFS3_diropargs_sz (NFS3_fh_sz+NFS3_filename_sz)
Trond Myklebust4fdc17b2007-07-14 15:39:57 -040053#define NFS3_removeargs_sz (NFS3_fh_sz+NFS3_filename_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#define NFS3_accessargs_sz (NFS3_fh_sz+1)
55#define NFS3_readlinkargs_sz (NFS3_fh_sz)
56#define NFS3_readargs_sz (NFS3_fh_sz+3)
57#define NFS3_writeargs_sz (NFS3_fh_sz+5)
58#define NFS3_createargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
59#define NFS3_mkdirargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
Chuck Lever94a6d752006-08-22 20:06:23 -040060#define NFS3_symlinkargs_sz (NFS3_diropargs_sz+1+NFS3_sattr_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#define NFS3_mknodargs_sz (NFS3_diropargs_sz+2+NFS3_sattr_sz)
62#define NFS3_renameargs_sz (NFS3_diropargs_sz+NFS3_diropargs_sz)
63#define NFS3_linkargs_sz (NFS3_fh_sz+NFS3_diropargs_sz)
64#define NFS3_readdirargs_sz (NFS3_fh_sz+2)
65#define NFS3_commitargs_sz (NFS3_fh_sz+3)
66
67#define NFS3_attrstat_sz (1+NFS3_fattr_sz)
68#define NFS3_wccstat_sz (1+NFS3_wcc_data_sz)
Trond Myklebust4fdc17b2007-07-14 15:39:57 -040069#define NFS3_removeres_sz (NFS3_wccstat_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#define NFS3_lookupres_sz (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
71#define NFS3_accessres_sz (1+NFS3_post_op_attr_sz+1)
72#define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz+1)
73#define NFS3_readres_sz (1+NFS3_post_op_attr_sz+3)
74#define NFS3_writeres_sz (1+NFS3_wcc_data_sz+4)
75#define NFS3_createres_sz (1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
76#define NFS3_renameres_sz (1+(2 * NFS3_wcc_data_sz))
77#define NFS3_linkres_sz (1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
78#define NFS3_readdirres_sz (1+NFS3_post_op_attr_sz+2)
79#define NFS3_fsstatres_sz (1+NFS3_post_op_attr_sz+13)
80#define NFS3_fsinfores_sz (1+NFS3_post_op_attr_sz+12)
81#define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6)
82#define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2)
83
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000084#define ACL3_getaclargs_sz (NFS3_fh_sz+1)
Trond Myklebustae461412009-03-10 20:33:18 -040085#define ACL3_setaclargs_sz (NFS3_fh_sz+1+ \
86 XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
87#define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+ \
88 XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000089#define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz)
90
Linus Torvalds1da177e2005-04-16 15:20:36 -070091/*
92 * Map file type to S_IFMT bits
93 */
94static struct {
95 unsigned int mode;
96 unsigned int nfs2type;
97} nfs_type2fmt[] = {
98 { 0, NFNON },
99 { S_IFREG, NFREG },
100 { S_IFDIR, NFDIR },
101 { S_IFBLK, NFBLK },
102 { S_IFCHR, NFCHR },
103 { S_IFLNK, NFLNK },
104 { S_IFSOCK, NFSOCK },
105 { S_IFIFO, NFFIFO },
106 { 0, NFBAD }
107};
108
109/*
110 * Common NFS XDR functions as inlines
111 */
Al Virod61005a2006-10-19 23:28:48 -0700112static inline __be32 *
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400113xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114{
115 return xdr_encode_array(p, fh->data, fh->size);
116}
117
Al Virod61005a2006-10-19 23:28:48 -0700118static inline __be32 *
119xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120{
121 if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
122 memcpy(fh->data, p, fh->size);
123 return p + XDR_QUADLEN(fh->size);
124 }
125 return NULL;
126}
127
128/*
129 * Encode/decode time.
130 */
Al Virod61005a2006-10-19 23:28:48 -0700131static inline __be32 *
132xdr_encode_time3(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133{
134 *p++ = htonl(timep->tv_sec);
135 *p++ = htonl(timep->tv_nsec);
136 return p;
137}
138
Al Virod61005a2006-10-19 23:28:48 -0700139static inline __be32 *
140xdr_decode_time3(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141{
142 timep->tv_sec = ntohl(*p++);
143 timep->tv_nsec = ntohl(*p++);
144 return p;
145}
146
Al Virod61005a2006-10-19 23:28:48 -0700147static __be32 *
148xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149{
150 unsigned int type, major, minor;
151 int fmode;
152
153 type = ntohl(*p++);
154 if (type >= NF3BAD)
155 type = NF3BAD;
156 fmode = nfs_type2fmt[type].mode;
157 fattr->type = nfs_type2fmt[type].nfs2type;
158 fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
159 fattr->nlink = ntohl(*p++);
160 fattr->uid = ntohl(*p++);
161 fattr->gid = ntohl(*p++);
162 p = xdr_decode_hyper(p, &fattr->size);
163 p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
164
165 /* Turn remote device info into Linux-specific dev_t */
166 major = ntohl(*p++);
167 minor = ntohl(*p++);
168 fattr->rdev = MKDEV(major, minor);
169 if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor)
170 fattr->rdev = 0;
171
Trond Myklebust8b4bdcf2006-06-09 09:34:19 -0400172 p = xdr_decode_hyper(p, &fattr->fsid.major);
173 fattr->fsid.minor = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 p = xdr_decode_hyper(p, &fattr->fileid);
175 p = xdr_decode_time3(p, &fattr->atime);
176 p = xdr_decode_time3(p, &fattr->mtime);
177 p = xdr_decode_time3(p, &fattr->ctime);
178
179 /* Update the mode bits */
180 fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 return p;
182}
183
Al Virod61005a2006-10-19 23:28:48 -0700184static inline __be32 *
185xdr_encode_sattr(__be32 *p, struct iattr *attr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186{
187 if (attr->ia_valid & ATTR_MODE) {
188 *p++ = xdr_one;
Trond Myklebustcf3fff52006-01-03 09:55:53 +0100189 *p++ = htonl(attr->ia_mode & S_IALLUGO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 } else {
191 *p++ = xdr_zero;
192 }
193 if (attr->ia_valid & ATTR_UID) {
194 *p++ = xdr_one;
195 *p++ = htonl(attr->ia_uid);
196 } else {
197 *p++ = xdr_zero;
198 }
199 if (attr->ia_valid & ATTR_GID) {
200 *p++ = xdr_one;
201 *p++ = htonl(attr->ia_gid);
202 } else {
203 *p++ = xdr_zero;
204 }
205 if (attr->ia_valid & ATTR_SIZE) {
206 *p++ = xdr_one;
207 p = xdr_encode_hyper(p, (__u64) attr->ia_size);
208 } else {
209 *p++ = xdr_zero;
210 }
211 if (attr->ia_valid & ATTR_ATIME_SET) {
212 *p++ = xdr_two;
213 p = xdr_encode_time3(p, &attr->ia_atime);
214 } else if (attr->ia_valid & ATTR_ATIME) {
215 *p++ = xdr_one;
216 } else {
217 *p++ = xdr_zero;
218 }
219 if (attr->ia_valid & ATTR_MTIME_SET) {
220 *p++ = xdr_two;
221 p = xdr_encode_time3(p, &attr->ia_mtime);
222 } else if (attr->ia_valid & ATTR_MTIME) {
223 *p++ = xdr_one;
224 } else {
225 *p++ = xdr_zero;
226 }
227 return p;
228}
229
Al Virod61005a2006-10-19 23:28:48 -0700230static inline __be32 *
231xdr_decode_wcc_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232{
233 p = xdr_decode_hyper(p, &fattr->pre_size);
234 p = xdr_decode_time3(p, &fattr->pre_mtime);
235 p = xdr_decode_time3(p, &fattr->pre_ctime);
236 fattr->valid |= NFS_ATTR_WCC;
237 return p;
238}
239
Al Virod61005a2006-10-19 23:28:48 -0700240static inline __be32 *
241xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
243 if (*p++)
244 p = xdr_decode_fattr(p, fattr);
245 return p;
246}
247
Al Virod61005a2006-10-19 23:28:48 -0700248static inline __be32 *
249xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250{
251 if (*p++)
252 return xdr_decode_wcc_attr(p, fattr);
253 return p;
254}
255
256
Al Virod61005a2006-10-19 23:28:48 -0700257static inline __be32 *
258xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259{
260 p = xdr_decode_pre_op_attr(p, fattr);
261 return xdr_decode_post_op_attr(p, fattr);
262}
263
264/*
265 * NFS encode functions
266 */
267
268/*
269 * Encode file handle argument
270 */
271static int
Al Virod61005a2006-10-19 23:28:48 -0700272nfs3_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273{
274 p = xdr_encode_fhandle(p, fh);
275 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
276 return 0;
277}
278
279/*
280 * Encode SETATTR arguments
281 */
282static int
Al Virod61005a2006-10-19 23:28:48 -0700283nfs3_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs3_sattrargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284{
285 p = xdr_encode_fhandle(p, args->fh);
286 p = xdr_encode_sattr(p, args->sattr);
287 *p++ = htonl(args->guard);
288 if (args->guard)
289 p = xdr_encode_time3(p, &args->guardtime);
290 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
291 return 0;
292}
293
294/*
295 * Encode directory ops argument
296 */
297static int
Al Virod61005a2006-10-19 23:28:48 -0700298nfs3_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs3_diropargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299{
300 p = xdr_encode_fhandle(p, args->fh);
301 p = xdr_encode_array(p, args->name, args->len);
302 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
303 return 0;
304}
305
306/*
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400307 * Encode REMOVE argument
308 */
309static int
310nfs3_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
311{
312 p = xdr_encode_fhandle(p, args->fh);
313 p = xdr_encode_array(p, args->name.name, args->name.len);
314 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
315 return 0;
316}
317
318/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 * Encode access() argument
320 */
321static int
Al Virod61005a2006-10-19 23:28:48 -0700322nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323{
324 p = xdr_encode_fhandle(p, args->fh);
325 *p++ = htonl(args->access);
326 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
327 return 0;
328}
329
330/*
331 * Arguments to a READ call. Since we read data directly into the page
332 * cache, we also set up the reply iovec here so that iov[1] points
333 * exactly to the page we want to fetch.
334 */
335static int
Al Virod61005a2006-10-19 23:28:48 -0700336nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400338 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 unsigned int replen;
340 u32 count = args->count;
341
342 p = xdr_encode_fhandle(p, args->fh);
343 p = xdr_encode_hyper(p, args->offset);
344 *p++ = htonl(count);
345 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
346
347 /* Inline the page array */
348 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
349 xdr_inline_pages(&req->rq_rcv_buf, replen,
350 args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400351 req->rq_rcv_buf.flags |= XDRBUF_READ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 return 0;
353}
354
355/*
356 * Write arguments. Splice the buffer to be written into the iovec.
357 */
358static int
Al Virod61005a2006-10-19 23:28:48 -0700359nfs3_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360{
361 struct xdr_buf *sndbuf = &req->rq_snd_buf;
362 u32 count = args->count;
363
364 p = xdr_encode_fhandle(p, args->fh);
365 p = xdr_encode_hyper(p, args->offset);
366 *p++ = htonl(count);
367 *p++ = htonl(args->stable);
368 *p++ = htonl(count);
369 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
370
371 /* Copy the page array */
372 xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400373 sndbuf->flags |= XDRBUF_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 return 0;
375}
376
377/*
378 * Encode CREATE arguments
379 */
380static int
Al Virod61005a2006-10-19 23:28:48 -0700381nfs3_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs3_createargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382{
383 p = xdr_encode_fhandle(p, args->fh);
384 p = xdr_encode_array(p, args->name, args->len);
385
386 *p++ = htonl(args->createmode);
387 if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
388 *p++ = args->verifier[0];
389 *p++ = args->verifier[1];
390 } else
391 p = xdr_encode_sattr(p, args->sattr);
392
393 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
394 return 0;
395}
396
397/*
398 * Encode MKDIR arguments
399 */
400static int
Al Virod61005a2006-10-19 23:28:48 -0700401nfs3_xdr_mkdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mkdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402{
403 p = xdr_encode_fhandle(p, args->fh);
404 p = xdr_encode_array(p, args->name, args->len);
405 p = xdr_encode_sattr(p, args->sattr);
406 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
407 return 0;
408}
409
410/*
411 * Encode SYMLINK arguments
412 */
413static int
Al Virod61005a2006-10-19 23:28:48 -0700414nfs3_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_symlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415{
416 p = xdr_encode_fhandle(p, args->fromfh);
417 p = xdr_encode_array(p, args->fromname, args->fromlen);
418 p = xdr_encode_sattr(p, args->sattr);
Chuck Lever94a6d752006-08-22 20:06:23 -0400419 *p++ = htonl(args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
Chuck Lever94a6d752006-08-22 20:06:23 -0400421
422 /* Copy the page */
423 xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 return 0;
425}
426
427/*
428 * Encode MKNOD arguments
429 */
430static int
Al Virod61005a2006-10-19 23:28:48 -0700431nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432{
433 p = xdr_encode_fhandle(p, args->fh);
434 p = xdr_encode_array(p, args->name, args->len);
435 *p++ = htonl(args->type);
436 p = xdr_encode_sattr(p, args->sattr);
437 if (args->type == NF3CHR || args->type == NF3BLK) {
438 *p++ = htonl(MAJOR(args->rdev));
439 *p++ = htonl(MINOR(args->rdev));
440 }
441
442 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
443 return 0;
444}
445
446/*
447 * Encode RENAME arguments
448 */
449static int
Al Virod61005a2006-10-19 23:28:48 -0700450nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs3_renameargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451{
452 p = xdr_encode_fhandle(p, args->fromfh);
453 p = xdr_encode_array(p, args->fromname, args->fromlen);
454 p = xdr_encode_fhandle(p, args->tofh);
455 p = xdr_encode_array(p, args->toname, args->tolen);
456 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
457 return 0;
458}
459
460/*
461 * Encode LINK arguments
462 */
463static int
Al Virod61005a2006-10-19 23:28:48 -0700464nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465{
466 p = xdr_encode_fhandle(p, args->fromfh);
467 p = xdr_encode_fhandle(p, args->tofh);
468 p = xdr_encode_array(p, args->toname, args->tolen);
469 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
470 return 0;
471}
472
473/*
474 * Encode arguments to readdir call
475 */
476static int
Al Virod61005a2006-10-19 23:28:48 -0700477nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400479 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 unsigned int replen;
481 u32 count = args->count;
482
483 p = xdr_encode_fhandle(p, args->fh);
484 p = xdr_encode_hyper(p, args->cookie);
485 *p++ = args->verf[0];
486 *p++ = args->verf[1];
487 if (args->plus) {
488 /* readdirplus: need dircount + buffer size.
489 * We just make sure we make dircount big enough */
490 *p++ = htonl(count >> 3);
491 }
492 *p++ = htonl(count);
493 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
494
495 /* Inline the page array */
496 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
497 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
498 return 0;
499}
500
501/*
502 * Decode the result of a readdir call.
503 * We just check for syntactical correctness.
504 */
505static int
Al Virod61005a2006-10-19 23:28:48 -0700506nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507{
508 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
509 struct kvec *iov = rcvbuf->head;
510 struct page **page;
Chuck Leverc957c522007-10-26 13:31:57 -0400511 size_t hdrlen;
512 u32 len, recvd, pglen;
Jeff Layton643f8112008-02-22 14:50:00 -0500513 int status, nr = 0;
Al Virod61005a2006-10-19 23:28:48 -0700514 __be32 *entry, *end, *kaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
516 status = ntohl(*p++);
517 /* Decode post_op_attrs */
518 p = xdr_decode_post_op_attr(p, res->dir_attr);
519 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300520 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 /* Decode verifier cookie */
522 if (res->verf) {
523 res->verf[0] = *p++;
524 res->verf[1] = *p++;
525 } else {
526 p += 2;
527 }
528
529 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
530 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400531 dprintk("NFS: READDIR reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400532 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 return -errno_NFSERR_IO;
534 } else if (iov->iov_len != hdrlen) {
535 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
536 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
537 }
538
539 pglen = rcvbuf->page_len;
540 recvd = rcvbuf->len - hdrlen;
541 if (pglen > recvd)
542 pglen = recvd;
543 page = rcvbuf->pages;
Al Virod61005a2006-10-19 23:28:48 -0700544 kaddr = p = kmap_atomic(*page, KM_USER0);
545 end = (__be32 *)((char *)p + pglen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 entry = p;
Jeff Layton643f8112008-02-22 14:50:00 -0500547
548 /* Make sure the packet actually has a value_follows and EOF entry */
549 if ((entry + 1) > end)
550 goto short_pkt;
551
552 for (; *p++; nr++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 if (p + 3 > end)
554 goto short_pkt;
555 p += 2; /* inode # */
556 len = ntohl(*p++); /* string length */
557 p += XDR_QUADLEN(len) + 2; /* name + cookie */
558 if (len > NFS3_MAXNAMLEN) {
Chuck Leverc957c522007-10-26 13:31:57 -0400559 dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 len);
561 goto err_unmap;
562 }
563
564 if (res->plus) {
565 /* post_op_attr */
566 if (p + 2 > end)
567 goto short_pkt;
568 if (*p++) {
569 p += 21;
570 if (p + 1 > end)
571 goto short_pkt;
572 }
573 /* post_op_fh3 */
574 if (*p++) {
575 if (p + 1 > end)
576 goto short_pkt;
577 len = ntohl(*p++);
578 if (len > NFS3_FHSIZE) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400579 dprintk("NFS: giant filehandle in "
Chuck Leverc957c522007-10-26 13:31:57 -0400580 "readdir (len 0x%x)!\n", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 goto err_unmap;
582 }
583 p += XDR_QUADLEN(len);
584 }
585 }
586
587 if (p + 2 > end)
588 goto short_pkt;
589 entry = p;
590 }
Jeff Layton643f8112008-02-22 14:50:00 -0500591
592 /*
593 * Apparently some server sends responses that are a valid size, but
594 * contain no entries, and have value_follows==0 and EOF==0. For
595 * those, just set the EOF marker.
596 */
597 if (!nr && entry[1] == 0) {
598 dprintk("NFS: readdir reply truncated!\n");
599 entry[1] = 1;
600 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 out:
602 kunmap_atomic(kaddr, KM_USER0);
603 return nr;
604 short_pkt:
Jeff Layton643f8112008-02-22 14:50:00 -0500605 /*
606 * When we get a short packet there are 2 possibilities. We can
607 * return an error, or fix up the response to look like a valid
608 * response and return what we have so far. If there are no
609 * entries and the packet was short, then return -EIO. If there
610 * are valid entries in the response, return them and pretend that
611 * the call was successful, but incomplete. The caller can retry the
612 * readdir starting at the last cookie.
613 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 entry[0] = entry[1] = 0;
Jeff Layton643f8112008-02-22 14:50:00 -0500615 if (!nr)
616 nr = -errno_NFSERR_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 goto out;
618err_unmap:
619 nr = -errno_NFSERR_IO;
620 goto out;
621}
622
Al Viro0dbb4c62006-10-19 23:28:49 -0700623__be32 *
624nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625{
626 struct nfs_entry old = *entry;
627
628 if (!*p++) {
629 if (!*p)
630 return ERR_PTR(-EAGAIN);
631 entry->eof = 1;
632 return ERR_PTR(-EBADCOOKIE);
633 }
634
635 p = xdr_decode_hyper(p, &entry->ino);
636 entry->len = ntohl(*p++);
637 entry->name = (const char *) p;
638 p += XDR_QUADLEN(entry->len);
639 entry->prev_cookie = entry->cookie;
640 p = xdr_decode_hyper(p, &entry->cookie);
641
642 if (plus) {
643 entry->fattr->valid = 0;
644 p = xdr_decode_post_op_attr(p, entry->fattr);
645 /* In fact, a post_op_fh3: */
646 if (*p++) {
647 p = xdr_decode_fhandle(p, entry->fh);
648 /* Ugh -- server reply was truncated */
649 if (p == NULL) {
650 dprintk("NFS: FH truncated\n");
651 *entry = old;
652 return ERR_PTR(-EAGAIN);
653 }
654 } else
655 memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
656 }
657
658 entry->eof = !p[0] && p[1];
659 return p;
660}
661
662/*
663 * Encode COMMIT arguments
664 */
665static int
Al Virod61005a2006-10-19 23:28:48 -0700666nfs3_xdr_commitargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667{
668 p = xdr_encode_fhandle(p, args->fh);
669 p = xdr_encode_hyper(p, args->offset);
670 *p++ = htonl(args->count);
671 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
672 return 0;
673}
674
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000675#ifdef CONFIG_NFS_V3_ACL
676/*
677 * Encode GETACL arguments
678 */
679static int
Al Virod61005a2006-10-19 23:28:48 -0700680nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000681 struct nfs3_getaclargs *args)
682{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400683 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000684 unsigned int replen;
685
686 p = xdr_encode_fhandle(p, args->fh);
687 *p++ = htonl(args->mask);
688 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
689
690 if (args->mask & (NFS_ACL | NFS_DFACL)) {
691 /* Inline the page array */
692 replen = (RPC_REPHDRSIZE + auth->au_rslack +
693 ACL3_getaclres_sz) << 2;
694 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
695 NFSACL_MAXPAGES << PAGE_SHIFT);
696 }
697 return 0;
698}
699
700/*
701 * Encode SETACL arguments
702 */
703static int
Al Virod61005a2006-10-19 23:28:48 -0700704nfs3_xdr_setaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000705 struct nfs3_setaclargs *args)
706{
707 struct xdr_buf *buf = &req->rq_snd_buf;
Trond Myklebustae461412009-03-10 20:33:18 -0400708 unsigned int base;
709 int err;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000710
711 p = xdr_encode_fhandle(p, NFS_FH(args->inode));
712 *p++ = htonl(args->mask);
Trond Myklebustae461412009-03-10 20:33:18 -0400713 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
714 base = req->rq_slen;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000715
Trond Myklebustae461412009-03-10 20:33:18 -0400716 if (args->npages != 0)
717 xdr_encode_pages(buf, args->pages, 0, args->len);
718 else
719 req->rq_slen += args->len;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000720
721 err = nfsacl_encode(buf, base, args->inode,
722 (args->mask & NFS_ACL) ?
723 args->acl_access : NULL, 1, 0);
724 if (err > 0)
725 err = nfsacl_encode(buf, base + err, args->inode,
726 (args->mask & NFS_DFACL) ?
727 args->acl_default : NULL, 1,
728 NFS_ACL_DEFAULT);
729 return (err > 0) ? 0 : err;
730}
731#endif /* CONFIG_NFS_V3_ACL */
732
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733/*
734 * NFS XDR decode functions
735 */
736
737/*
738 * Decode attrstat reply.
739 */
740static int
Al Virod61005a2006-10-19 23:28:48 -0700741nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742{
743 int status;
744
745 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300746 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 xdr_decode_fattr(p, fattr);
748 return 0;
749}
750
751/*
752 * Decode status+wcc_data reply
753 * SATTR, REMOVE, RMDIR
754 */
755static int
Al Virod61005a2006-10-19 23:28:48 -0700756nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757{
758 int status;
759
760 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300761 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 xdr_decode_wcc_data(p, fattr);
763 return status;
764}
765
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400766static int
767nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
768{
769 return nfs3_xdr_wccstat(req, p, &res->dir_attr);
770}
771
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772/*
773 * Decode LOOKUP reply
774 */
775static int
Al Virod61005a2006-10-19 23:28:48 -0700776nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777{
778 int status;
779
780 if ((status = ntohl(*p++))) {
Benny Halevy856dff32008-03-31 17:39:06 +0300781 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 } else {
783 if (!(p = xdr_decode_fhandle(p, res->fh)))
784 return -errno_NFSERR_IO;
785 p = xdr_decode_post_op_attr(p, res->fattr);
786 }
787 xdr_decode_post_op_attr(p, res->dir_attr);
788 return status;
789}
790
791/*
792 * Decode ACCESS reply
793 */
794static int
Al Virod61005a2006-10-19 23:28:48 -0700795nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796{
797 int status = ntohl(*p++);
798
799 p = xdr_decode_post_op_attr(p, res->fattr);
800 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300801 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 res->access = ntohl(*p++);
803 return 0;
804}
805
806static int
Al Virod61005a2006-10-19 23:28:48 -0700807nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400809 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 unsigned int replen;
811
812 p = xdr_encode_fhandle(p, args->fh);
813 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
814
815 /* Inline the page array */
816 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
817 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
818 return 0;
819}
820
821/*
822 * Decode READLINK reply
823 */
824static int
Al Virod61005a2006-10-19 23:28:48 -0700825nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826{
827 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
828 struct kvec *iov = rcvbuf->head;
Chuck Leverc957c522007-10-26 13:31:57 -0400829 size_t hdrlen;
830 u32 len, recvd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 char *kaddr;
832 int status;
833
834 status = ntohl(*p++);
835 p = xdr_decode_post_op_attr(p, fattr);
836
837 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300838 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
840 /* Convert length of symlink */
841 len = ntohl(*p++);
Chuck Leverc957c522007-10-26 13:31:57 -0400842 if (len >= rcvbuf->page_len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400843 dprintk("nfs: server returned giant symlink!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 return -ENAMETOOLONG;
845 }
846
847 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
848 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400849 dprintk("NFS: READLINK reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400850 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 return -errno_NFSERR_IO;
852 } else if (iov->iov_len != hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400853 dprintk("NFS: READLINK header is short. "
854 "iovec will be shifted.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
856 }
857 recvd = req->rq_rcv_buf.len - hdrlen;
858 if (recvd < len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400859 dprintk("NFS: server cheating in readlink reply: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 "count %u > recvd %u\n", len, recvd);
861 return -EIO;
862 }
863
864 /* NULL terminate the string we got */
865 kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
866 kaddr[len+rcvbuf->page_base] = '\0';
867 kunmap_atomic(kaddr, KM_USER0);
868 return 0;
869}
870
871/*
872 * Decode READ reply
873 */
874static int
Al Virod61005a2006-10-19 23:28:48 -0700875nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876{
877 struct kvec *iov = req->rq_rcv_buf.head;
Chuck Leverc957c522007-10-26 13:31:57 -0400878 size_t hdrlen;
879 u32 count, ocount, recvd;
880 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881
882 status = ntohl(*p++);
883 p = xdr_decode_post_op_attr(p, res->fattr);
884
885 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300886 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887
Chuck Leverc957c522007-10-26 13:31:57 -0400888 /* Decode reply count and EOF flag. NFSv3 is somewhat redundant
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 * in that it puts the count both in the res struct and in the
890 * opaque data count. */
891 count = ntohl(*p++);
892 res->eof = ntohl(*p++);
893 ocount = ntohl(*p++);
894
895 if (ocount != count) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400896 dprintk("NFS: READ count doesn't match RPC opaque count.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 return -errno_NFSERR_IO;
898 }
899
900 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
901 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400902 dprintk("NFS: READ reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400903 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 return -errno_NFSERR_IO;
905 } else if (iov->iov_len != hdrlen) {
906 dprintk("NFS: READ header is short. iovec will be shifted.\n");
907 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
908 }
909
910 recvd = req->rq_rcv_buf.len - hdrlen;
911 if (count > recvd) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400912 dprintk("NFS: server cheating in read reply: "
Chuck Leverc957c522007-10-26 13:31:57 -0400913 "count %u > recvd %u\n", count, recvd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 count = recvd;
915 res->eof = 0;
916 }
917
918 if (count < res->count)
919 res->count = count;
920
921 return count;
922}
923
924/*
925 * Decode WRITE response
926 */
927static int
Al Virod61005a2006-10-19 23:28:48 -0700928nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929{
930 int status;
931
932 status = ntohl(*p++);
933 p = xdr_decode_wcc_data(p, res->fattr);
934
935 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300936 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
938 res->count = ntohl(*p++);
939 res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
940 res->verf->verifier[0] = *p++;
941 res->verf->verifier[1] = *p++;
942
943 return res->count;
944}
945
946/*
947 * Decode a CREATE response
948 */
949static int
Al Virod61005a2006-10-19 23:28:48 -0700950nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951{
952 int status;
953
954 status = ntohl(*p++);
955 if (status == 0) {
956 if (*p++) {
957 if (!(p = xdr_decode_fhandle(p, res->fh)))
958 return -errno_NFSERR_IO;
959 p = xdr_decode_post_op_attr(p, res->fattr);
960 } else {
961 memset(res->fh, 0, sizeof(*res->fh));
962 /* Do decode post_op_attr but set it to NULL */
963 p = xdr_decode_post_op_attr(p, res->fattr);
964 res->fattr->valid = 0;
965 }
966 } else {
Benny Halevy856dff32008-03-31 17:39:06 +0300967 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 }
969 p = xdr_decode_wcc_data(p, res->dir_attr);
970 return status;
971}
972
973/*
974 * Decode RENAME reply
975 */
976static int
Al Virod61005a2006-10-19 23:28:48 -0700977nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs3_renameres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978{
979 int status;
980
981 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300982 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 p = xdr_decode_wcc_data(p, res->fromattr);
984 p = xdr_decode_wcc_data(p, res->toattr);
985 return status;
986}
987
988/*
989 * Decode LINK reply
990 */
991static int
Al Virod61005a2006-10-19 23:28:48 -0700992nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993{
994 int status;
995
996 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300997 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 p = xdr_decode_post_op_attr(p, res->fattr);
999 p = xdr_decode_wcc_data(p, res->dir_attr);
1000 return status;
1001}
1002
1003/*
1004 * Decode FSSTAT reply
1005 */
1006static int
Al Virod61005a2006-10-19 23:28:48 -07001007nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008{
1009 int status;
1010
1011 status = ntohl(*p++);
1012
1013 p = xdr_decode_post_op_attr(p, res->fattr);
1014 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001015 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016
1017 p = xdr_decode_hyper(p, &res->tbytes);
1018 p = xdr_decode_hyper(p, &res->fbytes);
1019 p = xdr_decode_hyper(p, &res->abytes);
1020 p = xdr_decode_hyper(p, &res->tfiles);
1021 p = xdr_decode_hyper(p, &res->ffiles);
1022 p = xdr_decode_hyper(p, &res->afiles);
1023
1024 /* ignore invarsec */
1025 return 0;
1026}
1027
1028/*
1029 * Decode FSINFO reply
1030 */
1031static int
Al Virod61005a2006-10-19 23:28:48 -07001032nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033{
1034 int status;
1035
1036 status = ntohl(*p++);
1037
1038 p = xdr_decode_post_op_attr(p, res->fattr);
1039 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001040 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041
1042 res->rtmax = ntohl(*p++);
1043 res->rtpref = ntohl(*p++);
1044 res->rtmult = ntohl(*p++);
1045 res->wtmax = ntohl(*p++);
1046 res->wtpref = ntohl(*p++);
1047 res->wtmult = ntohl(*p++);
1048 res->dtpref = ntohl(*p++);
1049 p = xdr_decode_hyper(p, &res->maxfilesize);
1050
1051 /* ignore time_delta and properties */
1052 res->lease_time = 0;
1053 return 0;
1054}
1055
1056/*
1057 * Decode PATHCONF reply
1058 */
1059static int
Al Virod61005a2006-10-19 23:28:48 -07001060nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061{
1062 int status;
1063
1064 status = ntohl(*p++);
1065
1066 p = xdr_decode_post_op_attr(p, res->fattr);
1067 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001068 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 res->max_link = ntohl(*p++);
1070 res->max_namelen = ntohl(*p++);
1071
1072 /* ignore remaining fields */
1073 return 0;
1074}
1075
1076/*
1077 * Decode COMMIT reply
1078 */
1079static int
Al Virod61005a2006-10-19 23:28:48 -07001080nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081{
1082 int status;
1083
1084 status = ntohl(*p++);
1085 p = xdr_decode_wcc_data(p, res->fattr);
1086 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001087 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088
1089 res->verf->verifier[0] = *p++;
1090 res->verf->verifier[1] = *p++;
1091 return 0;
1092}
1093
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001094#ifdef CONFIG_NFS_V3_ACL
1095/*
1096 * Decode GETACL reply
1097 */
1098static int
Al Virod61005a2006-10-19 23:28:48 -07001099nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001100 struct nfs3_getaclres *res)
1101{
1102 struct xdr_buf *buf = &req->rq_rcv_buf;
1103 int status = ntohl(*p++);
1104 struct posix_acl **acl;
1105 unsigned int *aclcnt;
1106 int err, base;
1107
1108 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001109 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001110 p = xdr_decode_post_op_attr(p, res->fattr);
1111 res->mask = ntohl(*p++);
1112 if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
1113 return -EINVAL;
1114 base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
1115
1116 acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
1117 aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
1118 err = nfsacl_decode(buf, base, aclcnt, acl);
1119
1120 acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
1121 aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
1122 if (err > 0)
1123 err = nfsacl_decode(buf, base + err, aclcnt, acl);
1124 return (err > 0) ? 0 : err;
1125}
1126
1127/*
1128 * Decode setacl reply.
1129 */
1130static int
Al Virod61005a2006-10-19 23:28:48 -07001131nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001132{
1133 int status = ntohl(*p++);
1134
1135 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +03001136 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001137 xdr_decode_post_op_attr(p, fattr);
1138 return 0;
1139}
1140#endif /* CONFIG_NFS_V3_ACL */
1141
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142#define PROC(proc, argtype, restype, timer) \
1143[NFS3PROC_##proc] = { \
1144 .p_proc = NFS3PROC_##proc, \
1145 .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
1146 .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
Chuck Lever2bea90d2007-03-29 16:47:53 -04001147 .p_arglen = NFS3_##argtype##_sz, \
1148 .p_replen = NFS3_##restype##_sz, \
Chuck Levercc0175c2006-03-20 13:44:22 -05001149 .p_timer = timer, \
1150 .p_statidx = NFS3PROC_##proc, \
1151 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 }
1153
1154struct rpc_procinfo nfs3_procedures[] = {
1155 PROC(GETATTR, fhandle, attrstat, 1),
1156 PROC(SETATTR, sattrargs, wccstat, 0),
1157 PROC(LOOKUP, diropargs, lookupres, 2),
1158 PROC(ACCESS, accessargs, accessres, 1),
1159 PROC(READLINK, readlinkargs, readlinkres, 3),
1160 PROC(READ, readargs, readres, 3),
1161 PROC(WRITE, writeargs, writeres, 4),
1162 PROC(CREATE, createargs, createres, 0),
1163 PROC(MKDIR, mkdirargs, createres, 0),
1164 PROC(SYMLINK, symlinkargs, createres, 0),
1165 PROC(MKNOD, mknodargs, createres, 0),
Trond Myklebust4fdc17b2007-07-14 15:39:57 -04001166 PROC(REMOVE, removeargs, removeres, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 PROC(RMDIR, diropargs, wccstat, 0),
1168 PROC(RENAME, renameargs, renameres, 0),
1169 PROC(LINK, linkargs, linkres, 0),
1170 PROC(READDIR, readdirargs, readdirres, 3),
1171 PROC(READDIRPLUS, readdirargs, readdirres, 3),
1172 PROC(FSSTAT, fhandle, fsstatres, 0),
1173 PROC(FSINFO, fhandle, fsinfores, 0),
1174 PROC(PATHCONF, fhandle, pathconfres, 0),
1175 PROC(COMMIT, commitargs, commitres, 5),
1176};
1177
1178struct rpc_version nfs_version3 = {
1179 .number = 3,
Tobias Klausere8c96f82006-03-24 03:15:34 -08001180 .nrprocs = ARRAY_SIZE(nfs3_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 .procs = nfs3_procedures
1182};
1183
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001184#ifdef CONFIG_NFS_V3_ACL
1185static struct rpc_procinfo nfs3_acl_procedures[] = {
1186 [ACLPROC3_GETACL] = {
1187 .p_proc = ACLPROC3_GETACL,
1188 .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
1189 .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001190 .p_arglen = ACL3_getaclargs_sz,
1191 .p_replen = ACL3_getaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001192 .p_timer = 1,
Chuck Levercc0175c2006-03-20 13:44:22 -05001193 .p_name = "GETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001194 },
1195 [ACLPROC3_SETACL] = {
1196 .p_proc = ACLPROC3_SETACL,
1197 .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
1198 .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001199 .p_arglen = ACL3_setaclargs_sz,
1200 .p_replen = ACL3_setaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001201 .p_timer = 0,
Chuck Levercc0175c2006-03-20 13:44:22 -05001202 .p_name = "SETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001203 },
1204};
1205
1206struct rpc_version nfsacl_version3 = {
1207 .number = 3,
1208 .nrprocs = sizeof(nfs3_acl_procedures)/
1209 sizeof(nfs3_acl_procedures[0]),
1210 .procs = nfs3_acl_procedures,
1211};
1212#endif /* CONFIG_NFS_V3_ACL */