blob: 5fe5492fbd29e51fab41de8930b29f6863e76747 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/fs/nfs/nfs3xdr.c
3 *
4 * XDR functions to encode/decode NFSv3 RPC arguments and results.
5 *
6 * Copyright (C) 1996, 1997 Olaf Kirch
7 */
8
9#include <linux/param.h>
10#include <linux/time.h>
11#include <linux/mm.h>
12#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <linux/errno.h>
14#include <linux/string.h>
15#include <linux/in.h>
16#include <linux/pagemap.h>
17#include <linux/proc_fs.h>
18#include <linux/kdev_t.h>
19#include <linux/sunrpc/clnt.h>
20#include <linux/nfs.h>
21#include <linux/nfs3.h>
22#include <linux/nfs_fs.h>
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000023#include <linux/nfsacl.h>
David Howellsf7b422b2006-06-09 09:34:33 -040024#include "internal.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
26#define NFSDBG_FACILITY NFSDBG_XDR
27
28/* Mapping from NFS error code to "errno" error code. */
29#define errno_NFSERR_IO EIO
30
Linus Torvalds1da177e2005-04-16 15:20:36 -070031/*
32 * Declare the space requirements for NFS arguments and replies as
33 * number of 32bit-words
34 */
35#define NFS3_fhandle_sz (1+16)
36#define NFS3_fh_sz (NFS3_fhandle_sz) /* shorthand */
37#define NFS3_sattr_sz (15)
38#define NFS3_filename_sz (1+(NFS3_MAXNAMLEN>>2))
39#define NFS3_path_sz (1+(NFS3_MAXPATHLEN>>2))
40#define NFS3_fattr_sz (21)
41#define NFS3_wcc_attr_sz (6)
42#define NFS3_pre_op_attr_sz (1+NFS3_wcc_attr_sz)
43#define NFS3_post_op_attr_sz (1+NFS3_fattr_sz)
44#define NFS3_wcc_data_sz (NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
45#define NFS3_fsstat_sz
46#define NFS3_fsinfo_sz
47#define NFS3_pathconf_sz
48#define NFS3_entry_sz (NFS3_filename_sz+3)
49
50#define NFS3_sattrargs_sz (NFS3_fh_sz+NFS3_sattr_sz+3)
51#define NFS3_diropargs_sz (NFS3_fh_sz+NFS3_filename_sz)
Trond Myklebust4fdc17b2007-07-14 15:39:57 -040052#define NFS3_removeargs_sz (NFS3_fh_sz+NFS3_filename_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#define NFS3_accessargs_sz (NFS3_fh_sz+1)
54#define NFS3_readlinkargs_sz (NFS3_fh_sz)
55#define NFS3_readargs_sz (NFS3_fh_sz+3)
56#define NFS3_writeargs_sz (NFS3_fh_sz+5)
57#define NFS3_createargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
58#define NFS3_mkdirargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
Chuck Lever94a6d752006-08-22 20:06:23 -040059#define NFS3_symlinkargs_sz (NFS3_diropargs_sz+1+NFS3_sattr_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#define NFS3_mknodargs_sz (NFS3_diropargs_sz+2+NFS3_sattr_sz)
61#define NFS3_renameargs_sz (NFS3_diropargs_sz+NFS3_diropargs_sz)
62#define NFS3_linkargs_sz (NFS3_fh_sz+NFS3_diropargs_sz)
63#define NFS3_readdirargs_sz (NFS3_fh_sz+2)
64#define NFS3_commitargs_sz (NFS3_fh_sz+3)
65
66#define NFS3_attrstat_sz (1+NFS3_fattr_sz)
67#define NFS3_wccstat_sz (1+NFS3_wcc_data_sz)
Trond Myklebust4fdc17b2007-07-14 15:39:57 -040068#define NFS3_removeres_sz (NFS3_wccstat_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070069#define NFS3_lookupres_sz (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
70#define NFS3_accessres_sz (1+NFS3_post_op_attr_sz+1)
71#define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz+1)
72#define NFS3_readres_sz (1+NFS3_post_op_attr_sz+3)
73#define NFS3_writeres_sz (1+NFS3_wcc_data_sz+4)
74#define NFS3_createres_sz (1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
75#define NFS3_renameres_sz (1+(2 * NFS3_wcc_data_sz))
76#define NFS3_linkres_sz (1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
77#define NFS3_readdirres_sz (1+NFS3_post_op_attr_sz+2)
78#define NFS3_fsstatres_sz (1+NFS3_post_op_attr_sz+13)
79#define NFS3_fsinfores_sz (1+NFS3_post_op_attr_sz+12)
80#define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6)
81#define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2)
82
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000083#define ACL3_getaclargs_sz (NFS3_fh_sz+1)
Trond Myklebustae461412009-03-10 20:33:18 -040084#define ACL3_setaclargs_sz (NFS3_fh_sz+1+ \
85 XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
86#define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+ \
87 XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000088#define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz)
89
Linus Torvalds1da177e2005-04-16 15:20:36 -070090/*
91 * Map file type to S_IFMT bits
92 */
Trond Myklebustbca79472009-03-11 14:10:26 -040093static const umode_t nfs_type2fmt[] = {
94 [NF3BAD] = 0,
95 [NF3REG] = S_IFREG,
96 [NF3DIR] = S_IFDIR,
97 [NF3BLK] = S_IFBLK,
98 [NF3CHR] = S_IFCHR,
99 [NF3LNK] = S_IFLNK,
100 [NF3SOCK] = S_IFSOCK,
101 [NF3FIFO] = S_IFIFO,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102};
103
104/*
105 * Common NFS XDR functions as inlines
106 */
Al Virod61005a2006-10-19 23:28:48 -0700107static inline __be32 *
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400108xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109{
110 return xdr_encode_array(p, fh->data, fh->size);
111}
112
Al Virod61005a2006-10-19 23:28:48 -0700113static inline __be32 *
114xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115{
116 if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
117 memcpy(fh->data, p, fh->size);
118 return p + XDR_QUADLEN(fh->size);
119 }
120 return NULL;
121}
122
123/*
124 * Encode/decode time.
125 */
Al Virod61005a2006-10-19 23:28:48 -0700126static inline __be32 *
127xdr_encode_time3(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128{
129 *p++ = htonl(timep->tv_sec);
130 *p++ = htonl(timep->tv_nsec);
131 return p;
132}
133
Al Virod61005a2006-10-19 23:28:48 -0700134static inline __be32 *
135xdr_decode_time3(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136{
137 timep->tv_sec = ntohl(*p++);
138 timep->tv_nsec = ntohl(*p++);
139 return p;
140}
141
Al Virod61005a2006-10-19 23:28:48 -0700142static __be32 *
143xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144{
145 unsigned int type, major, minor;
Trond Myklebustbca79472009-03-11 14:10:26 -0400146 umode_t fmode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
148 type = ntohl(*p++);
Trond Myklebustbca79472009-03-11 14:10:26 -0400149 if (type > NF3FIFO)
150 type = NF3NON;
151 fmode = nfs_type2fmt[type];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
153 fattr->nlink = ntohl(*p++);
154 fattr->uid = ntohl(*p++);
155 fattr->gid = ntohl(*p++);
156 p = xdr_decode_hyper(p, &fattr->size);
157 p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
158
159 /* Turn remote device info into Linux-specific dev_t */
160 major = ntohl(*p++);
161 minor = ntohl(*p++);
162 fattr->rdev = MKDEV(major, minor);
163 if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor)
164 fattr->rdev = 0;
165
Trond Myklebust8b4bdcf2006-06-09 09:34:19 -0400166 p = xdr_decode_hyper(p, &fattr->fsid.major);
167 fattr->fsid.minor = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 p = xdr_decode_hyper(p, &fattr->fileid);
169 p = xdr_decode_time3(p, &fattr->atime);
170 p = xdr_decode_time3(p, &fattr->mtime);
171 p = xdr_decode_time3(p, &fattr->ctime);
172
173 /* Update the mode bits */
Trond Myklebust9e6e70f2009-03-11 14:10:24 -0400174 fattr->valid |= NFS_ATTR_FATTR_V3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 return p;
176}
177
Al Virod61005a2006-10-19 23:28:48 -0700178static inline __be32 *
179xdr_encode_sattr(__be32 *p, struct iattr *attr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180{
181 if (attr->ia_valid & ATTR_MODE) {
182 *p++ = xdr_one;
Trond Myklebustcf3fff52006-01-03 09:55:53 +0100183 *p++ = htonl(attr->ia_mode & S_IALLUGO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 } else {
185 *p++ = xdr_zero;
186 }
187 if (attr->ia_valid & ATTR_UID) {
188 *p++ = xdr_one;
189 *p++ = htonl(attr->ia_uid);
190 } else {
191 *p++ = xdr_zero;
192 }
193 if (attr->ia_valid & ATTR_GID) {
194 *p++ = xdr_one;
195 *p++ = htonl(attr->ia_gid);
196 } else {
197 *p++ = xdr_zero;
198 }
199 if (attr->ia_valid & ATTR_SIZE) {
200 *p++ = xdr_one;
201 p = xdr_encode_hyper(p, (__u64) attr->ia_size);
202 } else {
203 *p++ = xdr_zero;
204 }
205 if (attr->ia_valid & ATTR_ATIME_SET) {
206 *p++ = xdr_two;
207 p = xdr_encode_time3(p, &attr->ia_atime);
208 } else if (attr->ia_valid & ATTR_ATIME) {
209 *p++ = xdr_one;
210 } else {
211 *p++ = xdr_zero;
212 }
213 if (attr->ia_valid & ATTR_MTIME_SET) {
214 *p++ = xdr_two;
215 p = xdr_encode_time3(p, &attr->ia_mtime);
216 } else if (attr->ia_valid & ATTR_MTIME) {
217 *p++ = xdr_one;
218 } else {
219 *p++ = xdr_zero;
220 }
221 return p;
222}
223
Al Virod61005a2006-10-19 23:28:48 -0700224static inline __be32 *
225xdr_decode_wcc_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226{
227 p = xdr_decode_hyper(p, &fattr->pre_size);
228 p = xdr_decode_time3(p, &fattr->pre_mtime);
229 p = xdr_decode_time3(p, &fattr->pre_ctime);
Trond Myklebust9e6e70f2009-03-11 14:10:24 -0400230 fattr->valid |= NFS_ATTR_FATTR_PRESIZE
231 | NFS_ATTR_FATTR_PREMTIME
232 | NFS_ATTR_FATTR_PRECTIME;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 return p;
234}
235
Al Virod61005a2006-10-19 23:28:48 -0700236static inline __be32 *
237xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238{
239 if (*p++)
240 p = xdr_decode_fattr(p, fattr);
241 return p;
242}
243
Al Virod61005a2006-10-19 23:28:48 -0700244static inline __be32 *
245xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246{
247 if (*p++)
248 return xdr_decode_wcc_attr(p, fattr);
249 return p;
250}
251
252
Al Virod61005a2006-10-19 23:28:48 -0700253static inline __be32 *
254xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255{
256 p = xdr_decode_pre_op_attr(p, fattr);
257 return xdr_decode_post_op_attr(p, fattr);
258}
259
260/*
261 * NFS encode functions
262 */
263
264/*
265 * Encode file handle argument
266 */
267static int
Al Virod61005a2006-10-19 23:28:48 -0700268nfs3_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269{
270 p = xdr_encode_fhandle(p, fh);
271 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
272 return 0;
273}
274
275/*
276 * Encode SETATTR arguments
277 */
278static int
Al Virod61005a2006-10-19 23:28:48 -0700279nfs3_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs3_sattrargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280{
281 p = xdr_encode_fhandle(p, args->fh);
282 p = xdr_encode_sattr(p, args->sattr);
283 *p++ = htonl(args->guard);
284 if (args->guard)
285 p = xdr_encode_time3(p, &args->guardtime);
286 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
287 return 0;
288}
289
290/*
291 * Encode directory ops argument
292 */
293static int
Al Virod61005a2006-10-19 23:28:48 -0700294nfs3_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs3_diropargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295{
296 p = xdr_encode_fhandle(p, args->fh);
297 p = xdr_encode_array(p, args->name, args->len);
298 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
299 return 0;
300}
301
302/*
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400303 * Encode REMOVE argument
304 */
305static int
306nfs3_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
307{
308 p = xdr_encode_fhandle(p, args->fh);
309 p = xdr_encode_array(p, args->name.name, args->name.len);
310 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
311 return 0;
312}
313
314/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 * Encode access() argument
316 */
317static int
Al Virod61005a2006-10-19 23:28:48 -0700318nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319{
320 p = xdr_encode_fhandle(p, args->fh);
321 *p++ = htonl(args->access);
322 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
323 return 0;
324}
325
326/*
327 * Arguments to a READ call. Since we read data directly into the page
328 * cache, we also set up the reply iovec here so that iov[1] points
329 * exactly to the page we want to fetch.
330 */
331static int
Al Virod61005a2006-10-19 23:28:48 -0700332nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400334 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 unsigned int replen;
336 u32 count = args->count;
337
338 p = xdr_encode_fhandle(p, args->fh);
339 p = xdr_encode_hyper(p, args->offset);
340 *p++ = htonl(count);
341 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
342
343 /* Inline the page array */
344 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
345 xdr_inline_pages(&req->rq_rcv_buf, replen,
346 args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400347 req->rq_rcv_buf.flags |= XDRBUF_READ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 return 0;
349}
350
351/*
352 * Write arguments. Splice the buffer to be written into the iovec.
353 */
354static int
Al Virod61005a2006-10-19 23:28:48 -0700355nfs3_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356{
357 struct xdr_buf *sndbuf = &req->rq_snd_buf;
358 u32 count = args->count;
359
360 p = xdr_encode_fhandle(p, args->fh);
361 p = xdr_encode_hyper(p, args->offset);
362 *p++ = htonl(count);
363 *p++ = htonl(args->stable);
364 *p++ = htonl(count);
365 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
366
367 /* Copy the page array */
368 xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400369 sndbuf->flags |= XDRBUF_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 return 0;
371}
372
373/*
374 * Encode CREATE arguments
375 */
376static int
Al Virod61005a2006-10-19 23:28:48 -0700377nfs3_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs3_createargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378{
379 p = xdr_encode_fhandle(p, args->fh);
380 p = xdr_encode_array(p, args->name, args->len);
381
382 *p++ = htonl(args->createmode);
383 if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
384 *p++ = args->verifier[0];
385 *p++ = args->verifier[1];
386 } else
387 p = xdr_encode_sattr(p, args->sattr);
388
389 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
390 return 0;
391}
392
393/*
394 * Encode MKDIR arguments
395 */
396static int
Al Virod61005a2006-10-19 23:28:48 -0700397nfs3_xdr_mkdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mkdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398{
399 p = xdr_encode_fhandle(p, args->fh);
400 p = xdr_encode_array(p, args->name, args->len);
401 p = xdr_encode_sattr(p, args->sattr);
402 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
403 return 0;
404}
405
406/*
407 * Encode SYMLINK arguments
408 */
409static int
Al Virod61005a2006-10-19 23:28:48 -0700410nfs3_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_symlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
412 p = xdr_encode_fhandle(p, args->fromfh);
413 p = xdr_encode_array(p, args->fromname, args->fromlen);
414 p = xdr_encode_sattr(p, args->sattr);
Chuck Lever94a6d752006-08-22 20:06:23 -0400415 *p++ = htonl(args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
Chuck Lever94a6d752006-08-22 20:06:23 -0400417
418 /* Copy the page */
419 xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 return 0;
421}
422
423/*
424 * Encode MKNOD arguments
425 */
426static int
Al Virod61005a2006-10-19 23:28:48 -0700427nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428{
429 p = xdr_encode_fhandle(p, args->fh);
430 p = xdr_encode_array(p, args->name, args->len);
431 *p++ = htonl(args->type);
432 p = xdr_encode_sattr(p, args->sattr);
433 if (args->type == NF3CHR || args->type == NF3BLK) {
434 *p++ = htonl(MAJOR(args->rdev));
435 *p++ = htonl(MINOR(args->rdev));
436 }
437
438 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
439 return 0;
440}
441
442/*
443 * Encode RENAME arguments
444 */
445static int
Al Virod61005a2006-10-19 23:28:48 -0700446nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs3_renameargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447{
448 p = xdr_encode_fhandle(p, args->fromfh);
449 p = xdr_encode_array(p, args->fromname, args->fromlen);
450 p = xdr_encode_fhandle(p, args->tofh);
451 p = xdr_encode_array(p, args->toname, args->tolen);
452 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
453 return 0;
454}
455
456/*
457 * Encode LINK arguments
458 */
459static int
Al Virod61005a2006-10-19 23:28:48 -0700460nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461{
462 p = xdr_encode_fhandle(p, args->fromfh);
463 p = xdr_encode_fhandle(p, args->tofh);
464 p = xdr_encode_array(p, args->toname, args->tolen);
465 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
466 return 0;
467}
468
469/*
470 * Encode arguments to readdir call
471 */
472static int
Al Virod61005a2006-10-19 23:28:48 -0700473nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400475 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 unsigned int replen;
477 u32 count = args->count;
478
479 p = xdr_encode_fhandle(p, args->fh);
480 p = xdr_encode_hyper(p, args->cookie);
481 *p++ = args->verf[0];
482 *p++ = args->verf[1];
483 if (args->plus) {
484 /* readdirplus: need dircount + buffer size.
485 * We just make sure we make dircount big enough */
486 *p++ = htonl(count >> 3);
487 }
488 *p++ = htonl(count);
489 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
490
491 /* Inline the page array */
492 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
493 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
494 return 0;
495}
496
497/*
498 * Decode the result of a readdir call.
499 * We just check for syntactical correctness.
500 */
501static int
Al Virod61005a2006-10-19 23:28:48 -0700502nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503{
504 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
505 struct kvec *iov = rcvbuf->head;
506 struct page **page;
Chuck Leverc957c522007-10-26 13:31:57 -0400507 size_t hdrlen;
508 u32 len, recvd, pglen;
Jeff Layton643f8112008-02-22 14:50:00 -0500509 int status, nr = 0;
Al Virod61005a2006-10-19 23:28:48 -0700510 __be32 *entry, *end, *kaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
512 status = ntohl(*p++);
513 /* Decode post_op_attrs */
514 p = xdr_decode_post_op_attr(p, res->dir_attr);
515 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300516 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 /* Decode verifier cookie */
518 if (res->verf) {
519 res->verf[0] = *p++;
520 res->verf[1] = *p++;
521 } else {
522 p += 2;
523 }
524
525 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
526 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400527 dprintk("NFS: READDIR reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400528 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 return -errno_NFSERR_IO;
530 } else if (iov->iov_len != hdrlen) {
531 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
532 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
533 }
534
535 pglen = rcvbuf->page_len;
536 recvd = rcvbuf->len - hdrlen;
537 if (pglen > recvd)
538 pglen = recvd;
539 page = rcvbuf->pages;
Al Virod61005a2006-10-19 23:28:48 -0700540 kaddr = p = kmap_atomic(*page, KM_USER0);
541 end = (__be32 *)((char *)p + pglen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 entry = p;
Jeff Layton643f8112008-02-22 14:50:00 -0500543
544 /* Make sure the packet actually has a value_follows and EOF entry */
545 if ((entry + 1) > end)
546 goto short_pkt;
547
548 for (; *p++; nr++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 if (p + 3 > end)
550 goto short_pkt;
551 p += 2; /* inode # */
552 len = ntohl(*p++); /* string length */
553 p += XDR_QUADLEN(len) + 2; /* name + cookie */
554 if (len > NFS3_MAXNAMLEN) {
Chuck Leverc957c522007-10-26 13:31:57 -0400555 dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 len);
557 goto err_unmap;
558 }
559
560 if (res->plus) {
561 /* post_op_attr */
562 if (p + 2 > end)
563 goto short_pkt;
564 if (*p++) {
565 p += 21;
566 if (p + 1 > end)
567 goto short_pkt;
568 }
569 /* post_op_fh3 */
570 if (*p++) {
571 if (p + 1 > end)
572 goto short_pkt;
573 len = ntohl(*p++);
574 if (len > NFS3_FHSIZE) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400575 dprintk("NFS: giant filehandle in "
Chuck Leverc957c522007-10-26 13:31:57 -0400576 "readdir (len 0x%x)!\n", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 goto err_unmap;
578 }
579 p += XDR_QUADLEN(len);
580 }
581 }
582
583 if (p + 2 > end)
584 goto short_pkt;
585 entry = p;
586 }
Jeff Layton643f8112008-02-22 14:50:00 -0500587
588 /*
589 * Apparently some server sends responses that are a valid size, but
590 * contain no entries, and have value_follows==0 and EOF==0. For
591 * those, just set the EOF marker.
592 */
593 if (!nr && entry[1] == 0) {
594 dprintk("NFS: readdir reply truncated!\n");
595 entry[1] = 1;
596 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 out:
598 kunmap_atomic(kaddr, KM_USER0);
599 return nr;
600 short_pkt:
Jeff Layton643f8112008-02-22 14:50:00 -0500601 /*
602 * When we get a short packet there are 2 possibilities. We can
603 * return an error, or fix up the response to look like a valid
604 * response and return what we have so far. If there are no
605 * entries and the packet was short, then return -EIO. If there
606 * are valid entries in the response, return them and pretend that
607 * the call was successful, but incomplete. The caller can retry the
608 * readdir starting at the last cookie.
609 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 entry[0] = entry[1] = 0;
Jeff Layton643f8112008-02-22 14:50:00 -0500611 if (!nr)
612 nr = -errno_NFSERR_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 goto out;
614err_unmap:
615 nr = -errno_NFSERR_IO;
616 goto out;
617}
618
Al Viro0dbb4c62006-10-19 23:28:49 -0700619__be32 *
620nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621{
622 struct nfs_entry old = *entry;
623
624 if (!*p++) {
625 if (!*p)
626 return ERR_PTR(-EAGAIN);
627 entry->eof = 1;
628 return ERR_PTR(-EBADCOOKIE);
629 }
630
631 p = xdr_decode_hyper(p, &entry->ino);
632 entry->len = ntohl(*p++);
633 entry->name = (const char *) p;
634 p += XDR_QUADLEN(entry->len);
635 entry->prev_cookie = entry->cookie;
636 p = xdr_decode_hyper(p, &entry->cookie);
637
638 if (plus) {
639 entry->fattr->valid = 0;
640 p = xdr_decode_post_op_attr(p, entry->fattr);
641 /* In fact, a post_op_fh3: */
642 if (*p++) {
643 p = xdr_decode_fhandle(p, entry->fh);
644 /* Ugh -- server reply was truncated */
645 if (p == NULL) {
646 dprintk("NFS: FH truncated\n");
647 *entry = old;
648 return ERR_PTR(-EAGAIN);
649 }
650 } else
651 memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
652 }
653
654 entry->eof = !p[0] && p[1];
655 return p;
656}
657
658/*
659 * Encode COMMIT arguments
660 */
661static int
Al Virod61005a2006-10-19 23:28:48 -0700662nfs3_xdr_commitargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663{
664 p = xdr_encode_fhandle(p, args->fh);
665 p = xdr_encode_hyper(p, args->offset);
666 *p++ = htonl(args->count);
667 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
668 return 0;
669}
670
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000671#ifdef CONFIG_NFS_V3_ACL
672/*
673 * Encode GETACL arguments
674 */
675static int
Al Virod61005a2006-10-19 23:28:48 -0700676nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000677 struct nfs3_getaclargs *args)
678{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400679 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000680 unsigned int replen;
681
682 p = xdr_encode_fhandle(p, args->fh);
683 *p++ = htonl(args->mask);
684 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
685
686 if (args->mask & (NFS_ACL | NFS_DFACL)) {
687 /* Inline the page array */
688 replen = (RPC_REPHDRSIZE + auth->au_rslack +
689 ACL3_getaclres_sz) << 2;
690 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
691 NFSACL_MAXPAGES << PAGE_SHIFT);
692 }
693 return 0;
694}
695
696/*
697 * Encode SETACL arguments
698 */
699static int
Al Virod61005a2006-10-19 23:28:48 -0700700nfs3_xdr_setaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000701 struct nfs3_setaclargs *args)
702{
703 struct xdr_buf *buf = &req->rq_snd_buf;
Trond Myklebustae461412009-03-10 20:33:18 -0400704 unsigned int base;
705 int err;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000706
707 p = xdr_encode_fhandle(p, NFS_FH(args->inode));
708 *p++ = htonl(args->mask);
Trond Myklebustae461412009-03-10 20:33:18 -0400709 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
710 base = req->rq_slen;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000711
Trond Myklebustae461412009-03-10 20:33:18 -0400712 if (args->npages != 0)
713 xdr_encode_pages(buf, args->pages, 0, args->len);
714 else
Trond Myklebust83404372009-04-20 14:58:35 -0400715 req->rq_slen = xdr_adjust_iovec(req->rq_svec,
716 p + XDR_QUADLEN(args->len));
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000717
718 err = nfsacl_encode(buf, base, args->inode,
719 (args->mask & NFS_ACL) ?
720 args->acl_access : NULL, 1, 0);
721 if (err > 0)
722 err = nfsacl_encode(buf, base + err, args->inode,
723 (args->mask & NFS_DFACL) ?
724 args->acl_default : NULL, 1,
725 NFS_ACL_DEFAULT);
726 return (err > 0) ? 0 : err;
727}
728#endif /* CONFIG_NFS_V3_ACL */
729
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730/*
731 * NFS XDR decode functions
732 */
733
734/*
735 * Decode attrstat reply.
736 */
737static int
Al Virod61005a2006-10-19 23:28:48 -0700738nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739{
740 int status;
741
742 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300743 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 xdr_decode_fattr(p, fattr);
745 return 0;
746}
747
748/*
749 * Decode status+wcc_data reply
750 * SATTR, REMOVE, RMDIR
751 */
752static int
Al Virod61005a2006-10-19 23:28:48 -0700753nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754{
755 int status;
756
757 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300758 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 xdr_decode_wcc_data(p, fattr);
760 return status;
761}
762
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400763static int
764nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
765{
766 return nfs3_xdr_wccstat(req, p, &res->dir_attr);
767}
768
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769/*
770 * Decode LOOKUP reply
771 */
772static int
Al Virod61005a2006-10-19 23:28:48 -0700773nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774{
775 int status;
776
777 if ((status = ntohl(*p++))) {
Benny Halevy856dff32008-03-31 17:39:06 +0300778 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 } else {
780 if (!(p = xdr_decode_fhandle(p, res->fh)))
781 return -errno_NFSERR_IO;
782 p = xdr_decode_post_op_attr(p, res->fattr);
783 }
784 xdr_decode_post_op_attr(p, res->dir_attr);
785 return status;
786}
787
788/*
789 * Decode ACCESS reply
790 */
791static int
Al Virod61005a2006-10-19 23:28:48 -0700792nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793{
794 int status = ntohl(*p++);
795
796 p = xdr_decode_post_op_attr(p, res->fattr);
797 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300798 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 res->access = ntohl(*p++);
800 return 0;
801}
802
803static int
Al Virod61005a2006-10-19 23:28:48 -0700804nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400806 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 unsigned int replen;
808
809 p = xdr_encode_fhandle(p, args->fh);
810 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
811
812 /* Inline the page array */
813 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
814 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
815 return 0;
816}
817
818/*
819 * Decode READLINK reply
820 */
821static int
Al Virod61005a2006-10-19 23:28:48 -0700822nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823{
824 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
825 struct kvec *iov = rcvbuf->head;
Chuck Leverc957c522007-10-26 13:31:57 -0400826 size_t hdrlen;
827 u32 len, recvd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 char *kaddr;
829 int status;
830
831 status = ntohl(*p++);
832 p = xdr_decode_post_op_attr(p, fattr);
833
834 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300835 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
837 /* Convert length of symlink */
838 len = ntohl(*p++);
Chuck Leverc957c522007-10-26 13:31:57 -0400839 if (len >= rcvbuf->page_len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400840 dprintk("nfs: server returned giant symlink!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 return -ENAMETOOLONG;
842 }
843
844 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
845 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400846 dprintk("NFS: READLINK reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400847 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 return -errno_NFSERR_IO;
849 } else if (iov->iov_len != hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400850 dprintk("NFS: READLINK header is short. "
851 "iovec will be shifted.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
853 }
854 recvd = req->rq_rcv_buf.len - hdrlen;
855 if (recvd < len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400856 dprintk("NFS: server cheating in readlink reply: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 "count %u > recvd %u\n", len, recvd);
858 return -EIO;
859 }
860
861 /* NULL terminate the string we got */
862 kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
863 kaddr[len+rcvbuf->page_base] = '\0';
864 kunmap_atomic(kaddr, KM_USER0);
865 return 0;
866}
867
868/*
869 * Decode READ reply
870 */
871static int
Al Virod61005a2006-10-19 23:28:48 -0700872nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873{
874 struct kvec *iov = req->rq_rcv_buf.head;
Chuck Leverc957c522007-10-26 13:31:57 -0400875 size_t hdrlen;
876 u32 count, ocount, recvd;
877 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
879 status = ntohl(*p++);
880 p = xdr_decode_post_op_attr(p, res->fattr);
881
882 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300883 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
Chuck Leverc957c522007-10-26 13:31:57 -0400885 /* Decode reply count and EOF flag. NFSv3 is somewhat redundant
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 * in that it puts the count both in the res struct and in the
887 * opaque data count. */
888 count = ntohl(*p++);
889 res->eof = ntohl(*p++);
890 ocount = ntohl(*p++);
891
892 if (ocount != count) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400893 dprintk("NFS: READ count doesn't match RPC opaque count.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 return -errno_NFSERR_IO;
895 }
896
897 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
898 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400899 dprintk("NFS: READ reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400900 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 return -errno_NFSERR_IO;
902 } else if (iov->iov_len != hdrlen) {
903 dprintk("NFS: READ header is short. iovec will be shifted.\n");
904 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
905 }
906
907 recvd = req->rq_rcv_buf.len - hdrlen;
908 if (count > recvd) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400909 dprintk("NFS: server cheating in read reply: "
Chuck Leverc957c522007-10-26 13:31:57 -0400910 "count %u > recvd %u\n", count, recvd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 count = recvd;
912 res->eof = 0;
913 }
914
915 if (count < res->count)
916 res->count = count;
917
918 return count;
919}
920
921/*
922 * Decode WRITE response
923 */
924static int
Al Virod61005a2006-10-19 23:28:48 -0700925nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926{
927 int status;
928
929 status = ntohl(*p++);
930 p = xdr_decode_wcc_data(p, res->fattr);
931
932 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300933 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
935 res->count = ntohl(*p++);
936 res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
937 res->verf->verifier[0] = *p++;
938 res->verf->verifier[1] = *p++;
939
940 return res->count;
941}
942
943/*
944 * Decode a CREATE response
945 */
946static int
Al Virod61005a2006-10-19 23:28:48 -0700947nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948{
949 int status;
950
951 status = ntohl(*p++);
952 if (status == 0) {
953 if (*p++) {
954 if (!(p = xdr_decode_fhandle(p, res->fh)))
955 return -errno_NFSERR_IO;
956 p = xdr_decode_post_op_attr(p, res->fattr);
957 } else {
958 memset(res->fh, 0, sizeof(*res->fh));
959 /* Do decode post_op_attr but set it to NULL */
960 p = xdr_decode_post_op_attr(p, res->fattr);
961 res->fattr->valid = 0;
962 }
963 } else {
Benny Halevy856dff32008-03-31 17:39:06 +0300964 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 }
966 p = xdr_decode_wcc_data(p, res->dir_attr);
967 return status;
968}
969
970/*
971 * Decode RENAME reply
972 */
973static int
Al Virod61005a2006-10-19 23:28:48 -0700974nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs3_renameres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975{
976 int status;
977
978 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300979 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 p = xdr_decode_wcc_data(p, res->fromattr);
981 p = xdr_decode_wcc_data(p, res->toattr);
982 return status;
983}
984
985/*
986 * Decode LINK reply
987 */
988static int
Al Virod61005a2006-10-19 23:28:48 -0700989nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990{
991 int status;
992
993 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300994 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 p = xdr_decode_post_op_attr(p, res->fattr);
996 p = xdr_decode_wcc_data(p, res->dir_attr);
997 return status;
998}
999
1000/*
1001 * Decode FSSTAT reply
1002 */
1003static int
Al Virod61005a2006-10-19 23:28:48 -07001004nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005{
1006 int status;
1007
1008 status = ntohl(*p++);
1009
1010 p = xdr_decode_post_op_attr(p, res->fattr);
1011 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001012 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013
1014 p = xdr_decode_hyper(p, &res->tbytes);
1015 p = xdr_decode_hyper(p, &res->fbytes);
1016 p = xdr_decode_hyper(p, &res->abytes);
1017 p = xdr_decode_hyper(p, &res->tfiles);
1018 p = xdr_decode_hyper(p, &res->ffiles);
1019 p = xdr_decode_hyper(p, &res->afiles);
1020
1021 /* ignore invarsec */
1022 return 0;
1023}
1024
1025/*
1026 * Decode FSINFO reply
1027 */
1028static int
Al Virod61005a2006-10-19 23:28:48 -07001029nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030{
1031 int status;
1032
1033 status = ntohl(*p++);
1034
1035 p = xdr_decode_post_op_attr(p, res->fattr);
1036 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001037 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038
1039 res->rtmax = ntohl(*p++);
1040 res->rtpref = ntohl(*p++);
1041 res->rtmult = ntohl(*p++);
1042 res->wtmax = ntohl(*p++);
1043 res->wtpref = ntohl(*p++);
1044 res->wtmult = ntohl(*p++);
1045 res->dtpref = ntohl(*p++);
1046 p = xdr_decode_hyper(p, &res->maxfilesize);
1047
1048 /* ignore time_delta and properties */
1049 res->lease_time = 0;
1050 return 0;
1051}
1052
1053/*
1054 * Decode PATHCONF reply
1055 */
1056static int
Al Virod61005a2006-10-19 23:28:48 -07001057nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058{
1059 int status;
1060
1061 status = ntohl(*p++);
1062
1063 p = xdr_decode_post_op_attr(p, res->fattr);
1064 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001065 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 res->max_link = ntohl(*p++);
1067 res->max_namelen = ntohl(*p++);
1068
1069 /* ignore remaining fields */
1070 return 0;
1071}
1072
1073/*
1074 * Decode COMMIT reply
1075 */
1076static int
Al Virod61005a2006-10-19 23:28:48 -07001077nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078{
1079 int status;
1080
1081 status = ntohl(*p++);
1082 p = xdr_decode_wcc_data(p, res->fattr);
1083 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001084 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085
1086 res->verf->verifier[0] = *p++;
1087 res->verf->verifier[1] = *p++;
1088 return 0;
1089}
1090
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001091#ifdef CONFIG_NFS_V3_ACL
1092/*
1093 * Decode GETACL reply
1094 */
1095static int
Al Virod61005a2006-10-19 23:28:48 -07001096nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001097 struct nfs3_getaclres *res)
1098{
1099 struct xdr_buf *buf = &req->rq_rcv_buf;
1100 int status = ntohl(*p++);
1101 struct posix_acl **acl;
1102 unsigned int *aclcnt;
1103 int err, base;
1104
1105 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001106 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001107 p = xdr_decode_post_op_attr(p, res->fattr);
1108 res->mask = ntohl(*p++);
1109 if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
1110 return -EINVAL;
1111 base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
1112
1113 acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
1114 aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
1115 err = nfsacl_decode(buf, base, aclcnt, acl);
1116
1117 acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
1118 aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
1119 if (err > 0)
1120 err = nfsacl_decode(buf, base + err, aclcnt, acl);
1121 return (err > 0) ? 0 : err;
1122}
1123
1124/*
1125 * Decode setacl reply.
1126 */
1127static int
Al Virod61005a2006-10-19 23:28:48 -07001128nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001129{
1130 int status = ntohl(*p++);
1131
1132 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +03001133 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001134 xdr_decode_post_op_attr(p, fattr);
1135 return 0;
1136}
1137#endif /* CONFIG_NFS_V3_ACL */
1138
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139#define PROC(proc, argtype, restype, timer) \
1140[NFS3PROC_##proc] = { \
1141 .p_proc = NFS3PROC_##proc, \
1142 .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
1143 .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
Chuck Lever2bea90d2007-03-29 16:47:53 -04001144 .p_arglen = NFS3_##argtype##_sz, \
1145 .p_replen = NFS3_##restype##_sz, \
Chuck Levercc0175c2006-03-20 13:44:22 -05001146 .p_timer = timer, \
1147 .p_statidx = NFS3PROC_##proc, \
1148 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 }
1150
1151struct rpc_procinfo nfs3_procedures[] = {
1152 PROC(GETATTR, fhandle, attrstat, 1),
1153 PROC(SETATTR, sattrargs, wccstat, 0),
1154 PROC(LOOKUP, diropargs, lookupres, 2),
1155 PROC(ACCESS, accessargs, accessres, 1),
1156 PROC(READLINK, readlinkargs, readlinkres, 3),
1157 PROC(READ, readargs, readres, 3),
1158 PROC(WRITE, writeargs, writeres, 4),
1159 PROC(CREATE, createargs, createres, 0),
1160 PROC(MKDIR, mkdirargs, createres, 0),
1161 PROC(SYMLINK, symlinkargs, createres, 0),
1162 PROC(MKNOD, mknodargs, createres, 0),
Trond Myklebust4fdc17b2007-07-14 15:39:57 -04001163 PROC(REMOVE, removeargs, removeres, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 PROC(RMDIR, diropargs, wccstat, 0),
1165 PROC(RENAME, renameargs, renameres, 0),
1166 PROC(LINK, linkargs, linkres, 0),
1167 PROC(READDIR, readdirargs, readdirres, 3),
1168 PROC(READDIRPLUS, readdirargs, readdirres, 3),
1169 PROC(FSSTAT, fhandle, fsstatres, 0),
1170 PROC(FSINFO, fhandle, fsinfores, 0),
1171 PROC(PATHCONF, fhandle, pathconfres, 0),
1172 PROC(COMMIT, commitargs, commitres, 5),
1173};
1174
1175struct rpc_version nfs_version3 = {
1176 .number = 3,
Tobias Klausere8c96f82006-03-24 03:15:34 -08001177 .nrprocs = ARRAY_SIZE(nfs3_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 .procs = nfs3_procedures
1179};
1180
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001181#ifdef CONFIG_NFS_V3_ACL
1182static struct rpc_procinfo nfs3_acl_procedures[] = {
1183 [ACLPROC3_GETACL] = {
1184 .p_proc = ACLPROC3_GETACL,
1185 .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
1186 .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001187 .p_arglen = ACL3_getaclargs_sz,
1188 .p_replen = ACL3_getaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001189 .p_timer = 1,
Chuck Levercc0175c2006-03-20 13:44:22 -05001190 .p_name = "GETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001191 },
1192 [ACLPROC3_SETACL] = {
1193 .p_proc = ACLPROC3_SETACL,
1194 .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
1195 .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001196 .p_arglen = ACL3_setaclargs_sz,
1197 .p_replen = ACL3_setaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001198 .p_timer = 0,
Chuck Levercc0175c2006-03-20 13:44:22 -05001199 .p_name = "SETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001200 },
1201};
1202
1203struct rpc_version nfsacl_version3 = {
1204 .number = 3,
1205 .nrprocs = sizeof(nfs3_acl_procedures)/
1206 sizeof(nfs3_acl_procedures[0]),
1207 .procs = nfs3_acl_procedures,
1208};
1209#endif /* CONFIG_NFS_V3_ACL */