blob: d562c8d9d56e670ec67a254917505e9efafed13f [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;
557 u32 len, recvd, pglen;
Jeff Layton643f8112008-02-22 14:50:00 -0500558 int status, nr = 0;
Al Virod61005a62006-10-19 23:28:48 -0700559 __be32 *entry, *end, *kaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
561 status = ntohl(*p++);
562 /* Decode post_op_attrs */
563 p = xdr_decode_post_op_attr(p, res->dir_attr);
564 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300565 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 /* Decode verifier cookie */
567 if (res->verf) {
568 res->verf[0] = *p++;
569 res->verf[1] = *p++;
570 } else {
571 p += 2;
572 }
573
574 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
575 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400576 dprintk("NFS: READDIR reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400577 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 return -errno_NFSERR_IO;
579 } else if (iov->iov_len != hdrlen) {
580 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
581 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
582 }
583
584 pglen = rcvbuf->page_len;
585 recvd = rcvbuf->len - hdrlen;
586 if (pglen > recvd)
587 pglen = recvd;
588 page = rcvbuf->pages;
Al Virod61005a62006-10-19 23:28:48 -0700589 kaddr = p = kmap_atomic(*page, KM_USER0);
590 end = (__be32 *)((char *)p + pglen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 entry = p;
Jeff Layton643f8112008-02-22 14:50:00 -0500592
593 /* Make sure the packet actually has a value_follows and EOF entry */
594 if ((entry + 1) > end)
595 goto short_pkt;
596
597 for (; *p++; nr++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 if (p + 3 > end)
599 goto short_pkt;
600 p += 2; /* inode # */
601 len = ntohl(*p++); /* string length */
602 p += XDR_QUADLEN(len) + 2; /* name + cookie */
603 if (len > NFS3_MAXNAMLEN) {
Chuck Leverc957c522007-10-26 13:31:57 -0400604 dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 len);
606 goto err_unmap;
607 }
608
609 if (res->plus) {
610 /* post_op_attr */
611 if (p + 2 > end)
612 goto short_pkt;
613 if (*p++) {
614 p += 21;
615 if (p + 1 > end)
616 goto short_pkt;
617 }
618 /* post_op_fh3 */
619 if (*p++) {
620 if (p + 1 > end)
621 goto short_pkt;
622 len = ntohl(*p++);
623 if (len > NFS3_FHSIZE) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400624 dprintk("NFS: giant filehandle in "
Chuck Leverc957c522007-10-26 13:31:57 -0400625 "readdir (len 0x%x)!\n", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 goto err_unmap;
627 }
628 p += XDR_QUADLEN(len);
629 }
630 }
631
632 if (p + 2 > end)
633 goto short_pkt;
634 entry = p;
635 }
Jeff Layton643f8112008-02-22 14:50:00 -0500636
637 /*
638 * Apparently some server sends responses that are a valid size, but
639 * contain no entries, and have value_follows==0 and EOF==0. For
640 * those, just set the EOF marker.
641 */
642 if (!nr && entry[1] == 0) {
643 dprintk("NFS: readdir reply truncated!\n");
644 entry[1] = 1;
645 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 out:
647 kunmap_atomic(kaddr, KM_USER0);
648 return nr;
649 short_pkt:
Jeff Layton643f8112008-02-22 14:50:00 -0500650 /*
651 * When we get a short packet there are 2 possibilities. We can
652 * return an error, or fix up the response to look like a valid
653 * response and return what we have so far. If there are no
654 * entries and the packet was short, then return -EIO. If there
655 * are valid entries in the response, return them and pretend that
656 * the call was successful, but incomplete. The caller can retry the
657 * readdir starting at the last cookie.
658 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 entry[0] = entry[1] = 0;
Jeff Layton643f8112008-02-22 14:50:00 -0500660 if (!nr)
661 nr = -errno_NFSERR_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 goto out;
663err_unmap:
664 nr = -errno_NFSERR_IO;
665 goto out;
666}
667
Al Viro0dbb4c62006-10-19 23:28:49 -0700668__be32 *
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400669nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670{
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400671 __be32 *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 struct nfs_entry old = *entry;
673
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400674 p = xdr_inline_decode(xdr, 4);
675 if (unlikely(!p))
676 goto out_overflow;
677 if (!ntohl(*p++)) {
678 p = xdr_inline_decode(xdr, 4);
679 if (unlikely(!p))
680 goto out_overflow;
681 if (!ntohl(*p++))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 return ERR_PTR(-EAGAIN);
683 entry->eof = 1;
684 return ERR_PTR(-EBADCOOKIE);
685 }
686
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400687 p = xdr_inline_decode(xdr, 12);
688 if (unlikely(!p))
689 goto out_overflow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 p = xdr_decode_hyper(p, &entry->ino);
691 entry->len = ntohl(*p++);
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400692
693 p = xdr_inline_decode(xdr, entry->len + 8);
694 if (unlikely(!p))
695 goto out_overflow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 entry->name = (const char *) p;
697 p += XDR_QUADLEN(entry->len);
698 entry->prev_cookie = entry->cookie;
699 p = xdr_decode_hyper(p, &entry->cookie);
700
701 if (plus) {
702 entry->fattr->valid = 0;
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400703 p = xdr_decode_post_op_attr_stream(xdr, entry->fattr);
704 if (IS_ERR(p))
705 goto out_overflow_exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 /* In fact, a post_op_fh3: */
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400707 p = xdr_inline_decode(xdr, 4);
708 if (unlikely(!p))
709 goto out_overflow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 if (*p++) {
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400711 p = xdr_decode_fhandle_stream(xdr, entry->fh);
712 if (IS_ERR(p))
713 goto out_overflow_exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 /* Ugh -- server reply was truncated */
715 if (p == NULL) {
716 dprintk("NFS: FH truncated\n");
717 *entry = old;
718 return ERR_PTR(-EAGAIN);
719 }
720 } else
721 memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
722 }
723
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400724 p = xdr_inline_peek(xdr, 8);
725 if (p != NULL)
726 entry->eof = !p[0] && p[1];
727 else
728 entry->eof = 0;
729
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 return p;
Bryan Schumakerbabddc72010-10-20 15:44:29 -0400731
732out_overflow:
733 print_overflow_msg(__func__, xdr);
734out_overflow_exit:
735 return ERR_PTR(-EIO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736}
737
738/*
739 * Encode COMMIT arguments
740 */
741static int
Al Virod61005a62006-10-19 23:28:48 -0700742nfs3_xdr_commitargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743{
744 p = xdr_encode_fhandle(p, args->fh);
745 p = xdr_encode_hyper(p, args->offset);
746 *p++ = htonl(args->count);
747 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
748 return 0;
749}
750
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000751#ifdef CONFIG_NFS_V3_ACL
752/*
753 * Encode GETACL arguments
754 */
755static int
Al Virod61005a62006-10-19 23:28:48 -0700756nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000757 struct nfs3_getaclargs *args)
758{
Trond Myklebusta17c2152010-07-31 14:29:08 -0400759 struct rpc_auth *auth = req->rq_cred->cr_auth;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000760 unsigned int replen;
761
762 p = xdr_encode_fhandle(p, args->fh);
763 *p++ = htonl(args->mask);
764 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
765
766 if (args->mask & (NFS_ACL | NFS_DFACL)) {
767 /* Inline the page array */
768 replen = (RPC_REPHDRSIZE + auth->au_rslack +
769 ACL3_getaclres_sz) << 2;
770 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
771 NFSACL_MAXPAGES << PAGE_SHIFT);
772 }
773 return 0;
774}
775
776/*
777 * Encode SETACL arguments
778 */
779static int
Al Virod61005a62006-10-19 23:28:48 -0700780nfs3_xdr_setaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000781 struct nfs3_setaclargs *args)
782{
783 struct xdr_buf *buf = &req->rq_snd_buf;
Trond Myklebustae461412009-03-10 20:33:18 -0400784 unsigned int base;
785 int err;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000786
787 p = xdr_encode_fhandle(p, NFS_FH(args->inode));
788 *p++ = htonl(args->mask);
Trond Myklebustae461412009-03-10 20:33:18 -0400789 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
790 base = req->rq_slen;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000791
Trond Myklebustae461412009-03-10 20:33:18 -0400792 if (args->npages != 0)
793 xdr_encode_pages(buf, args->pages, 0, args->len);
794 else
Trond Myklebust83404372009-04-20 14:58:35 -0400795 req->rq_slen = xdr_adjust_iovec(req->rq_svec,
796 p + XDR_QUADLEN(args->len));
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000797
798 err = nfsacl_encode(buf, base, args->inode,
799 (args->mask & NFS_ACL) ?
800 args->acl_access : NULL, 1, 0);
801 if (err > 0)
802 err = nfsacl_encode(buf, base + err, args->inode,
803 (args->mask & NFS_DFACL) ?
804 args->acl_default : NULL, 1,
805 NFS_ACL_DEFAULT);
806 return (err > 0) ? 0 : err;
807}
808#endif /* CONFIG_NFS_V3_ACL */
809
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810/*
811 * NFS XDR decode functions
812 */
813
814/*
815 * Decode attrstat reply.
816 */
817static int
Al Virod61005a62006-10-19 23:28:48 -0700818nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819{
820 int status;
821
822 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300823 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 xdr_decode_fattr(p, fattr);
825 return 0;
826}
827
828/*
829 * Decode status+wcc_data reply
830 * SATTR, REMOVE, RMDIR
831 */
832static int
Al Virod61005a62006-10-19 23:28:48 -0700833nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834{
835 int status;
836
837 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300838 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 xdr_decode_wcc_data(p, fattr);
840 return status;
841}
842
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400843static int
844nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
845{
Trond Myklebustd3468902010-04-16 16:22:50 -0400846 return nfs3_xdr_wccstat(req, p, res->dir_attr);
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400847}
848
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849/*
850 * Decode LOOKUP reply
851 */
852static int
Al Virod61005a62006-10-19 23:28:48 -0700853nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854{
855 int status;
856
857 if ((status = ntohl(*p++))) {
Benny Halevy856dff32008-03-31 17:39:06 +0300858 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 } else {
860 if (!(p = xdr_decode_fhandle(p, res->fh)))
861 return -errno_NFSERR_IO;
862 p = xdr_decode_post_op_attr(p, res->fattr);
863 }
864 xdr_decode_post_op_attr(p, res->dir_attr);
865 return status;
866}
867
868/*
869 * Decode ACCESS reply
870 */
871static int
Al Virod61005a62006-10-19 23:28:48 -0700872nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873{
874 int status = ntohl(*p++);
875
876 p = xdr_decode_post_op_attr(p, res->fattr);
877 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300878 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 res->access = ntohl(*p++);
880 return 0;
881}
882
883static int
Al Virod61005a62006-10-19 23:28:48 -0700884nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885{
Trond Myklebusta17c2152010-07-31 14:29:08 -0400886 struct rpc_auth *auth = req->rq_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 unsigned int replen;
888
889 p = xdr_encode_fhandle(p, args->fh);
890 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
891
892 /* Inline the page array */
893 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
894 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
895 return 0;
896}
897
898/*
899 * Decode READLINK reply
900 */
901static int
Al Virod61005a62006-10-19 23:28:48 -0700902nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903{
904 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
905 struct kvec *iov = rcvbuf->head;
Chuck Leverc957c522007-10-26 13:31:57 -0400906 size_t hdrlen;
907 u32 len, recvd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 int status;
909
910 status = ntohl(*p++);
911 p = xdr_decode_post_op_attr(p, fattr);
912
913 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300914 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
916 /* Convert length of symlink */
917 len = ntohl(*p++);
Chuck Leverc957c522007-10-26 13:31:57 -0400918 if (len >= rcvbuf->page_len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400919 dprintk("nfs: server returned giant symlink!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 return -ENAMETOOLONG;
921 }
922
923 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
924 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400925 dprintk("NFS: READLINK reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400926 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 return -errno_NFSERR_IO;
928 } else if (iov->iov_len != hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400929 dprintk("NFS: READLINK header is short. "
930 "iovec will be shifted.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
932 }
933 recvd = req->rq_rcv_buf.len - hdrlen;
934 if (recvd < len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400935 dprintk("NFS: server cheating in readlink reply: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 "count %u > recvd %u\n", len, recvd);
937 return -EIO;
938 }
939
Chuck Leverb4687da2010-09-21 16:55:48 -0400940 xdr_terminate_string(rcvbuf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 return 0;
942}
943
944/*
945 * Decode READ reply
946 */
947static int
Al Virod61005a62006-10-19 23:28:48 -0700948nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949{
950 struct kvec *iov = req->rq_rcv_buf.head;
Chuck Leverc957c522007-10-26 13:31:57 -0400951 size_t hdrlen;
952 u32 count, ocount, recvd;
953 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
955 status = ntohl(*p++);
956 p = xdr_decode_post_op_attr(p, res->fattr);
957
958 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300959 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960
Chuck Leverc957c522007-10-26 13:31:57 -0400961 /* Decode reply count and EOF flag. NFSv3 is somewhat redundant
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 * in that it puts the count both in the res struct and in the
963 * opaque data count. */
964 count = ntohl(*p++);
965 res->eof = ntohl(*p++);
966 ocount = ntohl(*p++);
967
968 if (ocount != count) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400969 dprintk("NFS: READ count doesn't match RPC opaque count.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 return -errno_NFSERR_IO;
971 }
972
973 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
974 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400975 dprintk("NFS: READ reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400976 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 return -errno_NFSERR_IO;
978 } else if (iov->iov_len != hdrlen) {
979 dprintk("NFS: READ header is short. iovec will be shifted.\n");
980 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
981 }
982
983 recvd = req->rq_rcv_buf.len - hdrlen;
984 if (count > recvd) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400985 dprintk("NFS: server cheating in read reply: "
Chuck Leverc957c522007-10-26 13:31:57 -0400986 "count %u > recvd %u\n", count, recvd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 count = recvd;
988 res->eof = 0;
989 }
990
991 if (count < res->count)
992 res->count = count;
993
994 return count;
995}
996
997/*
998 * Decode WRITE response
999 */
1000static int
Al Virod61005a62006-10-19 23:28:48 -07001001nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002{
1003 int status;
1004
1005 status = ntohl(*p++);
1006 p = xdr_decode_wcc_data(p, res->fattr);
1007
1008 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001009 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010
1011 res->count = ntohl(*p++);
1012 res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
1013 res->verf->verifier[0] = *p++;
1014 res->verf->verifier[1] = *p++;
1015
1016 return res->count;
1017}
1018
1019/*
1020 * Decode a CREATE response
1021 */
1022static int
Al Virod61005a62006-10-19 23:28:48 -07001023nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024{
1025 int status;
1026
1027 status = ntohl(*p++);
1028 if (status == 0) {
1029 if (*p++) {
1030 if (!(p = xdr_decode_fhandle(p, res->fh)))
1031 return -errno_NFSERR_IO;
1032 p = xdr_decode_post_op_attr(p, res->fattr);
1033 } else {
1034 memset(res->fh, 0, sizeof(*res->fh));
1035 /* Do decode post_op_attr but set it to NULL */
1036 p = xdr_decode_post_op_attr(p, res->fattr);
1037 res->fattr->valid = 0;
1038 }
1039 } else {
Benny Halevy856dff32008-03-31 17:39:06 +03001040 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 }
1042 p = xdr_decode_wcc_data(p, res->dir_attr);
1043 return status;
1044}
1045
1046/*
1047 * Decode RENAME reply
1048 */
1049static int
Jeff Laytone8582a82010-09-17 17:31:06 -04001050nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs_renameres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051{
1052 int status;
1053
1054 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001055 status = nfs_stat_to_errno(status);
Jeff Laytone8582a82010-09-17 17:31:06 -04001056 p = xdr_decode_wcc_data(p, res->old_fattr);
1057 p = xdr_decode_wcc_data(p, res->new_fattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 return status;
1059}
1060
1061/*
1062 * Decode LINK reply
1063 */
1064static int
Al Virod61005a62006-10-19 23:28:48 -07001065nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066{
1067 int status;
1068
1069 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001070 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 p = xdr_decode_post_op_attr(p, res->fattr);
1072 p = xdr_decode_wcc_data(p, res->dir_attr);
1073 return status;
1074}
1075
1076/*
1077 * Decode FSSTAT reply
1078 */
1079static int
Al Virod61005a62006-10-19 23:28:48 -07001080nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081{
1082 int status;
1083
1084 status = ntohl(*p++);
1085
1086 p = xdr_decode_post_op_attr(p, res->fattr);
1087 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001088 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089
1090 p = xdr_decode_hyper(p, &res->tbytes);
1091 p = xdr_decode_hyper(p, &res->fbytes);
1092 p = xdr_decode_hyper(p, &res->abytes);
1093 p = xdr_decode_hyper(p, &res->tfiles);
1094 p = xdr_decode_hyper(p, &res->ffiles);
1095 p = xdr_decode_hyper(p, &res->afiles);
1096
1097 /* ignore invarsec */
1098 return 0;
1099}
1100
1101/*
1102 * Decode FSINFO reply
1103 */
1104static int
Al Virod61005a62006-10-19 23:28:48 -07001105nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106{
1107 int status;
1108
1109 status = ntohl(*p++);
1110
1111 p = xdr_decode_post_op_attr(p, res->fattr);
1112 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001113 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114
1115 res->rtmax = ntohl(*p++);
1116 res->rtpref = ntohl(*p++);
1117 res->rtmult = ntohl(*p++);
1118 res->wtmax = ntohl(*p++);
1119 res->wtpref = ntohl(*p++);
1120 res->wtmult = ntohl(*p++);
1121 res->dtpref = ntohl(*p++);
1122 p = xdr_decode_hyper(p, &res->maxfilesize);
1123
1124 /* ignore time_delta and properties */
1125 res->lease_time = 0;
1126 return 0;
1127}
1128
1129/*
1130 * Decode PATHCONF reply
1131 */
1132static int
Al Virod61005a62006-10-19 23:28:48 -07001133nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134{
1135 int status;
1136
1137 status = ntohl(*p++);
1138
1139 p = xdr_decode_post_op_attr(p, res->fattr);
1140 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001141 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 res->max_link = ntohl(*p++);
1143 res->max_namelen = ntohl(*p++);
1144
1145 /* ignore remaining fields */
1146 return 0;
1147}
1148
1149/*
1150 * Decode COMMIT reply
1151 */
1152static int
Al Virod61005a62006-10-19 23:28:48 -07001153nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154{
1155 int status;
1156
1157 status = ntohl(*p++);
1158 p = xdr_decode_wcc_data(p, res->fattr);
1159 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001160 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161
1162 res->verf->verifier[0] = *p++;
1163 res->verf->verifier[1] = *p++;
1164 return 0;
1165}
1166
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001167#ifdef CONFIG_NFS_V3_ACL
1168/*
1169 * Decode GETACL reply
1170 */
1171static int
Al Virod61005a62006-10-19 23:28:48 -07001172nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001173 struct nfs3_getaclres *res)
1174{
1175 struct xdr_buf *buf = &req->rq_rcv_buf;
1176 int status = ntohl(*p++);
1177 struct posix_acl **acl;
1178 unsigned int *aclcnt;
1179 int err, base;
1180
1181 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001182 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001183 p = xdr_decode_post_op_attr(p, res->fattr);
1184 res->mask = ntohl(*p++);
1185 if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
1186 return -EINVAL;
1187 base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
1188
1189 acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
1190 aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
1191 err = nfsacl_decode(buf, base, aclcnt, acl);
1192
1193 acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
1194 aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
1195 if (err > 0)
1196 err = nfsacl_decode(buf, base + err, aclcnt, acl);
1197 return (err > 0) ? 0 : err;
1198}
1199
1200/*
1201 * Decode setacl reply.
1202 */
1203static int
Al Virod61005a62006-10-19 23:28:48 -07001204nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001205{
1206 int status = ntohl(*p++);
1207
1208 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +03001209 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001210 xdr_decode_post_op_attr(p, fattr);
1211 return 0;
1212}
1213#endif /* CONFIG_NFS_V3_ACL */
1214
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215#define PROC(proc, argtype, restype, timer) \
1216[NFS3PROC_##proc] = { \
1217 .p_proc = NFS3PROC_##proc, \
1218 .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
1219 .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
Chuck Lever2bea90d2007-03-29 16:47:53 -04001220 .p_arglen = NFS3_##argtype##_sz, \
1221 .p_replen = NFS3_##restype##_sz, \
Chuck Levercc0175c2006-03-20 13:44:22 -05001222 .p_timer = timer, \
1223 .p_statidx = NFS3PROC_##proc, \
1224 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 }
1226
1227struct rpc_procinfo nfs3_procedures[] = {
1228 PROC(GETATTR, fhandle, attrstat, 1),
1229 PROC(SETATTR, sattrargs, wccstat, 0),
1230 PROC(LOOKUP, diropargs, lookupres, 2),
1231 PROC(ACCESS, accessargs, accessres, 1),
1232 PROC(READLINK, readlinkargs, readlinkres, 3),
1233 PROC(READ, readargs, readres, 3),
1234 PROC(WRITE, writeargs, writeres, 4),
1235 PROC(CREATE, createargs, createres, 0),
1236 PROC(MKDIR, mkdirargs, createres, 0),
1237 PROC(SYMLINK, symlinkargs, createres, 0),
1238 PROC(MKNOD, mknodargs, createres, 0),
Trond Myklebust4fdc17b2007-07-14 15:39:57 -04001239 PROC(REMOVE, removeargs, removeres, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 PROC(RMDIR, diropargs, wccstat, 0),
1241 PROC(RENAME, renameargs, renameres, 0),
1242 PROC(LINK, linkargs, linkres, 0),
1243 PROC(READDIR, readdirargs, readdirres, 3),
1244 PROC(READDIRPLUS, readdirargs, readdirres, 3),
1245 PROC(FSSTAT, fhandle, fsstatres, 0),
1246 PROC(FSINFO, fhandle, fsinfores, 0),
1247 PROC(PATHCONF, fhandle, pathconfres, 0),
1248 PROC(COMMIT, commitargs, commitres, 5),
1249};
1250
1251struct rpc_version nfs_version3 = {
1252 .number = 3,
Tobias Klausere8c96f82006-03-24 03:15:34 -08001253 .nrprocs = ARRAY_SIZE(nfs3_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 .procs = nfs3_procedures
1255};
1256
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001257#ifdef CONFIG_NFS_V3_ACL
1258static struct rpc_procinfo nfs3_acl_procedures[] = {
1259 [ACLPROC3_GETACL] = {
1260 .p_proc = ACLPROC3_GETACL,
1261 .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
1262 .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001263 .p_arglen = ACL3_getaclargs_sz,
1264 .p_replen = ACL3_getaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001265 .p_timer = 1,
Chuck Levercc0175c2006-03-20 13:44:22 -05001266 .p_name = "GETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001267 },
1268 [ACLPROC3_SETACL] = {
1269 .p_proc = ACLPROC3_SETACL,
1270 .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
1271 .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001272 .p_arglen = ACL3_setaclargs_sz,
1273 .p_replen = ACL3_setaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001274 .p_timer = 0,
Chuck Levercc0175c2006-03-20 13:44:22 -05001275 .p_name = "SETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001276 },
1277};
1278
1279struct rpc_version nfsacl_version3 = {
1280 .number = 3,
1281 .nrprocs = sizeof(nfs3_acl_procedures)/
1282 sizeof(nfs3_acl_procedures[0]),
1283 .procs = nfs3_acl_procedures,
1284};
1285#endif /* CONFIG_NFS_V3_ACL */