blob: 11cdddec1432cfae1199aa2f89257ce00dd033d1 [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)
85#define ACL3_setaclargs_sz (NFS3_fh_sz+1+2*(2+5*3))
86#define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+2*(2+5*3))
87#define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz)
88
Linus Torvalds1da177e2005-04-16 15:20:36 -070089/*
90 * Map file type to S_IFMT bits
91 */
92static struct {
93 unsigned int mode;
94 unsigned int nfs2type;
95} nfs_type2fmt[] = {
96 { 0, NFNON },
97 { S_IFREG, NFREG },
98 { S_IFDIR, NFDIR },
99 { S_IFBLK, NFBLK },
100 { S_IFCHR, NFCHR },
101 { S_IFLNK, NFLNK },
102 { S_IFSOCK, NFSOCK },
103 { S_IFIFO, NFFIFO },
104 { 0, NFBAD }
105};
106
107/*
108 * Common NFS XDR functions as inlines
109 */
Al Virod61005a62006-10-19 23:28:48 -0700110static inline __be32 *
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400111xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112{
113 return xdr_encode_array(p, fh->data, fh->size);
114}
115
Al Virod61005a62006-10-19 23:28:48 -0700116static inline __be32 *
117xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118{
119 if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
120 memcpy(fh->data, p, fh->size);
121 return p + XDR_QUADLEN(fh->size);
122 }
123 return NULL;
124}
125
126/*
127 * Encode/decode time.
128 */
Al Virod61005a62006-10-19 23:28:48 -0700129static inline __be32 *
130xdr_encode_time3(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131{
132 *p++ = htonl(timep->tv_sec);
133 *p++ = htonl(timep->tv_nsec);
134 return p;
135}
136
Al Virod61005a62006-10-19 23:28:48 -0700137static inline __be32 *
138xdr_decode_time3(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139{
140 timep->tv_sec = ntohl(*p++);
141 timep->tv_nsec = ntohl(*p++);
142 return p;
143}
144
Al Virod61005a62006-10-19 23:28:48 -0700145static __be32 *
146xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147{
148 unsigned int type, major, minor;
149 int fmode;
150
151 type = ntohl(*p++);
152 if (type >= NF3BAD)
153 type = NF3BAD;
154 fmode = nfs_type2fmt[type].mode;
155 fattr->type = nfs_type2fmt[type].nfs2type;
156 fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
157 fattr->nlink = ntohl(*p++);
158 fattr->uid = ntohl(*p++);
159 fattr->gid = ntohl(*p++);
160 p = xdr_decode_hyper(p, &fattr->size);
161 p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
162
163 /* Turn remote device info into Linux-specific dev_t */
164 major = ntohl(*p++);
165 minor = ntohl(*p++);
166 fattr->rdev = MKDEV(major, minor);
167 if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor)
168 fattr->rdev = 0;
169
Trond Myklebust8b4bdcf2006-06-09 09:34:19 -0400170 p = xdr_decode_hyper(p, &fattr->fsid.major);
171 fattr->fsid.minor = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 p = xdr_decode_hyper(p, &fattr->fileid);
173 p = xdr_decode_time3(p, &fattr->atime);
174 p = xdr_decode_time3(p, &fattr->mtime);
175 p = xdr_decode_time3(p, &fattr->ctime);
176
177 /* Update the mode bits */
178 fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 return p;
180}
181
Al Virod61005a62006-10-19 23:28:48 -0700182static inline __be32 *
183xdr_encode_sattr(__be32 *p, struct iattr *attr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184{
185 if (attr->ia_valid & ATTR_MODE) {
186 *p++ = xdr_one;
Trond Myklebustcf3fff52006-01-03 09:55:53 +0100187 *p++ = htonl(attr->ia_mode & S_IALLUGO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 } else {
189 *p++ = xdr_zero;
190 }
191 if (attr->ia_valid & ATTR_UID) {
192 *p++ = xdr_one;
193 *p++ = htonl(attr->ia_uid);
194 } else {
195 *p++ = xdr_zero;
196 }
197 if (attr->ia_valid & ATTR_GID) {
198 *p++ = xdr_one;
199 *p++ = htonl(attr->ia_gid);
200 } else {
201 *p++ = xdr_zero;
202 }
203 if (attr->ia_valid & ATTR_SIZE) {
204 *p++ = xdr_one;
205 p = xdr_encode_hyper(p, (__u64) attr->ia_size);
206 } else {
207 *p++ = xdr_zero;
208 }
209 if (attr->ia_valid & ATTR_ATIME_SET) {
210 *p++ = xdr_two;
211 p = xdr_encode_time3(p, &attr->ia_atime);
212 } else if (attr->ia_valid & ATTR_ATIME) {
213 *p++ = xdr_one;
214 } else {
215 *p++ = xdr_zero;
216 }
217 if (attr->ia_valid & ATTR_MTIME_SET) {
218 *p++ = xdr_two;
219 p = xdr_encode_time3(p, &attr->ia_mtime);
220 } else if (attr->ia_valid & ATTR_MTIME) {
221 *p++ = xdr_one;
222 } else {
223 *p++ = xdr_zero;
224 }
225 return p;
226}
227
Al Virod61005a62006-10-19 23:28:48 -0700228static inline __be32 *
229xdr_decode_wcc_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230{
231 p = xdr_decode_hyper(p, &fattr->pre_size);
232 p = xdr_decode_time3(p, &fattr->pre_mtime);
233 p = xdr_decode_time3(p, &fattr->pre_ctime);
234 fattr->valid |= NFS_ATTR_WCC;
235 return p;
236}
237
Al Virod61005a62006-10-19 23:28:48 -0700238static inline __be32 *
239xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240{
241 if (*p++)
242 p = xdr_decode_fattr(p, fattr);
243 return p;
244}
245
Al Virod61005a62006-10-19 23:28:48 -0700246static inline __be32 *
247xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248{
249 if (*p++)
250 return xdr_decode_wcc_attr(p, fattr);
251 return p;
252}
253
254
Al Virod61005a62006-10-19 23:28:48 -0700255static inline __be32 *
256xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
258 p = xdr_decode_pre_op_attr(p, fattr);
259 return xdr_decode_post_op_attr(p, fattr);
260}
261
262/*
263 * NFS encode functions
264 */
265
266/*
267 * Encode file handle argument
268 */
269static int
Al Virod61005a62006-10-19 23:28:48 -0700270nfs3_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{
272 p = xdr_encode_fhandle(p, fh);
273 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
274 return 0;
275}
276
277/*
278 * Encode SETATTR arguments
279 */
280static int
Al Virod61005a62006-10-19 23:28:48 -0700281nfs3_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs3_sattrargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282{
283 p = xdr_encode_fhandle(p, args->fh);
284 p = xdr_encode_sattr(p, args->sattr);
285 *p++ = htonl(args->guard);
286 if (args->guard)
287 p = xdr_encode_time3(p, &args->guardtime);
288 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
289 return 0;
290}
291
292/*
293 * Encode directory ops argument
294 */
295static int
Al Virod61005a62006-10-19 23:28:48 -0700296nfs3_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs3_diropargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297{
298 p = xdr_encode_fhandle(p, args->fh);
299 p = xdr_encode_array(p, args->name, args->len);
300 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
301 return 0;
302}
303
304/*
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400305 * Encode REMOVE argument
306 */
307static int
308nfs3_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
309{
310 p = xdr_encode_fhandle(p, args->fh);
311 p = xdr_encode_array(p, args->name.name, args->name.len);
312 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
313 return 0;
314}
315
316/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 * Encode access() argument
318 */
319static int
Al Virod61005a62006-10-19 23:28:48 -0700320nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321{
322 p = xdr_encode_fhandle(p, args->fh);
323 *p++ = htonl(args->access);
324 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
325 return 0;
326}
327
328/*
329 * Arguments to a READ call. Since we read data directly into the page
330 * cache, we also set up the reply iovec here so that iov[1] points
331 * exactly to the page we want to fetch.
332 */
333static int
Al Virod61005a62006-10-19 23:28:48 -0700334nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400336 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 unsigned int replen;
338 u32 count = args->count;
339
340 p = xdr_encode_fhandle(p, args->fh);
341 p = xdr_encode_hyper(p, args->offset);
342 *p++ = htonl(count);
343 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
344
345 /* Inline the page array */
346 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
347 xdr_inline_pages(&req->rq_rcv_buf, replen,
348 args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400349 req->rq_rcv_buf.flags |= XDRBUF_READ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 return 0;
351}
352
353/*
354 * Write arguments. Splice the buffer to be written into the iovec.
355 */
356static int
Al Virod61005a62006-10-19 23:28:48 -0700357nfs3_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358{
359 struct xdr_buf *sndbuf = &req->rq_snd_buf;
360 u32 count = args->count;
361
362 p = xdr_encode_fhandle(p, args->fh);
363 p = xdr_encode_hyper(p, args->offset);
364 *p++ = htonl(count);
365 *p++ = htonl(args->stable);
366 *p++ = htonl(count);
367 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
368
369 /* Copy the page array */
370 xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400371 sndbuf->flags |= XDRBUF_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 return 0;
373}
374
375/*
376 * Encode CREATE arguments
377 */
378static int
Al Virod61005a62006-10-19 23:28:48 -0700379nfs3_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs3_createargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380{
381 p = xdr_encode_fhandle(p, args->fh);
382 p = xdr_encode_array(p, args->name, args->len);
383
384 *p++ = htonl(args->createmode);
385 if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
386 *p++ = args->verifier[0];
387 *p++ = args->verifier[1];
388 } else
389 p = xdr_encode_sattr(p, args->sattr);
390
391 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
392 return 0;
393}
394
395/*
396 * Encode MKDIR arguments
397 */
398static int
Al Virod61005a62006-10-19 23:28:48 -0700399nfs3_xdr_mkdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mkdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400{
401 p = xdr_encode_fhandle(p, args->fh);
402 p = xdr_encode_array(p, args->name, args->len);
403 p = xdr_encode_sattr(p, args->sattr);
404 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
405 return 0;
406}
407
408/*
409 * Encode SYMLINK arguments
410 */
411static int
Al Virod61005a62006-10-19 23:28:48 -0700412nfs3_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_symlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413{
414 p = xdr_encode_fhandle(p, args->fromfh);
415 p = xdr_encode_array(p, args->fromname, args->fromlen);
416 p = xdr_encode_sattr(p, args->sattr);
Chuck Lever94a6d752006-08-22 20:06:23 -0400417 *p++ = htonl(args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
Chuck Lever94a6d752006-08-22 20:06:23 -0400419
420 /* Copy the page */
421 xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 return 0;
423}
424
425/*
426 * Encode MKNOD arguments
427 */
428static int
Al Virod61005a62006-10-19 23:28:48 -0700429nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430{
431 p = xdr_encode_fhandle(p, args->fh);
432 p = xdr_encode_array(p, args->name, args->len);
433 *p++ = htonl(args->type);
434 p = xdr_encode_sattr(p, args->sattr);
435 if (args->type == NF3CHR || args->type == NF3BLK) {
436 *p++ = htonl(MAJOR(args->rdev));
437 *p++ = htonl(MINOR(args->rdev));
438 }
439
440 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
441 return 0;
442}
443
444/*
445 * Encode RENAME arguments
446 */
447static int
Al Virod61005a62006-10-19 23:28:48 -0700448nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs3_renameargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449{
450 p = xdr_encode_fhandle(p, args->fromfh);
451 p = xdr_encode_array(p, args->fromname, args->fromlen);
452 p = xdr_encode_fhandle(p, args->tofh);
453 p = xdr_encode_array(p, args->toname, args->tolen);
454 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
455 return 0;
456}
457
458/*
459 * Encode LINK arguments
460 */
461static int
Al Virod61005a62006-10-19 23:28:48 -0700462nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463{
464 p = xdr_encode_fhandle(p, args->fromfh);
465 p = xdr_encode_fhandle(p, args->tofh);
466 p = xdr_encode_array(p, args->toname, args->tolen);
467 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
468 return 0;
469}
470
471/*
472 * Encode arguments to readdir call
473 */
474static int
Al Virod61005a62006-10-19 23:28:48 -0700475nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400477 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 unsigned int replen;
479 u32 count = args->count;
480
481 p = xdr_encode_fhandle(p, args->fh);
482 p = xdr_encode_hyper(p, args->cookie);
483 *p++ = args->verf[0];
484 *p++ = args->verf[1];
485 if (args->plus) {
486 /* readdirplus: need dircount + buffer size.
487 * We just make sure we make dircount big enough */
488 *p++ = htonl(count >> 3);
489 }
490 *p++ = htonl(count);
491 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
492
493 /* Inline the page array */
494 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
495 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
496 return 0;
497}
498
499/*
500 * Decode the result of a readdir call.
501 * We just check for syntactical correctness.
502 */
503static int
Al Virod61005a62006-10-19 23:28:48 -0700504nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505{
506 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
507 struct kvec *iov = rcvbuf->head;
508 struct page **page;
Chuck Leverc957c522007-10-26 13:31:57 -0400509 size_t hdrlen;
510 u32 len, recvd, pglen;
Jeff Layton643f8112008-02-22 14:50:00 -0500511 int status, nr = 0;
Al Virod61005a62006-10-19 23:28:48 -0700512 __be32 *entry, *end, *kaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
514 status = ntohl(*p++);
515 /* Decode post_op_attrs */
516 p = xdr_decode_post_op_attr(p, res->dir_attr);
517 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300518 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 /* Decode verifier cookie */
520 if (res->verf) {
521 res->verf[0] = *p++;
522 res->verf[1] = *p++;
523 } else {
524 p += 2;
525 }
526
527 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
528 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400529 dprintk("NFS: READDIR reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400530 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 return -errno_NFSERR_IO;
532 } else if (iov->iov_len != hdrlen) {
533 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
534 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
535 }
536
537 pglen = rcvbuf->page_len;
538 recvd = rcvbuf->len - hdrlen;
539 if (pglen > recvd)
540 pglen = recvd;
541 page = rcvbuf->pages;
Al Virod61005a62006-10-19 23:28:48 -0700542 kaddr = p = kmap_atomic(*page, KM_USER0);
543 end = (__be32 *)((char *)p + pglen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 entry = p;
Jeff Layton643f8112008-02-22 14:50:00 -0500545
546 /* Make sure the packet actually has a value_follows and EOF entry */
547 if ((entry + 1) > end)
548 goto short_pkt;
549
550 for (; *p++; nr++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 if (p + 3 > end)
552 goto short_pkt;
553 p += 2; /* inode # */
554 len = ntohl(*p++); /* string length */
555 p += XDR_QUADLEN(len) + 2; /* name + cookie */
556 if (len > NFS3_MAXNAMLEN) {
Chuck Leverc957c522007-10-26 13:31:57 -0400557 dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 len);
559 goto err_unmap;
560 }
561
562 if (res->plus) {
563 /* post_op_attr */
564 if (p + 2 > end)
565 goto short_pkt;
566 if (*p++) {
567 p += 21;
568 if (p + 1 > end)
569 goto short_pkt;
570 }
571 /* post_op_fh3 */
572 if (*p++) {
573 if (p + 1 > end)
574 goto short_pkt;
575 len = ntohl(*p++);
576 if (len > NFS3_FHSIZE) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400577 dprintk("NFS: giant filehandle in "
Chuck Leverc957c522007-10-26 13:31:57 -0400578 "readdir (len 0x%x)!\n", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 goto err_unmap;
580 }
581 p += XDR_QUADLEN(len);
582 }
583 }
584
585 if (p + 2 > end)
586 goto short_pkt;
587 entry = p;
588 }
Jeff Layton643f8112008-02-22 14:50:00 -0500589
590 /*
591 * Apparently some server sends responses that are a valid size, but
592 * contain no entries, and have value_follows==0 and EOF==0. For
593 * those, just set the EOF marker.
594 */
595 if (!nr && entry[1] == 0) {
596 dprintk("NFS: readdir reply truncated!\n");
597 entry[1] = 1;
598 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 out:
600 kunmap_atomic(kaddr, KM_USER0);
601 return nr;
602 short_pkt:
Jeff Layton643f8112008-02-22 14:50:00 -0500603 /*
604 * When we get a short packet there are 2 possibilities. We can
605 * return an error, or fix up the response to look like a valid
606 * response and return what we have so far. If there are no
607 * entries and the packet was short, then return -EIO. If there
608 * are valid entries in the response, return them and pretend that
609 * the call was successful, but incomplete. The caller can retry the
610 * readdir starting at the last cookie.
611 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 entry[0] = entry[1] = 0;
Jeff Layton643f8112008-02-22 14:50:00 -0500613 if (!nr)
614 nr = -errno_NFSERR_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 goto out;
616err_unmap:
617 nr = -errno_NFSERR_IO;
618 goto out;
619}
620
Al Viro0dbb4c62006-10-19 23:28:49 -0700621__be32 *
622nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623{
624 struct nfs_entry old = *entry;
625
626 if (!*p++) {
627 if (!*p)
628 return ERR_PTR(-EAGAIN);
629 entry->eof = 1;
630 return ERR_PTR(-EBADCOOKIE);
631 }
632
633 p = xdr_decode_hyper(p, &entry->ino);
634 entry->len = ntohl(*p++);
635 entry->name = (const char *) p;
636 p += XDR_QUADLEN(entry->len);
637 entry->prev_cookie = entry->cookie;
638 p = xdr_decode_hyper(p, &entry->cookie);
639
640 if (plus) {
641 entry->fattr->valid = 0;
642 p = xdr_decode_post_op_attr(p, entry->fattr);
643 /* In fact, a post_op_fh3: */
644 if (*p++) {
645 p = xdr_decode_fhandle(p, entry->fh);
646 /* Ugh -- server reply was truncated */
647 if (p == NULL) {
648 dprintk("NFS: FH truncated\n");
649 *entry = old;
650 return ERR_PTR(-EAGAIN);
651 }
652 } else
653 memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
654 }
655
656 entry->eof = !p[0] && p[1];
657 return p;
658}
659
660/*
661 * Encode COMMIT arguments
662 */
663static int
Al Virod61005a62006-10-19 23:28:48 -0700664nfs3_xdr_commitargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665{
666 p = xdr_encode_fhandle(p, args->fh);
667 p = xdr_encode_hyper(p, args->offset);
668 *p++ = htonl(args->count);
669 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
670 return 0;
671}
672
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000673#ifdef CONFIG_NFS_V3_ACL
674/*
675 * Encode GETACL arguments
676 */
677static int
Al Virod61005a62006-10-19 23:28:48 -0700678nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000679 struct nfs3_getaclargs *args)
680{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400681 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000682 unsigned int replen;
683
684 p = xdr_encode_fhandle(p, args->fh);
685 *p++ = htonl(args->mask);
686 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
687
688 if (args->mask & (NFS_ACL | NFS_DFACL)) {
689 /* Inline the page array */
690 replen = (RPC_REPHDRSIZE + auth->au_rslack +
691 ACL3_getaclres_sz) << 2;
692 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
693 NFSACL_MAXPAGES << PAGE_SHIFT);
694 }
695 return 0;
696}
697
698/*
699 * Encode SETACL arguments
700 */
701static int
Al Virod61005a62006-10-19 23:28:48 -0700702nfs3_xdr_setaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000703 struct nfs3_setaclargs *args)
704{
705 struct xdr_buf *buf = &req->rq_snd_buf;
706 unsigned int base, len_in_head, len = nfsacl_size(
707 (args->mask & NFS_ACL) ? args->acl_access : NULL,
708 (args->mask & NFS_DFACL) ? args->acl_default : NULL);
709 int count, err;
710
711 p = xdr_encode_fhandle(p, NFS_FH(args->inode));
712 *p++ = htonl(args->mask);
713 base = (char *)p - (char *)buf->head->iov_base;
714 /* put as much of the acls into head as possible. */
715 len_in_head = min_t(unsigned int, buf->head->iov_len - base, len);
716 len -= len_in_head;
Andreas Gruenbacher21348422005-06-22 17:16:28 +0000717 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p + (len_in_head >> 2));
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000718
719 for (count = 0; (count << PAGE_SHIFT) < len; count++) {
720 args->pages[count] = alloc_page(GFP_KERNEL);
721 if (!args->pages[count]) {
722 while (count)
723 __free_page(args->pages[--count]);
724 return -ENOMEM;
725 }
726 }
727 xdr_encode_pages(buf, args->pages, 0, len);
728
729 err = nfsacl_encode(buf, base, args->inode,
730 (args->mask & NFS_ACL) ?
731 args->acl_access : NULL, 1, 0);
732 if (err > 0)
733 err = nfsacl_encode(buf, base + err, args->inode,
734 (args->mask & NFS_DFACL) ?
735 args->acl_default : NULL, 1,
736 NFS_ACL_DEFAULT);
737 return (err > 0) ? 0 : err;
738}
739#endif /* CONFIG_NFS_V3_ACL */
740
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741/*
742 * NFS XDR decode functions
743 */
744
745/*
746 * Decode attrstat reply.
747 */
748static int
Al Virod61005a62006-10-19 23:28:48 -0700749nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750{
751 int status;
752
753 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300754 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 xdr_decode_fattr(p, fattr);
756 return 0;
757}
758
759/*
760 * Decode status+wcc_data reply
761 * SATTR, REMOVE, RMDIR
762 */
763static int
Al Virod61005a62006-10-19 23:28:48 -0700764nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765{
766 int status;
767
768 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300769 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 xdr_decode_wcc_data(p, fattr);
771 return status;
772}
773
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400774static int
775nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
776{
777 return nfs3_xdr_wccstat(req, p, &res->dir_attr);
778}
779
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780/*
781 * Decode LOOKUP reply
782 */
783static int
Al Virod61005a62006-10-19 23:28:48 -0700784nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785{
786 int status;
787
788 if ((status = ntohl(*p++))) {
Benny Halevy856dff32008-03-31 17:39:06 +0300789 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 } else {
791 if (!(p = xdr_decode_fhandle(p, res->fh)))
792 return -errno_NFSERR_IO;
793 p = xdr_decode_post_op_attr(p, res->fattr);
794 }
795 xdr_decode_post_op_attr(p, res->dir_attr);
796 return status;
797}
798
799/*
800 * Decode ACCESS reply
801 */
802static int
Al Virod61005a62006-10-19 23:28:48 -0700803nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804{
805 int status = ntohl(*p++);
806
807 p = xdr_decode_post_op_attr(p, res->fattr);
808 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300809 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 res->access = ntohl(*p++);
811 return 0;
812}
813
814static int
Al Virod61005a62006-10-19 23:28:48 -0700815nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400817 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 unsigned int replen;
819
820 p = xdr_encode_fhandle(p, args->fh);
821 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
822
823 /* Inline the page array */
824 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
825 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
826 return 0;
827}
828
829/*
830 * Decode READLINK reply
831 */
832static int
Al Virod61005a62006-10-19 23:28:48 -0700833nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834{
835 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
836 struct kvec *iov = rcvbuf->head;
Chuck Leverc957c522007-10-26 13:31:57 -0400837 size_t hdrlen;
838 u32 len, recvd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 char *kaddr;
840 int status;
841
842 status = ntohl(*p++);
843 p = xdr_decode_post_op_attr(p, fattr);
844
845 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300846 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
848 /* Convert length of symlink */
849 len = ntohl(*p++);
Chuck Leverc957c522007-10-26 13:31:57 -0400850 if (len >= rcvbuf->page_len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400851 dprintk("nfs: server returned giant symlink!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 return -ENAMETOOLONG;
853 }
854
855 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
856 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400857 dprintk("NFS: READLINK reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400858 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 return -errno_NFSERR_IO;
860 } else if (iov->iov_len != hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400861 dprintk("NFS: READLINK header is short. "
862 "iovec will be shifted.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
864 }
865 recvd = req->rq_rcv_buf.len - hdrlen;
866 if (recvd < len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400867 dprintk("NFS: server cheating in readlink reply: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 "count %u > recvd %u\n", len, recvd);
869 return -EIO;
870 }
871
872 /* NULL terminate the string we got */
873 kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
874 kaddr[len+rcvbuf->page_base] = '\0';
875 kunmap_atomic(kaddr, KM_USER0);
876 return 0;
877}
878
879/*
880 * Decode READ reply
881 */
882static int
Al Virod61005a62006-10-19 23:28:48 -0700883nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884{
885 struct kvec *iov = req->rq_rcv_buf.head;
Chuck Leverc957c522007-10-26 13:31:57 -0400886 size_t hdrlen;
887 u32 count, ocount, recvd;
888 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889
890 status = ntohl(*p++);
891 p = xdr_decode_post_op_attr(p, res->fattr);
892
893 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300894 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
Chuck Leverc957c522007-10-26 13:31:57 -0400896 /* Decode reply count and EOF flag. NFSv3 is somewhat redundant
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 * in that it puts the count both in the res struct and in the
898 * opaque data count. */
899 count = ntohl(*p++);
900 res->eof = ntohl(*p++);
901 ocount = ntohl(*p++);
902
903 if (ocount != count) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400904 dprintk("NFS: READ count doesn't match RPC opaque count.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 return -errno_NFSERR_IO;
906 }
907
908 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
909 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400910 dprintk("NFS: READ reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400911 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 return -errno_NFSERR_IO;
913 } else if (iov->iov_len != hdrlen) {
914 dprintk("NFS: READ header is short. iovec will be shifted.\n");
915 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
916 }
917
918 recvd = req->rq_rcv_buf.len - hdrlen;
919 if (count > recvd) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400920 dprintk("NFS: server cheating in read reply: "
Chuck Leverc957c522007-10-26 13:31:57 -0400921 "count %u > recvd %u\n", count, recvd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 count = recvd;
923 res->eof = 0;
924 }
925
926 if (count < res->count)
927 res->count = count;
928
929 return count;
930}
931
932/*
933 * Decode WRITE response
934 */
935static int
Al Virod61005a62006-10-19 23:28:48 -0700936nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937{
938 int status;
939
940 status = ntohl(*p++);
941 p = xdr_decode_wcc_data(p, res->fattr);
942
943 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300944 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945
946 res->count = ntohl(*p++);
947 res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
948 res->verf->verifier[0] = *p++;
949 res->verf->verifier[1] = *p++;
950
951 return res->count;
952}
953
954/*
955 * Decode a CREATE response
956 */
957static int
Al Virod61005a62006-10-19 23:28:48 -0700958nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959{
960 int status;
961
962 status = ntohl(*p++);
963 if (status == 0) {
964 if (*p++) {
965 if (!(p = xdr_decode_fhandle(p, res->fh)))
966 return -errno_NFSERR_IO;
967 p = xdr_decode_post_op_attr(p, res->fattr);
968 } else {
969 memset(res->fh, 0, sizeof(*res->fh));
970 /* Do decode post_op_attr but set it to NULL */
971 p = xdr_decode_post_op_attr(p, res->fattr);
972 res->fattr->valid = 0;
973 }
974 } else {
Benny Halevy856dff32008-03-31 17:39:06 +0300975 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 }
977 p = xdr_decode_wcc_data(p, res->dir_attr);
978 return status;
979}
980
981/*
982 * Decode RENAME reply
983 */
984static int
Al Virod61005a62006-10-19 23:28:48 -0700985nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs3_renameres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986{
987 int status;
988
989 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300990 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 p = xdr_decode_wcc_data(p, res->fromattr);
992 p = xdr_decode_wcc_data(p, res->toattr);
993 return status;
994}
995
996/*
997 * Decode LINK reply
998 */
999static int
Al Virod61005a62006-10-19 23:28:48 -07001000nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001{
1002 int status;
1003
1004 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001005 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 p = xdr_decode_post_op_attr(p, res->fattr);
1007 p = xdr_decode_wcc_data(p, res->dir_attr);
1008 return status;
1009}
1010
1011/*
1012 * Decode FSSTAT reply
1013 */
1014static int
Al Virod61005a62006-10-19 23:28:48 -07001015nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016{
1017 int status;
1018
1019 status = ntohl(*p++);
1020
1021 p = xdr_decode_post_op_attr(p, res->fattr);
1022 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001023 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024
1025 p = xdr_decode_hyper(p, &res->tbytes);
1026 p = xdr_decode_hyper(p, &res->fbytes);
1027 p = xdr_decode_hyper(p, &res->abytes);
1028 p = xdr_decode_hyper(p, &res->tfiles);
1029 p = xdr_decode_hyper(p, &res->ffiles);
1030 p = xdr_decode_hyper(p, &res->afiles);
1031
1032 /* ignore invarsec */
1033 return 0;
1034}
1035
1036/*
1037 * Decode FSINFO reply
1038 */
1039static int
Al Virod61005a62006-10-19 23:28:48 -07001040nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041{
1042 int status;
1043
1044 status = ntohl(*p++);
1045
1046 p = xdr_decode_post_op_attr(p, res->fattr);
1047 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001048 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049
1050 res->rtmax = ntohl(*p++);
1051 res->rtpref = ntohl(*p++);
1052 res->rtmult = ntohl(*p++);
1053 res->wtmax = ntohl(*p++);
1054 res->wtpref = ntohl(*p++);
1055 res->wtmult = ntohl(*p++);
1056 res->dtpref = ntohl(*p++);
1057 p = xdr_decode_hyper(p, &res->maxfilesize);
1058
1059 /* ignore time_delta and properties */
1060 res->lease_time = 0;
1061 return 0;
1062}
1063
1064/*
1065 * Decode PATHCONF reply
1066 */
1067static int
Al Virod61005a62006-10-19 23:28:48 -07001068nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069{
1070 int status;
1071
1072 status = ntohl(*p++);
1073
1074 p = xdr_decode_post_op_attr(p, res->fattr);
1075 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001076 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 res->max_link = ntohl(*p++);
1078 res->max_namelen = ntohl(*p++);
1079
1080 /* ignore remaining fields */
1081 return 0;
1082}
1083
1084/*
1085 * Decode COMMIT reply
1086 */
1087static int
Al Virod61005a62006-10-19 23:28:48 -07001088nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089{
1090 int status;
1091
1092 status = ntohl(*p++);
1093 p = xdr_decode_wcc_data(p, res->fattr);
1094 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001095 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096
1097 res->verf->verifier[0] = *p++;
1098 res->verf->verifier[1] = *p++;
1099 return 0;
1100}
1101
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001102#ifdef CONFIG_NFS_V3_ACL
1103/*
1104 * Decode GETACL reply
1105 */
1106static int
Al Virod61005a62006-10-19 23:28:48 -07001107nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001108 struct nfs3_getaclres *res)
1109{
1110 struct xdr_buf *buf = &req->rq_rcv_buf;
1111 int status = ntohl(*p++);
1112 struct posix_acl **acl;
1113 unsigned int *aclcnt;
1114 int err, base;
1115
1116 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001117 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001118 p = xdr_decode_post_op_attr(p, res->fattr);
1119 res->mask = ntohl(*p++);
1120 if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
1121 return -EINVAL;
1122 base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
1123
1124 acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
1125 aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
1126 err = nfsacl_decode(buf, base, aclcnt, acl);
1127
1128 acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
1129 aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
1130 if (err > 0)
1131 err = nfsacl_decode(buf, base + err, aclcnt, acl);
1132 return (err > 0) ? 0 : err;
1133}
1134
1135/*
1136 * Decode setacl reply.
1137 */
1138static int
Al Virod61005a62006-10-19 23:28:48 -07001139nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001140{
1141 int status = ntohl(*p++);
1142
1143 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +03001144 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001145 xdr_decode_post_op_attr(p, fattr);
1146 return 0;
1147}
1148#endif /* CONFIG_NFS_V3_ACL */
1149
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150#define PROC(proc, argtype, restype, timer) \
1151[NFS3PROC_##proc] = { \
1152 .p_proc = NFS3PROC_##proc, \
1153 .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
1154 .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
Chuck Lever2bea90d2007-03-29 16:47:53 -04001155 .p_arglen = NFS3_##argtype##_sz, \
1156 .p_replen = NFS3_##restype##_sz, \
Chuck Levercc0175c2006-03-20 13:44:22 -05001157 .p_timer = timer, \
1158 .p_statidx = NFS3PROC_##proc, \
1159 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 }
1161
1162struct rpc_procinfo nfs3_procedures[] = {
1163 PROC(GETATTR, fhandle, attrstat, 1),
1164 PROC(SETATTR, sattrargs, wccstat, 0),
1165 PROC(LOOKUP, diropargs, lookupres, 2),
1166 PROC(ACCESS, accessargs, accessres, 1),
1167 PROC(READLINK, readlinkargs, readlinkres, 3),
1168 PROC(READ, readargs, readres, 3),
1169 PROC(WRITE, writeargs, writeres, 4),
1170 PROC(CREATE, createargs, createres, 0),
1171 PROC(MKDIR, mkdirargs, createres, 0),
1172 PROC(SYMLINK, symlinkargs, createres, 0),
1173 PROC(MKNOD, mknodargs, createres, 0),
Trond Myklebust4fdc17b2007-07-14 15:39:57 -04001174 PROC(REMOVE, removeargs, removeres, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 PROC(RMDIR, diropargs, wccstat, 0),
1176 PROC(RENAME, renameargs, renameres, 0),
1177 PROC(LINK, linkargs, linkres, 0),
1178 PROC(READDIR, readdirargs, readdirres, 3),
1179 PROC(READDIRPLUS, readdirargs, readdirres, 3),
1180 PROC(FSSTAT, fhandle, fsstatres, 0),
1181 PROC(FSINFO, fhandle, fsinfores, 0),
1182 PROC(PATHCONF, fhandle, pathconfres, 0),
1183 PROC(COMMIT, commitargs, commitres, 5),
1184};
1185
1186struct rpc_version nfs_version3 = {
1187 .number = 3,
Tobias Klausere8c96f82006-03-24 03:15:34 -08001188 .nrprocs = ARRAY_SIZE(nfs3_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 .procs = nfs3_procedures
1190};
1191
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001192#ifdef CONFIG_NFS_V3_ACL
1193static struct rpc_procinfo nfs3_acl_procedures[] = {
1194 [ACLPROC3_GETACL] = {
1195 .p_proc = ACLPROC3_GETACL,
1196 .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
1197 .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001198 .p_arglen = ACL3_getaclargs_sz,
1199 .p_replen = ACL3_getaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001200 .p_timer = 1,
Chuck Levercc0175c2006-03-20 13:44:22 -05001201 .p_name = "GETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001202 },
1203 [ACLPROC3_SETACL] = {
1204 .p_proc = ACLPROC3_SETACL,
1205 .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
1206 .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001207 .p_arglen = ACL3_setaclargs_sz,
1208 .p_replen = ACL3_setaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001209 .p_timer = 0,
Chuck Levercc0175c2006-03-20 13:44:22 -05001210 .p_name = "SETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001211 },
1212};
1213
1214struct rpc_version nfsacl_version3 = {
1215 .number = 3,
1216 .nrprocs = sizeof(nfs3_acl_procedures)/
1217 sizeof(nfs3_acl_procedures[0]),
1218 .procs = nfs3_acl_procedures,
1219};
1220#endif /* CONFIG_NFS_V3_ACL */