blob: 52b2fda66e638a4ce931d3b1d99d819589981d40 [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
103/*
104 * Common NFS XDR functions as inlines
105 */
Al Virod61005a2006-10-19 23:28:48 -0700106static inline __be32 *
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400107xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108{
109 return xdr_encode_array(p, fh->data, fh->size);
110}
111
Al Virod61005a2006-10-19 23:28:48 -0700112static inline __be32 *
113xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114{
115 if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
116 memcpy(fh->data, p, fh->size);
117 return p + XDR_QUADLEN(fh->size);
118 }
119 return NULL;
120}
121
122/*
123 * Encode/decode time.
124 */
Al Virod61005a2006-10-19 23:28:48 -0700125static inline __be32 *
126xdr_encode_time3(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127{
128 *p++ = htonl(timep->tv_sec);
129 *p++ = htonl(timep->tv_nsec);
130 return p;
131}
132
Al Virod61005a2006-10-19 23:28:48 -0700133static inline __be32 *
134xdr_decode_time3(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135{
136 timep->tv_sec = ntohl(*p++);
137 timep->tv_nsec = ntohl(*p++);
138 return p;
139}
140
Al Virod61005a2006-10-19 23:28:48 -0700141static __be32 *
142xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143{
144 unsigned int type, major, minor;
Trond Myklebustbca79472009-03-11 14:10:26 -0400145 umode_t fmode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
147 type = ntohl(*p++);
Trond Myklebustbca79472009-03-11 14:10:26 -0400148 if (type > NF3FIFO)
149 type = NF3NON;
150 fmode = nfs_type2fmt[type];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
152 fattr->nlink = ntohl(*p++);
153 fattr->uid = ntohl(*p++);
154 fattr->gid = ntohl(*p++);
155 p = xdr_decode_hyper(p, &fattr->size);
156 p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
157
158 /* Turn remote device info into Linux-specific dev_t */
159 major = ntohl(*p++);
160 minor = ntohl(*p++);
161 fattr->rdev = MKDEV(major, minor);
162 if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor)
163 fattr->rdev = 0;
164
Trond Myklebust8b4bdcf2006-06-09 09:34:19 -0400165 p = xdr_decode_hyper(p, &fattr->fsid.major);
166 fattr->fsid.minor = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 p = xdr_decode_hyper(p, &fattr->fileid);
168 p = xdr_decode_time3(p, &fattr->atime);
169 p = xdr_decode_time3(p, &fattr->mtime);
170 p = xdr_decode_time3(p, &fattr->ctime);
171
172 /* Update the mode bits */
Trond Myklebust9e6e70f2009-03-11 14:10:24 -0400173 fattr->valid |= NFS_ATTR_FATTR_V3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 return p;
175}
176
Al Virod61005a2006-10-19 23:28:48 -0700177static inline __be32 *
178xdr_encode_sattr(__be32 *p, struct iattr *attr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179{
180 if (attr->ia_valid & ATTR_MODE) {
181 *p++ = xdr_one;
Trond Myklebustcf3fff52006-01-03 09:55:53 +0100182 *p++ = htonl(attr->ia_mode & S_IALLUGO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 } else {
184 *p++ = xdr_zero;
185 }
186 if (attr->ia_valid & ATTR_UID) {
187 *p++ = xdr_one;
188 *p++ = htonl(attr->ia_uid);
189 } else {
190 *p++ = xdr_zero;
191 }
192 if (attr->ia_valid & ATTR_GID) {
193 *p++ = xdr_one;
194 *p++ = htonl(attr->ia_gid);
195 } else {
196 *p++ = xdr_zero;
197 }
198 if (attr->ia_valid & ATTR_SIZE) {
199 *p++ = xdr_one;
200 p = xdr_encode_hyper(p, (__u64) attr->ia_size);
201 } else {
202 *p++ = xdr_zero;
203 }
204 if (attr->ia_valid & ATTR_ATIME_SET) {
205 *p++ = xdr_two;
206 p = xdr_encode_time3(p, &attr->ia_atime);
207 } else if (attr->ia_valid & ATTR_ATIME) {
208 *p++ = xdr_one;
209 } else {
210 *p++ = xdr_zero;
211 }
212 if (attr->ia_valid & ATTR_MTIME_SET) {
213 *p++ = xdr_two;
214 p = xdr_encode_time3(p, &attr->ia_mtime);
215 } else if (attr->ia_valid & ATTR_MTIME) {
216 *p++ = xdr_one;
217 } else {
218 *p++ = xdr_zero;
219 }
220 return p;
221}
222
Al Virod61005a2006-10-19 23:28:48 -0700223static inline __be32 *
224xdr_decode_wcc_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225{
226 p = xdr_decode_hyper(p, &fattr->pre_size);
227 p = xdr_decode_time3(p, &fattr->pre_mtime);
228 p = xdr_decode_time3(p, &fattr->pre_ctime);
Trond Myklebust9e6e70f2009-03-11 14:10:24 -0400229 fattr->valid |= NFS_ATTR_FATTR_PRESIZE
230 | NFS_ATTR_FATTR_PREMTIME
231 | NFS_ATTR_FATTR_PRECTIME;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 return p;
233}
234
Al Virod61005a2006-10-19 23:28:48 -0700235static inline __be32 *
236xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237{
238 if (*p++)
239 p = xdr_decode_fattr(p, fattr);
240 return p;
241}
242
Al Virod61005a2006-10-19 23:28:48 -0700243static inline __be32 *
244xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245{
246 if (*p++)
247 return xdr_decode_wcc_attr(p, fattr);
248 return p;
249}
250
251
Al Virod61005a2006-10-19 23:28:48 -0700252static inline __be32 *
253xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254{
255 p = xdr_decode_pre_op_attr(p, fattr);
256 return xdr_decode_post_op_attr(p, fattr);
257}
258
259/*
260 * NFS encode functions
261 */
262
263/*
264 * Encode file handle argument
265 */
266static int
Al Virod61005a2006-10-19 23:28:48 -0700267nfs3_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
269 p = xdr_encode_fhandle(p, fh);
270 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
271 return 0;
272}
273
274/*
275 * Encode SETATTR arguments
276 */
277static int
Al Virod61005a2006-10-19 23:28:48 -0700278nfs3_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs3_sattrargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279{
280 p = xdr_encode_fhandle(p, args->fh);
281 p = xdr_encode_sattr(p, args->sattr);
282 *p++ = htonl(args->guard);
283 if (args->guard)
284 p = xdr_encode_time3(p, &args->guardtime);
285 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
286 return 0;
287}
288
289/*
290 * Encode directory ops argument
291 */
292static int
Al Virod61005a2006-10-19 23:28:48 -0700293nfs3_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs3_diropargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294{
295 p = xdr_encode_fhandle(p, args->fh);
296 p = xdr_encode_array(p, args->name, args->len);
297 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
298 return 0;
299}
300
301/*
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400302 * Encode REMOVE argument
303 */
304static int
305nfs3_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
306{
307 p = xdr_encode_fhandle(p, args->fh);
308 p = xdr_encode_array(p, args->name.name, args->name.len);
309 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
310 return 0;
311}
312
313/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 * Encode access() argument
315 */
316static int
Al Virod61005a2006-10-19 23:28:48 -0700317nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318{
319 p = xdr_encode_fhandle(p, args->fh);
320 *p++ = htonl(args->access);
321 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
322 return 0;
323}
324
325/*
326 * Arguments to a READ call. Since we read data directly into the page
327 * cache, we also set up the reply iovec here so that iov[1] points
328 * exactly to the page we want to fetch.
329 */
330static int
Al Virod61005a2006-10-19 23:28:48 -0700331nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332{
Trond Myklebusta17c2152010-07-31 14:29:08 -0400333 struct rpc_auth *auth = req->rq_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 unsigned int replen;
335 u32 count = args->count;
336
337 p = xdr_encode_fhandle(p, args->fh);
338 p = xdr_encode_hyper(p, args->offset);
339 *p++ = htonl(count);
340 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
341
342 /* Inline the page array */
343 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
344 xdr_inline_pages(&req->rq_rcv_buf, replen,
345 args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400346 req->rq_rcv_buf.flags |= XDRBUF_READ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 return 0;
348}
349
350/*
351 * Write arguments. Splice the buffer to be written into the iovec.
352 */
353static int
Al Virod61005a2006-10-19 23:28:48 -0700354nfs3_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
356 struct xdr_buf *sndbuf = &req->rq_snd_buf;
357 u32 count = args->count;
358
359 p = xdr_encode_fhandle(p, args->fh);
360 p = xdr_encode_hyper(p, args->offset);
361 *p++ = htonl(count);
362 *p++ = htonl(args->stable);
363 *p++ = htonl(count);
364 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
365
366 /* Copy the page array */
367 xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400368 sndbuf->flags |= XDRBUF_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 return 0;
370}
371
372/*
373 * Encode CREATE arguments
374 */
375static int
Al Virod61005a2006-10-19 23:28:48 -0700376nfs3_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs3_createargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377{
378 p = xdr_encode_fhandle(p, args->fh);
379 p = xdr_encode_array(p, args->name, args->len);
380
381 *p++ = htonl(args->createmode);
382 if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
383 *p++ = args->verifier[0];
384 *p++ = args->verifier[1];
385 } else
386 p = xdr_encode_sattr(p, args->sattr);
387
388 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
389 return 0;
390}
391
392/*
393 * Encode MKDIR arguments
394 */
395static int
Al Virod61005a2006-10-19 23:28:48 -0700396nfs3_xdr_mkdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mkdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397{
398 p = xdr_encode_fhandle(p, args->fh);
399 p = xdr_encode_array(p, args->name, args->len);
400 p = xdr_encode_sattr(p, args->sattr);
401 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
402 return 0;
403}
404
405/*
406 * Encode SYMLINK arguments
407 */
408static int
Al Virod61005a2006-10-19 23:28:48 -0700409nfs3_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_symlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410{
411 p = xdr_encode_fhandle(p, args->fromfh);
412 p = xdr_encode_array(p, args->fromname, args->fromlen);
413 p = xdr_encode_sattr(p, args->sattr);
Chuck Lever94a6d752006-08-22 20:06:23 -0400414 *p++ = htonl(args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
Chuck Lever94a6d752006-08-22 20:06:23 -0400416
417 /* Copy the page */
418 xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 return 0;
420}
421
422/*
423 * Encode MKNOD arguments
424 */
425static int
Al Virod61005a2006-10-19 23:28:48 -0700426nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *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 *p++ = htonl(args->type);
431 p = xdr_encode_sattr(p, args->sattr);
432 if (args->type == NF3CHR || args->type == NF3BLK) {
433 *p++ = htonl(MAJOR(args->rdev));
434 *p++ = htonl(MINOR(args->rdev));
435 }
436
437 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
438 return 0;
439}
440
441/*
442 * Encode RENAME arguments
443 */
444static int
Jeff Layton920769f2010-09-17 17:30:25 -0400445nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446{
Jeff Layton920769f2010-09-17 17:30:25 -0400447 p = xdr_encode_fhandle(p, args->old_dir);
448 p = xdr_encode_array(p, args->old_name->name, args->old_name->len);
449 p = xdr_encode_fhandle(p, args->new_dir);
450 p = xdr_encode_array(p, args->new_name->name, args->new_name->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
452 return 0;
453}
454
455/*
456 * Encode LINK arguments
457 */
458static int
Al Virod61005a2006-10-19 23:28:48 -0700459nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460{
461 p = xdr_encode_fhandle(p, args->fromfh);
462 p = xdr_encode_fhandle(p, args->tofh);
463 p = xdr_encode_array(p, args->toname, args->tolen);
464 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
465 return 0;
466}
467
468/*
469 * Encode arguments to readdir call
470 */
471static int
Al Virod61005a2006-10-19 23:28:48 -0700472nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473{
Trond Myklebusta17c2152010-07-31 14:29:08 -0400474 struct rpc_auth *auth = req->rq_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 unsigned int replen;
476 u32 count = args->count;
477
478 p = xdr_encode_fhandle(p, args->fh);
479 p = xdr_encode_hyper(p, args->cookie);
480 *p++ = args->verf[0];
481 *p++ = args->verf[1];
482 if (args->plus) {
483 /* readdirplus: need dircount + buffer size.
484 * We just make sure we make dircount big enough */
485 *p++ = htonl(count >> 3);
486 }
487 *p++ = htonl(count);
488 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
489
490 /* Inline the page array */
491 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
492 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
493 return 0;
494}
495
496/*
497 * Decode the result of a readdir call.
498 * We just check for syntactical correctness.
499 */
500static int
Al Virod61005a2006-10-19 23:28:48 -0700501nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502{
503 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
504 struct kvec *iov = rcvbuf->head;
505 struct page **page;
Chuck Leverc957c522007-10-26 13:31:57 -0400506 size_t hdrlen;
507 u32 len, recvd, pglen;
Jeff Layton643f8112008-02-22 14:50:00 -0500508 int status, nr = 0;
Al Virod61005a2006-10-19 23:28:48 -0700509 __be32 *entry, *end, *kaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
511 status = ntohl(*p++);
512 /* Decode post_op_attrs */
513 p = xdr_decode_post_op_attr(p, res->dir_attr);
514 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300515 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 /* Decode verifier cookie */
517 if (res->verf) {
518 res->verf[0] = *p++;
519 res->verf[1] = *p++;
520 } else {
521 p += 2;
522 }
523
524 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
525 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400526 dprintk("NFS: READDIR reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400527 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 return -errno_NFSERR_IO;
529 } else if (iov->iov_len != hdrlen) {
530 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
531 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
532 }
533
534 pglen = rcvbuf->page_len;
535 recvd = rcvbuf->len - hdrlen;
536 if (pglen > recvd)
537 pglen = recvd;
538 page = rcvbuf->pages;
Al Virod61005a2006-10-19 23:28:48 -0700539 kaddr = p = kmap_atomic(*page, KM_USER0);
540 end = (__be32 *)((char *)p + pglen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 entry = p;
Jeff Layton643f8112008-02-22 14:50:00 -0500542
543 /* Make sure the packet actually has a value_follows and EOF entry */
544 if ((entry + 1) > end)
545 goto short_pkt;
546
547 for (; *p++; nr++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 if (p + 3 > end)
549 goto short_pkt;
550 p += 2; /* inode # */
551 len = ntohl(*p++); /* string length */
552 p += XDR_QUADLEN(len) + 2; /* name + cookie */
553 if (len > NFS3_MAXNAMLEN) {
Chuck Leverc957c522007-10-26 13:31:57 -0400554 dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 len);
556 goto err_unmap;
557 }
558
559 if (res->plus) {
560 /* post_op_attr */
561 if (p + 2 > end)
562 goto short_pkt;
563 if (*p++) {
564 p += 21;
565 if (p + 1 > end)
566 goto short_pkt;
567 }
568 /* post_op_fh3 */
569 if (*p++) {
570 if (p + 1 > end)
571 goto short_pkt;
572 len = ntohl(*p++);
573 if (len > NFS3_FHSIZE) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400574 dprintk("NFS: giant filehandle in "
Chuck Leverc957c522007-10-26 13:31:57 -0400575 "readdir (len 0x%x)!\n", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 goto err_unmap;
577 }
578 p += XDR_QUADLEN(len);
579 }
580 }
581
582 if (p + 2 > end)
583 goto short_pkt;
584 entry = p;
585 }
Jeff Layton643f8112008-02-22 14:50:00 -0500586
587 /*
588 * Apparently some server sends responses that are a valid size, but
589 * contain no entries, and have value_follows==0 and EOF==0. For
590 * those, just set the EOF marker.
591 */
592 if (!nr && entry[1] == 0) {
593 dprintk("NFS: readdir reply truncated!\n");
594 entry[1] = 1;
595 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 out:
597 kunmap_atomic(kaddr, KM_USER0);
598 return nr;
599 short_pkt:
Jeff Layton643f8112008-02-22 14:50:00 -0500600 /*
601 * When we get a short packet there are 2 possibilities. We can
602 * return an error, or fix up the response to look like a valid
603 * response and return what we have so far. If there are no
604 * entries and the packet was short, then return -EIO. If there
605 * are valid entries in the response, return them and pretend that
606 * the call was successful, but incomplete. The caller can retry the
607 * readdir starting at the last cookie.
608 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 entry[0] = entry[1] = 0;
Jeff Layton643f8112008-02-22 14:50:00 -0500610 if (!nr)
611 nr = -errno_NFSERR_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 goto out;
613err_unmap:
614 nr = -errno_NFSERR_IO;
615 goto out;
616}
617
Al Viro0dbb4c62006-10-19 23:28:49 -0700618__be32 *
619nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620{
621 struct nfs_entry old = *entry;
622
623 if (!*p++) {
624 if (!*p)
625 return ERR_PTR(-EAGAIN);
626 entry->eof = 1;
627 return ERR_PTR(-EBADCOOKIE);
628 }
629
630 p = xdr_decode_hyper(p, &entry->ino);
631 entry->len = ntohl(*p++);
632 entry->name = (const char *) p;
633 p += XDR_QUADLEN(entry->len);
634 entry->prev_cookie = entry->cookie;
635 p = xdr_decode_hyper(p, &entry->cookie);
636
637 if (plus) {
638 entry->fattr->valid = 0;
639 p = xdr_decode_post_op_attr(p, entry->fattr);
640 /* In fact, a post_op_fh3: */
641 if (*p++) {
642 p = xdr_decode_fhandle(p, entry->fh);
643 /* Ugh -- server reply was truncated */
644 if (p == NULL) {
645 dprintk("NFS: FH truncated\n");
646 *entry = old;
647 return ERR_PTR(-EAGAIN);
648 }
649 } else
650 memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
651 }
652
653 entry->eof = !p[0] && p[1];
654 return p;
655}
656
657/*
658 * Encode COMMIT arguments
659 */
660static int
Al Virod61005a2006-10-19 23:28:48 -0700661nfs3_xdr_commitargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662{
663 p = xdr_encode_fhandle(p, args->fh);
664 p = xdr_encode_hyper(p, args->offset);
665 *p++ = htonl(args->count);
666 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
667 return 0;
668}
669
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000670#ifdef CONFIG_NFS_V3_ACL
671/*
672 * Encode GETACL arguments
673 */
674static int
Al Virod61005a2006-10-19 23:28:48 -0700675nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000676 struct nfs3_getaclargs *args)
677{
Trond Myklebusta17c2152010-07-31 14:29:08 -0400678 struct rpc_auth *auth = req->rq_cred->cr_auth;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000679 unsigned int replen;
680
681 p = xdr_encode_fhandle(p, args->fh);
682 *p++ = htonl(args->mask);
683 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
684
685 if (args->mask & (NFS_ACL | NFS_DFACL)) {
686 /* Inline the page array */
687 replen = (RPC_REPHDRSIZE + auth->au_rslack +
688 ACL3_getaclres_sz) << 2;
689 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
690 NFSACL_MAXPAGES << PAGE_SHIFT);
691 }
692 return 0;
693}
694
695/*
696 * Encode SETACL arguments
697 */
698static int
Al Virod61005a2006-10-19 23:28:48 -0700699nfs3_xdr_setaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000700 struct nfs3_setaclargs *args)
701{
702 struct xdr_buf *buf = &req->rq_snd_buf;
Trond Myklebustae461412009-03-10 20:33:18 -0400703 unsigned int base;
704 int err;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000705
706 p = xdr_encode_fhandle(p, NFS_FH(args->inode));
707 *p++ = htonl(args->mask);
Trond Myklebustae461412009-03-10 20:33:18 -0400708 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
709 base = req->rq_slen;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000710
Trond Myklebustae461412009-03-10 20:33:18 -0400711 if (args->npages != 0)
712 xdr_encode_pages(buf, args->pages, 0, args->len);
713 else
Trond Myklebust83404372009-04-20 14:58:35 -0400714 req->rq_slen = xdr_adjust_iovec(req->rq_svec,
715 p + XDR_QUADLEN(args->len));
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000716
717 err = nfsacl_encode(buf, base, args->inode,
718 (args->mask & NFS_ACL) ?
719 args->acl_access : NULL, 1, 0);
720 if (err > 0)
721 err = nfsacl_encode(buf, base + err, args->inode,
722 (args->mask & NFS_DFACL) ?
723 args->acl_default : NULL, 1,
724 NFS_ACL_DEFAULT);
725 return (err > 0) ? 0 : err;
726}
727#endif /* CONFIG_NFS_V3_ACL */
728
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729/*
730 * NFS XDR decode functions
731 */
732
733/*
734 * Decode attrstat reply.
735 */
736static int
Al Virod61005a2006-10-19 23:28:48 -0700737nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738{
739 int status;
740
741 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300742 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 xdr_decode_fattr(p, fattr);
744 return 0;
745}
746
747/*
748 * Decode status+wcc_data reply
749 * SATTR, REMOVE, RMDIR
750 */
751static int
Al Virod61005a2006-10-19 23:28:48 -0700752nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753{
754 int status;
755
756 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300757 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 xdr_decode_wcc_data(p, fattr);
759 return status;
760}
761
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400762static int
763nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
764{
Trond Myklebustd3468902010-04-16 16:22:50 -0400765 return nfs3_xdr_wccstat(req, p, res->dir_attr);
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400766}
767
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768/*
769 * Decode LOOKUP reply
770 */
771static int
Al Virod61005a2006-10-19 23:28:48 -0700772nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773{
774 int status;
775
776 if ((status = ntohl(*p++))) {
Benny Halevy856dff32008-03-31 17:39:06 +0300777 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 } else {
779 if (!(p = xdr_decode_fhandle(p, res->fh)))
780 return -errno_NFSERR_IO;
781 p = xdr_decode_post_op_attr(p, res->fattr);
782 }
783 xdr_decode_post_op_attr(p, res->dir_attr);
784 return status;
785}
786
787/*
788 * Decode ACCESS reply
789 */
790static int
Al Virod61005a2006-10-19 23:28:48 -0700791nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792{
793 int status = ntohl(*p++);
794
795 p = xdr_decode_post_op_attr(p, res->fattr);
796 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300797 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 res->access = ntohl(*p++);
799 return 0;
800}
801
802static int
Al Virod61005a2006-10-19 23:28:48 -0700803nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804{
Trond Myklebusta17c2152010-07-31 14:29:08 -0400805 struct rpc_auth *auth = req->rq_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 unsigned int replen;
807
808 p = xdr_encode_fhandle(p, args->fh);
809 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
810
811 /* Inline the page array */
812 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
813 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
814 return 0;
815}
816
817/*
818 * Decode READLINK reply
819 */
820static int
Al Virod61005a2006-10-19 23:28:48 -0700821nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822{
823 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
824 struct kvec *iov = rcvbuf->head;
Chuck Leverc957c522007-10-26 13:31:57 -0400825 size_t hdrlen;
826 u32 len, recvd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 int status;
828
829 status = ntohl(*p++);
830 p = xdr_decode_post_op_attr(p, fattr);
831
832 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300833 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
835 /* Convert length of symlink */
836 len = ntohl(*p++);
Chuck Leverc957c522007-10-26 13:31:57 -0400837 if (len >= rcvbuf->page_len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400838 dprintk("nfs: server returned giant symlink!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 return -ENAMETOOLONG;
840 }
841
842 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
843 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400844 dprintk("NFS: READLINK reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400845 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 return -errno_NFSERR_IO;
847 } else if (iov->iov_len != hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400848 dprintk("NFS: READLINK header is short. "
849 "iovec will be shifted.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
851 }
852 recvd = req->rq_rcv_buf.len - hdrlen;
853 if (recvd < len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400854 dprintk("NFS: server cheating in readlink reply: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 "count %u > recvd %u\n", len, recvd);
856 return -EIO;
857 }
858
Chuck Leverb4687da2010-09-21 16:55:48 -0400859 xdr_terminate_string(rcvbuf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 return 0;
861}
862
863/*
864 * Decode READ reply
865 */
866static int
Al Virod61005a2006-10-19 23:28:48 -0700867nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868{
869 struct kvec *iov = req->rq_rcv_buf.head;
Chuck Leverc957c522007-10-26 13:31:57 -0400870 size_t hdrlen;
871 u32 count, ocount, recvd;
872 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873
874 status = ntohl(*p++);
875 p = xdr_decode_post_op_attr(p, res->fattr);
876
877 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300878 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879
Chuck Leverc957c522007-10-26 13:31:57 -0400880 /* Decode reply count and EOF flag. NFSv3 is somewhat redundant
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 * in that it puts the count both in the res struct and in the
882 * opaque data count. */
883 count = ntohl(*p++);
884 res->eof = ntohl(*p++);
885 ocount = ntohl(*p++);
886
887 if (ocount != count) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400888 dprintk("NFS: READ count doesn't match RPC opaque count.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 return -errno_NFSERR_IO;
890 }
891
892 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
893 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400894 dprintk("NFS: READ reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400895 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 return -errno_NFSERR_IO;
897 } else if (iov->iov_len != hdrlen) {
898 dprintk("NFS: READ header is short. iovec will be shifted.\n");
899 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
900 }
901
902 recvd = req->rq_rcv_buf.len - hdrlen;
903 if (count > recvd) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400904 dprintk("NFS: server cheating in read reply: "
Chuck Leverc957c522007-10-26 13:31:57 -0400905 "count %u > recvd %u\n", count, recvd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 count = recvd;
907 res->eof = 0;
908 }
909
910 if (count < res->count)
911 res->count = count;
912
913 return count;
914}
915
916/*
917 * Decode WRITE response
918 */
919static int
Al Virod61005a2006-10-19 23:28:48 -0700920nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921{
922 int status;
923
924 status = ntohl(*p++);
925 p = xdr_decode_wcc_data(p, res->fattr);
926
927 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300928 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929
930 res->count = ntohl(*p++);
931 res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
932 res->verf->verifier[0] = *p++;
933 res->verf->verifier[1] = *p++;
934
935 return res->count;
936}
937
938/*
939 * Decode a CREATE response
940 */
941static int
Al Virod61005a2006-10-19 23:28:48 -0700942nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943{
944 int status;
945
946 status = ntohl(*p++);
947 if (status == 0) {
948 if (*p++) {
949 if (!(p = xdr_decode_fhandle(p, res->fh)))
950 return -errno_NFSERR_IO;
951 p = xdr_decode_post_op_attr(p, res->fattr);
952 } else {
953 memset(res->fh, 0, sizeof(*res->fh));
954 /* Do decode post_op_attr but set it to NULL */
955 p = xdr_decode_post_op_attr(p, res->fattr);
956 res->fattr->valid = 0;
957 }
958 } else {
Benny Halevy856dff32008-03-31 17:39:06 +0300959 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 }
961 p = xdr_decode_wcc_data(p, res->dir_attr);
962 return status;
963}
964
965/*
966 * Decode RENAME reply
967 */
968static int
Jeff Laytone8582a82010-09-17 17:31:06 -0400969nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs_renameres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970{
971 int status;
972
973 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300974 status = nfs_stat_to_errno(status);
Jeff Laytone8582a82010-09-17 17:31:06 -0400975 p = xdr_decode_wcc_data(p, res->old_fattr);
976 p = xdr_decode_wcc_data(p, res->new_fattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 return status;
978}
979
980/*
981 * Decode LINK reply
982 */
983static int
Al Virod61005a2006-10-19 23:28:48 -0700984nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985{
986 int status;
987
988 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300989 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 p = xdr_decode_post_op_attr(p, res->fattr);
991 p = xdr_decode_wcc_data(p, res->dir_attr);
992 return status;
993}
994
995/*
996 * Decode FSSTAT reply
997 */
998static int
Al Virod61005a2006-10-19 23:28:48 -0700999nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000{
1001 int status;
1002
1003 status = ntohl(*p++);
1004
1005 p = xdr_decode_post_op_attr(p, res->fattr);
1006 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001007 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
1009 p = xdr_decode_hyper(p, &res->tbytes);
1010 p = xdr_decode_hyper(p, &res->fbytes);
1011 p = xdr_decode_hyper(p, &res->abytes);
1012 p = xdr_decode_hyper(p, &res->tfiles);
1013 p = xdr_decode_hyper(p, &res->ffiles);
1014 p = xdr_decode_hyper(p, &res->afiles);
1015
1016 /* ignore invarsec */
1017 return 0;
1018}
1019
1020/*
1021 * Decode FSINFO reply
1022 */
1023static int
Al Virod61005a2006-10-19 23:28:48 -07001024nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025{
1026 int status;
1027
1028 status = ntohl(*p++);
1029
1030 p = xdr_decode_post_op_attr(p, res->fattr);
1031 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001032 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033
1034 res->rtmax = ntohl(*p++);
1035 res->rtpref = ntohl(*p++);
1036 res->rtmult = ntohl(*p++);
1037 res->wtmax = ntohl(*p++);
1038 res->wtpref = ntohl(*p++);
1039 res->wtmult = ntohl(*p++);
1040 res->dtpref = ntohl(*p++);
1041 p = xdr_decode_hyper(p, &res->maxfilesize);
1042
1043 /* ignore time_delta and properties */
1044 res->lease_time = 0;
1045 return 0;
1046}
1047
1048/*
1049 * Decode PATHCONF reply
1050 */
1051static int
Al Virod61005a2006-10-19 23:28:48 -07001052nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053{
1054 int status;
1055
1056 status = ntohl(*p++);
1057
1058 p = xdr_decode_post_op_attr(p, res->fattr);
1059 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001060 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 res->max_link = ntohl(*p++);
1062 res->max_namelen = ntohl(*p++);
1063
1064 /* ignore remaining fields */
1065 return 0;
1066}
1067
1068/*
1069 * Decode COMMIT reply
1070 */
1071static int
Al Virod61005a2006-10-19 23:28:48 -07001072nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073{
1074 int status;
1075
1076 status = ntohl(*p++);
1077 p = xdr_decode_wcc_data(p, res->fattr);
1078 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001079 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080
1081 res->verf->verifier[0] = *p++;
1082 res->verf->verifier[1] = *p++;
1083 return 0;
1084}
1085
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001086#ifdef CONFIG_NFS_V3_ACL
1087/*
1088 * Decode GETACL reply
1089 */
1090static int
Al Virod61005a2006-10-19 23:28:48 -07001091nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001092 struct nfs3_getaclres *res)
1093{
1094 struct xdr_buf *buf = &req->rq_rcv_buf;
1095 int status = ntohl(*p++);
1096 struct posix_acl **acl;
1097 unsigned int *aclcnt;
1098 int err, base;
1099
1100 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001101 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001102 p = xdr_decode_post_op_attr(p, res->fattr);
1103 res->mask = ntohl(*p++);
1104 if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
1105 return -EINVAL;
1106 base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
1107
1108 acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
1109 aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
1110 err = nfsacl_decode(buf, base, aclcnt, acl);
1111
1112 acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
1113 aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
1114 if (err > 0)
1115 err = nfsacl_decode(buf, base + err, aclcnt, acl);
1116 return (err > 0) ? 0 : err;
1117}
1118
1119/*
1120 * Decode setacl reply.
1121 */
1122static int
Al Virod61005a2006-10-19 23:28:48 -07001123nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001124{
1125 int status = ntohl(*p++);
1126
1127 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +03001128 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001129 xdr_decode_post_op_attr(p, fattr);
1130 return 0;
1131}
1132#endif /* CONFIG_NFS_V3_ACL */
1133
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134#define PROC(proc, argtype, restype, timer) \
1135[NFS3PROC_##proc] = { \
1136 .p_proc = NFS3PROC_##proc, \
1137 .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
1138 .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
Chuck Lever2bea90d2007-03-29 16:47:53 -04001139 .p_arglen = NFS3_##argtype##_sz, \
1140 .p_replen = NFS3_##restype##_sz, \
Chuck Levercc0175c2006-03-20 13:44:22 -05001141 .p_timer = timer, \
1142 .p_statidx = NFS3PROC_##proc, \
1143 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 }
1145
1146struct rpc_procinfo nfs3_procedures[] = {
1147 PROC(GETATTR, fhandle, attrstat, 1),
1148 PROC(SETATTR, sattrargs, wccstat, 0),
1149 PROC(LOOKUP, diropargs, lookupres, 2),
1150 PROC(ACCESS, accessargs, accessres, 1),
1151 PROC(READLINK, readlinkargs, readlinkres, 3),
1152 PROC(READ, readargs, readres, 3),
1153 PROC(WRITE, writeargs, writeres, 4),
1154 PROC(CREATE, createargs, createres, 0),
1155 PROC(MKDIR, mkdirargs, createres, 0),
1156 PROC(SYMLINK, symlinkargs, createres, 0),
1157 PROC(MKNOD, mknodargs, createres, 0),
Trond Myklebust4fdc17b2007-07-14 15:39:57 -04001158 PROC(REMOVE, removeargs, removeres, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 PROC(RMDIR, diropargs, wccstat, 0),
1160 PROC(RENAME, renameargs, renameres, 0),
1161 PROC(LINK, linkargs, linkres, 0),
1162 PROC(READDIR, readdirargs, readdirres, 3),
1163 PROC(READDIRPLUS, readdirargs, readdirres, 3),
1164 PROC(FSSTAT, fhandle, fsstatres, 0),
1165 PROC(FSINFO, fhandle, fsinfores, 0),
1166 PROC(PATHCONF, fhandle, pathconfres, 0),
1167 PROC(COMMIT, commitargs, commitres, 5),
1168};
1169
1170struct rpc_version nfs_version3 = {
1171 .number = 3,
Tobias Klausere8c96f82006-03-24 03:15:34 -08001172 .nrprocs = ARRAY_SIZE(nfs3_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 .procs = nfs3_procedures
1174};
1175
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001176#ifdef CONFIG_NFS_V3_ACL
1177static struct rpc_procinfo nfs3_acl_procedures[] = {
1178 [ACLPROC3_GETACL] = {
1179 .p_proc = ACLPROC3_GETACL,
1180 .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
1181 .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001182 .p_arglen = ACL3_getaclargs_sz,
1183 .p_replen = ACL3_getaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001184 .p_timer = 1,
Chuck Levercc0175c2006-03-20 13:44:22 -05001185 .p_name = "GETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001186 },
1187 [ACLPROC3_SETACL] = {
1188 .p_proc = ACLPROC3_SETACL,
1189 .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
1190 .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001191 .p_arglen = ACL3_setaclargs_sz,
1192 .p_replen = ACL3_setaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001193 .p_timer = 0,
Chuck Levercc0175c2006-03-20 13:44:22 -05001194 .p_name = "SETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001195 },
1196};
1197
1198struct rpc_version nfsacl_version3 = {
1199 .number = 3,
1200 .nrprocs = sizeof(nfs3_acl_procedures)/
1201 sizeof(nfs3_acl_procedures[0]),
1202 .procs = nfs3_acl_procedures,
1203};
1204#endif /* CONFIG_NFS_V3_ACL */