blob: 35869a4921f19e8c3ad3a273c28defabbf16283a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/fs/nfs/nfs3xdr.c
3 *
4 * XDR functions to encode/decode NFSv3 RPC arguments and results.
5 *
6 * Copyright (C) 1996, 1997 Olaf Kirch
7 */
8
9#include <linux/param.h>
10#include <linux/time.h>
11#include <linux/mm.h>
12#include <linux/slab.h>
13#include <linux/utsname.h>
14#include <linux/errno.h>
15#include <linux/string.h>
16#include <linux/in.h>
17#include <linux/pagemap.h>
18#include <linux/proc_fs.h>
19#include <linux/kdev_t.h>
20#include <linux/sunrpc/clnt.h>
21#include <linux/nfs.h>
22#include <linux/nfs3.h>
23#include <linux/nfs_fs.h>
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000024#include <linux/nfsacl.h>
David Howellsf7b422b2006-06-09 09:34:33 -040025#include "internal.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
27#define NFSDBG_FACILITY NFSDBG_XDR
28
29/* Mapping from NFS error code to "errno" error code. */
30#define errno_NFSERR_IO EIO
31
Linus Torvalds1da177e2005-04-16 15:20:36 -070032/*
33 * Declare the space requirements for NFS arguments and replies as
34 * number of 32bit-words
35 */
36#define NFS3_fhandle_sz (1+16)
37#define NFS3_fh_sz (NFS3_fhandle_sz) /* shorthand */
38#define NFS3_sattr_sz (15)
39#define NFS3_filename_sz (1+(NFS3_MAXNAMLEN>>2))
40#define NFS3_path_sz (1+(NFS3_MAXPATHLEN>>2))
41#define NFS3_fattr_sz (21)
42#define NFS3_wcc_attr_sz (6)
43#define NFS3_pre_op_attr_sz (1+NFS3_wcc_attr_sz)
44#define NFS3_post_op_attr_sz (1+NFS3_fattr_sz)
45#define NFS3_wcc_data_sz (NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
46#define NFS3_fsstat_sz
47#define NFS3_fsinfo_sz
48#define NFS3_pathconf_sz
49#define NFS3_entry_sz (NFS3_filename_sz+3)
50
51#define NFS3_sattrargs_sz (NFS3_fh_sz+NFS3_sattr_sz+3)
52#define NFS3_diropargs_sz (NFS3_fh_sz+NFS3_filename_sz)
Trond Myklebust4fdc17b2007-07-14 15:39:57 -040053#define NFS3_removeargs_sz (NFS3_fh_sz+NFS3_filename_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#define NFS3_accessargs_sz (NFS3_fh_sz+1)
55#define NFS3_readlinkargs_sz (NFS3_fh_sz)
56#define NFS3_readargs_sz (NFS3_fh_sz+3)
57#define NFS3_writeargs_sz (NFS3_fh_sz+5)
58#define NFS3_createargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
59#define NFS3_mkdirargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
Chuck Lever94a6d752006-08-22 20:06:23 -040060#define NFS3_symlinkargs_sz (NFS3_diropargs_sz+1+NFS3_sattr_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#define NFS3_mknodargs_sz (NFS3_diropargs_sz+2+NFS3_sattr_sz)
62#define NFS3_renameargs_sz (NFS3_diropargs_sz+NFS3_diropargs_sz)
63#define NFS3_linkargs_sz (NFS3_fh_sz+NFS3_diropargs_sz)
64#define NFS3_readdirargs_sz (NFS3_fh_sz+2)
65#define NFS3_commitargs_sz (NFS3_fh_sz+3)
66
67#define NFS3_attrstat_sz (1+NFS3_fattr_sz)
68#define NFS3_wccstat_sz (1+NFS3_wcc_data_sz)
Trond Myklebust4fdc17b2007-07-14 15:39:57 -040069#define NFS3_removeres_sz (NFS3_wccstat_sz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#define NFS3_lookupres_sz (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
71#define NFS3_accessres_sz (1+NFS3_post_op_attr_sz+1)
72#define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz+1)
73#define NFS3_readres_sz (1+NFS3_post_op_attr_sz+3)
74#define NFS3_writeres_sz (1+NFS3_wcc_data_sz+4)
75#define NFS3_createres_sz (1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
76#define NFS3_renameres_sz (1+(2 * NFS3_wcc_data_sz))
77#define NFS3_linkres_sz (1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
78#define NFS3_readdirres_sz (1+NFS3_post_op_attr_sz+2)
79#define NFS3_fsstatres_sz (1+NFS3_post_op_attr_sz+13)
80#define NFS3_fsinfores_sz (1+NFS3_post_op_attr_sz+12)
81#define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6)
82#define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2)
83
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000084#define ACL3_getaclargs_sz (NFS3_fh_sz+1)
Trond Myklebustae461412009-03-10 20:33:18 -040085#define ACL3_setaclargs_sz (NFS3_fh_sz+1+ \
86 XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
87#define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+ \
88 XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +000089#define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz)
90
Linus Torvalds1da177e2005-04-16 15:20:36 -070091/*
92 * Map file type to S_IFMT bits
93 */
Trond Myklebustbca79472009-03-11 14:10:26 -040094static const umode_t nfs_type2fmt[] = {
95 [NF3BAD] = 0,
96 [NF3REG] = S_IFREG,
97 [NF3DIR] = S_IFDIR,
98 [NF3BLK] = S_IFBLK,
99 [NF3CHR] = S_IFCHR,
100 [NF3LNK] = S_IFLNK,
101 [NF3SOCK] = S_IFSOCK,
102 [NF3FIFO] = S_IFIFO,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103};
104
105/*
106 * Common NFS XDR functions as inlines
107 */
Al Virod61005a2006-10-19 23:28:48 -0700108static inline __be32 *
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400109xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110{
111 return xdr_encode_array(p, fh->data, fh->size);
112}
113
Al Virod61005a2006-10-19 23:28:48 -0700114static inline __be32 *
115xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116{
117 if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
118 memcpy(fh->data, p, fh->size);
119 return p + XDR_QUADLEN(fh->size);
120 }
121 return NULL;
122}
123
124/*
125 * Encode/decode time.
126 */
Al Virod61005a2006-10-19 23:28:48 -0700127static inline __be32 *
128xdr_encode_time3(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129{
130 *p++ = htonl(timep->tv_sec);
131 *p++ = htonl(timep->tv_nsec);
132 return p;
133}
134
Al Virod61005a2006-10-19 23:28:48 -0700135static inline __be32 *
136xdr_decode_time3(__be32 *p, struct timespec *timep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137{
138 timep->tv_sec = ntohl(*p++);
139 timep->tv_nsec = ntohl(*p++);
140 return p;
141}
142
Al Virod61005a2006-10-19 23:28:48 -0700143static __be32 *
144xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145{
146 unsigned int type, major, minor;
Trond Myklebustbca79472009-03-11 14:10:26 -0400147 umode_t fmode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
149 type = ntohl(*p++);
Trond Myklebustbca79472009-03-11 14:10:26 -0400150 if (type > NF3FIFO)
151 type = NF3NON;
152 fmode = nfs_type2fmt[type];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
154 fattr->nlink = ntohl(*p++);
155 fattr->uid = ntohl(*p++);
156 fattr->gid = ntohl(*p++);
157 p = xdr_decode_hyper(p, &fattr->size);
158 p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
159
160 /* Turn remote device info into Linux-specific dev_t */
161 major = ntohl(*p++);
162 minor = ntohl(*p++);
163 fattr->rdev = MKDEV(major, minor);
164 if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor)
165 fattr->rdev = 0;
166
Trond Myklebust8b4bdcf2006-06-09 09:34:19 -0400167 p = xdr_decode_hyper(p, &fattr->fsid.major);
168 fattr->fsid.minor = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 p = xdr_decode_hyper(p, &fattr->fileid);
170 p = xdr_decode_time3(p, &fattr->atime);
171 p = xdr_decode_time3(p, &fattr->mtime);
172 p = xdr_decode_time3(p, &fattr->ctime);
173
174 /* Update the mode bits */
Trond Myklebust9e6e70f2009-03-11 14:10:24 -0400175 fattr->valid |= NFS_ATTR_FATTR_V3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 return p;
177}
178
Al Virod61005a2006-10-19 23:28:48 -0700179static inline __be32 *
180xdr_encode_sattr(__be32 *p, struct iattr *attr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181{
182 if (attr->ia_valid & ATTR_MODE) {
183 *p++ = xdr_one;
Trond Myklebustcf3fff52006-01-03 09:55:53 +0100184 *p++ = htonl(attr->ia_mode & S_IALLUGO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 } else {
186 *p++ = xdr_zero;
187 }
188 if (attr->ia_valid & ATTR_UID) {
189 *p++ = xdr_one;
190 *p++ = htonl(attr->ia_uid);
191 } else {
192 *p++ = xdr_zero;
193 }
194 if (attr->ia_valid & ATTR_GID) {
195 *p++ = xdr_one;
196 *p++ = htonl(attr->ia_gid);
197 } else {
198 *p++ = xdr_zero;
199 }
200 if (attr->ia_valid & ATTR_SIZE) {
201 *p++ = xdr_one;
202 p = xdr_encode_hyper(p, (__u64) attr->ia_size);
203 } else {
204 *p++ = xdr_zero;
205 }
206 if (attr->ia_valid & ATTR_ATIME_SET) {
207 *p++ = xdr_two;
208 p = xdr_encode_time3(p, &attr->ia_atime);
209 } else if (attr->ia_valid & ATTR_ATIME) {
210 *p++ = xdr_one;
211 } else {
212 *p++ = xdr_zero;
213 }
214 if (attr->ia_valid & ATTR_MTIME_SET) {
215 *p++ = xdr_two;
216 p = xdr_encode_time3(p, &attr->ia_mtime);
217 } else if (attr->ia_valid & ATTR_MTIME) {
218 *p++ = xdr_one;
219 } else {
220 *p++ = xdr_zero;
221 }
222 return p;
223}
224
Al Virod61005a2006-10-19 23:28:48 -0700225static inline __be32 *
226xdr_decode_wcc_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227{
228 p = xdr_decode_hyper(p, &fattr->pre_size);
229 p = xdr_decode_time3(p, &fattr->pre_mtime);
230 p = xdr_decode_time3(p, &fattr->pre_ctime);
Trond Myklebust9e6e70f2009-03-11 14:10:24 -0400231 fattr->valid |= NFS_ATTR_FATTR_PRESIZE
232 | NFS_ATTR_FATTR_PREMTIME
233 | NFS_ATTR_FATTR_PRECTIME;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 return p;
235}
236
Al Virod61005a2006-10-19 23:28:48 -0700237static inline __be32 *
238xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239{
240 if (*p++)
241 p = xdr_decode_fattr(p, fattr);
242 return p;
243}
244
Al Virod61005a2006-10-19 23:28:48 -0700245static inline __be32 *
246xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247{
248 if (*p++)
249 return xdr_decode_wcc_attr(p, fattr);
250 return p;
251}
252
253
Al Virod61005a2006-10-19 23:28:48 -0700254static inline __be32 *
255xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256{
257 p = xdr_decode_pre_op_attr(p, fattr);
258 return xdr_decode_post_op_attr(p, fattr);
259}
260
261/*
262 * NFS encode functions
263 */
264
265/*
266 * Encode file handle argument
267 */
268static int
Al Virod61005a2006-10-19 23:28:48 -0700269nfs3_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270{
271 p = xdr_encode_fhandle(p, fh);
272 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
273 return 0;
274}
275
276/*
277 * Encode SETATTR arguments
278 */
279static int
Al Virod61005a2006-10-19 23:28:48 -0700280nfs3_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs3_sattrargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281{
282 p = xdr_encode_fhandle(p, args->fh);
283 p = xdr_encode_sattr(p, args->sattr);
284 *p++ = htonl(args->guard);
285 if (args->guard)
286 p = xdr_encode_time3(p, &args->guardtime);
287 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
288 return 0;
289}
290
291/*
292 * Encode directory ops argument
293 */
294static int
Al Virod61005a2006-10-19 23:28:48 -0700295nfs3_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs3_diropargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
297 p = xdr_encode_fhandle(p, args->fh);
298 p = xdr_encode_array(p, args->name, args->len);
299 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
300 return 0;
301}
302
303/*
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400304 * Encode REMOVE argument
305 */
306static int
307nfs3_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
308{
309 p = xdr_encode_fhandle(p, args->fh);
310 p = xdr_encode_array(p, args->name.name, args->name.len);
311 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
312 return 0;
313}
314
315/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 * Encode access() argument
317 */
318static int
Al Virod61005a2006-10-19 23:28:48 -0700319nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320{
321 p = xdr_encode_fhandle(p, args->fh);
322 *p++ = htonl(args->access);
323 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
324 return 0;
325}
326
327/*
328 * Arguments to a READ call. Since we read data directly into the page
329 * cache, we also set up the reply iovec here so that iov[1] points
330 * exactly to the page we want to fetch.
331 */
332static int
Al Virod61005a2006-10-19 23:28:48 -0700333nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400335 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 unsigned int replen;
337 u32 count = args->count;
338
339 p = xdr_encode_fhandle(p, args->fh);
340 p = xdr_encode_hyper(p, args->offset);
341 *p++ = htonl(count);
342 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
343
344 /* Inline the page array */
345 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
346 xdr_inline_pages(&req->rq_rcv_buf, replen,
347 args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400348 req->rq_rcv_buf.flags |= XDRBUF_READ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 return 0;
350}
351
352/*
353 * Write arguments. Splice the buffer to be written into the iovec.
354 */
355static int
Al Virod61005a2006-10-19 23:28:48 -0700356nfs3_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357{
358 struct xdr_buf *sndbuf = &req->rq_snd_buf;
359 u32 count = args->count;
360
361 p = xdr_encode_fhandle(p, args->fh);
362 p = xdr_encode_hyper(p, args->offset);
363 *p++ = htonl(count);
364 *p++ = htonl(args->stable);
365 *p++ = htonl(count);
366 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
367
368 /* Copy the page array */
369 xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
\"Talpey, Thomas\4f22ccc2007-09-10 13:44:58 -0400370 sndbuf->flags |= XDRBUF_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 return 0;
372}
373
374/*
375 * Encode CREATE arguments
376 */
377static int
Al Virod61005a2006-10-19 23:28:48 -0700378nfs3_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs3_createargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379{
380 p = xdr_encode_fhandle(p, args->fh);
381 p = xdr_encode_array(p, args->name, args->len);
382
383 *p++ = htonl(args->createmode);
384 if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
385 *p++ = args->verifier[0];
386 *p++ = args->verifier[1];
387 } else
388 p = xdr_encode_sattr(p, args->sattr);
389
390 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
391 return 0;
392}
393
394/*
395 * Encode MKDIR arguments
396 */
397static int
Al Virod61005a2006-10-19 23:28:48 -0700398nfs3_xdr_mkdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mkdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399{
400 p = xdr_encode_fhandle(p, args->fh);
401 p = xdr_encode_array(p, args->name, args->len);
402 p = xdr_encode_sattr(p, args->sattr);
403 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
404 return 0;
405}
406
407/*
408 * Encode SYMLINK arguments
409 */
410static int
Al Virod61005a2006-10-19 23:28:48 -0700411nfs3_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_symlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412{
413 p = xdr_encode_fhandle(p, args->fromfh);
414 p = xdr_encode_array(p, args->fromname, args->fromlen);
415 p = xdr_encode_sattr(p, args->sattr);
Chuck Lever94a6d752006-08-22 20:06:23 -0400416 *p++ = htonl(args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
Chuck Lever94a6d752006-08-22 20:06:23 -0400418
419 /* Copy the page */
420 xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 return 0;
422}
423
424/*
425 * Encode MKNOD arguments
426 */
427static int
Al Virod61005a2006-10-19 23:28:48 -0700428nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429{
430 p = xdr_encode_fhandle(p, args->fh);
431 p = xdr_encode_array(p, args->name, args->len);
432 *p++ = htonl(args->type);
433 p = xdr_encode_sattr(p, args->sattr);
434 if (args->type == NF3CHR || args->type == NF3BLK) {
435 *p++ = htonl(MAJOR(args->rdev));
436 *p++ = htonl(MINOR(args->rdev));
437 }
438
439 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
440 return 0;
441}
442
443/*
444 * Encode RENAME arguments
445 */
446static int
Al Virod61005a2006-10-19 23:28:48 -0700447nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs3_renameargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448{
449 p = xdr_encode_fhandle(p, args->fromfh);
450 p = xdr_encode_array(p, args->fromname, args->fromlen);
451 p = xdr_encode_fhandle(p, args->tofh);
452 p = xdr_encode_array(p, args->toname, args->tolen);
453 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
454 return 0;
455}
456
457/*
458 * Encode LINK arguments
459 */
460static int
Al Virod61005a2006-10-19 23:28:48 -0700461nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462{
463 p = xdr_encode_fhandle(p, args->fromfh);
464 p = xdr_encode_fhandle(p, args->tofh);
465 p = xdr_encode_array(p, args->toname, args->tolen);
466 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
467 return 0;
468}
469
470/*
471 * Encode arguments to readdir call
472 */
473static int
Al Virod61005a2006-10-19 23:28:48 -0700474nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400476 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 unsigned int replen;
478 u32 count = args->count;
479
480 p = xdr_encode_fhandle(p, args->fh);
481 p = xdr_encode_hyper(p, args->cookie);
482 *p++ = args->verf[0];
483 *p++ = args->verf[1];
484 if (args->plus) {
485 /* readdirplus: need dircount + buffer size.
486 * We just make sure we make dircount big enough */
487 *p++ = htonl(count >> 3);
488 }
489 *p++ = htonl(count);
490 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
491
492 /* Inline the page array */
493 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
494 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
495 return 0;
496}
497
498/*
499 * Decode the result of a readdir call.
500 * We just check for syntactical correctness.
501 */
502static int
Al Virod61005a2006-10-19 23:28:48 -0700503nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504{
505 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
506 struct kvec *iov = rcvbuf->head;
507 struct page **page;
Chuck Leverc957c522007-10-26 13:31:57 -0400508 size_t hdrlen;
509 u32 len, recvd, pglen;
Jeff Layton643f8112008-02-22 14:50:00 -0500510 int status, nr = 0;
Al Virod61005a2006-10-19 23:28:48 -0700511 __be32 *entry, *end, *kaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
513 status = ntohl(*p++);
514 /* Decode post_op_attrs */
515 p = xdr_decode_post_op_attr(p, res->dir_attr);
516 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300517 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 /* Decode verifier cookie */
519 if (res->verf) {
520 res->verf[0] = *p++;
521 res->verf[1] = *p++;
522 } else {
523 p += 2;
524 }
525
526 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
527 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400528 dprintk("NFS: READDIR reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400529 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 return -errno_NFSERR_IO;
531 } else if (iov->iov_len != hdrlen) {
532 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
533 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
534 }
535
536 pglen = rcvbuf->page_len;
537 recvd = rcvbuf->len - hdrlen;
538 if (pglen > recvd)
539 pglen = recvd;
540 page = rcvbuf->pages;
Al Virod61005a2006-10-19 23:28:48 -0700541 kaddr = p = kmap_atomic(*page, KM_USER0);
542 end = (__be32 *)((char *)p + pglen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 entry = p;
Jeff Layton643f8112008-02-22 14:50:00 -0500544
545 /* Make sure the packet actually has a value_follows and EOF entry */
546 if ((entry + 1) > end)
547 goto short_pkt;
548
549 for (; *p++; nr++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 if (p + 3 > end)
551 goto short_pkt;
552 p += 2; /* inode # */
553 len = ntohl(*p++); /* string length */
554 p += XDR_QUADLEN(len) + 2; /* name + cookie */
555 if (len > NFS3_MAXNAMLEN) {
Chuck Leverc957c522007-10-26 13:31:57 -0400556 dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 len);
558 goto err_unmap;
559 }
560
561 if (res->plus) {
562 /* post_op_attr */
563 if (p + 2 > end)
564 goto short_pkt;
565 if (*p++) {
566 p += 21;
567 if (p + 1 > end)
568 goto short_pkt;
569 }
570 /* post_op_fh3 */
571 if (*p++) {
572 if (p + 1 > end)
573 goto short_pkt;
574 len = ntohl(*p++);
575 if (len > NFS3_FHSIZE) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400576 dprintk("NFS: giant filehandle in "
Chuck Leverc957c522007-10-26 13:31:57 -0400577 "readdir (len 0x%x)!\n", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 goto err_unmap;
579 }
580 p += XDR_QUADLEN(len);
581 }
582 }
583
584 if (p + 2 > end)
585 goto short_pkt;
586 entry = p;
587 }
Jeff Layton643f8112008-02-22 14:50:00 -0500588
589 /*
590 * Apparently some server sends responses that are a valid size, but
591 * contain no entries, and have value_follows==0 and EOF==0. For
592 * those, just set the EOF marker.
593 */
594 if (!nr && entry[1] == 0) {
595 dprintk("NFS: readdir reply truncated!\n");
596 entry[1] = 1;
597 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 out:
599 kunmap_atomic(kaddr, KM_USER0);
600 return nr;
601 short_pkt:
Jeff Layton643f8112008-02-22 14:50:00 -0500602 /*
603 * When we get a short packet there are 2 possibilities. We can
604 * return an error, or fix up the response to look like a valid
605 * response and return what we have so far. If there are no
606 * entries and the packet was short, then return -EIO. If there
607 * are valid entries in the response, return them and pretend that
608 * the call was successful, but incomplete. The caller can retry the
609 * readdir starting at the last cookie.
610 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 entry[0] = entry[1] = 0;
Jeff Layton643f8112008-02-22 14:50:00 -0500612 if (!nr)
613 nr = -errno_NFSERR_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 goto out;
615err_unmap:
616 nr = -errno_NFSERR_IO;
617 goto out;
618}
619
Al Viro0dbb4c62006-10-19 23:28:49 -0700620__be32 *
621nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622{
623 struct nfs_entry old = *entry;
624
625 if (!*p++) {
626 if (!*p)
627 return ERR_PTR(-EAGAIN);
628 entry->eof = 1;
629 return ERR_PTR(-EBADCOOKIE);
630 }
631
632 p = xdr_decode_hyper(p, &entry->ino);
633 entry->len = ntohl(*p++);
634 entry->name = (const char *) p;
635 p += XDR_QUADLEN(entry->len);
636 entry->prev_cookie = entry->cookie;
637 p = xdr_decode_hyper(p, &entry->cookie);
638
639 if (plus) {
640 entry->fattr->valid = 0;
641 p = xdr_decode_post_op_attr(p, entry->fattr);
642 /* In fact, a post_op_fh3: */
643 if (*p++) {
644 p = xdr_decode_fhandle(p, entry->fh);
645 /* Ugh -- server reply was truncated */
646 if (p == NULL) {
647 dprintk("NFS: FH truncated\n");
648 *entry = old;
649 return ERR_PTR(-EAGAIN);
650 }
651 } else
652 memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
653 }
654
655 entry->eof = !p[0] && p[1];
656 return p;
657}
658
659/*
660 * Encode COMMIT arguments
661 */
662static int
Al Virod61005a2006-10-19 23:28:48 -0700663nfs3_xdr_commitargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664{
665 p = xdr_encode_fhandle(p, args->fh);
666 p = xdr_encode_hyper(p, args->offset);
667 *p++ = htonl(args->count);
668 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
669 return 0;
670}
671
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000672#ifdef CONFIG_NFS_V3_ACL
673/*
674 * Encode GETACL arguments
675 */
676static int
Al Virod61005a2006-10-19 23:28:48 -0700677nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000678 struct nfs3_getaclargs *args)
679{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400680 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000681 unsigned int replen;
682
683 p = xdr_encode_fhandle(p, args->fh);
684 *p++ = htonl(args->mask);
685 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
686
687 if (args->mask & (NFS_ACL | NFS_DFACL)) {
688 /* Inline the page array */
689 replen = (RPC_REPHDRSIZE + auth->au_rslack +
690 ACL3_getaclres_sz) << 2;
691 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
692 NFSACL_MAXPAGES << PAGE_SHIFT);
693 }
694 return 0;
695}
696
697/*
698 * Encode SETACL arguments
699 */
700static int
Al Virod61005a2006-10-19 23:28:48 -0700701nfs3_xdr_setaclargs(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000702 struct nfs3_setaclargs *args)
703{
704 struct xdr_buf *buf = &req->rq_snd_buf;
Trond Myklebustae461412009-03-10 20:33:18 -0400705 unsigned int base;
706 int err;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000707
708 p = xdr_encode_fhandle(p, NFS_FH(args->inode));
709 *p++ = htonl(args->mask);
Trond Myklebustae461412009-03-10 20:33:18 -0400710 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
711 base = req->rq_slen;
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000712
Trond Myklebustae461412009-03-10 20:33:18 -0400713 if (args->npages != 0)
714 xdr_encode_pages(buf, args->pages, 0, args->len);
715 else
Trond Myklebust83404372009-04-20 14:58:35 -0400716 req->rq_slen = xdr_adjust_iovec(req->rq_svec,
717 p + XDR_QUADLEN(args->len));
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +0000718
719 err = nfsacl_encode(buf, base, args->inode,
720 (args->mask & NFS_ACL) ?
721 args->acl_access : NULL, 1, 0);
722 if (err > 0)
723 err = nfsacl_encode(buf, base + err, args->inode,
724 (args->mask & NFS_DFACL) ?
725 args->acl_default : NULL, 1,
726 NFS_ACL_DEFAULT);
727 return (err > 0) ? 0 : err;
728}
729#endif /* CONFIG_NFS_V3_ACL */
730
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731/*
732 * NFS XDR decode functions
733 */
734
735/*
736 * Decode attrstat reply.
737 */
738static int
Al Virod61005a2006-10-19 23:28:48 -0700739nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740{
741 int status;
742
743 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300744 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 xdr_decode_fattr(p, fattr);
746 return 0;
747}
748
749/*
750 * Decode status+wcc_data reply
751 * SATTR, REMOVE, RMDIR
752 */
753static int
Al Virod61005a2006-10-19 23:28:48 -0700754nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755{
756 int status;
757
758 if ((status = ntohl(*p++)))
Benny Halevy856dff32008-03-31 17:39:06 +0300759 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 xdr_decode_wcc_data(p, fattr);
761 return status;
762}
763
Trond Myklebust4fdc17b2007-07-14 15:39:57 -0400764static int
765nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
766{
767 return nfs3_xdr_wccstat(req, p, &res->dir_attr);
768}
769
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770/*
771 * Decode LOOKUP reply
772 */
773static int
Al Virod61005a2006-10-19 23:28:48 -0700774nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775{
776 int status;
777
778 if ((status = ntohl(*p++))) {
Benny Halevy856dff32008-03-31 17:39:06 +0300779 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 } else {
781 if (!(p = xdr_decode_fhandle(p, res->fh)))
782 return -errno_NFSERR_IO;
783 p = xdr_decode_post_op_attr(p, res->fattr);
784 }
785 xdr_decode_post_op_attr(p, res->dir_attr);
786 return status;
787}
788
789/*
790 * Decode ACCESS reply
791 */
792static int
Al Virod61005a2006-10-19 23:28:48 -0700793nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794{
795 int status = ntohl(*p++);
796
797 p = xdr_decode_post_op_attr(p, res->fattr);
798 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +0300799 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 res->access = ntohl(*p++);
801 return 0;
802}
803
804static int
Al Virod61005a2006-10-19 23:28:48 -0700805nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806{
Trond Myklebust1be27f32007-06-27 14:29:04 -0400807 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 unsigned int replen;
809
810 p = xdr_encode_fhandle(p, args->fh);
811 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
812
813 /* Inline the page array */
814 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
815 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
816 return 0;
817}
818
819/*
820 * Decode READLINK reply
821 */
822static int
Al Virod61005a2006-10-19 23:28:48 -0700823nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824{
825 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
826 struct kvec *iov = rcvbuf->head;
Chuck Leverc957c522007-10-26 13:31:57 -0400827 size_t hdrlen;
828 u32 len, recvd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 char *kaddr;
830 int status;
831
832 status = ntohl(*p++);
833 p = xdr_decode_post_op_attr(p, fattr);
834
835 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300836 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
838 /* Convert length of symlink */
839 len = ntohl(*p++);
Chuck Leverc957c522007-10-26 13:31:57 -0400840 if (len >= rcvbuf->page_len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400841 dprintk("nfs: server returned giant symlink!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 return -ENAMETOOLONG;
843 }
844
845 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
846 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400847 dprintk("NFS: READLINK reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400848 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 return -errno_NFSERR_IO;
850 } else if (iov->iov_len != hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400851 dprintk("NFS: READLINK header is short. "
852 "iovec will be shifted.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
854 }
855 recvd = req->rq_rcv_buf.len - hdrlen;
856 if (recvd < len) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400857 dprintk("NFS: server cheating in readlink reply: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 "count %u > recvd %u\n", len, recvd);
859 return -EIO;
860 }
861
862 /* NULL terminate the string we got */
863 kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
864 kaddr[len+rcvbuf->page_base] = '\0';
865 kunmap_atomic(kaddr, KM_USER0);
866 return 0;
867}
868
869/*
870 * Decode READ reply
871 */
872static int
Al Virod61005a2006-10-19 23:28:48 -0700873nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874{
875 struct kvec *iov = req->rq_rcv_buf.head;
Chuck Leverc957c522007-10-26 13:31:57 -0400876 size_t hdrlen;
877 u32 count, ocount, recvd;
878 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879
880 status = ntohl(*p++);
881 p = xdr_decode_post_op_attr(p, res->fattr);
882
883 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300884 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885
Chuck Leverc957c522007-10-26 13:31:57 -0400886 /* Decode reply count and EOF flag. NFSv3 is somewhat redundant
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 * in that it puts the count both in the res struct and in the
888 * opaque data count. */
889 count = ntohl(*p++);
890 res->eof = ntohl(*p++);
891 ocount = ntohl(*p++);
892
893 if (ocount != count) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400894 dprintk("NFS: READ count doesn't match RPC opaque count.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 return -errno_NFSERR_IO;
896 }
897
898 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
899 if (iov->iov_len < hdrlen) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400900 dprintk("NFS: READ reply header overflowed:"
Chuck Leverc957c522007-10-26 13:31:57 -0400901 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 return -errno_NFSERR_IO;
903 } else if (iov->iov_len != hdrlen) {
904 dprintk("NFS: READ header is short. iovec will be shifted.\n");
905 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
906 }
907
908 recvd = req->rq_rcv_buf.len - hdrlen;
909 if (count > recvd) {
Chuck Leverfe82a182007-09-11 18:01:10 -0400910 dprintk("NFS: server cheating in read reply: "
Chuck Leverc957c522007-10-26 13:31:57 -0400911 "count %u > recvd %u\n", count, recvd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 count = recvd;
913 res->eof = 0;
914 }
915
916 if (count < res->count)
917 res->count = count;
918
919 return count;
920}
921
922/*
923 * Decode WRITE response
924 */
925static int
Al Virod61005a2006-10-19 23:28:48 -0700926nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927{
928 int status;
929
930 status = ntohl(*p++);
931 p = xdr_decode_wcc_data(p, res->fattr);
932
933 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300934 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
936 res->count = ntohl(*p++);
937 res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
938 res->verf->verifier[0] = *p++;
939 res->verf->verifier[1] = *p++;
940
941 return res->count;
942}
943
944/*
945 * Decode a CREATE response
946 */
947static int
Al Virod61005a2006-10-19 23:28:48 -0700948nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949{
950 int status;
951
952 status = ntohl(*p++);
953 if (status == 0) {
954 if (*p++) {
955 if (!(p = xdr_decode_fhandle(p, res->fh)))
956 return -errno_NFSERR_IO;
957 p = xdr_decode_post_op_attr(p, res->fattr);
958 } else {
959 memset(res->fh, 0, sizeof(*res->fh));
960 /* Do decode post_op_attr but set it to NULL */
961 p = xdr_decode_post_op_attr(p, res->fattr);
962 res->fattr->valid = 0;
963 }
964 } else {
Benny Halevy856dff32008-03-31 17:39:06 +0300965 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 }
967 p = xdr_decode_wcc_data(p, res->dir_attr);
968 return status;
969}
970
971/*
972 * Decode RENAME reply
973 */
974static int
Al Virod61005a2006-10-19 23:28:48 -0700975nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs3_renameres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976{
977 int status;
978
979 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300980 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 p = xdr_decode_wcc_data(p, res->fromattr);
982 p = xdr_decode_wcc_data(p, res->toattr);
983 return status;
984}
985
986/*
987 * Decode LINK reply
988 */
989static int
Al Virod61005a2006-10-19 23:28:48 -0700990nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991{
992 int status;
993
994 if ((status = ntohl(*p++)) != 0)
Benny Halevy856dff32008-03-31 17:39:06 +0300995 status = nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 p = xdr_decode_post_op_attr(p, res->fattr);
997 p = xdr_decode_wcc_data(p, res->dir_attr);
998 return status;
999}
1000
1001/*
1002 * Decode FSSTAT reply
1003 */
1004static int
Al Virod61005a2006-10-19 23:28:48 -07001005nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006{
1007 int status;
1008
1009 status = ntohl(*p++);
1010
1011 p = xdr_decode_post_op_attr(p, res->fattr);
1012 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001013 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014
1015 p = xdr_decode_hyper(p, &res->tbytes);
1016 p = xdr_decode_hyper(p, &res->fbytes);
1017 p = xdr_decode_hyper(p, &res->abytes);
1018 p = xdr_decode_hyper(p, &res->tfiles);
1019 p = xdr_decode_hyper(p, &res->ffiles);
1020 p = xdr_decode_hyper(p, &res->afiles);
1021
1022 /* ignore invarsec */
1023 return 0;
1024}
1025
1026/*
1027 * Decode FSINFO reply
1028 */
1029static int
Al Virod61005a2006-10-19 23:28:48 -07001030nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031{
1032 int status;
1033
1034 status = ntohl(*p++);
1035
1036 p = xdr_decode_post_op_attr(p, res->fattr);
1037 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001038 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039
1040 res->rtmax = ntohl(*p++);
1041 res->rtpref = ntohl(*p++);
1042 res->rtmult = ntohl(*p++);
1043 res->wtmax = ntohl(*p++);
1044 res->wtpref = ntohl(*p++);
1045 res->wtmult = ntohl(*p++);
1046 res->dtpref = ntohl(*p++);
1047 p = xdr_decode_hyper(p, &res->maxfilesize);
1048
1049 /* ignore time_delta and properties */
1050 res->lease_time = 0;
1051 return 0;
1052}
1053
1054/*
1055 * Decode PATHCONF reply
1056 */
1057static int
Al Virod61005a2006-10-19 23:28:48 -07001058nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059{
1060 int status;
1061
1062 status = ntohl(*p++);
1063
1064 p = xdr_decode_post_op_attr(p, res->fattr);
1065 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001066 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 res->max_link = ntohl(*p++);
1068 res->max_namelen = ntohl(*p++);
1069
1070 /* ignore remaining fields */
1071 return 0;
1072}
1073
1074/*
1075 * Decode COMMIT reply
1076 */
1077static int
Al Virod61005a2006-10-19 23:28:48 -07001078nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079{
1080 int status;
1081
1082 status = ntohl(*p++);
1083 p = xdr_decode_wcc_data(p, res->fattr);
1084 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001085 return nfs_stat_to_errno(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086
1087 res->verf->verifier[0] = *p++;
1088 res->verf->verifier[1] = *p++;
1089 return 0;
1090}
1091
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001092#ifdef CONFIG_NFS_V3_ACL
1093/*
1094 * Decode GETACL reply
1095 */
1096static int
Al Virod61005a2006-10-19 23:28:48 -07001097nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001098 struct nfs3_getaclres *res)
1099{
1100 struct xdr_buf *buf = &req->rq_rcv_buf;
1101 int status = ntohl(*p++);
1102 struct posix_acl **acl;
1103 unsigned int *aclcnt;
1104 int err, base;
1105
1106 if (status != 0)
Benny Halevy856dff32008-03-31 17:39:06 +03001107 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001108 p = xdr_decode_post_op_attr(p, res->fattr);
1109 res->mask = ntohl(*p++);
1110 if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
1111 return -EINVAL;
1112 base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
1113
1114 acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
1115 aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
1116 err = nfsacl_decode(buf, base, aclcnt, acl);
1117
1118 acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
1119 aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
1120 if (err > 0)
1121 err = nfsacl_decode(buf, base + err, aclcnt, acl);
1122 return (err > 0) ? 0 : err;
1123}
1124
1125/*
1126 * Decode setacl reply.
1127 */
1128static int
Al Virod61005a2006-10-19 23:28:48 -07001129nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001130{
1131 int status = ntohl(*p++);
1132
1133 if (status)
Benny Halevy856dff32008-03-31 17:39:06 +03001134 return nfs_stat_to_errno(status);
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001135 xdr_decode_post_op_attr(p, fattr);
1136 return 0;
1137}
1138#endif /* CONFIG_NFS_V3_ACL */
1139
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140#define PROC(proc, argtype, restype, timer) \
1141[NFS3PROC_##proc] = { \
1142 .p_proc = NFS3PROC_##proc, \
1143 .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
1144 .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
Chuck Lever2bea90d2007-03-29 16:47:53 -04001145 .p_arglen = NFS3_##argtype##_sz, \
1146 .p_replen = NFS3_##restype##_sz, \
Chuck Levercc0175c2006-03-20 13:44:22 -05001147 .p_timer = timer, \
1148 .p_statidx = NFS3PROC_##proc, \
1149 .p_name = #proc, \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 }
1151
1152struct rpc_procinfo nfs3_procedures[] = {
1153 PROC(GETATTR, fhandle, attrstat, 1),
1154 PROC(SETATTR, sattrargs, wccstat, 0),
1155 PROC(LOOKUP, diropargs, lookupres, 2),
1156 PROC(ACCESS, accessargs, accessres, 1),
1157 PROC(READLINK, readlinkargs, readlinkres, 3),
1158 PROC(READ, readargs, readres, 3),
1159 PROC(WRITE, writeargs, writeres, 4),
1160 PROC(CREATE, createargs, createres, 0),
1161 PROC(MKDIR, mkdirargs, createres, 0),
1162 PROC(SYMLINK, symlinkargs, createres, 0),
1163 PROC(MKNOD, mknodargs, createres, 0),
Trond Myklebust4fdc17b2007-07-14 15:39:57 -04001164 PROC(REMOVE, removeargs, removeres, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 PROC(RMDIR, diropargs, wccstat, 0),
1166 PROC(RENAME, renameargs, renameres, 0),
1167 PROC(LINK, linkargs, linkres, 0),
1168 PROC(READDIR, readdirargs, readdirres, 3),
1169 PROC(READDIRPLUS, readdirargs, readdirres, 3),
1170 PROC(FSSTAT, fhandle, fsstatres, 0),
1171 PROC(FSINFO, fhandle, fsinfores, 0),
1172 PROC(PATHCONF, fhandle, pathconfres, 0),
1173 PROC(COMMIT, commitargs, commitres, 5),
1174};
1175
1176struct rpc_version nfs_version3 = {
1177 .number = 3,
Tobias Klausere8c96f82006-03-24 03:15:34 -08001178 .nrprocs = ARRAY_SIZE(nfs3_procedures),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 .procs = nfs3_procedures
1180};
1181
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001182#ifdef CONFIG_NFS_V3_ACL
1183static struct rpc_procinfo nfs3_acl_procedures[] = {
1184 [ACLPROC3_GETACL] = {
1185 .p_proc = ACLPROC3_GETACL,
1186 .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
1187 .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001188 .p_arglen = ACL3_getaclargs_sz,
1189 .p_replen = ACL3_getaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001190 .p_timer = 1,
Chuck Levercc0175c2006-03-20 13:44:22 -05001191 .p_name = "GETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001192 },
1193 [ACLPROC3_SETACL] = {
1194 .p_proc = ACLPROC3_SETACL,
1195 .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
1196 .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
Chuck Lever2bea90d2007-03-29 16:47:53 -04001197 .p_arglen = ACL3_setaclargs_sz,
1198 .p_replen = ACL3_setaclres_sz,
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001199 .p_timer = 0,
Chuck Levercc0175c2006-03-20 13:44:22 -05001200 .p_name = "SETACL",
Andreas Gruenbacherb7fa0552005-06-22 17:16:27 +00001201 },
1202};
1203
1204struct rpc_version nfsacl_version3 = {
1205 .number = 3,
1206 .nrprocs = sizeof(nfs3_acl_procedures)/
1207 sizeof(nfs3_acl_procedures[0]),
1208 .procs = nfs3_acl_procedures,
1209};
1210#endif /* CONFIG_NFS_V3_ACL */