blob: c0f7d02aced9f6cac57b2ebffc8ff8d7dd351cb7 [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 */
Trond Myklebust9e6e70f2009-03-11 14:10:24 -0400180 fattr->valid |= 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);
Trond Myklebust9e6e70f2009-03-11 14:10:24 -0400236 fattr->valid |= NFS_ATTR_FATTR_PRESIZE
237 | NFS_ATTR_FATTR_PREMTIME
238 | NFS_ATTR_FATTR_PRECTIME;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 return p;
240}
241
Al Virod61005a2006-10-19 23:28:48 -0700242static inline __be32 *
243xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244{
245 if (*p++)
246 p = xdr_decode_fattr(p, fattr);
247 return p;
248}
249
Al Virod61005a2006-10-19 23:28:48 -0700250static inline __be32 *
251xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252{
253 if (*p++)
254 return xdr_decode_wcc_attr(p, fattr);
255 return p;
256}
257
258
Al Virod61005a2006-10-19 23:28:48 -0700259static inline __be32 *
260xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261{
262 p = xdr_decode_pre_op_attr(p, fattr);
263 return xdr_decode_post_op_attr(p, fattr);
264}
265
266/*
267 * NFS encode functions
268 */
269
270/*
271 * Encode file handle argument
272 */
273static int
Al Virod61005a2006-10-19 23:28:48 -0700274nfs3_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275{
276 p = xdr_encode_fhandle(p, fh);
277 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
278 return 0;
279}
280
281/*
282 * Encode SETATTR arguments
283 */
284static int
Al Virod61005a2006-10-19 23:28:48 -0700285nfs3_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs3_sattrargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
287 p = xdr_encode_fhandle(p, args->fh);
288 p = xdr_encode_sattr(p, args->sattr);
289 *p++ = htonl(args->guard);
290 if (args->guard)
291 p = xdr_encode_time3(p, &args->guardtime);
292 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
293 return 0;
294}
295
296/*
297 * Encode directory ops argument
298 */
299static int
Al Virod61005a2006-10-19 23:28:48 -0700300nfs3_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs3_diropargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301{
302 p = xdr_encode_fhandle(p, args->fh);
303 p = xdr_encode_array(p, args->name, args->len);
304 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
305 return 0;
306}
307
308/*
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400309 * Encode REMOVE argument
310 */
311static int
312nfs3_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
313{
314 p = xdr_encode_fhandle(p, args->fh);
315 p = xdr_encode_array(p, args->name.name, args->name.len);
316 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
317 return 0;
318}
319
320/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 * Encode access() argument
322 */
323static int
Al Virod61005a2006-10-19 23:28:48 -0700324nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325{
326 p = xdr_encode_fhandle(p, args->fh);
327 *p++ = htonl(args->access);
328 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
329 return 0;
330}
331
332/*
333 * Arguments to a READ call. Since we read data directly into the page
334 * cache, we also set up the reply iovec here so that iov[1] points
335 * exactly to the page we want to fetch.
336 */
337static int
Al Virod61005a2006-10-19 23:28:48 -0700338nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400340 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 unsigned int replen;
342 u32 count = args->count;
343
344 p = xdr_encode_fhandle(p, args->fh);
345 p = xdr_encode_hyper(p, args->offset);
346 *p++ = htonl(count);
347 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
348
349 /* Inline the page array */
350 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
351 xdr_inline_pages(&req->rq_rcv_buf, replen,
352 args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400353 req->rq_rcv_buf.flags |= XDRBUF_READ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 return 0;
355}
356
357/*
358 * Write arguments. Splice the buffer to be written into the iovec.
359 */
360static int
Al Virod61005a2006-10-19 23:28:48 -0700361nfs3_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362{
363 struct xdr_buf *sndbuf = &req->rq_snd_buf;
364 u32 count = args->count;
365
366 p = xdr_encode_fhandle(p, args->fh);
367 p = xdr_encode_hyper(p, args->offset);
368 *p++ = htonl(count);
369 *p++ = htonl(args->stable);
370 *p++ = htonl(count);
371 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
372
373 /* Copy the page array */
374 xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400375 sndbuf->flags |= XDRBUF_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 return 0;
377}
378
379/*
380 * Encode CREATE arguments
381 */
382static int
Al Virod61005a2006-10-19 23:28:48 -0700383nfs3_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs3_createargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384{
385 p = xdr_encode_fhandle(p, args->fh);
386 p = xdr_encode_array(p, args->name, args->len);
387
388 *p++ = htonl(args->createmode);
389 if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
390 *p++ = args->verifier[0];
391 *p++ = args->verifier[1];
392 } else
393 p = xdr_encode_sattr(p, args->sattr);
394
395 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
396 return 0;
397}
398
399/*
400 * Encode MKDIR arguments
401 */
402static int
Al Virod61005a2006-10-19 23:28:48 -0700403nfs3_xdr_mkdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mkdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404{
405 p = xdr_encode_fhandle(p, args->fh);
406 p = xdr_encode_array(p, args->name, args->len);
407 p = xdr_encode_sattr(p, args->sattr);
408 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
409 return 0;
410}
411
412/*
413 * Encode SYMLINK arguments
414 */
415static int
Al Virod61005a2006-10-19 23:28:48 -0700416nfs3_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_symlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417{
418 p = xdr_encode_fhandle(p, args->fromfh);
419 p = xdr_encode_array(p, args->fromname, args->fromlen);
420 p = xdr_encode_sattr(p, args->sattr);
Chuck Lever94a6d752006-08-22 20:06:23 -0400421 *p++ = htonl(args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
Chuck Lever94a6d752006-08-22 20:06:23 -0400423
424 /* Copy the page */
425 xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 return 0;
427}
428
429/*
430 * Encode MKNOD arguments
431 */
432static int
Al Virod61005a2006-10-19 23:28:48 -0700433nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434{
435 p = xdr_encode_fhandle(p, args->fh);
436 p = xdr_encode_array(p, args->name, args->len);
437 *p++ = htonl(args->type);
438 p = xdr_encode_sattr(p, args->sattr);
439 if (args->type == NF3CHR || args->type == NF3BLK) {
440 *p++ = htonl(MAJOR(args->rdev));
441 *p++ = htonl(MINOR(args->rdev));
442 }
443
444 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
445 return 0;
446}
447
448/*
449 * Encode RENAME arguments
450 */
451static int
Al Virod61005a2006-10-19 23:28:48 -0700452nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs3_renameargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453{
454 p = xdr_encode_fhandle(p, args->fromfh);
455 p = xdr_encode_array(p, args->fromname, args->fromlen);
456 p = xdr_encode_fhandle(p, args->tofh);
457 p = xdr_encode_array(p, args->toname, args->tolen);
458 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
459 return 0;
460}
461
462/*
463 * Encode LINK arguments
464 */
465static int
Al Virod61005a2006-10-19 23:28:48 -0700466nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467{
468 p = xdr_encode_fhandle(p, args->fromfh);
469 p = xdr_encode_fhandle(p, args->tofh);
470 p = xdr_encode_array(p, args->toname, args->tolen);
471 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
472 return 0;
473}
474
475/*
476 * Encode arguments to readdir call
477 */
478static int
Al Virod61005a2006-10-19 23:28:48 -0700479nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400481 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 unsigned int replen;
483 u32 count = args->count;
484
485 p = xdr_encode_fhandle(p, args->fh);
486 p = xdr_encode_hyper(p, args->cookie);
487 *p++ = args->verf[0];
488 *p++ = args->verf[1];
489 if (args->plus) {
490 /* readdirplus: need dircount + buffer size.
491 * We just make sure we make dircount big enough */
492 *p++ = htonl(count >> 3);
493 }
494 *p++ = htonl(count);
495 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
496
497 /* Inline the page array */
498 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
499 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
500 return 0;
501}
502
503/*
504 * Decode the result of a readdir call.
505 * We just check for syntactical correctness.
506 */
507static int
Al Virod61005a2006-10-19 23:28:48 -0700508nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509{
510 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
511 struct kvec *iov = rcvbuf->head;
512 struct page **page;
Chuck Leverc957c522007-10-26 13:31:57 -0400513 size_t hdrlen;
514 u32 len, recvd, pglen;
Jeff Layton643f8112008-02-22 14:50:00 -0500515 int status, nr = 0;
Al Virod61005a2006-10-19 23:28:48 -0700516 __be32 *entry, *end, *kaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
518 status = ntohl(*p++);
519 /* Decode post_op_attrs */
520 p = xdr_decode_post_op_attr(p, res->dir_attr);
521 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300522 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 /* Decode verifier cookie */
524 if (res->verf) {
525 res->verf[0] = *p++;
526 res->verf[1] = *p++;
527 } else {
528 p += 2;
529 }
530
531 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
532 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400533 dprintk("NFS: READDIR reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400534 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 return -errno_NFSERR_IO;
536 } else if (iov->iov_len != hdrlen) {
537 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
538 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
539 }
540
541 pglen = rcvbuf->page_len;
542 recvd = rcvbuf->len - hdrlen;
543 if (pglen > recvd)
544 pglen = recvd;
545 page = rcvbuf->pages;
Al Virod61005a2006-10-19 23:28:48 -0700546 kaddr = p = kmap_atomic(*page, KM_USER0);
547 end = (__be32 *)((char *)p + pglen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 entry = p;
Jeff Layton643f8112008-02-22 14:50:00 -0500549
550 /* Make sure the packet actually has a value_follows and EOF entry */
551 if ((entry + 1) > end)
552 goto short_pkt;
553
554 for (; *p++; nr++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 if (p + 3 > end)
556 goto short_pkt;
557 p += 2; /* inode # */
558 len = ntohl(*p++); /* string length */
559 p += XDR_QUADLEN(len) + 2; /* name + cookie */
560 if (len > NFS3_MAXNAMLEN) {
Chuck Leverc957c522007-10-26 13:31:57 -0400561 dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 len);
563 goto err_unmap;
564 }
565
566 if (res->plus) {
567 /* post_op_attr */
568 if (p + 2 > end)
569 goto short_pkt;
570 if (*p++) {
571 p += 21;
572 if (p + 1 > end)
573 goto short_pkt;
574 }
575 /* post_op_fh3 */
576 if (*p++) {
577 if (p + 1 > end)
578 goto short_pkt;
579 len = ntohl(*p++);
580 if (len > NFS3_FHSIZE) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400581 dprintk("NFS: giant filehandle in "
Chuck Leverc957c522007-10-26 13:31:57 -0400582 "readdir (len 0x%x)!\n", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 goto err_unmap;
584 }
585 p += XDR_QUADLEN(len);
586 }
587 }
588
589 if (p + 2 > end)
590 goto short_pkt;
591 entry = p;
592 }
Jeff Layton643f8112008-02-22 14:50:00 -0500593
594 /*
595 * Apparently some server sends responses that are a valid size, but
596 * contain no entries, and have value_follows==0 and EOF==0. For
597 * those, just set the EOF marker.
598 */
599 if (!nr && entry[1] == 0) {
600 dprintk("NFS: readdir reply truncated!\n");
601 entry[1] = 1;
602 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 out:
604 kunmap_atomic(kaddr, KM_USER0);
605 return nr;
606 short_pkt:
Jeff Layton643f8112008-02-22 14:50:00 -0500607 /*
608 * When we get a short packet there are 2 possibilities. We can
609 * return an error, or fix up the response to look like a valid
610 * response and return what we have so far. If there are no
611 * entries and the packet was short, then return -EIO. If there
612 * are valid entries in the response, return them and pretend that
613 * the call was successful, but incomplete. The caller can retry the
614 * readdir starting at the last cookie.
615 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 entry[0] = entry[1] = 0;
Jeff Layton643f8112008-02-22 14:50:00 -0500617 if (!nr)
618 nr = -errno_NFSERR_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 goto out;
620err_unmap:
621 nr = -errno_NFSERR_IO;
622 goto out;
623}
624
Al Viro0dbb4c62006-10-19 23:28:49 -0700625__be32 *
626nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627{
628 struct nfs_entry old = *entry;
629
630 if (!*p++) {
631 if (!*p)
632 return ERR_PTR(-EAGAIN);
633 entry->eof = 1;
634 return ERR_PTR(-EBADCOOKIE);
635 }
636
637 p = xdr_decode_hyper(p, &entry->ino);
638 entry->len = ntohl(*p++);
639 entry->name = (const char *) p;
640 p += XDR_QUADLEN(entry->len);
641 entry->prev_cookie = entry->cookie;
642 p = xdr_decode_hyper(p, &entry->cookie);
643
644 if (plus) {
645 entry->fattr->valid = 0;
646 p = xdr_decode_post_op_attr(p, entry->fattr);
647 /* In fact, a post_op_fh3: */
648 if (*p++) {
649 p = xdr_decode_fhandle(p, entry->fh);
650 /* Ugh -- server reply was truncated */
651 if (p == NULL) {
652 dprintk("NFS: FH truncated\n");
653 *entry = old;
654 return ERR_PTR(-EAGAIN);
655 }
656 } else
657 memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
658 }
659
660 entry->eof = !p[0] && p[1];
661 return p;
662}
663
664/*
665 * Encode COMMIT arguments
666 */
667static int
Al Virod61005a2006-10-19 23:28:48 -0700668nfs3_xdr_commitargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669{
670 p = xdr_encode_fhandle(p, args->fh);
671 p = xdr_encode_hyper(p, args->offset);
672 *p++ = htonl(args->count);
673 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
674 return 0;
675}
676
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000677#ifdef CONFIG_NFS_V3_ACL
678/*
679 * Encode GETACL arguments
680 */
681static int
Al Virod61005a2006-10-19 23:28:48 -0700682nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000683 struct nfs3_getaclargs *args)
684{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400685 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000686 unsigned int replen;
687
688 p = xdr_encode_fhandle(p, args->fh);
689 *p++ = htonl(args->mask);
690 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
691
692 if (args->mask & (NFS_ACL | NFS_DFACL)) {
693 /* Inline the page array */
694 replen = (RPC_REPHDRSIZE + auth->au_rslack +
695 ACL3_getaclres_sz) << 2;
696 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
697 NFSACL_MAXPAGES << PAGE_SHIFT);
698 }
699 return 0;
700}
701
702/*
703 * Encode SETACL arguments
704 */
705static int
Al Virod61005a2006-10-19 23:28:48 -0700706nfs3_xdr_setaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000707 struct nfs3_setaclargs *args)
708{
709 struct xdr_buf *buf = &req->rq_snd_buf;
Trond Myklebustae461412009-03-10 20:33:18 -0400710 unsigned int base;
711 int err;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000712
713 p = xdr_encode_fhandle(p, NFS_FH(args->inode));
714 *p++ = htonl(args->mask);
Trond Myklebustae461412009-03-10 20:33:18 -0400715 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
716 base = req->rq_slen;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000717
Trond Myklebustae461412009-03-10 20:33:18 -0400718 if (args->npages != 0)
719 xdr_encode_pages(buf, args->pages, 0, args->len);
720 else
721 req->rq_slen += args->len;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000722
723 err = nfsacl_encode(buf, base, args->inode,
724 (args->mask & NFS_ACL) ?
725 args->acl_access : NULL, 1, 0);
726 if (err > 0)
727 err = nfsacl_encode(buf, base + err, args->inode,
728 (args->mask & NFS_DFACL) ?
729 args->acl_default : NULL, 1,
730 NFS_ACL_DEFAULT);
731 return (err > 0) ? 0 : err;
732}
733#endif /* CONFIG_NFS_V3_ACL */
734
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735/*
736 * NFS XDR decode functions
737 */
738
739/*
740 * Decode attrstat reply.
741 */
742static int
Al Virod61005a2006-10-19 23:28:48 -0700743nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744{
745 int status;
746
747 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300748 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 xdr_decode_fattr(p, fattr);
750 return 0;
751}
752
753/*
754 * Decode status+wcc_data reply
755 * SATTR, REMOVE, RMDIR
756 */
757static int
Al Virod61005a2006-10-19 23:28:48 -0700758nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759{
760 int status;
761
762 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300763 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 xdr_decode_wcc_data(p, fattr);
765 return status;
766}
767
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400768static int
769nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
770{
771 return nfs3_xdr_wccstat(req, p, &res->dir_attr);
772}
773
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774/*
775 * Decode LOOKUP reply
776 */
777static int
Al Virod61005a2006-10-19 23:28:48 -0700778nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779{
780 int status;
781
782 if ((status = ntohl(*p++))) {
Benny Halevy856dff32008-03-31 17:39:06 +0300783 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 } else {
785 if (!(p = xdr_decode_fhandle(p, res->fh)))
786 return -errno_NFSERR_IO;
787 p = xdr_decode_post_op_attr(p, res->fattr);
788 }
789 xdr_decode_post_op_attr(p, res->dir_attr);
790 return status;
791}
792
793/*
794 * Decode ACCESS reply
795 */
796static int
Al Virod61005a2006-10-19 23:28:48 -0700797nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798{
799 int status = ntohl(*p++);
800
801 p = xdr_decode_post_op_attr(p, res->fattr);
802 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300803 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 res->access = ntohl(*p++);
805 return 0;
806}
807
808static int
Al Virod61005a2006-10-19 23:28:48 -0700809nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400811 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 unsigned int replen;
813
814 p = xdr_encode_fhandle(p, args->fh);
815 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
816
817 /* Inline the page array */
818 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
819 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
820 return 0;
821}
822
823/*
824 * Decode READLINK reply
825 */
826static int
Al Virod61005a2006-10-19 23:28:48 -0700827nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828{
829 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
830 struct kvec *iov = rcvbuf->head;
Chuck Leverc957c522007-10-26 13:31:57 -0400831 size_t hdrlen;
832 u32 len, recvd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 char *kaddr;
834 int status;
835
836 status = ntohl(*p++);
837 p = xdr_decode_post_op_attr(p, fattr);
838
839 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300840 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
842 /* Convert length of symlink */
843 len = ntohl(*p++);
Chuck Leverc957c522007-10-26 13:31:57 -0400844 if (len >= rcvbuf->page_len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400845 dprintk("nfs: server returned giant symlink!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 return -ENAMETOOLONG;
847 }
848
849 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
850 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400851 dprintk("NFS: READLINK reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400852 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 return -errno_NFSERR_IO;
854 } else if (iov->iov_len != hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400855 dprintk("NFS: READLINK header is short. "
856 "iovec will be shifted.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
858 }
859 recvd = req->rq_rcv_buf.len - hdrlen;
860 if (recvd < len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400861 dprintk("NFS: server cheating in readlink reply: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 "count %u > recvd %u\n", len, recvd);
863 return -EIO;
864 }
865
866 /* NULL terminate the string we got */
867 kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
868 kaddr[len+rcvbuf->page_base] = '\0';
869 kunmap_atomic(kaddr, KM_USER0);
870 return 0;
871}
872
873/*
874 * Decode READ reply
875 */
876static int
Al Virod61005a2006-10-19 23:28:48 -0700877nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878{
879 struct kvec *iov = req->rq_rcv_buf.head;
Chuck Leverc957c522007-10-26 13:31:57 -0400880 size_t hdrlen;
881 u32 count, ocount, recvd;
882 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
884 status = ntohl(*p++);
885 p = xdr_decode_post_op_attr(p, res->fattr);
886
887 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300888 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889
Chuck Leverc957c522007-10-26 13:31:57 -0400890 /* Decode reply count and EOF flag. NFSv3 is somewhat redundant
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 * in that it puts the count both in the res struct and in the
892 * opaque data count. */
893 count = ntohl(*p++);
894 res->eof = ntohl(*p++);
895 ocount = ntohl(*p++);
896
897 if (ocount != count) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400898 dprintk("NFS: READ count doesn't match RPC opaque count.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 return -errno_NFSERR_IO;
900 }
901
902 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
903 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400904 dprintk("NFS: READ reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400905 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 return -errno_NFSERR_IO;
907 } else if (iov->iov_len != hdrlen) {
908 dprintk("NFS: READ header is short. iovec will be shifted.\n");
909 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
910 }
911
912 recvd = req->rq_rcv_buf.len - hdrlen;
913 if (count > recvd) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400914 dprintk("NFS: server cheating in read reply: "
Chuck Leverc957c522007-10-26 13:31:57 -0400915 "count %u > recvd %u\n", count, recvd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 count = recvd;
917 res->eof = 0;
918 }
919
920 if (count < res->count)
921 res->count = count;
922
923 return count;
924}
925
926/*
927 * Decode WRITE response
928 */
929static int
Al Virod61005a2006-10-19 23:28:48 -0700930nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931{
932 int status;
933
934 status = ntohl(*p++);
935 p = xdr_decode_wcc_data(p, res->fattr);
936
937 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300938 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939
940 res->count = ntohl(*p++);
941 res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
942 res->verf->verifier[0] = *p++;
943 res->verf->verifier[1] = *p++;
944
945 return res->count;
946}
947
948/*
949 * Decode a CREATE response
950 */
951static int
Al Virod61005a2006-10-19 23:28:48 -0700952nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953{
954 int status;
955
956 status = ntohl(*p++);
957 if (status == 0) {
958 if (*p++) {
959 if (!(p = xdr_decode_fhandle(p, res->fh)))
960 return -errno_NFSERR_IO;
961 p = xdr_decode_post_op_attr(p, res->fattr);
962 } else {
963 memset(res->fh, 0, sizeof(*res->fh));
964 /* Do decode post_op_attr but set it to NULL */
965 p = xdr_decode_post_op_attr(p, res->fattr);
966 res->fattr->valid = 0;
967 }
968 } else {
Benny Halevy856dff32008-03-31 17:39:06 +0300969 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 }
971 p = xdr_decode_wcc_data(p, res->dir_attr);
972 return status;
973}
974
975/*
976 * Decode RENAME reply
977 */
978static int
Al Virod61005a2006-10-19 23:28:48 -0700979nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs3_renameres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980{
981 int status;
982
983 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300984 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 p = xdr_decode_wcc_data(p, res->fromattr);
986 p = xdr_decode_wcc_data(p, res->toattr);
987 return status;
988}
989
990/*
991 * Decode LINK reply
992 */
993static int
Al Virod61005a2006-10-19 23:28:48 -0700994nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995{
996 int status;
997
998 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300999 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 p = xdr_decode_post_op_attr(p, res->fattr);
1001 p = xdr_decode_wcc_data(p, res->dir_attr);
1002 return status;
1003}
1004
1005/*
1006 * Decode FSSTAT reply
1007 */
1008static int
Al Virod61005a2006-10-19 23:28:48 -07001009nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010{
1011 int status;
1012
1013 status = ntohl(*p++);
1014
1015 p = xdr_decode_post_op_attr(p, res->fattr);
1016 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001017 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018
1019 p = xdr_decode_hyper(p, &res->tbytes);
1020 p = xdr_decode_hyper(p, &res->fbytes);
1021 p = xdr_decode_hyper(p, &res->abytes);
1022 p = xdr_decode_hyper(p, &res->tfiles);
1023 p = xdr_decode_hyper(p, &res->ffiles);
1024 p = xdr_decode_hyper(p, &res->afiles);
1025
1026 /* ignore invarsec */
1027 return 0;
1028}
1029
1030/*
1031 * Decode FSINFO reply
1032 */
1033static int
Al Virod61005a2006-10-19 23:28:48 -07001034nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035{
1036 int status;
1037
1038 status = ntohl(*p++);
1039
1040 p = xdr_decode_post_op_attr(p, res->fattr);
1041 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001042 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043
1044 res->rtmax = ntohl(*p++);
1045 res->rtpref = ntohl(*p++);
1046 res->rtmult = ntohl(*p++);
1047 res->wtmax = ntohl(*p++);
1048 res->wtpref = ntohl(*p++);
1049 res->wtmult = ntohl(*p++);
1050 res->dtpref = ntohl(*p++);
1051 p = xdr_decode_hyper(p, &res->maxfilesize);
1052
1053 /* ignore time_delta and properties */
1054 res->lease_time = 0;
1055 return 0;
1056}
1057
1058/*
1059 * Decode PATHCONF reply
1060 */
1061static int
Al Virod61005a2006-10-19 23:28:48 -07001062nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063{
1064 int status;
1065
1066 status = ntohl(*p++);
1067
1068 p = xdr_decode_post_op_attr(p, res->fattr);
1069 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001070 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 res->max_link = ntohl(*p++);
1072 res->max_namelen = ntohl(*p++);
1073
1074 /* ignore remaining fields */
1075 return 0;
1076}
1077
1078/*
1079 * Decode COMMIT reply
1080 */
1081static int
Al Virod61005a2006-10-19 23:28:48 -07001082nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083{
1084 int status;
1085
1086 status = ntohl(*p++);
1087 p = xdr_decode_wcc_data(p, res->fattr);
1088 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001089 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090
1091 res->verf->verifier[0] = *p++;
1092 res->verf->verifier[1] = *p++;
1093 return 0;
1094}
1095
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001096#ifdef CONFIG_NFS_V3_ACL
1097/*
1098 * Decode GETACL reply
1099 */
1100static int
Al Virod61005a2006-10-19 23:28:48 -07001101nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001102 struct nfs3_getaclres *res)
1103{
1104 struct xdr_buf *buf = &req->rq_rcv_buf;
1105 int status = ntohl(*p++);
1106 struct posix_acl **acl;
1107 unsigned int *aclcnt;
1108 int err, base;
1109
1110 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001111 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001112 p = xdr_decode_post_op_attr(p, res->fattr);
1113 res->mask = ntohl(*p++);
1114 if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
1115 return -EINVAL;
1116 base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
1117
1118 acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
1119 aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
1120 err = nfsacl_decode(buf, base, aclcnt, acl);
1121
1122 acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
1123 aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
1124 if (err > 0)
1125 err = nfsacl_decode(buf, base + err, aclcnt, acl);
1126 return (err > 0) ? 0 : err;
1127}
1128
1129/*
1130 * Decode setacl reply.
1131 */
1132static int
Al Virod61005a2006-10-19 23:28:48 -07001133nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001134{
1135 int status = ntohl(*p++);
1136
1137 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +03001138 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001139 xdr_decode_post_op_attr(p, fattr);
1140 return 0;
1141}
1142#endif /* CONFIG_NFS_V3_ACL */
1143
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144#define PROC(proc, argtype, restype, timer) \
1145[NFS3PROC_##proc] = { \
1146 .p_proc = NFS3PROC_##proc, \
1147 .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
1148 .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
Chuck Lever2bea90d2007-03-29 16:47:53 -04001149 .p_arglen = NFS3_##argtype##_sz, \
1150 .p_replen = NFS3_##restype##_sz, \
Chuck Levercc0175c2006-03-20 13:44:22 -05001151 .p_timer = timer, \
1152 .p_statidx = NFS3PROC_##proc, \
1153 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 }
1155
1156struct rpc_procinfo nfs3_procedures[] = {
1157 PROC(GETATTR, fhandle, attrstat, 1),
1158 PROC(SETATTR, sattrargs, wccstat, 0),
1159 PROC(LOOKUP, diropargs, lookupres, 2),
1160 PROC(ACCESS, accessargs, accessres, 1),
1161 PROC(READLINK, readlinkargs, readlinkres, 3),
1162 PROC(READ, readargs, readres, 3),
1163 PROC(WRITE, writeargs, writeres, 4),
1164 PROC(CREATE, createargs, createres, 0),
1165 PROC(MKDIR, mkdirargs, createres, 0),
1166 PROC(SYMLINK, symlinkargs, createres, 0),
1167 PROC(MKNOD, mknodargs, createres, 0),
Trond Myklebust4fdc17b2007-07-14 15:39:57 -04001168 PROC(REMOVE, removeargs, removeres, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 PROC(RMDIR, diropargs, wccstat, 0),
1170 PROC(RENAME, renameargs, renameres, 0),
1171 PROC(LINK, linkargs, linkres, 0),
1172 PROC(READDIR, readdirargs, readdirres, 3),
1173 PROC(READDIRPLUS, readdirargs, readdirres, 3),
1174 PROC(FSSTAT, fhandle, fsstatres, 0),
1175 PROC(FSINFO, fhandle, fsinfores, 0),
1176 PROC(PATHCONF, fhandle, pathconfres, 0),
1177 PROC(COMMIT, commitargs, commitres, 5),
1178};
1179
1180struct rpc_version nfs_version3 = {
1181 .number = 3,
Tobias Klausere8c96f82006-03-24 03:15:34 -08001182 .nrprocs = ARRAY_SIZE(nfs3_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 .procs = nfs3_procedures
1184};
1185
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001186#ifdef CONFIG_NFS_V3_ACL
1187static struct rpc_procinfo nfs3_acl_procedures[] = {
1188 [ACLPROC3_GETACL] = {
1189 .p_proc = ACLPROC3_GETACL,
1190 .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
1191 .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001192 .p_arglen = ACL3_getaclargs_sz,
1193 .p_replen = ACL3_getaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001194 .p_timer = 1,
Chuck Levercc0175c2006-03-20 13:44:22 -05001195 .p_name = "GETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001196 },
1197 [ACLPROC3_SETACL] = {
1198 .p_proc = ACLPROC3_SETACL,
1199 .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
1200 .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001201 .p_arglen = ACL3_setaclargs_sz,
1202 .p_replen = ACL3_setaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001203 .p_timer = 0,
Chuck Levercc0175c2006-03-20 13:44:22 -05001204 .p_name = "SETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001205 },
1206};
1207
1208struct rpc_version nfsacl_version3 = {
1209 .number = 3,
1210 .nrprocs = sizeof(nfs3_acl_procedures)/
1211 sizeof(nfs3_acl_procedures[0]),
1212 .procs = nfs3_acl_procedures,
1213};
1214#endif /* CONFIG_NFS_V3_ACL */