blob: 616d3267b7e7b545d02d0bb88c02a48e2cbe6e80 [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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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 Virod61005a2006-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;
509 int hdrlen, recvd;
510 int status, nr;
511 unsigned int len, pglen;
Al Virod61005a2006-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)
518 return -nfs_stat_to_errno(status);
519 /* 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:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 "length %d > %Zu\n", hdrlen, iov->iov_len);
531 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 Virod61005a2006-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;
545 for (nr = 0; *p++; nr++) {
546 if (p + 3 > end)
547 goto short_pkt;
548 p += 2; /* inode # */
549 len = ntohl(*p++); /* string length */
550 p += XDR_QUADLEN(len) + 2; /* name + cookie */
551 if (len > NFS3_MAXNAMLEN) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400552 dprintk("NFS: giant filename in readdir (len %x)!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 len);
554 goto err_unmap;
555 }
556
557 if (res->plus) {
558 /* post_op_attr */
559 if (p + 2 > end)
560 goto short_pkt;
561 if (*p++) {
562 p += 21;
563 if (p + 1 > end)
564 goto short_pkt;
565 }
566 /* post_op_fh3 */
567 if (*p++) {
568 if (p + 1 > end)
569 goto short_pkt;
570 len = ntohl(*p++);
571 if (len > NFS3_FHSIZE) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400572 dprintk("NFS: giant filehandle in "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 "readdir (len %x)!\n", len);
574 goto err_unmap;
575 }
576 p += XDR_QUADLEN(len);
577 }
578 }
579
580 if (p + 2 > end)
581 goto short_pkt;
582 entry = p;
583 }
584 if (!nr && (entry[0] != 0 || entry[1] == 0))
585 goto short_pkt;
586 out:
587 kunmap_atomic(kaddr, KM_USER0);
588 return nr;
589 short_pkt:
590 entry[0] = entry[1] = 0;
591 /* truncate listing ? */
592 if (!nr) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400593 dprintk("NFS: readdir reply truncated!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 entry[1] = 1;
595 }
596 goto out;
597err_unmap:
598 nr = -errno_NFSERR_IO;
599 goto out;
600}
601
Al Viro0dbb4c62006-10-19 23:28:49 -0700602__be32 *
603nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604{
605 struct nfs_entry old = *entry;
606
607 if (!*p++) {
608 if (!*p)
609 return ERR_PTR(-EAGAIN);
610 entry->eof = 1;
611 return ERR_PTR(-EBADCOOKIE);
612 }
613
614 p = xdr_decode_hyper(p, &entry->ino);
615 entry->len = ntohl(*p++);
616 entry->name = (const char *) p;
617 p += XDR_QUADLEN(entry->len);
618 entry->prev_cookie = entry->cookie;
619 p = xdr_decode_hyper(p, &entry->cookie);
620
621 if (plus) {
622 entry->fattr->valid = 0;
623 p = xdr_decode_post_op_attr(p, entry->fattr);
624 /* In fact, a post_op_fh3: */
625 if (*p++) {
626 p = xdr_decode_fhandle(p, entry->fh);
627 /* Ugh -- server reply was truncated */
628 if (p == NULL) {
629 dprintk("NFS: FH truncated\n");
630 *entry = old;
631 return ERR_PTR(-EAGAIN);
632 }
633 } else
634 memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
635 }
636
637 entry->eof = !p[0] && p[1];
638 return p;
639}
640
641/*
642 * Encode COMMIT arguments
643 */
644static int
Al Virod61005a2006-10-19 23:28:48 -0700645nfs3_xdr_commitargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646{
647 p = xdr_encode_fhandle(p, args->fh);
648 p = xdr_encode_hyper(p, args->offset);
649 *p++ = htonl(args->count);
650 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
651 return 0;
652}
653
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000654#ifdef CONFIG_NFS_V3_ACL
655/*
656 * Encode GETACL arguments
657 */
658static int
Al Virod61005a2006-10-19 23:28:48 -0700659nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000660 struct nfs3_getaclargs *args)
661{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400662 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000663 unsigned int replen;
664
665 p = xdr_encode_fhandle(p, args->fh);
666 *p++ = htonl(args->mask);
667 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
668
669 if (args->mask & (NFS_ACL | NFS_DFACL)) {
670 /* Inline the page array */
671 replen = (RPC_REPHDRSIZE + auth->au_rslack +
672 ACL3_getaclres_sz) << 2;
673 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
674 NFSACL_MAXPAGES << PAGE_SHIFT);
675 }
676 return 0;
677}
678
679/*
680 * Encode SETACL arguments
681 */
682static int
Al Virod61005a2006-10-19 23:28:48 -0700683nfs3_xdr_setaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000684 struct nfs3_setaclargs *args)
685{
686 struct xdr_buf *buf = &req->rq_snd_buf;
687 unsigned int base, len_in_head, len = nfsacl_size(
688 (args->mask & NFS_ACL) ? args->acl_access : NULL,
689 (args->mask & NFS_DFACL) ? args->acl_default : NULL);
690 int count, err;
691
692 p = xdr_encode_fhandle(p, NFS_FH(args->inode));
693 *p++ = htonl(args->mask);
694 base = (char *)p - (char *)buf->head->iov_base;
695 /* put as much of the acls into head as possible. */
696 len_in_head = min_t(unsigned int, buf->head->iov_len - base, len);
697 len -= len_in_head;
Andreas Gruenbacher21348422005-06-22 17:16:28 +0000698 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p + (len_in_head >> 2));
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000699
700 for (count = 0; (count << PAGE_SHIFT) < len; count++) {
701 args->pages[count] = alloc_page(GFP_KERNEL);
702 if (!args->pages[count]) {
703 while (count)
704 __free_page(args->pages[--count]);
705 return -ENOMEM;
706 }
707 }
708 xdr_encode_pages(buf, args->pages, 0, len);
709
710 err = nfsacl_encode(buf, base, args->inode,
711 (args->mask & NFS_ACL) ?
712 args->acl_access : NULL, 1, 0);
713 if (err > 0)
714 err = nfsacl_encode(buf, base + err, args->inode,
715 (args->mask & NFS_DFACL) ?
716 args->acl_default : NULL, 1,
717 NFS_ACL_DEFAULT);
718 return (err > 0) ? 0 : err;
719}
720#endif /* CONFIG_NFS_V3_ACL */
721
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722/*
723 * NFS XDR decode functions
724 */
725
726/*
727 * Decode attrstat reply.
728 */
729static int
Al Virod61005a2006-10-19 23:28:48 -0700730nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731{
732 int status;
733
734 if ((status = ntohl(*p++)))
735 return -nfs_stat_to_errno(status);
736 xdr_decode_fattr(p, fattr);
737 return 0;
738}
739
740/*
741 * Decode status+wcc_data reply
742 * SATTR, REMOVE, RMDIR
743 */
744static int
Al Virod61005a2006-10-19 23:28:48 -0700745nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746{
747 int status;
748
749 if ((status = ntohl(*p++)))
750 status = -nfs_stat_to_errno(status);
751 xdr_decode_wcc_data(p, fattr);
752 return status;
753}
754
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400755static int
756nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
757{
758 return nfs3_xdr_wccstat(req, p, &res->dir_attr);
759}
760
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761/*
762 * Decode LOOKUP reply
763 */
764static int
Al Virod61005a2006-10-19 23:28:48 -0700765nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766{
767 int status;
768
769 if ((status = ntohl(*p++))) {
770 status = -nfs_stat_to_errno(status);
771 } else {
772 if (!(p = xdr_decode_fhandle(p, res->fh)))
773 return -errno_NFSERR_IO;
774 p = xdr_decode_post_op_attr(p, res->fattr);
775 }
776 xdr_decode_post_op_attr(p, res->dir_attr);
777 return status;
778}
779
780/*
781 * Decode ACCESS reply
782 */
783static int
Al Virod61005a2006-10-19 23:28:48 -0700784nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785{
786 int status = ntohl(*p++);
787
788 p = xdr_decode_post_op_attr(p, res->fattr);
789 if (status)
790 return -nfs_stat_to_errno(status);
791 res->access = ntohl(*p++);
792 return 0;
793}
794
795static int
Al Virod61005a2006-10-19 23:28:48 -0700796nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400798 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 unsigned int replen;
800
801 p = xdr_encode_fhandle(p, args->fh);
802 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
803
804 /* Inline the page array */
805 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
806 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
807 return 0;
808}
809
810/*
811 * Decode READLINK reply
812 */
813static int
Al Virod61005a2006-10-19 23:28:48 -0700814nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815{
816 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
817 struct kvec *iov = rcvbuf->head;
818 int hdrlen, len, recvd;
819 char *kaddr;
820 int status;
821
822 status = ntohl(*p++);
823 p = xdr_decode_post_op_attr(p, fattr);
824
825 if (status != 0)
826 return -nfs_stat_to_errno(status);
827
828 /* Convert length of symlink */
829 len = ntohl(*p++);
830 if (len >= rcvbuf->page_len || len <= 0) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400831 dprintk("nfs: server returned giant symlink!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 return -ENAMETOOLONG;
833 }
834
835 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
836 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400837 dprintk("NFS: READLINK reply header overflowed:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 "length %d > %Zu\n", hdrlen, iov->iov_len);
839 return -errno_NFSERR_IO;
840 } else if (iov->iov_len != hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400841 dprintk("NFS: READLINK header is short. "
842 "iovec will be shifted.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
844 }
845 recvd = req->rq_rcv_buf.len - hdrlen;
846 if (recvd < len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400847 dprintk("NFS: server cheating in readlink reply: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 "count %u > recvd %u\n", len, recvd);
849 return -EIO;
850 }
851
852 /* NULL terminate the string we got */
853 kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
854 kaddr[len+rcvbuf->page_base] = '\0';
855 kunmap_atomic(kaddr, KM_USER0);
856 return 0;
857}
858
859/*
860 * Decode READ reply
861 */
862static int
Al Virod61005a2006-10-19 23:28:48 -0700863nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864{
865 struct kvec *iov = req->rq_rcv_buf.head;
866 int status, count, ocount, recvd, hdrlen;
867
868 status = ntohl(*p++);
869 p = xdr_decode_post_op_attr(p, res->fattr);
870
871 if (status != 0)
872 return -nfs_stat_to_errno(status);
873
874 /* Decode reply could and EOF flag. NFSv3 is somewhat redundant
875 * in that it puts the count both in the res struct and in the
876 * opaque data count. */
877 count = ntohl(*p++);
878 res->eof = ntohl(*p++);
879 ocount = ntohl(*p++);
880
881 if (ocount != count) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400882 dprintk("NFS: READ count doesn't match RPC opaque count.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 return -errno_NFSERR_IO;
884 }
885
886 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
887 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400888 dprintk("NFS: READ reply header overflowed:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 "length %d > %Zu\n", hdrlen, iov->iov_len);
890 return -errno_NFSERR_IO;
891 } else if (iov->iov_len != hdrlen) {
892 dprintk("NFS: READ header is short. iovec will be shifted.\n");
893 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
894 }
895
896 recvd = req->rq_rcv_buf.len - hdrlen;
897 if (count > recvd) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400898 dprintk("NFS: server cheating in read reply: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 "count %d > recvd %d\n", count, recvd);
900 count = recvd;
901 res->eof = 0;
902 }
903
904 if (count < res->count)
905 res->count = count;
906
907 return count;
908}
909
910/*
911 * Decode WRITE response
912 */
913static int
Al Virod61005a2006-10-19 23:28:48 -0700914nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915{
916 int status;
917
918 status = ntohl(*p++);
919 p = xdr_decode_wcc_data(p, res->fattr);
920
921 if (status != 0)
922 return -nfs_stat_to_errno(status);
923
924 res->count = ntohl(*p++);
925 res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
926 res->verf->verifier[0] = *p++;
927 res->verf->verifier[1] = *p++;
928
929 return res->count;
930}
931
932/*
933 * Decode a CREATE response
934 */
935static int
Al Virod61005a2006-10-19 23:28:48 -0700936nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937{
938 int status;
939
940 status = ntohl(*p++);
941 if (status == 0) {
942 if (*p++) {
943 if (!(p = xdr_decode_fhandle(p, res->fh)))
944 return -errno_NFSERR_IO;
945 p = xdr_decode_post_op_attr(p, res->fattr);
946 } else {
947 memset(res->fh, 0, sizeof(*res->fh));
948 /* Do decode post_op_attr but set it to NULL */
949 p = xdr_decode_post_op_attr(p, res->fattr);
950 res->fattr->valid = 0;
951 }
952 } else {
953 status = -nfs_stat_to_errno(status);
954 }
955 p = xdr_decode_wcc_data(p, res->dir_attr);
956 return status;
957}
958
959/*
960 * Decode RENAME reply
961 */
962static int
Al Virod61005a2006-10-19 23:28:48 -0700963nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs3_renameres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964{
965 int status;
966
967 if ((status = ntohl(*p++)) != 0)
968 status = -nfs_stat_to_errno(status);
969 p = xdr_decode_wcc_data(p, res->fromattr);
970 p = xdr_decode_wcc_data(p, res->toattr);
971 return status;
972}
973
974/*
975 * Decode LINK reply
976 */
977static int
Al Virod61005a2006-10-19 23:28:48 -0700978nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979{
980 int status;
981
982 if ((status = ntohl(*p++)) != 0)
983 status = -nfs_stat_to_errno(status);
984 p = xdr_decode_post_op_attr(p, res->fattr);
985 p = xdr_decode_wcc_data(p, res->dir_attr);
986 return status;
987}
988
989/*
990 * Decode FSSTAT reply
991 */
992static int
Al Virod61005a2006-10-19 23:28:48 -0700993nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994{
995 int status;
996
997 status = ntohl(*p++);
998
999 p = xdr_decode_post_op_attr(p, res->fattr);
1000 if (status != 0)
1001 return -nfs_stat_to_errno(status);
1002
1003 p = xdr_decode_hyper(p, &res->tbytes);
1004 p = xdr_decode_hyper(p, &res->fbytes);
1005 p = xdr_decode_hyper(p, &res->abytes);
1006 p = xdr_decode_hyper(p, &res->tfiles);
1007 p = xdr_decode_hyper(p, &res->ffiles);
1008 p = xdr_decode_hyper(p, &res->afiles);
1009
1010 /* ignore invarsec */
1011 return 0;
1012}
1013
1014/*
1015 * Decode FSINFO reply
1016 */
1017static int
Al Virod61005a2006-10-19 23:28:48 -07001018nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019{
1020 int status;
1021
1022 status = ntohl(*p++);
1023
1024 p = xdr_decode_post_op_attr(p, res->fattr);
1025 if (status != 0)
1026 return -nfs_stat_to_errno(status);
1027
1028 res->rtmax = ntohl(*p++);
1029 res->rtpref = ntohl(*p++);
1030 res->rtmult = ntohl(*p++);
1031 res->wtmax = ntohl(*p++);
1032 res->wtpref = ntohl(*p++);
1033 res->wtmult = ntohl(*p++);
1034 res->dtpref = ntohl(*p++);
1035 p = xdr_decode_hyper(p, &res->maxfilesize);
1036
1037 /* ignore time_delta and properties */
1038 res->lease_time = 0;
1039 return 0;
1040}
1041
1042/*
1043 * Decode PATHCONF reply
1044 */
1045static int
Al Virod61005a2006-10-19 23:28:48 -07001046nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047{
1048 int status;
1049
1050 status = ntohl(*p++);
1051
1052 p = xdr_decode_post_op_attr(p, res->fattr);
1053 if (status != 0)
1054 return -nfs_stat_to_errno(status);
1055 res->max_link = ntohl(*p++);
1056 res->max_namelen = ntohl(*p++);
1057
1058 /* ignore remaining fields */
1059 return 0;
1060}
1061
1062/*
1063 * Decode COMMIT reply
1064 */
1065static int
Al Virod61005a2006-10-19 23:28:48 -07001066nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067{
1068 int status;
1069
1070 status = ntohl(*p++);
1071 p = xdr_decode_wcc_data(p, res->fattr);
1072 if (status != 0)
1073 return -nfs_stat_to_errno(status);
1074
1075 res->verf->verifier[0] = *p++;
1076 res->verf->verifier[1] = *p++;
1077 return 0;
1078}
1079
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001080#ifdef CONFIG_NFS_V3_ACL
1081/*
1082 * Decode GETACL reply
1083 */
1084static int
Al Virod61005a2006-10-19 23:28:48 -07001085nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001086 struct nfs3_getaclres *res)
1087{
1088 struct xdr_buf *buf = &req->rq_rcv_buf;
1089 int status = ntohl(*p++);
1090 struct posix_acl **acl;
1091 unsigned int *aclcnt;
1092 int err, base;
1093
1094 if (status != 0)
1095 return -nfs_stat_to_errno(status);
1096 p = xdr_decode_post_op_attr(p, res->fattr);
1097 res->mask = ntohl(*p++);
1098 if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
1099 return -EINVAL;
1100 base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
1101
1102 acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
1103 aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
1104 err = nfsacl_decode(buf, base, aclcnt, acl);
1105
1106 acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
1107 aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
1108 if (err > 0)
1109 err = nfsacl_decode(buf, base + err, aclcnt, acl);
1110 return (err > 0) ? 0 : err;
1111}
1112
1113/*
1114 * Decode setacl reply.
1115 */
1116static int
Al Virod61005a2006-10-19 23:28:48 -07001117nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001118{
1119 int status = ntohl(*p++);
1120
1121 if (status)
1122 return -nfs_stat_to_errno(status);
1123 xdr_decode_post_op_attr(p, fattr);
1124 return 0;
1125}
1126#endif /* CONFIG_NFS_V3_ACL */
1127
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128#define PROC(proc, argtype, restype, timer) \
1129[NFS3PROC_##proc] = { \
1130 .p_proc = NFS3PROC_##proc, \
1131 .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
1132 .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
Chuck Lever2bea90d2007-03-29 16:47:53 -04001133 .p_arglen = NFS3_##argtype##_sz, \
1134 .p_replen = NFS3_##restype##_sz, \
Chuck Levercc0175c2006-03-20 13:44:22 -05001135 .p_timer = timer, \
1136 .p_statidx = NFS3PROC_##proc, \
1137 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 }
1139
1140struct rpc_procinfo nfs3_procedures[] = {
1141 PROC(GETATTR, fhandle, attrstat, 1),
1142 PROC(SETATTR, sattrargs, wccstat, 0),
1143 PROC(LOOKUP, diropargs, lookupres, 2),
1144 PROC(ACCESS, accessargs, accessres, 1),
1145 PROC(READLINK, readlinkargs, readlinkres, 3),
1146 PROC(READ, readargs, readres, 3),
1147 PROC(WRITE, writeargs, writeres, 4),
1148 PROC(CREATE, createargs, createres, 0),
1149 PROC(MKDIR, mkdirargs, createres, 0),
1150 PROC(SYMLINK, symlinkargs, createres, 0),
1151 PROC(MKNOD, mknodargs, createres, 0),
Trond Myklebust4fdc17b2007-07-14 15:39:57 -04001152 PROC(REMOVE, removeargs, removeres, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 PROC(RMDIR, diropargs, wccstat, 0),
1154 PROC(RENAME, renameargs, renameres, 0),
1155 PROC(LINK, linkargs, linkres, 0),
1156 PROC(READDIR, readdirargs, readdirres, 3),
1157 PROC(READDIRPLUS, readdirargs, readdirres, 3),
1158 PROC(FSSTAT, fhandle, fsstatres, 0),
1159 PROC(FSINFO, fhandle, fsinfores, 0),
1160 PROC(PATHCONF, fhandle, pathconfres, 0),
1161 PROC(COMMIT, commitargs, commitres, 5),
1162};
1163
1164struct rpc_version nfs_version3 = {
1165 .number = 3,
Tobias Klausere8c96f82006-03-24 03:15:34 -08001166 .nrprocs = ARRAY_SIZE(nfs3_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 .procs = nfs3_procedures
1168};
1169
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001170#ifdef CONFIG_NFS_V3_ACL
1171static struct rpc_procinfo nfs3_acl_procedures[] = {
1172 [ACLPROC3_GETACL] = {
1173 .p_proc = ACLPROC3_GETACL,
1174 .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
1175 .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001176 .p_arglen = ACL3_getaclargs_sz,
1177 .p_replen = ACL3_getaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001178 .p_timer = 1,
Chuck Levercc0175c2006-03-20 13:44:22 -05001179 .p_name = "GETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001180 },
1181 [ACLPROC3_SETACL] = {
1182 .p_proc = ACLPROC3_SETACL,
1183 .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
1184 .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001185 .p_arglen = ACL3_setaclargs_sz,
1186 .p_replen = ACL3_setaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001187 .p_timer = 0,
Chuck Levercc0175c2006-03-20 13:44:22 -05001188 .p_name = "SETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001189 },
1190};
1191
1192struct rpc_version nfsacl_version3 = {
1193 .number = 3,
1194 .nrprocs = sizeof(nfs3_acl_procedures)/
1195 sizeof(nfs3_acl_procedures[0]),
1196 .procs = nfs3_acl_procedures,
1197};
1198#endif /* CONFIG_NFS_V3_ACL */