Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
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> | |
24 | ||
25 | #define NFSDBG_FACILITY NFSDBG_XDR | |
26 | ||
27 | /* Mapping from NFS error code to "errno" error code. */ | |
28 | #define errno_NFSERR_IO EIO | |
29 | ||
30 | extern int nfs_stat_to_errno(int); | |
31 | ||
32 | /* | |
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) | |
53 | #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) | |
59 | #define NFS3_symlinkargs_sz (NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz) | |
60 | #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) | |
68 | #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 | ||
82 | /* | |
83 | * Map file type to S_IFMT bits | |
84 | */ | |
85 | static struct { | |
86 | unsigned int mode; | |
87 | unsigned int nfs2type; | |
88 | } nfs_type2fmt[] = { | |
89 | { 0, NFNON }, | |
90 | { S_IFREG, NFREG }, | |
91 | { S_IFDIR, NFDIR }, | |
92 | { S_IFBLK, NFBLK }, | |
93 | { S_IFCHR, NFCHR }, | |
94 | { S_IFLNK, NFLNK }, | |
95 | { S_IFSOCK, NFSOCK }, | |
96 | { S_IFIFO, NFFIFO }, | |
97 | { 0, NFBAD } | |
98 | }; | |
99 | ||
100 | /* | |
101 | * Common NFS XDR functions as inlines | |
102 | */ | |
103 | static inline u32 * | |
104 | xdr_encode_fhandle(u32 *p, struct nfs_fh *fh) | |
105 | { | |
106 | return xdr_encode_array(p, fh->data, fh->size); | |
107 | } | |
108 | ||
109 | static inline u32 * | |
110 | xdr_decode_fhandle(u32 *p, struct nfs_fh *fh) | |
111 | { | |
112 | if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) { | |
113 | memcpy(fh->data, p, fh->size); | |
114 | return p + XDR_QUADLEN(fh->size); | |
115 | } | |
116 | return NULL; | |
117 | } | |
118 | ||
119 | /* | |
120 | * Encode/decode time. | |
121 | */ | |
122 | static inline u32 * | |
123 | xdr_encode_time3(u32 *p, struct timespec *timep) | |
124 | { | |
125 | *p++ = htonl(timep->tv_sec); | |
126 | *p++ = htonl(timep->tv_nsec); | |
127 | return p; | |
128 | } | |
129 | ||
130 | static inline u32 * | |
131 | xdr_decode_time3(u32 *p, struct timespec *timep) | |
132 | { | |
133 | timep->tv_sec = ntohl(*p++); | |
134 | timep->tv_nsec = ntohl(*p++); | |
135 | return p; | |
136 | } | |
137 | ||
138 | static u32 * | |
139 | xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr) | |
140 | { | |
141 | unsigned int type, major, minor; | |
142 | int fmode; | |
143 | ||
144 | type = ntohl(*p++); | |
145 | if (type >= NF3BAD) | |
146 | type = NF3BAD; | |
147 | fmode = nfs_type2fmt[type].mode; | |
148 | fattr->type = nfs_type2fmt[type].nfs2type; | |
149 | fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode; | |
150 | fattr->nlink = ntohl(*p++); | |
151 | fattr->uid = ntohl(*p++); | |
152 | fattr->gid = ntohl(*p++); | |
153 | p = xdr_decode_hyper(p, &fattr->size); | |
154 | p = xdr_decode_hyper(p, &fattr->du.nfs3.used); | |
155 | ||
156 | /* Turn remote device info into Linux-specific dev_t */ | |
157 | major = ntohl(*p++); | |
158 | minor = ntohl(*p++); | |
159 | fattr->rdev = MKDEV(major, minor); | |
160 | if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor) | |
161 | fattr->rdev = 0; | |
162 | ||
163 | p = xdr_decode_hyper(p, &fattr->fsid_u.nfs3); | |
164 | p = xdr_decode_hyper(p, &fattr->fileid); | |
165 | p = xdr_decode_time3(p, &fattr->atime); | |
166 | p = xdr_decode_time3(p, &fattr->mtime); | |
167 | p = xdr_decode_time3(p, &fattr->ctime); | |
168 | ||
169 | /* Update the mode bits */ | |
170 | fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3); | |
171 | fattr->timestamp = jiffies; | |
172 | return p; | |
173 | } | |
174 | ||
175 | static inline u32 * | |
176 | xdr_encode_sattr(u32 *p, struct iattr *attr) | |
177 | { | |
178 | if (attr->ia_valid & ATTR_MODE) { | |
179 | *p++ = xdr_one; | |
180 | *p++ = htonl(attr->ia_mode); | |
181 | } else { | |
182 | *p++ = xdr_zero; | |
183 | } | |
184 | if (attr->ia_valid & ATTR_UID) { | |
185 | *p++ = xdr_one; | |
186 | *p++ = htonl(attr->ia_uid); | |
187 | } else { | |
188 | *p++ = xdr_zero; | |
189 | } | |
190 | if (attr->ia_valid & ATTR_GID) { | |
191 | *p++ = xdr_one; | |
192 | *p++ = htonl(attr->ia_gid); | |
193 | } else { | |
194 | *p++ = xdr_zero; | |
195 | } | |
196 | if (attr->ia_valid & ATTR_SIZE) { | |
197 | *p++ = xdr_one; | |
198 | p = xdr_encode_hyper(p, (__u64) attr->ia_size); | |
199 | } else { | |
200 | *p++ = xdr_zero; | |
201 | } | |
202 | if (attr->ia_valid & ATTR_ATIME_SET) { | |
203 | *p++ = xdr_two; | |
204 | p = xdr_encode_time3(p, &attr->ia_atime); | |
205 | } else if (attr->ia_valid & ATTR_ATIME) { | |
206 | *p++ = xdr_one; | |
207 | } else { | |
208 | *p++ = xdr_zero; | |
209 | } | |
210 | if (attr->ia_valid & ATTR_MTIME_SET) { | |
211 | *p++ = xdr_two; | |
212 | p = xdr_encode_time3(p, &attr->ia_mtime); | |
213 | } else if (attr->ia_valid & ATTR_MTIME) { | |
214 | *p++ = xdr_one; | |
215 | } else { | |
216 | *p++ = xdr_zero; | |
217 | } | |
218 | return p; | |
219 | } | |
220 | ||
221 | static inline u32 * | |
222 | xdr_decode_wcc_attr(u32 *p, struct nfs_fattr *fattr) | |
223 | { | |
224 | p = xdr_decode_hyper(p, &fattr->pre_size); | |
225 | p = xdr_decode_time3(p, &fattr->pre_mtime); | |
226 | p = xdr_decode_time3(p, &fattr->pre_ctime); | |
227 | fattr->valid |= NFS_ATTR_WCC; | |
228 | return p; | |
229 | } | |
230 | ||
231 | static inline u32 * | |
232 | xdr_decode_post_op_attr(u32 *p, struct nfs_fattr *fattr) | |
233 | { | |
234 | if (*p++) | |
235 | p = xdr_decode_fattr(p, fattr); | |
236 | return p; | |
237 | } | |
238 | ||
239 | static inline u32 * | |
240 | xdr_decode_pre_op_attr(u32 *p, struct nfs_fattr *fattr) | |
241 | { | |
242 | if (*p++) | |
243 | return xdr_decode_wcc_attr(p, fattr); | |
244 | return p; | |
245 | } | |
246 | ||
247 | ||
248 | static inline u32 * | |
249 | xdr_decode_wcc_data(u32 *p, struct nfs_fattr *fattr) | |
250 | { | |
251 | p = xdr_decode_pre_op_attr(p, fattr); | |
252 | return xdr_decode_post_op_attr(p, fattr); | |
253 | } | |
254 | ||
255 | /* | |
256 | * NFS encode functions | |
257 | */ | |
258 | ||
259 | /* | |
260 | * Encode file handle argument | |
261 | */ | |
262 | static int | |
263 | nfs3_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh) | |
264 | { | |
265 | p = xdr_encode_fhandle(p, fh); | |
266 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
267 | return 0; | |
268 | } | |
269 | ||
270 | /* | |
271 | * Encode SETATTR arguments | |
272 | */ | |
273 | static int | |
274 | nfs3_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs3_sattrargs *args) | |
275 | { | |
276 | p = xdr_encode_fhandle(p, args->fh); | |
277 | p = xdr_encode_sattr(p, args->sattr); | |
278 | *p++ = htonl(args->guard); | |
279 | if (args->guard) | |
280 | p = xdr_encode_time3(p, &args->guardtime); | |
281 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
282 | return 0; | |
283 | } | |
284 | ||
285 | /* | |
286 | * Encode directory ops argument | |
287 | */ | |
288 | static int | |
289 | nfs3_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs3_diropargs *args) | |
290 | { | |
291 | p = xdr_encode_fhandle(p, args->fh); | |
292 | p = xdr_encode_array(p, args->name, args->len); | |
293 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
294 | return 0; | |
295 | } | |
296 | ||
297 | /* | |
298 | * Encode access() argument | |
299 | */ | |
300 | static int | |
301 | nfs3_xdr_accessargs(struct rpc_rqst *req, u32 *p, struct nfs3_accessargs *args) | |
302 | { | |
303 | p = xdr_encode_fhandle(p, args->fh); | |
304 | *p++ = htonl(args->access); | |
305 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
306 | return 0; | |
307 | } | |
308 | ||
309 | /* | |
310 | * Arguments to a READ call. Since we read data directly into the page | |
311 | * cache, we also set up the reply iovec here so that iov[1] points | |
312 | * exactly to the page we want to fetch. | |
313 | */ | |
314 | static int | |
315 | nfs3_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args) | |
316 | { | |
317 | struct rpc_auth *auth = req->rq_task->tk_auth; | |
318 | unsigned int replen; | |
319 | u32 count = args->count; | |
320 | ||
321 | p = xdr_encode_fhandle(p, args->fh); | |
322 | p = xdr_encode_hyper(p, args->offset); | |
323 | *p++ = htonl(count); | |
324 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
325 | ||
326 | /* Inline the page array */ | |
327 | replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2; | |
328 | xdr_inline_pages(&req->rq_rcv_buf, replen, | |
329 | args->pages, args->pgbase, count); | |
330 | return 0; | |
331 | } | |
332 | ||
333 | /* | |
334 | * Write arguments. Splice the buffer to be written into the iovec. | |
335 | */ | |
336 | static int | |
337 | nfs3_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args) | |
338 | { | |
339 | struct xdr_buf *sndbuf = &req->rq_snd_buf; | |
340 | u32 count = args->count; | |
341 | ||
342 | p = xdr_encode_fhandle(p, args->fh); | |
343 | p = xdr_encode_hyper(p, args->offset); | |
344 | *p++ = htonl(count); | |
345 | *p++ = htonl(args->stable); | |
346 | *p++ = htonl(count); | |
347 | sndbuf->len = xdr_adjust_iovec(sndbuf->head, p); | |
348 | ||
349 | /* Copy the page array */ | |
350 | xdr_encode_pages(sndbuf, args->pages, args->pgbase, count); | |
351 | return 0; | |
352 | } | |
353 | ||
354 | /* | |
355 | * Encode CREATE arguments | |
356 | */ | |
357 | static int | |
358 | nfs3_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs3_createargs *args) | |
359 | { | |
360 | p = xdr_encode_fhandle(p, args->fh); | |
361 | p = xdr_encode_array(p, args->name, args->len); | |
362 | ||
363 | *p++ = htonl(args->createmode); | |
364 | if (args->createmode == NFS3_CREATE_EXCLUSIVE) { | |
365 | *p++ = args->verifier[0]; | |
366 | *p++ = args->verifier[1]; | |
367 | } else | |
368 | p = xdr_encode_sattr(p, args->sattr); | |
369 | ||
370 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
371 | return 0; | |
372 | } | |
373 | ||
374 | /* | |
375 | * Encode MKDIR arguments | |
376 | */ | |
377 | static int | |
378 | nfs3_xdr_mkdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_mkdirargs *args) | |
379 | { | |
380 | p = xdr_encode_fhandle(p, args->fh); | |
381 | p = xdr_encode_array(p, args->name, args->len); | |
382 | p = xdr_encode_sattr(p, args->sattr); | |
383 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
384 | return 0; | |
385 | } | |
386 | ||
387 | /* | |
388 | * Encode SYMLINK arguments | |
389 | */ | |
390 | static int | |
391 | nfs3_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_symlinkargs *args) | |
392 | { | |
393 | p = xdr_encode_fhandle(p, args->fromfh); | |
394 | p = xdr_encode_array(p, args->fromname, args->fromlen); | |
395 | p = xdr_encode_sattr(p, args->sattr); | |
396 | p = xdr_encode_array(p, args->topath, args->tolen); | |
397 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
398 | return 0; | |
399 | } | |
400 | ||
401 | /* | |
402 | * Encode MKNOD arguments | |
403 | */ | |
404 | static int | |
405 | nfs3_xdr_mknodargs(struct rpc_rqst *req, u32 *p, struct nfs3_mknodargs *args) | |
406 | { | |
407 | p = xdr_encode_fhandle(p, args->fh); | |
408 | p = xdr_encode_array(p, args->name, args->len); | |
409 | *p++ = htonl(args->type); | |
410 | p = xdr_encode_sattr(p, args->sattr); | |
411 | if (args->type == NF3CHR || args->type == NF3BLK) { | |
412 | *p++ = htonl(MAJOR(args->rdev)); | |
413 | *p++ = htonl(MINOR(args->rdev)); | |
414 | } | |
415 | ||
416 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
417 | return 0; | |
418 | } | |
419 | ||
420 | /* | |
421 | * Encode RENAME arguments | |
422 | */ | |
423 | static int | |
424 | nfs3_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs3_renameargs *args) | |
425 | { | |
426 | p = xdr_encode_fhandle(p, args->fromfh); | |
427 | p = xdr_encode_array(p, args->fromname, args->fromlen); | |
428 | p = xdr_encode_fhandle(p, args->tofh); | |
429 | p = xdr_encode_array(p, args->toname, args->tolen); | |
430 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
431 | return 0; | |
432 | } | |
433 | ||
434 | /* | |
435 | * Encode LINK arguments | |
436 | */ | |
437 | static int | |
438 | nfs3_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs3_linkargs *args) | |
439 | { | |
440 | p = xdr_encode_fhandle(p, args->fromfh); | |
441 | p = xdr_encode_fhandle(p, args->tofh); | |
442 | p = xdr_encode_array(p, args->toname, args->tolen); | |
443 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
444 | return 0; | |
445 | } | |
446 | ||
447 | /* | |
448 | * Encode arguments to readdir call | |
449 | */ | |
450 | static int | |
451 | nfs3_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_readdirargs *args) | |
452 | { | |
453 | struct rpc_auth *auth = req->rq_task->tk_auth; | |
454 | unsigned int replen; | |
455 | u32 count = args->count; | |
456 | ||
457 | p = xdr_encode_fhandle(p, args->fh); | |
458 | p = xdr_encode_hyper(p, args->cookie); | |
459 | *p++ = args->verf[0]; | |
460 | *p++ = args->verf[1]; | |
461 | if (args->plus) { | |
462 | /* readdirplus: need dircount + buffer size. | |
463 | * We just make sure we make dircount big enough */ | |
464 | *p++ = htonl(count >> 3); | |
465 | } | |
466 | *p++ = htonl(count); | |
467 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
468 | ||
469 | /* Inline the page array */ | |
470 | replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2; | |
471 | xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count); | |
472 | return 0; | |
473 | } | |
474 | ||
475 | /* | |
476 | * Decode the result of a readdir call. | |
477 | * We just check for syntactical correctness. | |
478 | */ | |
479 | static int | |
480 | nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res) | |
481 | { | |
482 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | |
483 | struct kvec *iov = rcvbuf->head; | |
484 | struct page **page; | |
485 | int hdrlen, recvd; | |
486 | int status, nr; | |
487 | unsigned int len, pglen; | |
488 | u32 *entry, *end, *kaddr; | |
489 | ||
490 | status = ntohl(*p++); | |
491 | /* Decode post_op_attrs */ | |
492 | p = xdr_decode_post_op_attr(p, res->dir_attr); | |
493 | if (status) | |
494 | return -nfs_stat_to_errno(status); | |
495 | /* Decode verifier cookie */ | |
496 | if (res->verf) { | |
497 | res->verf[0] = *p++; | |
498 | res->verf[1] = *p++; | |
499 | } else { | |
500 | p += 2; | |
501 | } | |
502 | ||
503 | hdrlen = (u8 *) p - (u8 *) iov->iov_base; | |
504 | if (iov->iov_len < hdrlen) { | |
505 | printk(KERN_WARNING "NFS: READDIR reply header overflowed:" | |
506 | "length %d > %Zu\n", hdrlen, iov->iov_len); | |
507 | return -errno_NFSERR_IO; | |
508 | } else if (iov->iov_len != hdrlen) { | |
509 | dprintk("NFS: READDIR header is short. iovec will be shifted.\n"); | |
510 | xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen); | |
511 | } | |
512 | ||
513 | pglen = rcvbuf->page_len; | |
514 | recvd = rcvbuf->len - hdrlen; | |
515 | if (pglen > recvd) | |
516 | pglen = recvd; | |
517 | page = rcvbuf->pages; | |
518 | kaddr = p = (u32 *)kmap_atomic(*page, KM_USER0); | |
519 | end = (u32 *)((char *)p + pglen); | |
520 | entry = p; | |
521 | for (nr = 0; *p++; nr++) { | |
522 | if (p + 3 > end) | |
523 | goto short_pkt; | |
524 | p += 2; /* inode # */ | |
525 | len = ntohl(*p++); /* string length */ | |
526 | p += XDR_QUADLEN(len) + 2; /* name + cookie */ | |
527 | if (len > NFS3_MAXNAMLEN) { | |
528 | printk(KERN_WARNING "NFS: giant filename in readdir (len %x)!\n", | |
529 | len); | |
530 | goto err_unmap; | |
531 | } | |
532 | ||
533 | if (res->plus) { | |
534 | /* post_op_attr */ | |
535 | if (p + 2 > end) | |
536 | goto short_pkt; | |
537 | if (*p++) { | |
538 | p += 21; | |
539 | if (p + 1 > end) | |
540 | goto short_pkt; | |
541 | } | |
542 | /* post_op_fh3 */ | |
543 | if (*p++) { | |
544 | if (p + 1 > end) | |
545 | goto short_pkt; | |
546 | len = ntohl(*p++); | |
547 | if (len > NFS3_FHSIZE) { | |
548 | printk(KERN_WARNING "NFS: giant filehandle in " | |
549 | "readdir (len %x)!\n", len); | |
550 | goto err_unmap; | |
551 | } | |
552 | p += XDR_QUADLEN(len); | |
553 | } | |
554 | } | |
555 | ||
556 | if (p + 2 > end) | |
557 | goto short_pkt; | |
558 | entry = p; | |
559 | } | |
560 | if (!nr && (entry[0] != 0 || entry[1] == 0)) | |
561 | goto short_pkt; | |
562 | out: | |
563 | kunmap_atomic(kaddr, KM_USER0); | |
564 | return nr; | |
565 | short_pkt: | |
566 | entry[0] = entry[1] = 0; | |
567 | /* truncate listing ? */ | |
568 | if (!nr) { | |
569 | printk(KERN_NOTICE "NFS: readdir reply truncated!\n"); | |
570 | entry[1] = 1; | |
571 | } | |
572 | goto out; | |
573 | err_unmap: | |
574 | nr = -errno_NFSERR_IO; | |
575 | goto out; | |
576 | } | |
577 | ||
578 | u32 * | |
579 | nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus) | |
580 | { | |
581 | struct nfs_entry old = *entry; | |
582 | ||
583 | if (!*p++) { | |
584 | if (!*p) | |
585 | return ERR_PTR(-EAGAIN); | |
586 | entry->eof = 1; | |
587 | return ERR_PTR(-EBADCOOKIE); | |
588 | } | |
589 | ||
590 | p = xdr_decode_hyper(p, &entry->ino); | |
591 | entry->len = ntohl(*p++); | |
592 | entry->name = (const char *) p; | |
593 | p += XDR_QUADLEN(entry->len); | |
594 | entry->prev_cookie = entry->cookie; | |
595 | p = xdr_decode_hyper(p, &entry->cookie); | |
596 | ||
597 | if (plus) { | |
598 | entry->fattr->valid = 0; | |
599 | p = xdr_decode_post_op_attr(p, entry->fattr); | |
600 | /* In fact, a post_op_fh3: */ | |
601 | if (*p++) { | |
602 | p = xdr_decode_fhandle(p, entry->fh); | |
603 | /* Ugh -- server reply was truncated */ | |
604 | if (p == NULL) { | |
605 | dprintk("NFS: FH truncated\n"); | |
606 | *entry = old; | |
607 | return ERR_PTR(-EAGAIN); | |
608 | } | |
609 | } else | |
610 | memset((u8*)(entry->fh), 0, sizeof(*entry->fh)); | |
611 | } | |
612 | ||
613 | entry->eof = !p[0] && p[1]; | |
614 | return p; | |
615 | } | |
616 | ||
617 | /* | |
618 | * Encode COMMIT arguments | |
619 | */ | |
620 | static int | |
621 | nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args) | |
622 | { | |
623 | p = xdr_encode_fhandle(p, args->fh); | |
624 | p = xdr_encode_hyper(p, args->offset); | |
625 | *p++ = htonl(args->count); | |
626 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
627 | return 0; | |
628 | } | |
629 | ||
630 | /* | |
631 | * NFS XDR decode functions | |
632 | */ | |
633 | ||
634 | /* | |
635 | * Decode attrstat reply. | |
636 | */ | |
637 | static int | |
638 | nfs3_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr) | |
639 | { | |
640 | int status; | |
641 | ||
642 | if ((status = ntohl(*p++))) | |
643 | return -nfs_stat_to_errno(status); | |
644 | xdr_decode_fattr(p, fattr); | |
645 | return 0; | |
646 | } | |
647 | ||
648 | /* | |
649 | * Decode status+wcc_data reply | |
650 | * SATTR, REMOVE, RMDIR | |
651 | */ | |
652 | static int | |
653 | nfs3_xdr_wccstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr) | |
654 | { | |
655 | int status; | |
656 | ||
657 | if ((status = ntohl(*p++))) | |
658 | status = -nfs_stat_to_errno(status); | |
659 | xdr_decode_wcc_data(p, fattr); | |
660 | return status; | |
661 | } | |
662 | ||
663 | /* | |
664 | * Decode LOOKUP reply | |
665 | */ | |
666 | static int | |
667 | nfs3_xdr_lookupres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res) | |
668 | { | |
669 | int status; | |
670 | ||
671 | if ((status = ntohl(*p++))) { | |
672 | status = -nfs_stat_to_errno(status); | |
673 | } else { | |
674 | if (!(p = xdr_decode_fhandle(p, res->fh))) | |
675 | return -errno_NFSERR_IO; | |
676 | p = xdr_decode_post_op_attr(p, res->fattr); | |
677 | } | |
678 | xdr_decode_post_op_attr(p, res->dir_attr); | |
679 | return status; | |
680 | } | |
681 | ||
682 | /* | |
683 | * Decode ACCESS reply | |
684 | */ | |
685 | static int | |
686 | nfs3_xdr_accessres(struct rpc_rqst *req, u32 *p, struct nfs3_accessres *res) | |
687 | { | |
688 | int status = ntohl(*p++); | |
689 | ||
690 | p = xdr_decode_post_op_attr(p, res->fattr); | |
691 | if (status) | |
692 | return -nfs_stat_to_errno(status); | |
693 | res->access = ntohl(*p++); | |
694 | return 0; | |
695 | } | |
696 | ||
697 | static int | |
698 | nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *args) | |
699 | { | |
700 | struct rpc_auth *auth = req->rq_task->tk_auth; | |
701 | unsigned int replen; | |
702 | ||
703 | p = xdr_encode_fhandle(p, args->fh); | |
704 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
705 | ||
706 | /* Inline the page array */ | |
707 | replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2; | |
708 | xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen); | |
709 | return 0; | |
710 | } | |
711 | ||
712 | /* | |
713 | * Decode READLINK reply | |
714 | */ | |
715 | static int | |
716 | nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr) | |
717 | { | |
718 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | |
719 | struct kvec *iov = rcvbuf->head; | |
720 | int hdrlen, len, recvd; | |
721 | char *kaddr; | |
722 | int status; | |
723 | ||
724 | status = ntohl(*p++); | |
725 | p = xdr_decode_post_op_attr(p, fattr); | |
726 | ||
727 | if (status != 0) | |
728 | return -nfs_stat_to_errno(status); | |
729 | ||
730 | /* Convert length of symlink */ | |
731 | len = ntohl(*p++); | |
732 | if (len >= rcvbuf->page_len || len <= 0) { | |
733 | dprintk(KERN_WARNING "nfs: server returned giant symlink!\n"); | |
734 | return -ENAMETOOLONG; | |
735 | } | |
736 | ||
737 | hdrlen = (u8 *) p - (u8 *) iov->iov_base; | |
738 | if (iov->iov_len < hdrlen) { | |
739 | printk(KERN_WARNING "NFS: READLINK reply header overflowed:" | |
740 | "length %d > %Zu\n", hdrlen, iov->iov_len); | |
741 | return -errno_NFSERR_IO; | |
742 | } else if (iov->iov_len != hdrlen) { | |
743 | dprintk("NFS: READLINK header is short. iovec will be shifted.\n"); | |
744 | xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen); | |
745 | } | |
746 | recvd = req->rq_rcv_buf.len - hdrlen; | |
747 | if (recvd < len) { | |
748 | printk(KERN_WARNING "NFS: server cheating in readlink reply: " | |
749 | "count %u > recvd %u\n", len, recvd); | |
750 | return -EIO; | |
751 | } | |
752 | ||
753 | /* NULL terminate the string we got */ | |
754 | kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0); | |
755 | kaddr[len+rcvbuf->page_base] = '\0'; | |
756 | kunmap_atomic(kaddr, KM_USER0); | |
757 | return 0; | |
758 | } | |
759 | ||
760 | /* | |
761 | * Decode READ reply | |
762 | */ | |
763 | static int | |
764 | nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res) | |
765 | { | |
766 | struct kvec *iov = req->rq_rcv_buf.head; | |
767 | int status, count, ocount, recvd, hdrlen; | |
768 | ||
769 | status = ntohl(*p++); | |
770 | p = xdr_decode_post_op_attr(p, res->fattr); | |
771 | ||
772 | if (status != 0) | |
773 | return -nfs_stat_to_errno(status); | |
774 | ||
775 | /* Decode reply could and EOF flag. NFSv3 is somewhat redundant | |
776 | * in that it puts the count both in the res struct and in the | |
777 | * opaque data count. */ | |
778 | count = ntohl(*p++); | |
779 | res->eof = ntohl(*p++); | |
780 | ocount = ntohl(*p++); | |
781 | ||
782 | if (ocount != count) { | |
783 | printk(KERN_WARNING "NFS: READ count doesn't match RPC opaque count.\n"); | |
784 | return -errno_NFSERR_IO; | |
785 | } | |
786 | ||
787 | hdrlen = (u8 *) p - (u8 *) iov->iov_base; | |
788 | if (iov->iov_len < hdrlen) { | |
789 | printk(KERN_WARNING "NFS: READ reply header overflowed:" | |
790 | "length %d > %Zu\n", hdrlen, iov->iov_len); | |
791 | return -errno_NFSERR_IO; | |
792 | } else if (iov->iov_len != hdrlen) { | |
793 | dprintk("NFS: READ header is short. iovec will be shifted.\n"); | |
794 | xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen); | |
795 | } | |
796 | ||
797 | recvd = req->rq_rcv_buf.len - hdrlen; | |
798 | if (count > recvd) { | |
799 | printk(KERN_WARNING "NFS: server cheating in read reply: " | |
800 | "count %d > recvd %d\n", count, recvd); | |
801 | count = recvd; | |
802 | res->eof = 0; | |
803 | } | |
804 | ||
805 | if (count < res->count) | |
806 | res->count = count; | |
807 | ||
808 | return count; | |
809 | } | |
810 | ||
811 | /* | |
812 | * Decode WRITE response | |
813 | */ | |
814 | static int | |
815 | nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res) | |
816 | { | |
817 | int status; | |
818 | ||
819 | status = ntohl(*p++); | |
820 | p = xdr_decode_wcc_data(p, res->fattr); | |
821 | ||
822 | if (status != 0) | |
823 | return -nfs_stat_to_errno(status); | |
824 | ||
825 | res->count = ntohl(*p++); | |
826 | res->verf->committed = (enum nfs3_stable_how)ntohl(*p++); | |
827 | res->verf->verifier[0] = *p++; | |
828 | res->verf->verifier[1] = *p++; | |
829 | ||
830 | return res->count; | |
831 | } | |
832 | ||
833 | /* | |
834 | * Decode a CREATE response | |
835 | */ | |
836 | static int | |
837 | nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res) | |
838 | { | |
839 | int status; | |
840 | ||
841 | status = ntohl(*p++); | |
842 | if (status == 0) { | |
843 | if (*p++) { | |
844 | if (!(p = xdr_decode_fhandle(p, res->fh))) | |
845 | return -errno_NFSERR_IO; | |
846 | p = xdr_decode_post_op_attr(p, res->fattr); | |
847 | } else { | |
848 | memset(res->fh, 0, sizeof(*res->fh)); | |
849 | /* Do decode post_op_attr but set it to NULL */ | |
850 | p = xdr_decode_post_op_attr(p, res->fattr); | |
851 | res->fattr->valid = 0; | |
852 | } | |
853 | } else { | |
854 | status = -nfs_stat_to_errno(status); | |
855 | } | |
856 | p = xdr_decode_wcc_data(p, res->dir_attr); | |
857 | return status; | |
858 | } | |
859 | ||
860 | /* | |
861 | * Decode RENAME reply | |
862 | */ | |
863 | static int | |
864 | nfs3_xdr_renameres(struct rpc_rqst *req, u32 *p, struct nfs3_renameres *res) | |
865 | { | |
866 | int status; | |
867 | ||
868 | if ((status = ntohl(*p++)) != 0) | |
869 | status = -nfs_stat_to_errno(status); | |
870 | p = xdr_decode_wcc_data(p, res->fromattr); | |
871 | p = xdr_decode_wcc_data(p, res->toattr); | |
872 | return status; | |
873 | } | |
874 | ||
875 | /* | |
876 | * Decode LINK reply | |
877 | */ | |
878 | static int | |
879 | nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res) | |
880 | { | |
881 | int status; | |
882 | ||
883 | if ((status = ntohl(*p++)) != 0) | |
884 | status = -nfs_stat_to_errno(status); | |
885 | p = xdr_decode_post_op_attr(p, res->fattr); | |
886 | p = xdr_decode_wcc_data(p, res->dir_attr); | |
887 | return status; | |
888 | } | |
889 | ||
890 | /* | |
891 | * Decode FSSTAT reply | |
892 | */ | |
893 | static int | |
894 | nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsstat *res) | |
895 | { | |
896 | int status; | |
897 | ||
898 | status = ntohl(*p++); | |
899 | ||
900 | p = xdr_decode_post_op_attr(p, res->fattr); | |
901 | if (status != 0) | |
902 | return -nfs_stat_to_errno(status); | |
903 | ||
904 | p = xdr_decode_hyper(p, &res->tbytes); | |
905 | p = xdr_decode_hyper(p, &res->fbytes); | |
906 | p = xdr_decode_hyper(p, &res->abytes); | |
907 | p = xdr_decode_hyper(p, &res->tfiles); | |
908 | p = xdr_decode_hyper(p, &res->ffiles); | |
909 | p = xdr_decode_hyper(p, &res->afiles); | |
910 | ||
911 | /* ignore invarsec */ | |
912 | return 0; | |
913 | } | |
914 | ||
915 | /* | |
916 | * Decode FSINFO reply | |
917 | */ | |
918 | static int | |
919 | nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res) | |
920 | { | |
921 | int status; | |
922 | ||
923 | status = ntohl(*p++); | |
924 | ||
925 | p = xdr_decode_post_op_attr(p, res->fattr); | |
926 | if (status != 0) | |
927 | return -nfs_stat_to_errno(status); | |
928 | ||
929 | res->rtmax = ntohl(*p++); | |
930 | res->rtpref = ntohl(*p++); | |
931 | res->rtmult = ntohl(*p++); | |
932 | res->wtmax = ntohl(*p++); | |
933 | res->wtpref = ntohl(*p++); | |
934 | res->wtmult = ntohl(*p++); | |
935 | res->dtpref = ntohl(*p++); | |
936 | p = xdr_decode_hyper(p, &res->maxfilesize); | |
937 | ||
938 | /* ignore time_delta and properties */ | |
939 | res->lease_time = 0; | |
940 | return 0; | |
941 | } | |
942 | ||
943 | /* | |
944 | * Decode PATHCONF reply | |
945 | */ | |
946 | static int | |
947 | nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_pathconf *res) | |
948 | { | |
949 | int status; | |
950 | ||
951 | status = ntohl(*p++); | |
952 | ||
953 | p = xdr_decode_post_op_attr(p, res->fattr); | |
954 | if (status != 0) | |
955 | return -nfs_stat_to_errno(status); | |
956 | res->max_link = ntohl(*p++); | |
957 | res->max_namelen = ntohl(*p++); | |
958 | ||
959 | /* ignore remaining fields */ | |
960 | return 0; | |
961 | } | |
962 | ||
963 | /* | |
964 | * Decode COMMIT reply | |
965 | */ | |
966 | static int | |
967 | nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res) | |
968 | { | |
969 | int status; | |
970 | ||
971 | status = ntohl(*p++); | |
972 | p = xdr_decode_wcc_data(p, res->fattr); | |
973 | if (status != 0) | |
974 | return -nfs_stat_to_errno(status); | |
975 | ||
976 | res->verf->verifier[0] = *p++; | |
977 | res->verf->verifier[1] = *p++; | |
978 | return 0; | |
979 | } | |
980 | ||
981 | #ifndef MAX | |
982 | # define MAX(a, b) (((a) > (b))? (a) : (b)) | |
983 | #endif | |
984 | ||
985 | #define PROC(proc, argtype, restype, timer) \ | |
986 | [NFS3PROC_##proc] = { \ | |
987 | .p_proc = NFS3PROC_##proc, \ | |
988 | .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \ | |
989 | .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \ | |
990 | .p_bufsiz = MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2, \ | |
991 | .p_timer = timer \ | |
992 | } | |
993 | ||
994 | struct rpc_procinfo nfs3_procedures[] = { | |
995 | PROC(GETATTR, fhandle, attrstat, 1), | |
996 | PROC(SETATTR, sattrargs, wccstat, 0), | |
997 | PROC(LOOKUP, diropargs, lookupres, 2), | |
998 | PROC(ACCESS, accessargs, accessres, 1), | |
999 | PROC(READLINK, readlinkargs, readlinkres, 3), | |
1000 | PROC(READ, readargs, readres, 3), | |
1001 | PROC(WRITE, writeargs, writeres, 4), | |
1002 | PROC(CREATE, createargs, createres, 0), | |
1003 | PROC(MKDIR, mkdirargs, createres, 0), | |
1004 | PROC(SYMLINK, symlinkargs, createres, 0), | |
1005 | PROC(MKNOD, mknodargs, createres, 0), | |
1006 | PROC(REMOVE, diropargs, wccstat, 0), | |
1007 | PROC(RMDIR, diropargs, wccstat, 0), | |
1008 | PROC(RENAME, renameargs, renameres, 0), | |
1009 | PROC(LINK, linkargs, linkres, 0), | |
1010 | PROC(READDIR, readdirargs, readdirres, 3), | |
1011 | PROC(READDIRPLUS, readdirargs, readdirres, 3), | |
1012 | PROC(FSSTAT, fhandle, fsstatres, 0), | |
1013 | PROC(FSINFO, fhandle, fsinfores, 0), | |
1014 | PROC(PATHCONF, fhandle, pathconfres, 0), | |
1015 | PROC(COMMIT, commitargs, commitres, 5), | |
1016 | }; | |
1017 | ||
1018 | struct rpc_version nfs_version3 = { | |
1019 | .number = 3, | |
1020 | .nrprocs = sizeof(nfs3_procedures)/sizeof(nfs3_procedures[0]), | |
1021 | .procs = nfs3_procedures | |
1022 | }; | |
1023 |