blob: f6cc60f06dac4e517f7743bb3c0c148106807120 [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/errno.h>
13#include <linux/string.h>
14#include <linux/in.h>
15#include <linux/pagemap.h>
16#include <linux/proc_fs.h>
17#include <linux/kdev_t.h>
18#include <linux/sunrpc/clnt.h>
19#include <linux/nfs.h>
20#include <linux/nfs3.h>
21#include <linux/nfs_fs.h>
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000022#include <linux/nfsacl.h>
David Howellsf7b422b2006-06-09 09:34:33 -040023#include "internal.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
25#define NFSDBG_FACILITY NFSDBG_XDR
26
27/* Mapping from NFS error code to "errno" error code. */
28#define errno_NFSERR_IO EIO
29
Linus Torvalds1da177e2005-04-16 15:20:36 -070030/*
31 * Declare the space requirements for NFS arguments and replies as
32 * number of 32bit-words
33 */
34#define NFS3_fhandle_sz (1+16)
35#define NFS3_fh_sz (NFS3_fhandle_sz) /* shorthand */
36#define NFS3_sattr_sz (15)
37#define NFS3_filename_sz (1+(NFS3_MAXNAMLEN>>2))
38#define NFS3_path_sz (1+(NFS3_MAXPATHLEN>>2))
39#define NFS3_fattr_sz (21)
40#define NFS3_wcc_attr_sz (6)
41#define NFS3_pre_op_attr_sz (1+NFS3_wcc_attr_sz)
42#define NFS3_post_op_attr_sz (1+NFS3_fattr_sz)
43#define NFS3_wcc_data_sz (NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
44#define NFS3_fsstat_sz
45#define NFS3_fsinfo_sz
46#define NFS3_pathconf_sz
47#define NFS3_entry_sz (NFS3_filename_sz+3)
48
49#define NFS3_sattrargs_sz (NFS3_fh_sz+NFS3_sattr_sz+3)
50#define NFS3_diropargs_sz (NFS3_fh_sz+NFS3_filename_sz)
Trond Myklebust4fdc17b2007-07-14 15:39:57 -040051#define NFS3_removeargs_sz (NFS3_fh_sz+NFS3_filename_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#define NFS3_accessargs_sz (NFS3_fh_sz+1)
53#define NFS3_readlinkargs_sz (NFS3_fh_sz)
54#define NFS3_readargs_sz (NFS3_fh_sz+3)
55#define NFS3_writeargs_sz (NFS3_fh_sz+5)
56#define NFS3_createargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
57#define NFS3_mkdirargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
Chuck Lever94a6d752006-08-22 20:06:23 -040058#define NFS3_symlinkargs_sz (NFS3_diropargs_sz+1+NFS3_sattr_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#define NFS3_mknodargs_sz (NFS3_diropargs_sz+2+NFS3_sattr_sz)
60#define NFS3_renameargs_sz (NFS3_diropargs_sz+NFS3_diropargs_sz)
61#define NFS3_linkargs_sz (NFS3_fh_sz+NFS3_diropargs_sz)
62#define NFS3_readdirargs_sz (NFS3_fh_sz+2)
63#define NFS3_commitargs_sz (NFS3_fh_sz+3)
64
65#define NFS3_attrstat_sz (1+NFS3_fattr_sz)
66#define NFS3_wccstat_sz (1+NFS3_wcc_data_sz)
Trond Myklebust4fdc17b2007-07-14 15:39:57 -040067#define NFS3_removeres_sz (NFS3_wccstat_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070068#define NFS3_lookupres_sz (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
69#define NFS3_accessres_sz (1+NFS3_post_op_attr_sz+1)
70#define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz+1)
71#define NFS3_readres_sz (1+NFS3_post_op_attr_sz+3)
72#define NFS3_writeres_sz (1+NFS3_wcc_data_sz+4)
73#define NFS3_createres_sz (1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
74#define NFS3_renameres_sz (1+(2 * NFS3_wcc_data_sz))
75#define NFS3_linkres_sz (1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
76#define NFS3_readdirres_sz (1+NFS3_post_op_attr_sz+2)
77#define NFS3_fsstatres_sz (1+NFS3_post_op_attr_sz+13)
78#define NFS3_fsinfores_sz (1+NFS3_post_op_attr_sz+12)
79#define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6)
80#define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2)
81
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000082#define ACL3_getaclargs_sz (NFS3_fh_sz+1)
Trond Myklebustae461412009-03-10 20:33:18 -040083#define ACL3_setaclargs_sz (NFS3_fh_sz+1+ \
84 XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
85#define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+ \
86 XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000087#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 */
Trond Myklebustbca79472009-03-11 14:10:26 -040092static const umode_t nfs_type2fmt[] = {
93 [NF3BAD] = 0,
94 [NF3REG] = S_IFREG,
95 [NF3DIR] = S_IFDIR,
96 [NF3BLK] = S_IFBLK,
97 [NF3CHR] = S_IFCHR,
98 [NF3LNK] = S_IFLNK,
99 [NF3SOCK] = S_IFSOCK,
100 [NF3FIFO] = S_IFIFO,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101};
102
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400103static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
104{
105 dprintk("nfs: %s: prematurely hit end of receive buffer. "
106 "Remaining buffer length is %tu words.\n",
107 func, xdr->end - xdr->p);
108}
109
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110/*
111 * Common NFS XDR functions as inlines
112 */
Al Virod61005a62006-10-19 23:28:48 -0700113static inline __be32 *
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400114xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115{
116 return xdr_encode_array(p, fh->data, fh->size);
117}
118
Al Virod61005a62006-10-19 23:28:48 -0700119static inline __be32 *
120xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121{
122 if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
123 memcpy(fh->data, p, fh->size);
124 return p + XDR_QUADLEN(fh->size);
125 }
126 return NULL;
127}
128
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400129static inline __be32 *
130xdr_decode_fhandle_stream(struct xdr_stream *xdr, struct nfs_fh *fh)
131{
132 __be32 *p;
133 p = xdr_inline_decode(xdr, 4);
134 if (unlikely(!p))
135 goto out_overflow;
136 fh->size = ntohl(*p++);
137
138 if (fh->size <= NFS3_FHSIZE) {
139 p = xdr_inline_decode(xdr, fh->size);
140 if (unlikely(!p))
141 goto out_overflow;
142 memcpy(fh->data, p, fh->size);
143 return p + XDR_QUADLEN(fh->size);
144 }
145 return NULL;
146
147out_overflow:
148 print_overflow_msg(__func__, xdr);
149 return ERR_PTR(-EIO);
150}
151
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152/*
153 * Encode/decode time.
154 */
Al Virod61005a62006-10-19 23:28:48 -0700155static inline __be32 *
156xdr_encode_time3(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157{
158 *p++ = htonl(timep->tv_sec);
159 *p++ = htonl(timep->tv_nsec);
160 return p;
161}
162
Al Virod61005a62006-10-19 23:28:48 -0700163static inline __be32 *
164xdr_decode_time3(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165{
166 timep->tv_sec = ntohl(*p++);
167 timep->tv_nsec = ntohl(*p++);
168 return p;
169}
170
Al Virod61005a62006-10-19 23:28:48 -0700171static __be32 *
172xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173{
174 unsigned int type, major, minor;
Trond Myklebustbca79472009-03-11 14:10:26 -0400175 umode_t fmode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176
177 type = ntohl(*p++);
Trond Myklebustbca79472009-03-11 14:10:26 -0400178 if (type > NF3FIFO)
179 type = NF3NON;
180 fmode = nfs_type2fmt[type];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
182 fattr->nlink = ntohl(*p++);
183 fattr->uid = ntohl(*p++);
184 fattr->gid = ntohl(*p++);
185 p = xdr_decode_hyper(p, &fattr->size);
186 p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
187
188 /* Turn remote device info into Linux-specific dev_t */
189 major = ntohl(*p++);
190 minor = ntohl(*p++);
191 fattr->rdev = MKDEV(major, minor);
192 if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor)
193 fattr->rdev = 0;
194
Trond Myklebust8b4bdcf2006-06-09 09:34:19 -0400195 p = xdr_decode_hyper(p, &fattr->fsid.major);
196 fattr->fsid.minor = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 p = xdr_decode_hyper(p, &fattr->fileid);
198 p = xdr_decode_time3(p, &fattr->atime);
199 p = xdr_decode_time3(p, &fattr->mtime);
200 p = xdr_decode_time3(p, &fattr->ctime);
201
202 /* Update the mode bits */
Trond Myklebust9e6e70f2009-03-11 14:10:24 -0400203 fattr->valid |= NFS_ATTR_FATTR_V3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 return p;
205}
206
Al Virod61005a62006-10-19 23:28:48 -0700207static inline __be32 *
208xdr_encode_sattr(__be32 *p, struct iattr *attr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209{
210 if (attr->ia_valid & ATTR_MODE) {
211 *p++ = xdr_one;
Trond Myklebustcf3fff52006-01-03 09:55:53 +0100212 *p++ = htonl(attr->ia_mode & S_IALLUGO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 } else {
214 *p++ = xdr_zero;
215 }
216 if (attr->ia_valid & ATTR_UID) {
217 *p++ = xdr_one;
218 *p++ = htonl(attr->ia_uid);
219 } else {
220 *p++ = xdr_zero;
221 }
222 if (attr->ia_valid & ATTR_GID) {
223 *p++ = xdr_one;
224 *p++ = htonl(attr->ia_gid);
225 } else {
226 *p++ = xdr_zero;
227 }
228 if (attr->ia_valid & ATTR_SIZE) {
229 *p++ = xdr_one;
230 p = xdr_encode_hyper(p, (__u64) attr->ia_size);
231 } else {
232 *p++ = xdr_zero;
233 }
234 if (attr->ia_valid & ATTR_ATIME_SET) {
235 *p++ = xdr_two;
236 p = xdr_encode_time3(p, &attr->ia_atime);
237 } else if (attr->ia_valid & ATTR_ATIME) {
238 *p++ = xdr_one;
239 } else {
240 *p++ = xdr_zero;
241 }
242 if (attr->ia_valid & ATTR_MTIME_SET) {
243 *p++ = xdr_two;
244 p = xdr_encode_time3(p, &attr->ia_mtime);
245 } else if (attr->ia_valid & ATTR_MTIME) {
246 *p++ = xdr_one;
247 } else {
248 *p++ = xdr_zero;
249 }
250 return p;
251}
252
Al Virod61005a62006-10-19 23:28:48 -0700253static inline __be32 *
254xdr_decode_wcc_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255{
256 p = xdr_decode_hyper(p, &fattr->pre_size);
257 p = xdr_decode_time3(p, &fattr->pre_mtime);
258 p = xdr_decode_time3(p, &fattr->pre_ctime);
Trond Myklebust9e6e70f2009-03-11 14:10:24 -0400259 fattr->valid |= NFS_ATTR_FATTR_PRESIZE
260 | NFS_ATTR_FATTR_PREMTIME
261 | NFS_ATTR_FATTR_PRECTIME;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 return p;
263}
264
Al Virod61005a62006-10-19 23:28:48 -0700265static inline __be32 *
266xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267{
268 if (*p++)
269 p = xdr_decode_fattr(p, fattr);
270 return p;
271}
272
Al Virod61005a62006-10-19 23:28:48 -0700273static inline __be32 *
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400274xdr_decode_post_op_attr_stream(struct xdr_stream *xdr, struct nfs_fattr *fattr)
275{
276 __be32 *p;
277
278 p = xdr_inline_decode(xdr, 4);
279 if (unlikely(!p))
280 goto out_overflow;
281 if (ntohl(*p++)) {
282 p = xdr_inline_decode(xdr, 84);
283 if (unlikely(!p))
284 goto out_overflow;
285 p = xdr_decode_fattr(p, fattr);
286 }
287 return p;
288out_overflow:
289 print_overflow_msg(__func__, xdr);
290 return ERR_PTR(-EIO);
291}
292
293static inline __be32 *
Al Virod61005a62006-10-19 23:28:48 -0700294xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295{
296 if (*p++)
297 return xdr_decode_wcc_attr(p, fattr);
298 return p;
299}
300
301
Al Virod61005a62006-10-19 23:28:48 -0700302static inline __be32 *
303xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304{
305 p = xdr_decode_pre_op_attr(p, fattr);
306 return xdr_decode_post_op_attr(p, fattr);
307}
308
309/*
310 * NFS encode functions
311 */
312
313/*
314 * Encode file handle argument
315 */
316static int
Al Virod61005a62006-10-19 23:28:48 -0700317nfs3_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318{
319 p = xdr_encode_fhandle(p, fh);
320 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
321 return 0;
322}
323
324/*
325 * Encode SETATTR arguments
326 */
327static int
Al Virod61005a62006-10-19 23:28:48 -0700328nfs3_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs3_sattrargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329{
330 p = xdr_encode_fhandle(p, args->fh);
331 p = xdr_encode_sattr(p, args->sattr);
332 *p++ = htonl(args->guard);
333 if (args->guard)
334 p = xdr_encode_time3(p, &args->guardtime);
335 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
336 return 0;
337}
338
339/*
340 * Encode directory ops argument
341 */
342static int
Al Virod61005a62006-10-19 23:28:48 -0700343nfs3_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs3_diropargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344{
345 p = xdr_encode_fhandle(p, args->fh);
346 p = xdr_encode_array(p, args->name, args->len);
347 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
348 return 0;
349}
350
351/*
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400352 * Encode REMOVE argument
353 */
354static int
355nfs3_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
356{
357 p = xdr_encode_fhandle(p, args->fh);
358 p = xdr_encode_array(p, args->name.name, args->name.len);
359 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
360 return 0;
361}
362
363/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 * Encode access() argument
365 */
366static int
Al Virod61005a62006-10-19 23:28:48 -0700367nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368{
369 p = xdr_encode_fhandle(p, args->fh);
370 *p++ = htonl(args->access);
371 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
372 return 0;
373}
374
375/*
376 * Arguments to a READ call. Since we read data directly into the page
377 * cache, we also set up the reply iovec here so that iov[1] points
378 * exactly to the page we want to fetch.
379 */
380static int
Al Virod61005a62006-10-19 23:28:48 -0700381nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382{
Trond Myklebusta17c2152010-07-31 14:29:08 -0400383 struct rpc_auth *auth = req->rq_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 unsigned int replen;
385 u32 count = args->count;
386
387 p = xdr_encode_fhandle(p, args->fh);
388 p = xdr_encode_hyper(p, args->offset);
389 *p++ = htonl(count);
390 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
391
392 /* Inline the page array */
393 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
394 xdr_inline_pages(&req->rq_rcv_buf, replen,
395 args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400396 req->rq_rcv_buf.flags |= XDRBUF_READ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 return 0;
398}
399
400/*
401 * Write arguments. Splice the buffer to be written into the iovec.
402 */
403static int
Al Virod61005a62006-10-19 23:28:48 -0700404nfs3_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405{
406 struct xdr_buf *sndbuf = &req->rq_snd_buf;
407 u32 count = args->count;
408
409 p = xdr_encode_fhandle(p, args->fh);
410 p = xdr_encode_hyper(p, args->offset);
411 *p++ = htonl(count);
412 *p++ = htonl(args->stable);
413 *p++ = htonl(count);
414 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
415
416 /* Copy the page array */
417 xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400418 sndbuf->flags |= XDRBUF_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 return 0;
420}
421
422/*
423 * Encode CREATE arguments
424 */
425static int
Al Virod61005a62006-10-19 23:28:48 -0700426nfs3_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs3_createargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427{
428 p = xdr_encode_fhandle(p, args->fh);
429 p = xdr_encode_array(p, args->name, args->len);
430
431 *p++ = htonl(args->createmode);
432 if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
433 *p++ = args->verifier[0];
434 *p++ = args->verifier[1];
435 } else
436 p = xdr_encode_sattr(p, args->sattr);
437
438 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
439 return 0;
440}
441
442/*
443 * Encode MKDIR arguments
444 */
445static int
Al Virod61005a62006-10-19 23:28:48 -0700446nfs3_xdr_mkdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mkdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447{
448 p = xdr_encode_fhandle(p, args->fh);
449 p = xdr_encode_array(p, args->name, args->len);
450 p = xdr_encode_sattr(p, args->sattr);
451 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
452 return 0;
453}
454
455/*
456 * Encode SYMLINK arguments
457 */
458static int
Al Virod61005a62006-10-19 23:28:48 -0700459nfs3_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_symlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460{
461 p = xdr_encode_fhandle(p, args->fromfh);
462 p = xdr_encode_array(p, args->fromname, args->fromlen);
463 p = xdr_encode_sattr(p, args->sattr);
Chuck Lever94a6d752006-08-22 20:06:23 -0400464 *p++ = htonl(args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
Chuck Lever94a6d752006-08-22 20:06:23 -0400466
467 /* Copy the page */
468 xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 return 0;
470}
471
472/*
473 * Encode MKNOD arguments
474 */
475static int
Al Virod61005a62006-10-19 23:28:48 -0700476nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477{
478 p = xdr_encode_fhandle(p, args->fh);
479 p = xdr_encode_array(p, args->name, args->len);
480 *p++ = htonl(args->type);
481 p = xdr_encode_sattr(p, args->sattr);
482 if (args->type == NF3CHR || args->type == NF3BLK) {
483 *p++ = htonl(MAJOR(args->rdev));
484 *p++ = htonl(MINOR(args->rdev));
485 }
486
487 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
488 return 0;
489}
490
491/*
492 * Encode RENAME arguments
493 */
494static int
Jeff Layton920769f2010-09-17 17:30:25 -0400495nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496{
Jeff Layton920769f2010-09-17 17:30:25 -0400497 p = xdr_encode_fhandle(p, args->old_dir);
498 p = xdr_encode_array(p, args->old_name->name, args->old_name->len);
499 p = xdr_encode_fhandle(p, args->new_dir);
500 p = xdr_encode_array(p, args->new_name->name, args->new_name->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
502 return 0;
503}
504
505/*
506 * Encode LINK arguments
507 */
508static int
Al Virod61005a62006-10-19 23:28:48 -0700509nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510{
511 p = xdr_encode_fhandle(p, args->fromfh);
512 p = xdr_encode_fhandle(p, args->tofh);
513 p = xdr_encode_array(p, args->toname, args->tolen);
514 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
515 return 0;
516}
517
518/*
519 * Encode arguments to readdir call
520 */
521static int
Al Virod61005a62006-10-19 23:28:48 -0700522nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523{
Trond Myklebusta17c2152010-07-31 14:29:08 -0400524 struct rpc_auth *auth = req->rq_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 unsigned int replen;
526 u32 count = args->count;
527
528 p = xdr_encode_fhandle(p, args->fh);
529 p = xdr_encode_hyper(p, args->cookie);
530 *p++ = args->verf[0];
531 *p++ = args->verf[1];
532 if (args->plus) {
533 /* readdirplus: need dircount + buffer size.
534 * We just make sure we make dircount big enough */
535 *p++ = htonl(count >> 3);
536 }
537 *p++ = htonl(count);
538 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
539
540 /* Inline the page array */
541 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
542 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
543 return 0;
544}
545
546/*
547 * Decode the result of a readdir call.
548 * We just check for syntactical correctness.
549 */
550static int
Al Virod61005a62006-10-19 23:28:48 -0700551nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552{
553 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
554 struct kvec *iov = rcvbuf->head;
555 struct page **page;
Chuck Leverc957c522007-10-26 13:31:57 -0400556 size_t hdrlen;
Bryan Schumakerafa8ccc2010-10-20 15:44:31 -0400557 u32 recvd, pglen;
Trond Myklebustac396122010-11-15 20:26:22 -0500558 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
560 status = ntohl(*p++);
561 /* Decode post_op_attrs */
562 p = xdr_decode_post_op_attr(p, res->dir_attr);
563 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300564 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 /* Decode verifier cookie */
566 if (res->verf) {
567 res->verf[0] = *p++;
568 res->verf[1] = *p++;
569 } else {
570 p += 2;
571 }
572
573 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
574 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400575 dprintk("NFS: READDIR reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400576 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 return -errno_NFSERR_IO;
578 } else if (iov->iov_len != hdrlen) {
579 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
580 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
581 }
582
583 pglen = rcvbuf->page_len;
584 recvd = rcvbuf->len - hdrlen;
585 if (pglen > recvd)
586 pglen = recvd;
587 page = rcvbuf->pages;
Jeff Layton643f8112008-02-22 14:50:00 -0500588
Trond Myklebustac396122010-11-15 20:26:22 -0500589 return pglen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590}
591
Al Viro0dbb4c62006-10-19 23:28:49 -0700592__be32 *
Bryan Schumaker82f2e542010-10-21 16:33:18 -0400593nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594{
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400595 __be32 *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 struct nfs_entry old = *entry;
597
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400598 p = xdr_inline_decode(xdr, 4);
599 if (unlikely(!p))
600 goto out_overflow;
601 if (!ntohl(*p++)) {
602 p = xdr_inline_decode(xdr, 4);
603 if (unlikely(!p))
604 goto out_overflow;
605 if (!ntohl(*p++))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 return ERR_PTR(-EAGAIN);
607 entry->eof = 1;
608 return ERR_PTR(-EBADCOOKIE);
609 }
610
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400611 p = xdr_inline_decode(xdr, 12);
612 if (unlikely(!p))
613 goto out_overflow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 p = xdr_decode_hyper(p, &entry->ino);
615 entry->len = ntohl(*p++);
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400616
617 p = xdr_inline_decode(xdr, entry->len + 8);
618 if (unlikely(!p))
619 goto out_overflow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 entry->name = (const char *) p;
621 p += XDR_QUADLEN(entry->len);
622 entry->prev_cookie = entry->cookie;
623 p = xdr_decode_hyper(p, &entry->cookie);
624
Trond Myklebust0b26a0b2010-11-20 14:26:44 -0500625 entry->d_type = DT_UNKNOWN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 if (plus) {
627 entry->fattr->valid = 0;
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400628 p = xdr_decode_post_op_attr_stream(xdr, entry->fattr);
629 if (IS_ERR(p))
630 goto out_overflow_exit;
Trond Myklebust0b26a0b2010-11-20 14:26:44 -0500631 entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 /* In fact, a post_op_fh3: */
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400633 p = xdr_inline_decode(xdr, 4);
634 if (unlikely(!p))
635 goto out_overflow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 if (*p++) {
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400637 p = xdr_decode_fhandle_stream(xdr, entry->fh);
638 if (IS_ERR(p))
639 goto out_overflow_exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 /* Ugh -- server reply was truncated */
641 if (p == NULL) {
642 dprintk("NFS: FH truncated\n");
643 *entry = old;
644 return ERR_PTR(-EAGAIN);
645 }
646 } else
647 memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
648 }
649
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400650 p = xdr_inline_peek(xdr, 8);
651 if (p != NULL)
652 entry->eof = !p[0] && p[1];
653 else
654 entry->eof = 0;
655
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 return p;
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400657
658out_overflow:
659 print_overflow_msg(__func__, xdr);
660out_overflow_exit:
Trond Myklebust463a3762010-11-20 12:22:20 -0500661 return ERR_PTR(-EAGAIN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662}
663
664/*
665 * Encode COMMIT arguments
666 */
667static int
Al Virod61005a62006-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 Virod61005a62006-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 Myklebusta17c2152010-07-31 14:29:08 -0400685 struct rpc_auth *auth = req->rq_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 Virod61005a62006-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
Trond Myklebust83404372009-04-20 14:58:35 -0400721 req->rq_slen = xdr_adjust_iovec(req->rq_svec,
722 p + XDR_QUADLEN(args->len));
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000723
724 err = nfsacl_encode(buf, base, args->inode,
725 (args->mask & NFS_ACL) ?
726 args->acl_access : NULL, 1, 0);
727 if (err > 0)
728 err = nfsacl_encode(buf, base + err, args->inode,
729 (args->mask & NFS_DFACL) ?
730 args->acl_default : NULL, 1,
731 NFS_ACL_DEFAULT);
732 return (err > 0) ? 0 : err;
733}
734#endif /* CONFIG_NFS_V3_ACL */
735
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736/*
737 * NFS XDR decode functions
738 */
739
740/*
741 * Decode attrstat reply.
742 */
743static int
Al Virod61005a62006-10-19 23:28:48 -0700744nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745{
746 int status;
747
748 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300749 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 xdr_decode_fattr(p, fattr);
751 return 0;
752}
753
754/*
755 * Decode status+wcc_data reply
756 * SATTR, REMOVE, RMDIR
757 */
758static int
Al Virod61005a62006-10-19 23:28:48 -0700759nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760{
761 int status;
762
763 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300764 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 xdr_decode_wcc_data(p, fattr);
766 return status;
767}
768
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400769static int
770nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
771{
Trond Myklebustd3468902010-04-16 16:22:50 -0400772 return nfs3_xdr_wccstat(req, p, res->dir_attr);
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400773}
774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775/*
776 * Decode LOOKUP reply
777 */
778static int
Al Virod61005a62006-10-19 23:28:48 -0700779nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780{
781 int status;
782
783 if ((status = ntohl(*p++))) {
Benny Halevy856dff32008-03-31 17:39:06 +0300784 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 } else {
786 if (!(p = xdr_decode_fhandle(p, res->fh)))
787 return -errno_NFSERR_IO;
788 p = xdr_decode_post_op_attr(p, res->fattr);
789 }
790 xdr_decode_post_op_attr(p, res->dir_attr);
791 return status;
792}
793
794/*
795 * Decode ACCESS reply
796 */
797static int
Al Virod61005a62006-10-19 23:28:48 -0700798nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799{
800 int status = ntohl(*p++);
801
802 p = xdr_decode_post_op_attr(p, res->fattr);
803 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300804 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 res->access = ntohl(*p++);
806 return 0;
807}
808
809static int
Al Virod61005a62006-10-19 23:28:48 -0700810nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811{
Trond Myklebusta17c2152010-07-31 14:29:08 -0400812 struct rpc_auth *auth = req->rq_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 unsigned int replen;
814
815 p = xdr_encode_fhandle(p, args->fh);
816 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
817
818 /* Inline the page array */
819 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
820 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
821 return 0;
822}
823
824/*
825 * Decode READLINK reply
826 */
827static int
Al Virod61005a62006-10-19 23:28:48 -0700828nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829{
830 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
831 struct kvec *iov = rcvbuf->head;
Chuck Leverc957c522007-10-26 13:31:57 -0400832 size_t hdrlen;
833 u32 len, recvd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 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
Chuck Leverb4687da2010-09-21 16:55:48 -0400866 xdr_terminate_string(rcvbuf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 return 0;
868}
869
870/*
871 * Decode READ reply
872 */
873static int
Al Virod61005a62006-10-19 23:28:48 -0700874nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875{
876 struct kvec *iov = req->rq_rcv_buf.head;
Chuck Leverc957c522007-10-26 13:31:57 -0400877 size_t hdrlen;
878 u32 count, ocount, recvd;
879 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
881 status = ntohl(*p++);
882 p = xdr_decode_post_op_attr(p, res->fattr);
883
884 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300885 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886
Chuck Leverc957c522007-10-26 13:31:57 -0400887 /* Decode reply count and EOF flag. NFSv3 is somewhat redundant
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 * in that it puts the count both in the res struct and in the
889 * opaque data count. */
890 count = ntohl(*p++);
891 res->eof = ntohl(*p++);
892 ocount = ntohl(*p++);
893
894 if (ocount != count) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400895 dprintk("NFS: READ count doesn't match RPC opaque count.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 return -errno_NFSERR_IO;
897 }
898
899 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
900 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400901 dprintk("NFS: READ reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400902 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 return -errno_NFSERR_IO;
904 } else if (iov->iov_len != hdrlen) {
905 dprintk("NFS: READ header is short. iovec will be shifted.\n");
906 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
907 }
908
909 recvd = req->rq_rcv_buf.len - hdrlen;
910 if (count > recvd) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400911 dprintk("NFS: server cheating in read reply: "
Chuck Leverc957c522007-10-26 13:31:57 -0400912 "count %u > recvd %u\n", count, recvd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 count = recvd;
914 res->eof = 0;
915 }
916
917 if (count < res->count)
918 res->count = count;
919
920 return count;
921}
922
923/*
924 * Decode WRITE response
925 */
926static int
Al Virod61005a62006-10-19 23:28:48 -0700927nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928{
929 int status;
930
931 status = ntohl(*p++);
932 p = xdr_decode_wcc_data(p, res->fattr);
933
934 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300935 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
937 res->count = ntohl(*p++);
938 res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
939 res->verf->verifier[0] = *p++;
940 res->verf->verifier[1] = *p++;
941
942 return res->count;
943}
944
945/*
946 * Decode a CREATE response
947 */
948static int
Al Virod61005a62006-10-19 23:28:48 -0700949nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950{
951 int status;
952
953 status = ntohl(*p++);
954 if (status == 0) {
955 if (*p++) {
956 if (!(p = xdr_decode_fhandle(p, res->fh)))
957 return -errno_NFSERR_IO;
958 p = xdr_decode_post_op_attr(p, res->fattr);
959 } else {
960 memset(res->fh, 0, sizeof(*res->fh));
961 /* Do decode post_op_attr but set it to NULL */
962 p = xdr_decode_post_op_attr(p, res->fattr);
963 res->fattr->valid = 0;
964 }
965 } else {
Benny Halevy856dff32008-03-31 17:39:06 +0300966 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 }
968 p = xdr_decode_wcc_data(p, res->dir_attr);
969 return status;
970}
971
972/*
973 * Decode RENAME reply
974 */
975static int
Jeff Laytone8582a82010-09-17 17:31:06 -0400976nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs_renameres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977{
978 int status;
979
980 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300981 status = nfs_stat_to_errno(status);
Jeff Laytone8582a82010-09-17 17:31:06 -0400982 p = xdr_decode_wcc_data(p, res->old_fattr);
983 p = xdr_decode_wcc_data(p, res->new_fattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 return status;
985}
986
987/*
988 * Decode LINK reply
989 */
990static int
Al Virod61005a62006-10-19 23:28:48 -0700991nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992{
993 int status;
994
995 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300996 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 p = xdr_decode_post_op_attr(p, res->fattr);
998 p = xdr_decode_wcc_data(p, res->dir_attr);
999 return status;
1000}
1001
1002/*
1003 * Decode FSSTAT reply
1004 */
1005static int
Al Virod61005a62006-10-19 23:28:48 -07001006nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007{
1008 int status;
1009
1010 status = ntohl(*p++);
1011
1012 p = xdr_decode_post_op_attr(p, res->fattr);
1013 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001014 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015
1016 p = xdr_decode_hyper(p, &res->tbytes);
1017 p = xdr_decode_hyper(p, &res->fbytes);
1018 p = xdr_decode_hyper(p, &res->abytes);
1019 p = xdr_decode_hyper(p, &res->tfiles);
1020 p = xdr_decode_hyper(p, &res->ffiles);
1021 p = xdr_decode_hyper(p, &res->afiles);
1022
1023 /* ignore invarsec */
1024 return 0;
1025}
1026
1027/*
1028 * Decode FSINFO reply
1029 */
1030static int
Al Virod61005a62006-10-19 23:28:48 -07001031nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032{
1033 int status;
1034
1035 status = ntohl(*p++);
1036
1037 p = xdr_decode_post_op_attr(p, res->fattr);
1038 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001039 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040
1041 res->rtmax = ntohl(*p++);
1042 res->rtpref = ntohl(*p++);
1043 res->rtmult = ntohl(*p++);
1044 res->wtmax = ntohl(*p++);
1045 res->wtpref = ntohl(*p++);
1046 res->wtmult = ntohl(*p++);
1047 res->dtpref = ntohl(*p++);
1048 p = xdr_decode_hyper(p, &res->maxfilesize);
Ricardo Labiaga6b967242010-10-12 16:30:05 -07001049 p = xdr_decode_time3(p, &res->time_delta);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050
Ricardo Labiaga6b967242010-10-12 16:30:05 -07001051 /* ignore properties */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 res->lease_time = 0;
1053 return 0;
1054}
1055
1056/*
1057 * Decode PATHCONF reply
1058 */
1059static int
Al Virod61005a62006-10-19 23:28:48 -07001060nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061{
1062 int status;
1063
1064 status = ntohl(*p++);
1065
1066 p = xdr_decode_post_op_attr(p, res->fattr);
1067 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001068 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 res->max_link = ntohl(*p++);
1070 res->max_namelen = ntohl(*p++);
1071
1072 /* ignore remaining fields */
1073 return 0;
1074}
1075
1076/*
1077 * Decode COMMIT reply
1078 */
1079static int
Al Virod61005a62006-10-19 23:28:48 -07001080nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081{
1082 int status;
1083
1084 status = ntohl(*p++);
1085 p = xdr_decode_wcc_data(p, res->fattr);
1086 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001087 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088
1089 res->verf->verifier[0] = *p++;
1090 res->verf->verifier[1] = *p++;
1091 return 0;
1092}
1093
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001094#ifdef CONFIG_NFS_V3_ACL
1095/*
1096 * Decode GETACL reply
1097 */
1098static int
Al Virod61005a62006-10-19 23:28:48 -07001099nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001100 struct nfs3_getaclres *res)
1101{
1102 struct xdr_buf *buf = &req->rq_rcv_buf;
1103 int status = ntohl(*p++);
1104 struct posix_acl **acl;
1105 unsigned int *aclcnt;
1106 int err, base;
1107
1108 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001109 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001110 p = xdr_decode_post_op_attr(p, res->fattr);
1111 res->mask = ntohl(*p++);
1112 if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
1113 return -EINVAL;
1114 base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
1115
1116 acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
1117 aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
1118 err = nfsacl_decode(buf, base, aclcnt, acl);
1119
1120 acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
1121 aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
1122 if (err > 0)
1123 err = nfsacl_decode(buf, base + err, aclcnt, acl);
1124 return (err > 0) ? 0 : err;
1125}
1126
1127/*
1128 * Decode setacl reply.
1129 */
1130static int
Al Virod61005a62006-10-19 23:28:48 -07001131nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001132{
1133 int status = ntohl(*p++);
1134
1135 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +03001136 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001137 xdr_decode_post_op_attr(p, fattr);
1138 return 0;
1139}
1140#endif /* CONFIG_NFS_V3_ACL */
1141
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142#define PROC(proc, argtype, restype, timer) \
1143[NFS3PROC_##proc] = { \
1144 .p_proc = NFS3PROC_##proc, \
1145 .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
1146 .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
Chuck Lever2bea90d2007-03-29 16:47:53 -04001147 .p_arglen = NFS3_##argtype##_sz, \
1148 .p_replen = NFS3_##restype##_sz, \
Chuck Levercc0175c2006-03-20 13:44:22 -05001149 .p_timer = timer, \
1150 .p_statidx = NFS3PROC_##proc, \
1151 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 }
1153
1154struct rpc_procinfo nfs3_procedures[] = {
1155 PROC(GETATTR, fhandle, attrstat, 1),
1156 PROC(SETATTR, sattrargs, wccstat, 0),
1157 PROC(LOOKUP, diropargs, lookupres, 2),
1158 PROC(ACCESS, accessargs, accessres, 1),
1159 PROC(READLINK, readlinkargs, readlinkres, 3),
1160 PROC(READ, readargs, readres, 3),
1161 PROC(WRITE, writeargs, writeres, 4),
1162 PROC(CREATE, createargs, createres, 0),
1163 PROC(MKDIR, mkdirargs, createres, 0),
1164 PROC(SYMLINK, symlinkargs, createres, 0),
1165 PROC(MKNOD, mknodargs, createres, 0),
Trond Myklebust4fdc17b2007-07-14 15:39:57 -04001166 PROC(REMOVE, removeargs, removeres, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 PROC(RMDIR, diropargs, wccstat, 0),
1168 PROC(RENAME, renameargs, renameres, 0),
1169 PROC(LINK, linkargs, linkres, 0),
1170 PROC(READDIR, readdirargs, readdirres, 3),
1171 PROC(READDIRPLUS, readdirargs, readdirres, 3),
1172 PROC(FSSTAT, fhandle, fsstatres, 0),
1173 PROC(FSINFO, fhandle, fsinfores, 0),
1174 PROC(PATHCONF, fhandle, pathconfres, 0),
1175 PROC(COMMIT, commitargs, commitres, 5),
1176};
1177
1178struct rpc_version nfs_version3 = {
1179 .number = 3,
Tobias Klausere8c96f82006-03-24 03:15:34 -08001180 .nrprocs = ARRAY_SIZE(nfs3_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 .procs = nfs3_procedures
1182};
1183
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001184#ifdef CONFIG_NFS_V3_ACL
1185static struct rpc_procinfo nfs3_acl_procedures[] = {
1186 [ACLPROC3_GETACL] = {
1187 .p_proc = ACLPROC3_GETACL,
1188 .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
1189 .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001190 .p_arglen = ACL3_getaclargs_sz,
1191 .p_replen = ACL3_getaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001192 .p_timer = 1,
Chuck Levercc0175c2006-03-20 13:44:22 -05001193 .p_name = "GETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001194 },
1195 [ACLPROC3_SETACL] = {
1196 .p_proc = ACLPROC3_SETACL,
1197 .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
1198 .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001199 .p_arglen = ACL3_setaclargs_sz,
1200 .p_replen = ACL3_setaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001201 .p_timer = 0,
Chuck Levercc0175c2006-03-20 13:44:22 -05001202 .p_name = "SETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001203 },
1204};
1205
1206struct rpc_version nfsacl_version3 = {
1207 .number = 3,
1208 .nrprocs = sizeof(nfs3_acl_procedures)/
1209 sizeof(nfs3_acl_procedures[0]),
1210 .procs = nfs3_acl_procedures,
1211};
1212#endif /* CONFIG_NFS_V3_ACL */