diff options
Diffstat (limited to 'fs/nfs/nfs2xdr.c')
-rw-r--r-- | fs/nfs/nfs2xdr.c | 1294 |
1 files changed, 860 insertions, 434 deletions
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 5914a1911c95..792cb13a4304 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c | |||
@@ -61,584 +61,1008 @@ | |||
61 | #define NFS_readdirres_sz (1) | 61 | #define NFS_readdirres_sz (1) |
62 | #define NFS_statfsres_sz (1+NFS_info_sz) | 62 | #define NFS_statfsres_sz (1+NFS_info_sz) |
63 | 63 | ||
64 | |||
64 | /* | 65 | /* |
65 | * Common NFS XDR functions as inlines | 66 | * While encoding arguments, set up the reply buffer in advance to |
67 | * receive reply data directly into the page cache. | ||
66 | */ | 68 | */ |
67 | static inline __be32 * | 69 | static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages, |
68 | xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fhandle) | 70 | unsigned int base, unsigned int len, |
71 | unsigned int bufsize) | ||
69 | { | 72 | { |
70 | memcpy(p, fhandle->data, NFS2_FHSIZE); | 73 | struct rpc_auth *auth = req->rq_cred->cr_auth; |
71 | return p + XDR_QUADLEN(NFS2_FHSIZE); | 74 | unsigned int replen; |
75 | |||
76 | replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize; | ||
77 | xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len); | ||
72 | } | 78 | } |
73 | 79 | ||
74 | static inline __be32 * | 80 | /* |
75 | xdr_decode_fhandle(__be32 *p, struct nfs_fh *fhandle) | 81 | * Handle decode buffer overflows out-of-line. |
82 | */ | ||
83 | static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) | ||
76 | { | 84 | { |
77 | /* NFSv2 handles have a fixed length */ | 85 | dprintk("NFS: %s prematurely hit the end of our receive buffer. " |
78 | fhandle->size = NFS2_FHSIZE; | 86 | "Remaining buffer length is %tu words.\n", |
79 | memcpy(fhandle->data, p, NFS2_FHSIZE); | 87 | func, xdr->end - xdr->p); |
80 | return p + XDR_QUADLEN(NFS2_FHSIZE); | 88 | } |
89 | |||
90 | |||
91 | /* | ||
92 | * Encode/decode NFSv2 basic data types | ||
93 | * | ||
94 | * Basic NFSv2 data types are defined in section 2.3 of RFC 1094: | ||
95 | * "NFS: Network File System Protocol Specification". | ||
96 | * | ||
97 | * Not all basic data types have their own encoding and decoding | ||
98 | * functions. For run-time efficiency, some data types are encoded | ||
99 | * or decoded inline. | ||
100 | */ | ||
101 | |||
102 | /* | ||
103 | * typedef opaque nfsdata<>; | ||
104 | */ | ||
105 | static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result) | ||
106 | { | ||
107 | u32 recvd, count; | ||
108 | size_t hdrlen; | ||
109 | __be32 *p; | ||
110 | |||
111 | p = xdr_inline_decode(xdr, 4); | ||
112 | if (unlikely(p == NULL)) | ||
113 | goto out_overflow; | ||
114 | count = be32_to_cpup(p); | ||
115 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | ||
116 | recvd = xdr->buf->len - hdrlen; | ||
117 | if (unlikely(count > recvd)) | ||
118 | goto out_cheating; | ||
119 | out: | ||
120 | xdr_read_pages(xdr, count); | ||
121 | result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */ | ||
122 | result->count = count; | ||
123 | return count; | ||
124 | out_cheating: | ||
125 | dprintk("NFS: server cheating in read result: " | ||
126 | "count %u > recvd %u\n", count, recvd); | ||
127 | count = recvd; | ||
128 | goto out; | ||
129 | out_overflow: | ||
130 | print_overflow_msg(__func__, xdr); | ||
131 | return -EIO; | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * enum stat { | ||
136 | * NFS_OK = 0, | ||
137 | * NFSERR_PERM = 1, | ||
138 | * NFSERR_NOENT = 2, | ||
139 | * NFSERR_IO = 5, | ||
140 | * NFSERR_NXIO = 6, | ||
141 | * NFSERR_ACCES = 13, | ||
142 | * NFSERR_EXIST = 17, | ||
143 | * NFSERR_NODEV = 19, | ||
144 | * NFSERR_NOTDIR = 20, | ||
145 | * NFSERR_ISDIR = 21, | ||
146 | * NFSERR_FBIG = 27, | ||
147 | * NFSERR_NOSPC = 28, | ||
148 | * NFSERR_ROFS = 30, | ||
149 | * NFSERR_NAMETOOLONG = 63, | ||
150 | * NFSERR_NOTEMPTY = 66, | ||
151 | * NFSERR_DQUOT = 69, | ||
152 | * NFSERR_STALE = 70, | ||
153 | * NFSERR_WFLUSH = 99 | ||
154 | * }; | ||
155 | */ | ||
156 | static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status) | ||
157 | { | ||
158 | __be32 *p; | ||
159 | |||
160 | p = xdr_inline_decode(xdr, 4); | ||
161 | if (unlikely(p == NULL)) | ||
162 | goto out_overflow; | ||
163 | *status = be32_to_cpup(p); | ||
164 | return 0; | ||
165 | out_overflow: | ||
166 | print_overflow_msg(__func__, xdr); | ||
167 | return -EIO; | ||
81 | } | 168 | } |
82 | 169 | ||
83 | static inline __be32* | 170 | /* |
84 | xdr_encode_time(__be32 *p, struct timespec *timep) | 171 | * 2.3.2. ftype |
172 | * | ||
173 | * enum ftype { | ||
174 | * NFNON = 0, | ||
175 | * NFREG = 1, | ||
176 | * NFDIR = 2, | ||
177 | * NFBLK = 3, | ||
178 | * NFCHR = 4, | ||
179 | * NFLNK = 5 | ||
180 | * }; | ||
181 | * | ||
182 | */ | ||
183 | static __be32 *xdr_decode_ftype(__be32 *p, u32 *type) | ||
85 | { | 184 | { |
86 | *p++ = htonl(timep->tv_sec); | 185 | *type = be32_to_cpup(p++); |
87 | /* Convert nanoseconds into microseconds */ | 186 | if (unlikely(*type > NF2FIFO)) |
88 | *p++ = htonl(timep->tv_nsec ? timep->tv_nsec / 1000 : 0); | 187 | *type = NFBAD; |
89 | return p; | 188 | return p; |
90 | } | 189 | } |
91 | 190 | ||
92 | static inline __be32* | 191 | /* |
93 | xdr_encode_current_server_time(__be32 *p, struct timespec *timep) | 192 | * 2.3.3. fhandle |
193 | * | ||
194 | * typedef opaque fhandle[FHSIZE]; | ||
195 | */ | ||
196 | static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh) | ||
94 | { | 197 | { |
95 | /* | 198 | __be32 *p; |
96 | * Passing the invalid value useconds=1000000 is a | 199 | |
97 | * Sun convention for "set to current server time". | 200 | BUG_ON(fh->size != NFS2_FHSIZE); |
98 | * It's needed to make permissions checks for the | 201 | p = xdr_reserve_space(xdr, NFS2_FHSIZE); |
99 | * "touch" program across v2 mounts to Solaris and | 202 | memcpy(p, fh->data, NFS2_FHSIZE); |
100 | * Irix boxes work correctly. See description of | 203 | } |
101 | * sattr in section 6.1 of "NFS Illustrated" by | 204 | |
102 | * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5 | 205 | static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh) |
103 | */ | 206 | { |
104 | *p++ = htonl(timep->tv_sec); | 207 | __be32 *p; |
105 | *p++ = htonl(1000000); | 208 | |
209 | p = xdr_inline_decode(xdr, NFS2_FHSIZE); | ||
210 | if (unlikely(p == NULL)) | ||
211 | goto out_overflow; | ||
212 | fh->size = NFS2_FHSIZE; | ||
213 | memcpy(fh->data, p, NFS2_FHSIZE); | ||
214 | return 0; | ||
215 | out_overflow: | ||
216 | print_overflow_msg(__func__, xdr); | ||
217 | return -EIO; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * 2.3.4. timeval | ||
222 | * | ||
223 | * struct timeval { | ||
224 | * unsigned int seconds; | ||
225 | * unsigned int useconds; | ||
226 | * }; | ||
227 | */ | ||
228 | static __be32 *xdr_encode_time(__be32 *p, const struct timespec *timep) | ||
229 | { | ||
230 | *p++ = cpu_to_be32(timep->tv_sec); | ||
231 | if (timep->tv_nsec != 0) | ||
232 | *p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC); | ||
233 | else | ||
234 | *p++ = cpu_to_be32(0); | ||
106 | return p; | 235 | return p; |
107 | } | 236 | } |
108 | 237 | ||
109 | static inline __be32* | 238 | /* |
110 | xdr_decode_time(__be32 *p, struct timespec *timep) | 239 | * Passing the invalid value useconds=1000000 is a Sun convention for |
240 | * "set to current server time". It's needed to make permissions checks | ||
241 | * for the "touch" program across v2 mounts to Solaris and Irix servers | ||
242 | * work correctly. See description of sattr in section 6.1 of "NFS | ||
243 | * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5. | ||
244 | */ | ||
245 | static __be32 *xdr_encode_current_server_time(__be32 *p, | ||
246 | const struct timespec *timep) | ||
111 | { | 247 | { |
112 | timep->tv_sec = ntohl(*p++); | 248 | *p++ = cpu_to_be32(timep->tv_sec); |
113 | /* Convert microseconds into nanoseconds */ | 249 | *p++ = cpu_to_be32(1000000); |
114 | timep->tv_nsec = ntohl(*p++) * 1000; | ||
115 | return p; | 250 | return p; |
116 | } | 251 | } |
117 | 252 | ||
118 | static __be32 * | 253 | static __be32 *xdr_decode_time(__be32 *p, struct timespec *timep) |
119 | xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr) | 254 | { |
255 | timep->tv_sec = be32_to_cpup(p++); | ||
256 | timep->tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC; | ||
257 | return p; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * 2.3.5. fattr | ||
262 | * | ||
263 | * struct fattr { | ||
264 | * ftype type; | ||
265 | * unsigned int mode; | ||
266 | * unsigned int nlink; | ||
267 | * unsigned int uid; | ||
268 | * unsigned int gid; | ||
269 | * unsigned int size; | ||
270 | * unsigned int blocksize; | ||
271 | * unsigned int rdev; | ||
272 | * unsigned int blocks; | ||
273 | * unsigned int fsid; | ||
274 | * unsigned int fileid; | ||
275 | * timeval atime; | ||
276 | * timeval mtime; | ||
277 | * timeval ctime; | ||
278 | * }; | ||
279 | * | ||
280 | */ | ||
281 | static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) | ||
120 | { | 282 | { |
121 | u32 rdev, type; | 283 | u32 rdev, type; |
122 | type = ntohl(*p++); | 284 | __be32 *p; |
123 | fattr->mode = ntohl(*p++); | 285 | |
124 | fattr->nlink = ntohl(*p++); | 286 | p = xdr_inline_decode(xdr, NFS_fattr_sz << 2); |
125 | fattr->uid = ntohl(*p++); | 287 | if (unlikely(p == NULL)) |
126 | fattr->gid = ntohl(*p++); | 288 | goto out_overflow; |
127 | fattr->size = ntohl(*p++); | 289 | |
128 | fattr->du.nfs2.blocksize = ntohl(*p++); | ||
129 | rdev = ntohl(*p++); | ||
130 | fattr->du.nfs2.blocks = ntohl(*p++); | ||
131 | fattr->fsid.major = ntohl(*p++); | ||
132 | fattr->fsid.minor = 0; | ||
133 | fattr->fileid = ntohl(*p++); | ||
134 | p = xdr_decode_time(p, &fattr->atime); | ||
135 | p = xdr_decode_time(p, &fattr->mtime); | ||
136 | p = xdr_decode_time(p, &fattr->ctime); | ||
137 | fattr->valid |= NFS_ATTR_FATTR_V2; | 290 | fattr->valid |= NFS_ATTR_FATTR_V2; |
291 | |||
292 | p = xdr_decode_ftype(p, &type); | ||
293 | |||
294 | fattr->mode = be32_to_cpup(p++); | ||
295 | fattr->nlink = be32_to_cpup(p++); | ||
296 | fattr->uid = be32_to_cpup(p++); | ||
297 | fattr->gid = be32_to_cpup(p++); | ||
298 | fattr->size = be32_to_cpup(p++); | ||
299 | fattr->du.nfs2.blocksize = be32_to_cpup(p++); | ||
300 | |||
301 | rdev = be32_to_cpup(p++); | ||
138 | fattr->rdev = new_decode_dev(rdev); | 302 | fattr->rdev = new_decode_dev(rdev); |
139 | if (type == NFCHR && rdev == NFS2_FIFO_DEV) { | 303 | if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) { |
140 | fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO; | 304 | fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO; |
141 | fattr->rdev = 0; | 305 | fattr->rdev = 0; |
142 | } | 306 | } |
307 | |||
308 | fattr->du.nfs2.blocks = be32_to_cpup(p++); | ||
309 | fattr->fsid.major = be32_to_cpup(p++); | ||
310 | fattr->fsid.minor = 0; | ||
311 | fattr->fileid = be32_to_cpup(p++); | ||
312 | |||
313 | p = xdr_decode_time(p, &fattr->atime); | ||
314 | p = xdr_decode_time(p, &fattr->mtime); | ||
315 | xdr_decode_time(p, &fattr->ctime); | ||
316 | return 0; | ||
317 | out_overflow: | ||
318 | print_overflow_msg(__func__, xdr); | ||
319 | return -EIO; | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * 2.3.6. sattr | ||
324 | * | ||
325 | * struct sattr { | ||
326 | * unsigned int mode; | ||
327 | * unsigned int uid; | ||
328 | * unsigned int gid; | ||
329 | * unsigned int size; | ||
330 | * timeval atime; | ||
331 | * timeval mtime; | ||
332 | * }; | ||
333 | */ | ||
334 | |||
335 | #define NFS2_SATTR_NOT_SET (0xffffffff) | ||
336 | |||
337 | static __be32 *xdr_time_not_set(__be32 *p) | ||
338 | { | ||
339 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | ||
340 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | ||
143 | return p; | 341 | return p; |
144 | } | 342 | } |
145 | 343 | ||
146 | static inline __be32 * | 344 | static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr) |
147 | xdr_encode_sattr(__be32 *p, struct iattr *attr) | ||
148 | { | 345 | { |
149 | const __be32 not_set = __constant_htonl(0xFFFFFFFF); | 346 | __be32 *p; |
150 | 347 | ||
151 | *p++ = (attr->ia_valid & ATTR_MODE) ? htonl(attr->ia_mode) : not_set; | 348 | p = xdr_reserve_space(xdr, NFS_sattr_sz << 2); |
152 | *p++ = (attr->ia_valid & ATTR_UID) ? htonl(attr->ia_uid) : not_set; | ||
153 | *p++ = (attr->ia_valid & ATTR_GID) ? htonl(attr->ia_gid) : not_set; | ||
154 | *p++ = (attr->ia_valid & ATTR_SIZE) ? htonl(attr->ia_size) : not_set; | ||
155 | 349 | ||
156 | if (attr->ia_valid & ATTR_ATIME_SET) { | 350 | if (attr->ia_valid & ATTR_MODE) |
351 | *p++ = cpu_to_be32(attr->ia_mode); | ||
352 | else | ||
353 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | ||
354 | if (attr->ia_valid & ATTR_UID) | ||
355 | *p++ = cpu_to_be32(attr->ia_uid); | ||
356 | else | ||
357 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | ||
358 | if (attr->ia_valid & ATTR_GID) | ||
359 | *p++ = cpu_to_be32(attr->ia_gid); | ||
360 | else | ||
361 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | ||
362 | if (attr->ia_valid & ATTR_SIZE) | ||
363 | *p++ = cpu_to_be32((u32)attr->ia_size); | ||
364 | else | ||
365 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | ||
366 | |||
367 | if (attr->ia_valid & ATTR_ATIME_SET) | ||
157 | p = xdr_encode_time(p, &attr->ia_atime); | 368 | p = xdr_encode_time(p, &attr->ia_atime); |
158 | } else if (attr->ia_valid & ATTR_ATIME) { | 369 | else if (attr->ia_valid & ATTR_ATIME) |
159 | p = xdr_encode_current_server_time(p, &attr->ia_atime); | 370 | p = xdr_encode_current_server_time(p, &attr->ia_atime); |
160 | } else { | 371 | else |
161 | *p++ = not_set; | 372 | p = xdr_time_not_set(p); |
162 | *p++ = not_set; | 373 | if (attr->ia_valid & ATTR_MTIME_SET) |
163 | } | 374 | xdr_encode_time(p, &attr->ia_mtime); |
164 | 375 | else if (attr->ia_valid & ATTR_MTIME) | |
165 | if (attr->ia_valid & ATTR_MTIME_SET) { | 376 | xdr_encode_current_server_time(p, &attr->ia_mtime); |
166 | p = xdr_encode_time(p, &attr->ia_mtime); | 377 | else |
167 | } else if (attr->ia_valid & ATTR_MTIME) { | 378 | xdr_time_not_set(p); |
168 | p = xdr_encode_current_server_time(p, &attr->ia_mtime); | ||
169 | } else { | ||
170 | *p++ = not_set; | ||
171 | *p++ = not_set; | ||
172 | } | ||
173 | return p; | ||
174 | } | 379 | } |
175 | 380 | ||
176 | /* | 381 | /* |
177 | * NFS encode functions | 382 | * 2.3.7. filename |
383 | * | ||
384 | * typedef string filename<MAXNAMLEN>; | ||
178 | */ | 385 | */ |
386 | static void encode_filename(struct xdr_stream *xdr, | ||
387 | const char *name, u32 length) | ||
388 | { | ||
389 | __be32 *p; | ||
390 | |||
391 | BUG_ON(length > NFS2_MAXNAMLEN); | ||
392 | p = xdr_reserve_space(xdr, 4 + length); | ||
393 | xdr_encode_opaque(p, name, length); | ||
394 | } | ||
395 | |||
396 | static int decode_filename_inline(struct xdr_stream *xdr, | ||
397 | const char **name, u32 *length) | ||
398 | { | ||
399 | __be32 *p; | ||
400 | u32 count; | ||
401 | |||
402 | p = xdr_inline_decode(xdr, 4); | ||
403 | if (unlikely(p == NULL)) | ||
404 | goto out_overflow; | ||
405 | count = be32_to_cpup(p); | ||
406 | if (count > NFS3_MAXNAMLEN) | ||
407 | goto out_nametoolong; | ||
408 | p = xdr_inline_decode(xdr, count); | ||
409 | if (unlikely(p == NULL)) | ||
410 | goto out_overflow; | ||
411 | *name = (const char *)p; | ||
412 | *length = count; | ||
413 | return 0; | ||
414 | out_nametoolong: | ||
415 | dprintk("NFS: returned filename too long: %u\n", count); | ||
416 | return -ENAMETOOLONG; | ||
417 | out_overflow: | ||
418 | print_overflow_msg(__func__, xdr); | ||
419 | return -EIO; | ||
420 | } | ||
421 | |||
179 | /* | 422 | /* |
180 | * Encode file handle argument | 423 | * 2.3.8. path |
181 | * GETATTR, READLINK, STATFS | 424 | * |
425 | * typedef string path<MAXPATHLEN>; | ||
182 | */ | 426 | */ |
183 | static int | 427 | static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length) |
184 | nfs_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh) | ||
185 | { | 428 | { |
186 | p = xdr_encode_fhandle(p, fh); | 429 | __be32 *p; |
187 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 430 | |
431 | BUG_ON(length > NFS2_MAXPATHLEN); | ||
432 | p = xdr_reserve_space(xdr, 4); | ||
433 | *p = cpu_to_be32(length); | ||
434 | xdr_write_pages(xdr, pages, 0, length); | ||
435 | } | ||
436 | |||
437 | static int decode_path(struct xdr_stream *xdr) | ||
438 | { | ||
439 | u32 length, recvd; | ||
440 | size_t hdrlen; | ||
441 | __be32 *p; | ||
442 | |||
443 | p = xdr_inline_decode(xdr, 4); | ||
444 | if (unlikely(p == NULL)) | ||
445 | goto out_overflow; | ||
446 | length = be32_to_cpup(p); | ||
447 | if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN)) | ||
448 | goto out_size; | ||
449 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | ||
450 | recvd = xdr->buf->len - hdrlen; | ||
451 | if (unlikely(length > recvd)) | ||
452 | goto out_cheating; | ||
453 | |||
454 | xdr_read_pages(xdr, length); | ||
455 | xdr_terminate_string(xdr->buf, length); | ||
188 | return 0; | 456 | return 0; |
457 | out_size: | ||
458 | dprintk("NFS: returned pathname too long: %u\n", length); | ||
459 | return -ENAMETOOLONG; | ||
460 | out_cheating: | ||
461 | dprintk("NFS: server cheating in pathname result: " | ||
462 | "length %u > received %u\n", length, recvd); | ||
463 | return -EIO; | ||
464 | out_overflow: | ||
465 | print_overflow_msg(__func__, xdr); | ||
466 | return -EIO; | ||
189 | } | 467 | } |
190 | 468 | ||
191 | /* | 469 | /* |
192 | * Encode SETATTR arguments | 470 | * 2.3.9. attrstat |
471 | * | ||
472 | * union attrstat switch (stat status) { | ||
473 | * case NFS_OK: | ||
474 | * fattr attributes; | ||
475 | * default: | ||
476 | * void; | ||
477 | * }; | ||
193 | */ | 478 | */ |
194 | static int | 479 | static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result) |
195 | nfs_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs_sattrargs *args) | ||
196 | { | 480 | { |
197 | p = xdr_encode_fhandle(p, args->fh); | 481 | enum nfs_stat status; |
198 | p = xdr_encode_sattr(p, args->sattr); | 482 | int error; |
199 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 483 | |
200 | return 0; | 484 | error = decode_stat(xdr, &status); |
485 | if (unlikely(error)) | ||
486 | goto out; | ||
487 | if (status != NFS_OK) | ||
488 | goto out_default; | ||
489 | error = decode_fattr(xdr, result); | ||
490 | out: | ||
491 | return error; | ||
492 | out_default: | ||
493 | return nfs_stat_to_errno(status); | ||
201 | } | 494 | } |
202 | 495 | ||
203 | /* | 496 | /* |
204 | * Encode directory ops argument | 497 | * 2.3.10. diropargs |
205 | * LOOKUP, RMDIR | 498 | * |
499 | * struct diropargs { | ||
500 | * fhandle dir; | ||
501 | * filename name; | ||
502 | * }; | ||
206 | */ | 503 | */ |
207 | static int | 504 | static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh, |
208 | nfs_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs_diropargs *args) | 505 | const char *name, u32 length) |
209 | { | 506 | { |
210 | p = xdr_encode_fhandle(p, args->fh); | 507 | encode_fhandle(xdr, fh); |
211 | p = xdr_encode_array(p, args->name, args->len); | 508 | encode_filename(xdr, name, length); |
212 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
213 | return 0; | ||
214 | } | 509 | } |
215 | 510 | ||
216 | /* | 511 | /* |
217 | * Encode REMOVE argument | 512 | * 2.3.11. diropres |
513 | * | ||
514 | * union diropres switch (stat status) { | ||
515 | * case NFS_OK: | ||
516 | * struct { | ||
517 | * fhandle file; | ||
518 | * fattr attributes; | ||
519 | * } diropok; | ||
520 | * default: | ||
521 | * void; | ||
522 | * }; | ||
218 | */ | 523 | */ |
219 | static int | 524 | static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result) |
220 | nfs_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args) | ||
221 | { | 525 | { |
222 | p = xdr_encode_fhandle(p, args->fh); | 526 | int error; |
223 | p = xdr_encode_array(p, args->name.name, args->name.len); | 527 | |
224 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 528 | error = decode_fhandle(xdr, result->fh); |
225 | return 0; | 529 | if (unlikely(error)) |
530 | goto out; | ||
531 | error = decode_fattr(xdr, result->fattr); | ||
532 | out: | ||
533 | return error; | ||
534 | } | ||
535 | |||
536 | static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result) | ||
537 | { | ||
538 | enum nfs_stat status; | ||
539 | int error; | ||
540 | |||
541 | error = decode_stat(xdr, &status); | ||
542 | if (unlikely(error)) | ||
543 | goto out; | ||
544 | if (status != NFS_OK) | ||
545 | goto out_default; | ||
546 | error = decode_diropok(xdr, result); | ||
547 | out: | ||
548 | return error; | ||
549 | out_default: | ||
550 | return nfs_stat_to_errno(status); | ||
226 | } | 551 | } |
227 | 552 | ||
553 | |||
228 | /* | 554 | /* |
229 | * Arguments to a READ call. Since we read data directly into the page | 555 | * NFSv2 XDR encode functions |
230 | * cache, we also set up the reply iovec here so that iov[1] points | 556 | * |
231 | * exactly to the page we want to fetch. | 557 | * NFSv2 argument types are defined in section 2.2 of RFC 1094: |
558 | * "NFS: Network File System Protocol Specification". | ||
232 | */ | 559 | */ |
233 | static int | 560 | |
234 | nfs_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args) | 561 | static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req, |
562 | struct xdr_stream *xdr, | ||
563 | const struct nfs_fh *fh) | ||
235 | { | 564 | { |
236 | struct rpc_auth *auth = req->rq_cred->cr_auth; | 565 | encode_fhandle(xdr, fh); |
237 | unsigned int replen; | 566 | } |
238 | u32 offset = (u32)args->offset; | 567 | |
568 | /* | ||
569 | * 2.2.3. sattrargs | ||
570 | * | ||
571 | * struct sattrargs { | ||
572 | * fhandle file; | ||
573 | * sattr attributes; | ||
574 | * }; | ||
575 | */ | ||
576 | static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req, | ||
577 | struct xdr_stream *xdr, | ||
578 | const struct nfs_sattrargs *args) | ||
579 | { | ||
580 | encode_fhandle(xdr, args->fh); | ||
581 | encode_sattr(xdr, args->sattr); | ||
582 | } | ||
583 | |||
584 | static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req, | ||
585 | struct xdr_stream *xdr, | ||
586 | const struct nfs_diropargs *args) | ||
587 | { | ||
588 | encode_diropargs(xdr, args->fh, args->name, args->len); | ||
589 | } | ||
590 | |||
591 | static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req, | ||
592 | struct xdr_stream *xdr, | ||
593 | const struct nfs_readlinkargs *args) | ||
594 | { | ||
595 | encode_fhandle(xdr, args->fh); | ||
596 | prepare_reply_buffer(req, args->pages, args->pgbase, | ||
597 | args->pglen, NFS_readlinkres_sz); | ||
598 | } | ||
599 | |||
600 | /* | ||
601 | * 2.2.7. readargs | ||
602 | * | ||
603 | * struct readargs { | ||
604 | * fhandle file; | ||
605 | * unsigned offset; | ||
606 | * unsigned count; | ||
607 | * unsigned totalcount; | ||
608 | * }; | ||
609 | */ | ||
610 | static void encode_readargs(struct xdr_stream *xdr, | ||
611 | const struct nfs_readargs *args) | ||
612 | { | ||
613 | u32 offset = args->offset; | ||
239 | u32 count = args->count; | 614 | u32 count = args->count; |
615 | __be32 *p; | ||
240 | 616 | ||
241 | p = xdr_encode_fhandle(p, args->fh); | 617 | encode_fhandle(xdr, args->fh); |
242 | *p++ = htonl(offset); | ||
243 | *p++ = htonl(count); | ||
244 | *p++ = htonl(count); | ||
245 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
246 | 618 | ||
247 | /* Inline the page array */ | 619 | p = xdr_reserve_space(xdr, 4 + 4 + 4); |
248 | replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2; | 620 | *p++ = cpu_to_be32(offset); |
249 | xdr_inline_pages(&req->rq_rcv_buf, replen, | 621 | *p++ = cpu_to_be32(count); |
250 | args->pages, args->pgbase, count); | 622 | *p = cpu_to_be32(count); |
623 | } | ||
624 | |||
625 | static void nfs2_xdr_enc_readargs(struct rpc_rqst *req, | ||
626 | struct xdr_stream *xdr, | ||
627 | const struct nfs_readargs *args) | ||
628 | { | ||
629 | encode_readargs(xdr, args); | ||
630 | prepare_reply_buffer(req, args->pages, args->pgbase, | ||
631 | args->count, NFS_readres_sz); | ||
251 | req->rq_rcv_buf.flags |= XDRBUF_READ; | 632 | req->rq_rcv_buf.flags |= XDRBUF_READ; |
252 | return 0; | ||
253 | } | 633 | } |
254 | 634 | ||
255 | /* | 635 | /* |
256 | * Decode READ reply | 636 | * 2.2.9. writeargs |
637 | * | ||
638 | * struct writeargs { | ||
639 | * fhandle file; | ||
640 | * unsigned beginoffset; | ||
641 | * unsigned offset; | ||
642 | * unsigned totalcount; | ||
643 | * nfsdata data; | ||
644 | * }; | ||
257 | */ | 645 | */ |
258 | static int | 646 | static void encode_writeargs(struct xdr_stream *xdr, |
259 | nfs_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res) | 647 | const struct nfs_writeargs *args) |
260 | { | 648 | { |
261 | struct kvec *iov = req->rq_rcv_buf.head; | 649 | u32 offset = args->offset; |
262 | size_t hdrlen; | 650 | u32 count = args->count; |
263 | u32 count, recvd; | 651 | __be32 *p; |
264 | int status; | ||
265 | |||
266 | if ((status = ntohl(*p++))) | ||
267 | return nfs_stat_to_errno(status); | ||
268 | p = xdr_decode_fattr(p, res->fattr); | ||
269 | |||
270 | count = ntohl(*p++); | ||
271 | res->eof = 0; | ||
272 | hdrlen = (u8 *) p - (u8 *) iov->iov_base; | ||
273 | if (iov->iov_len < hdrlen) { | ||
274 | dprintk("NFS: READ reply header overflowed:" | ||
275 | "length %Zu > %Zu\n", hdrlen, iov->iov_len); | ||
276 | return -errno_NFSERR_IO; | ||
277 | } else if (iov->iov_len != hdrlen) { | ||
278 | dprintk("NFS: READ header is short. iovec will be shifted.\n"); | ||
279 | xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen); | ||
280 | } | ||
281 | 652 | ||
282 | recvd = req->rq_rcv_buf.len - hdrlen; | 653 | encode_fhandle(xdr, args->fh); |
283 | if (count > recvd) { | ||
284 | dprintk("NFS: server cheating in read reply: " | ||
285 | "count %u > recvd %u\n", count, recvd); | ||
286 | count = recvd; | ||
287 | } | ||
288 | 654 | ||
289 | dprintk("RPC: readres OK count %u\n", count); | 655 | p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4); |
290 | if (count < res->count) | 656 | *p++ = cpu_to_be32(offset); |
291 | res->count = count; | 657 | *p++ = cpu_to_be32(offset); |
658 | *p++ = cpu_to_be32(count); | ||
292 | 659 | ||
293 | return count; | 660 | /* nfsdata */ |
661 | *p = cpu_to_be32(count); | ||
662 | xdr_write_pages(xdr, args->pages, args->pgbase, count); | ||
294 | } | 663 | } |
295 | 664 | ||
665 | static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req, | ||
666 | struct xdr_stream *xdr, | ||
667 | const struct nfs_writeargs *args) | ||
668 | { | ||
669 | encode_writeargs(xdr, args); | ||
670 | xdr->buf->flags |= XDRBUF_WRITE; | ||
671 | } | ||
296 | 672 | ||
297 | /* | 673 | /* |
298 | * Write arguments. Splice the buffer to be written into the iovec. | 674 | * 2.2.10. createargs |
675 | * | ||
676 | * struct createargs { | ||
677 | * diropargs where; | ||
678 | * sattr attributes; | ||
679 | * }; | ||
299 | */ | 680 | */ |
300 | static int | 681 | static void nfs2_xdr_enc_createargs(struct rpc_rqst *req, |
301 | nfs_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args) | 682 | struct xdr_stream *xdr, |
683 | const struct nfs_createargs *args) | ||
302 | { | 684 | { |
303 | struct xdr_buf *sndbuf = &req->rq_snd_buf; | 685 | encode_diropargs(xdr, args->fh, args->name, args->len); |
304 | u32 offset = (u32)args->offset; | 686 | encode_sattr(xdr, args->sattr); |
305 | u32 count = args->count; | 687 | } |
306 | |||
307 | p = xdr_encode_fhandle(p, args->fh); | ||
308 | *p++ = htonl(offset); | ||
309 | *p++ = htonl(offset); | ||
310 | *p++ = htonl(count); | ||
311 | *p++ = htonl(count); | ||
312 | sndbuf->len = xdr_adjust_iovec(sndbuf->head, p); | ||
313 | 688 | ||
314 | /* Copy the page array */ | 689 | static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req, |
315 | xdr_encode_pages(sndbuf, args->pages, args->pgbase, count); | 690 | struct xdr_stream *xdr, |
316 | sndbuf->flags |= XDRBUF_WRITE; | 691 | const struct nfs_removeargs *args) |
317 | return 0; | 692 | { |
693 | encode_diropargs(xdr, args->fh, args->name.name, args->name.len); | ||
318 | } | 694 | } |
319 | 695 | ||
320 | /* | 696 | /* |
321 | * Encode create arguments | 697 | * 2.2.12. renameargs |
322 | * CREATE, MKDIR | 698 | * |
699 | * struct renameargs { | ||
700 | * diropargs from; | ||
701 | * diropargs to; | ||
702 | * }; | ||
323 | */ | 703 | */ |
324 | static int | 704 | static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req, |
325 | nfs_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs_createargs *args) | 705 | struct xdr_stream *xdr, |
706 | const struct nfs_renameargs *args) | ||
326 | { | 707 | { |
327 | p = xdr_encode_fhandle(p, args->fh); | 708 | const struct qstr *old = args->old_name; |
328 | p = xdr_encode_array(p, args->name, args->len); | 709 | const struct qstr *new = args->new_name; |
329 | p = xdr_encode_sattr(p, args->sattr); | 710 | |
330 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 711 | encode_diropargs(xdr, args->old_dir, old->name, old->len); |
331 | return 0; | 712 | encode_diropargs(xdr, args->new_dir, new->name, new->len); |
332 | } | 713 | } |
333 | 714 | ||
334 | /* | 715 | /* |
335 | * Encode RENAME arguments | 716 | * 2.2.13. linkargs |
717 | * | ||
718 | * struct linkargs { | ||
719 | * fhandle from; | ||
720 | * diropargs to; | ||
721 | * }; | ||
336 | */ | 722 | */ |
337 | static int | 723 | static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req, |
338 | nfs_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args) | 724 | struct xdr_stream *xdr, |
725 | const struct nfs_linkargs *args) | ||
339 | { | 726 | { |
340 | p = xdr_encode_fhandle(p, args->old_dir); | 727 | encode_fhandle(xdr, args->fromfh); |
341 | p = xdr_encode_array(p, args->old_name->name, args->old_name->len); | 728 | encode_diropargs(xdr, args->tofh, args->toname, args->tolen); |
342 | p = xdr_encode_fhandle(p, args->new_dir); | ||
343 | p = xdr_encode_array(p, args->new_name->name, args->new_name->len); | ||
344 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
345 | return 0; | ||
346 | } | 729 | } |
347 | 730 | ||
348 | /* | 731 | /* |
349 | * Encode LINK arguments | 732 | * 2.2.14. symlinkargs |
733 | * | ||
734 | * struct symlinkargs { | ||
735 | * diropargs from; | ||
736 | * path to; | ||
737 | * sattr attributes; | ||
738 | * }; | ||
350 | */ | 739 | */ |
351 | static int | 740 | static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req, |
352 | nfs_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs_linkargs *args) | 741 | struct xdr_stream *xdr, |
742 | const struct nfs_symlinkargs *args) | ||
353 | { | 743 | { |
354 | p = xdr_encode_fhandle(p, args->fromfh); | 744 | encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen); |
355 | p = xdr_encode_fhandle(p, args->tofh); | 745 | encode_path(xdr, args->pages, args->pathlen); |
356 | p = xdr_encode_array(p, args->toname, args->tolen); | 746 | encode_sattr(xdr, args->sattr); |
357 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
358 | return 0; | ||
359 | } | 747 | } |
360 | 748 | ||
361 | /* | 749 | /* |
362 | * Encode SYMLINK arguments | 750 | * 2.2.17. readdirargs |
751 | * | ||
752 | * struct readdirargs { | ||
753 | * fhandle dir; | ||
754 | * nfscookie cookie; | ||
755 | * unsigned count; | ||
756 | * }; | ||
363 | */ | 757 | */ |
364 | static int | 758 | static void encode_readdirargs(struct xdr_stream *xdr, |
365 | nfs_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_symlinkargs *args) | 759 | const struct nfs_readdirargs *args) |
366 | { | 760 | { |
367 | struct xdr_buf *sndbuf = &req->rq_snd_buf; | 761 | __be32 *p; |
368 | size_t pad; | ||
369 | 762 | ||
370 | p = xdr_encode_fhandle(p, args->fromfh); | 763 | encode_fhandle(xdr, args->fh); |
371 | p = xdr_encode_array(p, args->fromname, args->fromlen); | ||
372 | *p++ = htonl(args->pathlen); | ||
373 | sndbuf->len = xdr_adjust_iovec(sndbuf->head, p); | ||
374 | 764 | ||
375 | xdr_encode_pages(sndbuf, args->pages, 0, args->pathlen); | 765 | p = xdr_reserve_space(xdr, 4 + 4); |
766 | *p++ = cpu_to_be32(args->cookie); | ||
767 | *p = cpu_to_be32(args->count); | ||
768 | } | ||
376 | 769 | ||
377 | /* | 770 | static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req, |
378 | * xdr_encode_pages may have added a few bytes to ensure the | 771 | struct xdr_stream *xdr, |
379 | * pathname ends on a 4-byte boundary. Start encoding the | 772 | const struct nfs_readdirargs *args) |
380 | * attributes after the pad bytes. | 773 | { |
381 | */ | 774 | encode_readdirargs(xdr, args); |
382 | pad = sndbuf->tail->iov_len; | 775 | prepare_reply_buffer(req, args->pages, 0, |
383 | if (pad > 0) | 776 | args->count, NFS_readdirres_sz); |
384 | p++; | ||
385 | p = xdr_encode_sattr(p, args->sattr); | ||
386 | sndbuf->len += xdr_adjust_iovec(sndbuf->tail, p) - pad; | ||
387 | return 0; | ||
388 | } | 777 | } |
389 | 778 | ||
390 | /* | 779 | /* |
391 | * Encode arguments to readdir call | 780 | * NFSv2 XDR decode functions |
781 | * | ||
782 | * NFSv2 result types are defined in section 2.2 of RFC 1094: | ||
783 | * "NFS: Network File System Protocol Specification". | ||
392 | */ | 784 | */ |
393 | static int | 785 | |
394 | nfs_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs_readdirargs *args) | 786 | static int nfs2_xdr_dec_stat(struct rpc_rqst *req, struct xdr_stream *xdr, |
787 | void *__unused) | ||
395 | { | 788 | { |
396 | struct rpc_auth *auth = req->rq_cred->cr_auth; | 789 | enum nfs_stat status; |
397 | unsigned int replen; | 790 | int error; |
398 | u32 count = args->count; | 791 | |
792 | error = decode_stat(xdr, &status); | ||
793 | if (unlikely(error)) | ||
794 | goto out; | ||
795 | if (status != NFS_OK) | ||
796 | goto out_default; | ||
797 | out: | ||
798 | return error; | ||
799 | out_default: | ||
800 | return nfs_stat_to_errno(status); | ||
801 | } | ||
399 | 802 | ||
400 | p = xdr_encode_fhandle(p, args->fh); | 803 | static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr, |
401 | *p++ = htonl(args->cookie); | 804 | struct nfs_fattr *result) |
402 | *p++ = htonl(count); /* see above */ | 805 | { |
403 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 806 | return decode_attrstat(xdr, result); |
807 | } | ||
404 | 808 | ||
405 | /* Inline the page array */ | 809 | static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr, |
406 | replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2; | 810 | struct nfs_diropok *result) |
407 | xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count); | 811 | { |
408 | return 0; | 812 | return decode_diropres(xdr, result); |
409 | } | 813 | } |
410 | 814 | ||
411 | /* | 815 | /* |
412 | * Decode the result of a readdir call. | 816 | * 2.2.6. readlinkres |
413 | * We're not really decoding anymore, we just leave the buffer untouched | 817 | * |
414 | * and only check that it is syntactically correct. | 818 | * union readlinkres switch (stat status) { |
415 | * The real decoding happens in nfs_decode_entry below, called directly | 819 | * case NFS_OK: |
416 | * from nfs_readdir for each entry. | 820 | * path data; |
821 | * default: | ||
822 | * void; | ||
823 | * }; | ||
417 | */ | 824 | */ |
418 | static int | 825 | static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req, |
419 | nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy) | 826 | struct xdr_stream *xdr, void *__unused) |
420 | { | 827 | { |
421 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | 828 | enum nfs_stat status; |
422 | struct kvec *iov = rcvbuf->head; | 829 | int error; |
423 | struct page **page; | 830 | |
424 | size_t hdrlen; | 831 | error = decode_stat(xdr, &status); |
425 | unsigned int pglen, recvd; | 832 | if (unlikely(error)) |
426 | int status; | 833 | goto out; |
427 | 834 | if (status != NFS_OK) | |
428 | if ((status = ntohl(*p++))) | 835 | goto out_default; |
429 | return nfs_stat_to_errno(status); | 836 | error = decode_path(xdr); |
430 | 837 | out: | |
431 | hdrlen = (u8 *) p - (u8 *) iov->iov_base; | 838 | return error; |
432 | if (iov->iov_len < hdrlen) { | 839 | out_default: |
433 | dprintk("NFS: READDIR reply header overflowed:" | 840 | return nfs_stat_to_errno(status); |
434 | "length %Zu > %Zu\n", hdrlen, iov->iov_len); | 841 | } |
435 | return -errno_NFSERR_IO; | ||
436 | } else if (iov->iov_len != hdrlen) { | ||
437 | dprintk("NFS: READDIR header is short. iovec will be shifted.\n"); | ||
438 | xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen); | ||
439 | } | ||
440 | 842 | ||
441 | pglen = rcvbuf->page_len; | 843 | /* |
442 | recvd = rcvbuf->len - hdrlen; | 844 | * 2.2.7. readres |
443 | if (pglen > recvd) | 845 | * |
444 | pglen = recvd; | 846 | * union readres switch (stat status) { |
445 | page = rcvbuf->pages; | 847 | * case NFS_OK: |
446 | return pglen; | 848 | * fattr attributes; |
849 | * nfsdata data; | ||
850 | * default: | ||
851 | * void; | ||
852 | * }; | ||
853 | */ | ||
854 | static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr, | ||
855 | struct nfs_readres *result) | ||
856 | { | ||
857 | enum nfs_stat status; | ||
858 | int error; | ||
859 | |||
860 | error = decode_stat(xdr, &status); | ||
861 | if (unlikely(error)) | ||
862 | goto out; | ||
863 | if (status != NFS_OK) | ||
864 | goto out_default; | ||
865 | error = decode_fattr(xdr, result->fattr); | ||
866 | if (unlikely(error)) | ||
867 | goto out; | ||
868 | error = decode_nfsdata(xdr, result); | ||
869 | out: | ||
870 | return error; | ||
871 | out_default: | ||
872 | return nfs_stat_to_errno(status); | ||
447 | } | 873 | } |
448 | 874 | ||
449 | static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) | 875 | static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr, |
876 | struct nfs_writeres *result) | ||
450 | { | 877 | { |
451 | dprintk("nfs: %s: prematurely hit end of receive buffer. " | 878 | /* All NFSv2 writes are "file sync" writes */ |
452 | "Remaining buffer length is %tu words.\n", | 879 | result->verf->committed = NFS_FILE_SYNC; |
453 | func, xdr->end - xdr->p); | 880 | return decode_attrstat(xdr, result->fattr); |
454 | } | 881 | } |
455 | 882 | ||
456 | __be32 * | 883 | /** |
457 | nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus) | 884 | * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in |
885 | * the local page cache. | ||
886 | * @xdr: XDR stream where entry resides | ||
887 | * @entry: buffer to fill in with entry data | ||
888 | * @plus: boolean indicating whether this should be a readdirplus entry | ||
889 | * | ||
890 | * Returns zero if successful, otherwise a negative errno value is | ||
891 | * returned. | ||
892 | * | ||
893 | * This function is not invoked during READDIR reply decoding, but | ||
894 | * rather whenever an application invokes the getdents(2) system call | ||
895 | * on a directory already in our cache. | ||
896 | * | ||
897 | * 2.2.17. entry | ||
898 | * | ||
899 | * struct entry { | ||
900 | * unsigned fileid; | ||
901 | * filename name; | ||
902 | * nfscookie cookie; | ||
903 | * entry *nextentry; | ||
904 | * }; | ||
905 | */ | ||
906 | int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, | ||
907 | int plus) | ||
458 | { | 908 | { |
459 | __be32 *p; | 909 | __be32 *p; |
910 | int error; | ||
911 | |||
460 | p = xdr_inline_decode(xdr, 4); | 912 | p = xdr_inline_decode(xdr, 4); |
461 | if (unlikely(!p)) | 913 | if (unlikely(p == NULL)) |
462 | goto out_overflow; | 914 | goto out_overflow; |
463 | if (!ntohl(*p++)) { | 915 | if (*p++ == xdr_zero) { |
464 | p = xdr_inline_decode(xdr, 4); | 916 | p = xdr_inline_decode(xdr, 4); |
465 | if (unlikely(!p)) | 917 | if (unlikely(p == NULL)) |
466 | goto out_overflow; | 918 | goto out_overflow; |
467 | if (!ntohl(*p++)) | 919 | if (*p++ == xdr_zero) |
468 | return ERR_PTR(-EAGAIN); | 920 | return -EAGAIN; |
469 | entry->eof = 1; | 921 | entry->eof = 1; |
470 | return ERR_PTR(-EBADCOOKIE); | 922 | return -EBADCOOKIE; |
471 | } | 923 | } |
472 | 924 | ||
473 | p = xdr_inline_decode(xdr, 8); | 925 | p = xdr_inline_decode(xdr, 4); |
474 | if (unlikely(!p)) | 926 | if (unlikely(p == NULL)) |
475 | goto out_overflow; | 927 | goto out_overflow; |
928 | entry->ino = be32_to_cpup(p); | ||
476 | 929 | ||
477 | entry->ino = ntohl(*p++); | 930 | error = decode_filename_inline(xdr, &entry->name, &entry->len); |
478 | entry->len = ntohl(*p++); | 931 | if (unlikely(error)) |
932 | return error; | ||
479 | 933 | ||
480 | p = xdr_inline_decode(xdr, entry->len + 4); | 934 | /* |
481 | if (unlikely(!p)) | 935 | * The type (size and byte order) of nfscookie isn't defined in |
936 | * RFC 1094. This implementation assumes that it's an XDR uint32. | ||
937 | */ | ||
938 | entry->prev_cookie = entry->cookie; | ||
939 | p = xdr_inline_decode(xdr, 4); | ||
940 | if (unlikely(p == NULL)) | ||
482 | goto out_overflow; | 941 | goto out_overflow; |
483 | entry->name = (const char *) p; | 942 | entry->cookie = be32_to_cpup(p); |
484 | p += XDR_QUADLEN(entry->len); | ||
485 | entry->prev_cookie = entry->cookie; | ||
486 | entry->cookie = ntohl(*p++); | ||
487 | 943 | ||
488 | entry->d_type = DT_UNKNOWN; | 944 | entry->d_type = DT_UNKNOWN; |
489 | 945 | ||
490 | p = xdr_inline_peek(xdr, 8); | 946 | return 0; |
491 | if (p != NULL) | ||
492 | entry->eof = !p[0] && p[1]; | ||
493 | else | ||
494 | entry->eof = 0; | ||
495 | |||
496 | return p; | ||
497 | 947 | ||
498 | out_overflow: | 948 | out_overflow: |
499 | print_overflow_msg(__func__, xdr); | 949 | print_overflow_msg(__func__, xdr); |
500 | return ERR_PTR(-EAGAIN); | 950 | return -EAGAIN; |
501 | } | ||
502 | |||
503 | /* | ||
504 | * NFS XDR decode functions | ||
505 | */ | ||
506 | /* | ||
507 | * Decode simple status reply | ||
508 | */ | ||
509 | static int | ||
510 | nfs_xdr_stat(struct rpc_rqst *req, __be32 *p, void *dummy) | ||
511 | { | ||
512 | int status; | ||
513 | |||
514 | if ((status = ntohl(*p++)) != 0) | ||
515 | status = nfs_stat_to_errno(status); | ||
516 | return status; | ||
517 | } | 951 | } |
518 | 952 | ||
519 | /* | 953 | /* |
520 | * Decode attrstat reply | 954 | * 2.2.17. readdirres |
521 | * GETATTR, SETATTR, WRITE | 955 | * |
522 | */ | 956 | * union readdirres switch (stat status) { |
523 | static int | 957 | * case NFS_OK: |
524 | nfs_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr) | 958 | * struct { |
525 | { | 959 | * entry *entries; |
526 | int status; | 960 | * bool eof; |
527 | 961 | * } readdirok; | |
528 | if ((status = ntohl(*p++))) | 962 | * default: |
529 | return nfs_stat_to_errno(status); | 963 | * void; |
530 | xdr_decode_fattr(p, fattr); | 964 | * }; |
531 | return 0; | 965 | * |
532 | } | 966 | * Read the directory contents into the page cache, but don't |
533 | 967 | * touch them. The actual decoding is done by nfs2_decode_dirent() | |
534 | /* | 968 | * during subsequent nfs_readdir() calls. |
535 | * Decode diropres reply | ||
536 | * LOOKUP, CREATE, MKDIR | ||
537 | */ | 969 | */ |
538 | static int | 970 | static int decode_readdirok(struct xdr_stream *xdr) |
539 | nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res) | ||
540 | { | 971 | { |
541 | int status; | 972 | u32 recvd, pglen; |
973 | size_t hdrlen; | ||
542 | 974 | ||
543 | if ((status = ntohl(*p++))) | 975 | pglen = xdr->buf->page_len; |
544 | return nfs_stat_to_errno(status); | 976 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; |
545 | p = xdr_decode_fhandle(p, res->fh); | 977 | recvd = xdr->buf->len - hdrlen; |
546 | xdr_decode_fattr(p, res->fattr); | 978 | if (unlikely(pglen > recvd)) |
547 | return 0; | 979 | goto out_cheating; |
980 | out: | ||
981 | xdr_read_pages(xdr, pglen); | ||
982 | return pglen; | ||
983 | out_cheating: | ||
984 | dprintk("NFS: server cheating in readdir result: " | ||
985 | "pglen %u > recvd %u\n", pglen, recvd); | ||
986 | pglen = recvd; | ||
987 | goto out; | ||
548 | } | 988 | } |
549 | 989 | ||
550 | /* | 990 | static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req, |
551 | * Encode READLINK args | 991 | struct xdr_stream *xdr, void *__unused) |
552 | */ | ||
553 | static int | ||
554 | nfs_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_readlinkargs *args) | ||
555 | { | 992 | { |
556 | struct rpc_auth *auth = req->rq_cred->cr_auth; | 993 | enum nfs_stat status; |
557 | unsigned int replen; | 994 | int error; |
558 | 995 | ||
559 | p = xdr_encode_fhandle(p, args->fh); | 996 | error = decode_stat(xdr, &status); |
560 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 997 | if (unlikely(error)) |
561 | 998 | goto out; | |
562 | /* Inline the page array */ | 999 | if (status != NFS_OK) |
563 | replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2; | 1000 | goto out_default; |
564 | xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen); | 1001 | error = decode_readdirok(xdr); |
565 | return 0; | 1002 | out: |
1003 | return error; | ||
1004 | out_default: | ||
1005 | return nfs_stat_to_errno(status); | ||
566 | } | 1006 | } |
567 | 1007 | ||
568 | /* | 1008 | /* |
569 | * Decode READLINK reply | 1009 | * 2.2.18. statfsres |
1010 | * | ||
1011 | * union statfsres (stat status) { | ||
1012 | * case NFS_OK: | ||
1013 | * struct { | ||
1014 | * unsigned tsize; | ||
1015 | * unsigned bsize; | ||
1016 | * unsigned blocks; | ||
1017 | * unsigned bfree; | ||
1018 | * unsigned bavail; | ||
1019 | * } info; | ||
1020 | * default: | ||
1021 | * void; | ||
1022 | * }; | ||
570 | */ | 1023 | */ |
571 | static int | 1024 | static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result) |
572 | nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy) | ||
573 | { | 1025 | { |
574 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | 1026 | __be32 *p; |
575 | struct kvec *iov = rcvbuf->head; | ||
576 | size_t hdrlen; | ||
577 | u32 len, recvd; | ||
578 | int status; | ||
579 | |||
580 | if ((status = ntohl(*p++))) | ||
581 | return nfs_stat_to_errno(status); | ||
582 | /* Convert length of symlink */ | ||
583 | len = ntohl(*p++); | ||
584 | if (len >= rcvbuf->page_len) { | ||
585 | dprintk("nfs: server returned giant symlink!\n"); | ||
586 | return -ENAMETOOLONG; | ||
587 | } | ||
588 | hdrlen = (u8 *) p - (u8 *) iov->iov_base; | ||
589 | if (iov->iov_len < hdrlen) { | ||
590 | dprintk("NFS: READLINK reply header overflowed:" | ||
591 | "length %Zu > %Zu\n", hdrlen, iov->iov_len); | ||
592 | return -errno_NFSERR_IO; | ||
593 | } else if (iov->iov_len != hdrlen) { | ||
594 | dprintk("NFS: READLINK header is short. iovec will be shifted.\n"); | ||
595 | xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen); | ||
596 | } | ||
597 | recvd = req->rq_rcv_buf.len - hdrlen; | ||
598 | if (recvd < len) { | ||
599 | dprintk("NFS: server cheating in readlink reply: " | ||
600 | "count %u > recvd %u\n", len, recvd); | ||
601 | return -EIO; | ||
602 | } | ||
603 | 1027 | ||
604 | xdr_terminate_string(rcvbuf, len); | 1028 | p = xdr_inline_decode(xdr, NFS_info_sz << 2); |
1029 | if (unlikely(p == NULL)) | ||
1030 | goto out_overflow; | ||
1031 | result->tsize = be32_to_cpup(p++); | ||
1032 | result->bsize = be32_to_cpup(p++); | ||
1033 | result->blocks = be32_to_cpup(p++); | ||
1034 | result->bfree = be32_to_cpup(p++); | ||
1035 | result->bavail = be32_to_cpup(p); | ||
605 | return 0; | 1036 | return 0; |
1037 | out_overflow: | ||
1038 | print_overflow_msg(__func__, xdr); | ||
1039 | return -EIO; | ||
606 | } | 1040 | } |
607 | 1041 | ||
608 | /* | 1042 | static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr, |
609 | * Decode WRITE reply | 1043 | struct nfs2_fsstat *result) |
610 | */ | ||
611 | static int | ||
612 | nfs_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res) | ||
613 | { | 1044 | { |
614 | res->verf->committed = NFS_FILE_SYNC; | 1045 | enum nfs_stat status; |
615 | return nfs_xdr_attrstat(req, p, res->fattr); | 1046 | int error; |
1047 | |||
1048 | error = decode_stat(xdr, &status); | ||
1049 | if (unlikely(error)) | ||
1050 | goto out; | ||
1051 | if (status != NFS_OK) | ||
1052 | goto out_default; | ||
1053 | error = decode_info(xdr, result); | ||
1054 | out: | ||
1055 | return error; | ||
1056 | out_default: | ||
1057 | return nfs_stat_to_errno(status); | ||
616 | } | 1058 | } |
617 | 1059 | ||
618 | /* | ||
619 | * Decode STATFS reply | ||
620 | */ | ||
621 | static int | ||
622 | nfs_xdr_statfsres(struct rpc_rqst *req, __be32 *p, struct nfs2_fsstat *res) | ||
623 | { | ||
624 | int status; | ||
625 | |||
626 | if ((status = ntohl(*p++))) | ||
627 | return nfs_stat_to_errno(status); | ||
628 | |||
629 | res->tsize = ntohl(*p++); | ||
630 | res->bsize = ntohl(*p++); | ||
631 | res->blocks = ntohl(*p++); | ||
632 | res->bfree = ntohl(*p++); | ||
633 | res->bavail = ntohl(*p++); | ||
634 | return 0; | ||
635 | } | ||
636 | 1060 | ||
637 | /* | 1061 | /* |
638 | * We need to translate between nfs status return values and | 1062 | * We need to translate between nfs status return values and |
639 | * the local errno values which may not be the same. | 1063 | * the local errno values which may not be the same. |
640 | */ | 1064 | */ |
641 | static struct { | 1065 | static const struct { |
642 | int stat; | 1066 | int stat; |
643 | int errno; | 1067 | int errno; |
644 | } nfs_errtbl[] = { | 1068 | } nfs_errtbl[] = { |
@@ -678,28 +1102,30 @@ static struct { | |||
678 | { -1, -EIO } | 1102 | { -1, -EIO } |
679 | }; | 1103 | }; |
680 | 1104 | ||
681 | /* | 1105 | /** |
682 | * Convert an NFS error code to a local one. | 1106 | * nfs_stat_to_errno - convert an NFS status code to a local errno |
683 | * This one is used jointly by NFSv2 and NFSv3. | 1107 | * @status: NFS status code to convert |
1108 | * | ||
1109 | * Returns a local errno value, or -EIO if the NFS status code is | ||
1110 | * not recognized. This function is used jointly by NFSv2 and NFSv3. | ||
684 | */ | 1111 | */ |
685 | int | 1112 | int nfs_stat_to_errno(enum nfs_stat status) |
686 | nfs_stat_to_errno(int stat) | ||
687 | { | 1113 | { |
688 | int i; | 1114 | int i; |
689 | 1115 | ||
690 | for (i = 0; nfs_errtbl[i].stat != -1; i++) { | 1116 | for (i = 0; nfs_errtbl[i].stat != -1; i++) { |
691 | if (nfs_errtbl[i].stat == stat) | 1117 | if (nfs_errtbl[i].stat == (int)status) |
692 | return nfs_errtbl[i].errno; | 1118 | return nfs_errtbl[i].errno; |
693 | } | 1119 | } |
694 | dprintk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat); | 1120 | dprintk("NFS: Unrecognized nfs status value: %u\n", status); |
695 | return nfs_errtbl[i].errno; | 1121 | return nfs_errtbl[i].errno; |
696 | } | 1122 | } |
697 | 1123 | ||
698 | #define PROC(proc, argtype, restype, timer) \ | 1124 | #define PROC(proc, argtype, restype, timer) \ |
699 | [NFSPROC_##proc] = { \ | 1125 | [NFSPROC_##proc] = { \ |
700 | .p_proc = NFSPROC_##proc, \ | 1126 | .p_proc = NFSPROC_##proc, \ |
701 | .p_encode = (kxdrproc_t) nfs_xdr_##argtype, \ | 1127 | .p_encode = (kxdreproc_t)nfs2_xdr_enc_##argtype, \ |
702 | .p_decode = (kxdrproc_t) nfs_xdr_##restype, \ | 1128 | .p_decode = (kxdrdproc_t)nfs2_xdr_dec_##restype, \ |
703 | .p_arglen = NFS_##argtype##_sz, \ | 1129 | .p_arglen = NFS_##argtype##_sz, \ |
704 | .p_replen = NFS_##restype##_sz, \ | 1130 | .p_replen = NFS_##restype##_sz, \ |
705 | .p_timer = timer, \ | 1131 | .p_timer = timer, \ |
@@ -707,21 +1133,21 @@ nfs_stat_to_errno(int stat) | |||
707 | .p_name = #proc, \ | 1133 | .p_name = #proc, \ |
708 | } | 1134 | } |
709 | struct rpc_procinfo nfs_procedures[] = { | 1135 | struct rpc_procinfo nfs_procedures[] = { |
710 | PROC(GETATTR, fhandle, attrstat, 1), | 1136 | PROC(GETATTR, fhandle, attrstat, 1), |
711 | PROC(SETATTR, sattrargs, attrstat, 0), | 1137 | PROC(SETATTR, sattrargs, attrstat, 0), |
712 | PROC(LOOKUP, diropargs, diropres, 2), | 1138 | PROC(LOOKUP, diropargs, diropres, 2), |
713 | PROC(READLINK, readlinkargs, readlinkres, 3), | 1139 | PROC(READLINK, readlinkargs, readlinkres, 3), |
714 | PROC(READ, readargs, readres, 3), | 1140 | PROC(READ, readargs, readres, 3), |
715 | PROC(WRITE, writeargs, writeres, 4), | 1141 | PROC(WRITE, writeargs, writeres, 4), |
716 | PROC(CREATE, createargs, diropres, 0), | 1142 | PROC(CREATE, createargs, diropres, 0), |
717 | PROC(REMOVE, removeargs, stat, 0), | 1143 | PROC(REMOVE, removeargs, stat, 0), |
718 | PROC(RENAME, renameargs, stat, 0), | 1144 | PROC(RENAME, renameargs, stat, 0), |
719 | PROC(LINK, linkargs, stat, 0), | 1145 | PROC(LINK, linkargs, stat, 0), |
720 | PROC(SYMLINK, symlinkargs, stat, 0), | 1146 | PROC(SYMLINK, symlinkargs, stat, 0), |
721 | PROC(MKDIR, createargs, diropres, 0), | 1147 | PROC(MKDIR, createargs, diropres, 0), |
722 | PROC(RMDIR, diropargs, stat, 0), | 1148 | PROC(RMDIR, diropargs, stat, 0), |
723 | PROC(READDIR, readdirargs, readdirres, 3), | 1149 | PROC(READDIR, readdirargs, readdirres, 3), |
724 | PROC(STATFS, fhandle, statfsres, 0), | 1150 | PROC(STATFS, fhandle, statfsres, 0), |
725 | }; | 1151 | }; |
726 | 1152 | ||
727 | struct rpc_version nfs_version2 = { | 1153 | struct rpc_version nfs_version2 = { |