diff options
Diffstat (limited to 'fs/nfs/nfs2xdr.c')
-rw-r--r-- | fs/nfs/nfs2xdr.c | 564 |
1 files changed, 556 insertions, 8 deletions
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 2da9824d432a..827d1b8ad55b 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c | |||
@@ -77,6 +77,16 @@ static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages, | |||
77 | xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len); | 77 | xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len); |
78 | } | 78 | } |
79 | 79 | ||
80 | /* | ||
81 | * Handle decode buffer overflows out-of-line. | ||
82 | */ | ||
83 | static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) | ||
84 | { | ||
85 | dprintk("NFS: %s prematurely hit the end of our receive buffer. " | ||
86 | "Remaining buffer length is %tu words.\n", | ||
87 | func, xdr->end - xdr->p); | ||
88 | } | ||
89 | |||
80 | 90 | ||
81 | /* | 91 | /* |
82 | * Common NFS XDR functions as inlines | 92 | * Common NFS XDR functions as inlines |
@@ -139,6 +149,74 @@ xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr) | |||
139 | */ | 149 | */ |
140 | 150 | ||
141 | /* | 151 | /* |
152 | * typedef opaque nfsdata<>; | ||
153 | */ | ||
154 | static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result) | ||
155 | { | ||
156 | u32 recvd, count; | ||
157 | size_t hdrlen; | ||
158 | __be32 *p; | ||
159 | |||
160 | p = xdr_inline_decode(xdr, 4); | ||
161 | if (unlikely(p == NULL)) | ||
162 | goto out_overflow; | ||
163 | count = be32_to_cpup(p); | ||
164 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | ||
165 | recvd = xdr->buf->len - hdrlen; | ||
166 | if (unlikely(count > recvd)) | ||
167 | goto out_cheating; | ||
168 | out: | ||
169 | xdr_read_pages(xdr, count); | ||
170 | result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */ | ||
171 | result->count = count; | ||
172 | return count; | ||
173 | out_cheating: | ||
174 | dprintk("NFS: server cheating in read result: " | ||
175 | "count %u > recvd %u\n", count, recvd); | ||
176 | count = recvd; | ||
177 | goto out; | ||
178 | out_overflow: | ||
179 | print_overflow_msg(__func__, xdr); | ||
180 | return -EIO; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * enum stat { | ||
185 | * NFS_OK = 0, | ||
186 | * NFSERR_PERM = 1, | ||
187 | * NFSERR_NOENT = 2, | ||
188 | * NFSERR_IO = 5, | ||
189 | * NFSERR_NXIO = 6, | ||
190 | * NFSERR_ACCES = 13, | ||
191 | * NFSERR_EXIST = 17, | ||
192 | * NFSERR_NODEV = 19, | ||
193 | * NFSERR_NOTDIR = 20, | ||
194 | * NFSERR_ISDIR = 21, | ||
195 | * NFSERR_FBIG = 27, | ||
196 | * NFSERR_NOSPC = 28, | ||
197 | * NFSERR_ROFS = 30, | ||
198 | * NFSERR_NAMETOOLONG = 63, | ||
199 | * NFSERR_NOTEMPTY = 66, | ||
200 | * NFSERR_DQUOT = 69, | ||
201 | * NFSERR_STALE = 70, | ||
202 | * NFSERR_WFLUSH = 99 | ||
203 | * }; | ||
204 | */ | ||
205 | static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status) | ||
206 | { | ||
207 | __be32 *p; | ||
208 | |||
209 | p = xdr_inline_decode(xdr, 4); | ||
210 | if (unlikely(p == NULL)) | ||
211 | goto out_overflow; | ||
212 | *status = be32_to_cpup(p); | ||
213 | return 0; | ||
214 | out_overflow: | ||
215 | print_overflow_msg(__func__, xdr); | ||
216 | return -EIO; | ||
217 | } | ||
218 | |||
219 | /* | ||
142 | * 2.3.3. fhandle | 220 | * 2.3.3. fhandle |
143 | * | 221 | * |
144 | * typedef opaque fhandle[FHSIZE]; | 222 | * typedef opaque fhandle[FHSIZE]; |
@@ -152,6 +230,21 @@ static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh) | |||
152 | memcpy(p, fh->data, NFS2_FHSIZE); | 230 | memcpy(p, fh->data, NFS2_FHSIZE); |
153 | } | 231 | } |
154 | 232 | ||
233 | static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh) | ||
234 | { | ||
235 | __be32 *p; | ||
236 | |||
237 | p = xdr_inline_decode(xdr, NFS2_FHSIZE); | ||
238 | if (unlikely(p == NULL)) | ||
239 | goto out_overflow; | ||
240 | fh->size = NFS2_FHSIZE; | ||
241 | memcpy(fh->data, p, NFS2_FHSIZE); | ||
242 | return 0; | ||
243 | out_overflow: | ||
244 | print_overflow_msg(__func__, xdr); | ||
245 | return -EIO; | ||
246 | } | ||
247 | |||
155 | /* | 248 | /* |
156 | * 2.3.4. timeval | 249 | * 2.3.4. timeval |
157 | * | 250 | * |
@@ -186,6 +279,41 @@ static __be32 *xdr_encode_current_server_time(__be32 *p, | |||
186 | } | 279 | } |
187 | 280 | ||
188 | /* | 281 | /* |
282 | * 2.3.5. fattr | ||
283 | * | ||
284 | * struct fattr { | ||
285 | * ftype type; | ||
286 | * unsigned int mode; | ||
287 | * unsigned int nlink; | ||
288 | * unsigned int uid; | ||
289 | * unsigned int gid; | ||
290 | * unsigned int size; | ||
291 | * unsigned int blocksize; | ||
292 | * unsigned int rdev; | ||
293 | * unsigned int blocks; | ||
294 | * unsigned int fsid; | ||
295 | * unsigned int fileid; | ||
296 | * timeval atime; | ||
297 | * timeval mtime; | ||
298 | * timeval ctime; | ||
299 | * }; | ||
300 | * | ||
301 | */ | ||
302 | static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) | ||
303 | { | ||
304 | __be32 *p; | ||
305 | |||
306 | p = xdr_inline_decode(xdr, NFS_fattr_sz << 2); | ||
307 | if (unlikely(p == NULL)) | ||
308 | goto out_overflow; | ||
309 | xdr_decode_fattr(p, fattr); | ||
310 | return 0; | ||
311 | out_overflow: | ||
312 | print_overflow_msg(__func__, xdr); | ||
313 | return -EIO; | ||
314 | } | ||
315 | |||
316 | /* | ||
189 | * 2.3.6. sattr | 317 | * 2.3.6. sattr |
190 | * | 318 | * |
191 | * struct sattr { | 319 | * struct sattr { |
@@ -259,6 +387,32 @@ static void encode_filename(struct xdr_stream *xdr, | |||
259 | xdr_encode_opaque(p, name, length); | 387 | xdr_encode_opaque(p, name, length); |
260 | } | 388 | } |
261 | 389 | ||
390 | static int decode_filename_inline(struct xdr_stream *xdr, | ||
391 | const char **name, u32 *length) | ||
392 | { | ||
393 | __be32 *p; | ||
394 | u32 count; | ||
395 | |||
396 | p = xdr_inline_decode(xdr, 4); | ||
397 | if (unlikely(p == NULL)) | ||
398 | goto out_overflow; | ||
399 | count = be32_to_cpup(p); | ||
400 | if (count > NFS3_MAXNAMLEN) | ||
401 | goto out_nametoolong; | ||
402 | p = xdr_inline_decode(xdr, count); | ||
403 | if (unlikely(p == NULL)) | ||
404 | goto out_overflow; | ||
405 | *name = (const char *)p; | ||
406 | *length = count; | ||
407 | return 0; | ||
408 | out_nametoolong: | ||
409 | dprintk("NFS: returned filename too long: %u\n", count); | ||
410 | return -ENAMETOOLONG; | ||
411 | out_overflow: | ||
412 | print_overflow_msg(__func__, xdr); | ||
413 | return -EIO; | ||
414 | } | ||
415 | |||
262 | /* | 416 | /* |
263 | * 2.3.8. path | 417 | * 2.3.8. path |
264 | * | 418 | * |
@@ -274,6 +428,65 @@ static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length) | |||
274 | xdr_write_pages(xdr, pages, 0, length); | 428 | xdr_write_pages(xdr, pages, 0, length); |
275 | } | 429 | } |
276 | 430 | ||
431 | static int decode_path(struct xdr_stream *xdr) | ||
432 | { | ||
433 | u32 length, recvd; | ||
434 | size_t hdrlen; | ||
435 | __be32 *p; | ||
436 | |||
437 | p = xdr_inline_decode(xdr, 4); | ||
438 | if (unlikely(p == NULL)) | ||
439 | goto out_overflow; | ||
440 | length = be32_to_cpup(p); | ||
441 | if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN)) | ||
442 | goto out_size; | ||
443 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | ||
444 | recvd = xdr->buf->len - hdrlen; | ||
445 | if (unlikely(length > recvd)) | ||
446 | goto out_cheating; | ||
447 | |||
448 | xdr_read_pages(xdr, length); | ||
449 | xdr_terminate_string(xdr->buf, length); | ||
450 | return 0; | ||
451 | out_size: | ||
452 | dprintk("NFS: returned pathname too long: %u\n", length); | ||
453 | return -ENAMETOOLONG; | ||
454 | out_cheating: | ||
455 | dprintk("NFS: server cheating in pathname result: " | ||
456 | "length %u > received %u\n", length, recvd); | ||
457 | return -EIO; | ||
458 | out_overflow: | ||
459 | print_overflow_msg(__func__, xdr); | ||
460 | return -EIO; | ||
461 | } | ||
462 | |||
463 | /* | ||
464 | * 2.3.9. attrstat | ||
465 | * | ||
466 | * union attrstat switch (stat status) { | ||
467 | * case NFS_OK: | ||
468 | * fattr attributes; | ||
469 | * default: | ||
470 | * void; | ||
471 | * }; | ||
472 | */ | ||
473 | static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result) | ||
474 | { | ||
475 | enum nfs_stat status; | ||
476 | int error; | ||
477 | |||
478 | error = decode_stat(xdr, &status); | ||
479 | if (unlikely(error)) | ||
480 | goto out; | ||
481 | if (status != NFS_OK) | ||
482 | goto out_default; | ||
483 | error = decode_fattr(xdr, result); | ||
484 | out: | ||
485 | return error; | ||
486 | out_default: | ||
487 | return nfs_stat_to_errno(status); | ||
488 | } | ||
489 | |||
277 | /* | 490 | /* |
278 | * 2.3.10. diropargs | 491 | * 2.3.10. diropargs |
279 | * | 492 | * |
@@ -289,6 +502,48 @@ static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh, | |||
289 | encode_filename(xdr, name, length); | 502 | encode_filename(xdr, name, length); |
290 | } | 503 | } |
291 | 504 | ||
505 | /* | ||
506 | * 2.3.11. diropres | ||
507 | * | ||
508 | * union diropres switch (stat status) { | ||
509 | * case NFS_OK: | ||
510 | * struct { | ||
511 | * fhandle file; | ||
512 | * fattr attributes; | ||
513 | * } diropok; | ||
514 | * default: | ||
515 | * void; | ||
516 | * }; | ||
517 | */ | ||
518 | static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result) | ||
519 | { | ||
520 | int error; | ||
521 | |||
522 | error = decode_fhandle(xdr, result->fh); | ||
523 | if (unlikely(error)) | ||
524 | goto out; | ||
525 | error = decode_fattr(xdr, result->fattr); | ||
526 | out: | ||
527 | return error; | ||
528 | } | ||
529 | |||
530 | static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result) | ||
531 | { | ||
532 | enum nfs_stat status; | ||
533 | int error; | ||
534 | |||
535 | error = decode_stat(xdr, &status); | ||
536 | if (unlikely(error)) | ||
537 | goto out; | ||
538 | if (status != NFS_OK) | ||
539 | goto out_default; | ||
540 | error = decode_diropok(xdr, result); | ||
541 | out: | ||
542 | return error; | ||
543 | out_default: | ||
544 | return nfs_stat_to_errno(status); | ||
545 | } | ||
546 | |||
292 | 547 | ||
293 | /* | 548 | /* |
294 | * NFSv2 XDR encode functions | 549 | * NFSv2 XDR encode functions |
@@ -630,13 +885,6 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy) | |||
630 | return pglen; | 885 | return pglen; |
631 | } | 886 | } |
632 | 887 | ||
633 | static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) | ||
634 | { | ||
635 | dprintk("nfs: %s: prematurely hit end of receive buffer. " | ||
636 | "Remaining buffer length is %tu words.\n", | ||
637 | func, xdr->end - xdr->p); | ||
638 | } | ||
639 | |||
640 | __be32 * | 888 | __be32 * |
641 | nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus) | 889 | nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus) |
642 | { | 890 | { |
@@ -700,6 +948,25 @@ nfs_xdr_stat(struct rpc_rqst *req, __be32 *p, void *dummy) | |||
700 | return status; | 948 | return status; |
701 | } | 949 | } |
702 | 950 | ||
951 | static int nfs2_xdr_dec_stat(struct rpc_rqst *req, __be32 *p, | ||
952 | void *__unused) | ||
953 | { | ||
954 | struct xdr_stream xdr; | ||
955 | enum nfs_stat status; | ||
956 | int error; | ||
957 | |||
958 | xdr_init_decode(&xdr, &req->rq_rcv_buf, p); | ||
959 | error = decode_stat(&xdr, &status); | ||
960 | if (unlikely(error)) | ||
961 | goto out; | ||
962 | if (status != NFS_OK) | ||
963 | goto out_default; | ||
964 | out: | ||
965 | return error; | ||
966 | out_default: | ||
967 | return nfs_stat_to_errno(status); | ||
968 | } | ||
969 | |||
703 | /* | 970 | /* |
704 | * Decode attrstat reply | 971 | * Decode attrstat reply |
705 | * GETATTR, SETATTR, WRITE | 972 | * GETATTR, SETATTR, WRITE |
@@ -715,6 +982,15 @@ nfs_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr) | |||
715 | return 0; | 982 | return 0; |
716 | } | 983 | } |
717 | 984 | ||
985 | static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, __be32 *p, | ||
986 | struct nfs_fattr *result) | ||
987 | { | ||
988 | struct xdr_stream xdr; | ||
989 | |||
990 | xdr_init_decode(&xdr, &req->rq_rcv_buf, p); | ||
991 | return decode_attrstat(&xdr, result); | ||
992 | } | ||
993 | |||
718 | /* | 994 | /* |
719 | * Decode diropres reply | 995 | * Decode diropres reply |
720 | * LOOKUP, CREATE, MKDIR | 996 | * LOOKUP, CREATE, MKDIR |
@@ -731,6 +1007,15 @@ nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res) | |||
731 | return 0; | 1007 | return 0; |
732 | } | 1008 | } |
733 | 1009 | ||
1010 | static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, __be32 *p, | ||
1011 | struct nfs_diropok *result) | ||
1012 | { | ||
1013 | struct xdr_stream xdr; | ||
1014 | |||
1015 | xdr_init_decode(&xdr, &req->rq_rcv_buf, p); | ||
1016 | return decode_diropres(&xdr, result); | ||
1017 | } | ||
1018 | |||
734 | /* | 1019 | /* |
735 | * Decode READLINK reply | 1020 | * Decode READLINK reply |
736 | */ | 1021 | */ |
@@ -772,6 +1057,70 @@ nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy) | |||
772 | } | 1057 | } |
773 | 1058 | ||
774 | /* | 1059 | /* |
1060 | * 2.2.6. readlinkres | ||
1061 | * | ||
1062 | * union readlinkres switch (stat status) { | ||
1063 | * case NFS_OK: | ||
1064 | * path data; | ||
1065 | * default: | ||
1066 | * void; | ||
1067 | * }; | ||
1068 | */ | ||
1069 | static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req, __be32 *p, | ||
1070 | void *__unused) | ||
1071 | { | ||
1072 | struct xdr_stream xdr; | ||
1073 | enum nfs_stat status; | ||
1074 | int error; | ||
1075 | |||
1076 | xdr_init_decode(&xdr, &req->rq_rcv_buf, p); | ||
1077 | error = decode_stat(&xdr, &status); | ||
1078 | if (unlikely(error)) | ||
1079 | goto out; | ||
1080 | if (status != NFS_OK) | ||
1081 | goto out_default; | ||
1082 | error = decode_path(&xdr); | ||
1083 | out: | ||
1084 | return error; | ||
1085 | out_default: | ||
1086 | return nfs_stat_to_errno(status); | ||
1087 | } | ||
1088 | |||
1089 | /* | ||
1090 | * 2.2.7. readres | ||
1091 | * | ||
1092 | * union readres switch (stat status) { | ||
1093 | * case NFS_OK: | ||
1094 | * fattr attributes; | ||
1095 | * nfsdata data; | ||
1096 | * default: | ||
1097 | * void; | ||
1098 | * }; | ||
1099 | */ | ||
1100 | static int nfs2_xdr_dec_readres(struct rpc_rqst *req, __be32 *p, | ||
1101 | struct nfs_readres *result) | ||
1102 | { | ||
1103 | struct xdr_stream xdr; | ||
1104 | enum nfs_stat status; | ||
1105 | int error; | ||
1106 | |||
1107 | xdr_init_decode(&xdr, &req->rq_rcv_buf, p); | ||
1108 | error = decode_stat(&xdr, &status); | ||
1109 | if (unlikely(error)) | ||
1110 | goto out; | ||
1111 | if (status != NFS_OK) | ||
1112 | goto out_default; | ||
1113 | error = decode_fattr(&xdr, result->fattr); | ||
1114 | if (unlikely(error)) | ||
1115 | goto out; | ||
1116 | error = decode_nfsdata(&xdr, result); | ||
1117 | out: | ||
1118 | return error; | ||
1119 | out_default: | ||
1120 | return nfs_stat_to_errno(status); | ||
1121 | } | ||
1122 | |||
1123 | /* | ||
775 | * Decode WRITE reply | 1124 | * Decode WRITE reply |
776 | */ | 1125 | */ |
777 | static int | 1126 | static int |
@@ -781,6 +1130,150 @@ nfs_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res) | |||
781 | return nfs_xdr_attrstat(req, p, res->fattr); | 1130 | return nfs_xdr_attrstat(req, p, res->fattr); |
782 | } | 1131 | } |
783 | 1132 | ||
1133 | static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, __be32 *p, | ||
1134 | struct nfs_writeres *result) | ||
1135 | { | ||
1136 | struct xdr_stream xdr; | ||
1137 | |||
1138 | /* All NFSv2 writes are "file sync" writes */ | ||
1139 | result->verf->committed = NFS_FILE_SYNC; | ||
1140 | |||
1141 | xdr_init_decode(&xdr, &req->rq_rcv_buf, p); | ||
1142 | return decode_attrstat(&xdr, result->fattr); | ||
1143 | } | ||
1144 | |||
1145 | /** | ||
1146 | * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in | ||
1147 | * the local page cache. | ||
1148 | * @xdr: XDR stream where entry resides | ||
1149 | * @entry: buffer to fill in with entry data | ||
1150 | * @server: nfs_server data for this directory | ||
1151 | * @plus: boolean indicating whether this should be a readdirplus entry | ||
1152 | * | ||
1153 | * Returns the position of the next item in the buffer, or an ERR_PTR. | ||
1154 | * | ||
1155 | * This function is not invoked during READDIR reply decoding, but | ||
1156 | * rather whenever an application invokes the getdents(2) system call | ||
1157 | * on a directory already in our cache. | ||
1158 | * | ||
1159 | * 2.2.17. entry | ||
1160 | * | ||
1161 | * struct entry { | ||
1162 | * unsigned fileid; | ||
1163 | * filename name; | ||
1164 | * nfscookie cookie; | ||
1165 | * entry *nextentry; | ||
1166 | * }; | ||
1167 | */ | ||
1168 | __be32 *nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, | ||
1169 | struct nfs_server *server, int plus) | ||
1170 | { | ||
1171 | __be32 *p; | ||
1172 | int error; | ||
1173 | |||
1174 | p = xdr_inline_decode(xdr, 4); | ||
1175 | if (unlikely(p == NULL)) | ||
1176 | goto out_overflow; | ||
1177 | if (*p++ == xdr_zero) { | ||
1178 | p = xdr_inline_decode(xdr, 4); | ||
1179 | if (unlikely(p == NULL)) | ||
1180 | goto out_overflow; | ||
1181 | if (*p++ == xdr_zero) | ||
1182 | return ERR_PTR(-EAGAIN); | ||
1183 | entry->eof = 1; | ||
1184 | return ERR_PTR(-EBADCOOKIE); | ||
1185 | } | ||
1186 | |||
1187 | p = xdr_inline_decode(xdr, 4); | ||
1188 | if (unlikely(p == NULL)) | ||
1189 | goto out_overflow; | ||
1190 | entry->ino = be32_to_cpup(p); | ||
1191 | |||
1192 | error = decode_filename_inline(xdr, &entry->name, &entry->len); | ||
1193 | if (unlikely(error)) | ||
1194 | return ERR_PTR(error); | ||
1195 | |||
1196 | /* | ||
1197 | * The type (size and byte order) of nfscookie isn't defined in | ||
1198 | * RFC 1094. This implementation assumes that it's an XDR uint32. | ||
1199 | */ | ||
1200 | entry->prev_cookie = entry->cookie; | ||
1201 | p = xdr_inline_decode(xdr, 4); | ||
1202 | if (unlikely(p == NULL)) | ||
1203 | goto out_overflow; | ||
1204 | entry->cookie = be32_to_cpup(p); | ||
1205 | |||
1206 | entry->d_type = DT_UNKNOWN; | ||
1207 | |||
1208 | /* Peek at the next entry to see if we're at EOD */ | ||
1209 | p = xdr_inline_peek(xdr, 4 + 4); | ||
1210 | entry->eof = 0; | ||
1211 | if (p != NULL) | ||
1212 | entry->eof = (p[0] == xdr_zero) && (p[1] != xdr_zero); | ||
1213 | return p; | ||
1214 | |||
1215 | out_overflow: | ||
1216 | print_overflow_msg(__func__, xdr); | ||
1217 | return ERR_PTR(-EAGAIN); | ||
1218 | } | ||
1219 | |||
1220 | /* | ||
1221 | * 2.2.17. readdirres | ||
1222 | * | ||
1223 | * union readdirres switch (stat status) { | ||
1224 | * case NFS_OK: | ||
1225 | * struct { | ||
1226 | * entry *entries; | ||
1227 | * bool eof; | ||
1228 | * } readdirok; | ||
1229 | * default: | ||
1230 | * void; | ||
1231 | * }; | ||
1232 | * | ||
1233 | * Read the directory contents into the page cache, but don't | ||
1234 | * touch them. The actual decoding is done by nfs2_decode_dirent() | ||
1235 | * during subsequent nfs_readdir() calls. | ||
1236 | */ | ||
1237 | static int decode_readdirok(struct xdr_stream *xdr) | ||
1238 | { | ||
1239 | u32 recvd, pglen; | ||
1240 | size_t hdrlen; | ||
1241 | |||
1242 | pglen = xdr->buf->page_len; | ||
1243 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | ||
1244 | recvd = xdr->buf->len - hdrlen; | ||
1245 | if (unlikely(pglen > recvd)) | ||
1246 | goto out_cheating; | ||
1247 | out: | ||
1248 | xdr_read_pages(xdr, pglen); | ||
1249 | return pglen; | ||
1250 | out_cheating: | ||
1251 | dprintk("NFS: server cheating in readdir result: " | ||
1252 | "pglen %u > recvd %u\n", pglen, recvd); | ||
1253 | pglen = recvd; | ||
1254 | goto out; | ||
1255 | } | ||
1256 | |||
1257 | static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req, __be32 *p, | ||
1258 | void *__unused) | ||
1259 | { | ||
1260 | struct xdr_stream xdr; | ||
1261 | enum nfs_stat status; | ||
1262 | int error; | ||
1263 | |||
1264 | xdr_init_decode(&xdr, &req->rq_rcv_buf, p); | ||
1265 | error = decode_stat(&xdr, &status); | ||
1266 | if (unlikely(error)) | ||
1267 | goto out; | ||
1268 | if (status != NFS_OK) | ||
1269 | goto out_default; | ||
1270 | error = decode_readdirok(&xdr); | ||
1271 | out: | ||
1272 | return error; | ||
1273 | out_default: | ||
1274 | return nfs_stat_to_errno(status); | ||
1275 | } | ||
1276 | |||
784 | /* | 1277 | /* |
785 | * Decode STATFS reply | 1278 | * Decode STATFS reply |
786 | */ | 1279 | */ |
@@ -801,6 +1294,61 @@ nfs_xdr_statfsres(struct rpc_rqst *req, __be32 *p, struct nfs2_fsstat *res) | |||
801 | } | 1294 | } |
802 | 1295 | ||
803 | /* | 1296 | /* |
1297 | * 2.2.18. statfsres | ||
1298 | * | ||
1299 | * union statfsres (stat status) { | ||
1300 | * case NFS_OK: | ||
1301 | * struct { | ||
1302 | * unsigned tsize; | ||
1303 | * unsigned bsize; | ||
1304 | * unsigned blocks; | ||
1305 | * unsigned bfree; | ||
1306 | * unsigned bavail; | ||
1307 | * } info; | ||
1308 | * default: | ||
1309 | * void; | ||
1310 | * }; | ||
1311 | */ | ||
1312 | static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result) | ||
1313 | { | ||
1314 | __be32 *p; | ||
1315 | |||
1316 | p = xdr_inline_decode(xdr, NFS_info_sz << 2); | ||
1317 | if (unlikely(p == NULL)) | ||
1318 | goto out_overflow; | ||
1319 | result->tsize = be32_to_cpup(p++); | ||
1320 | result->bsize = be32_to_cpup(p++); | ||
1321 | result->blocks = be32_to_cpup(p++); | ||
1322 | result->bfree = be32_to_cpup(p++); | ||
1323 | result->bavail = be32_to_cpup(p); | ||
1324 | return 0; | ||
1325 | out_overflow: | ||
1326 | print_overflow_msg(__func__, xdr); | ||
1327 | return -EIO; | ||
1328 | } | ||
1329 | |||
1330 | static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, __be32 *p, | ||
1331 | struct nfs2_fsstat *result) | ||
1332 | { | ||
1333 | struct xdr_stream xdr; | ||
1334 | enum nfs_stat status; | ||
1335 | int error; | ||
1336 | |||
1337 | xdr_init_decode(&xdr, &req->rq_rcv_buf, p); | ||
1338 | error = decode_stat(&xdr, &status); | ||
1339 | if (unlikely(error)) | ||
1340 | goto out; | ||
1341 | if (status != NFS_OK) | ||
1342 | goto out_default; | ||
1343 | error = decode_info(&xdr, result); | ||
1344 | out: | ||
1345 | return error; | ||
1346 | out_default: | ||
1347 | return nfs_stat_to_errno(status); | ||
1348 | } | ||
1349 | |||
1350 | |||
1351 | /* | ||
804 | * We need to translate between nfs status return values and | 1352 | * We need to translate between nfs status return values and |
805 | * the local errno values which may not be the same. | 1353 | * the local errno values which may not be the same. |
806 | */ | 1354 | */ |
@@ -867,7 +1415,7 @@ int nfs_stat_to_errno(enum nfs_stat status) | |||
867 | [NFSPROC_##proc] = { \ | 1415 | [NFSPROC_##proc] = { \ |
868 | .p_proc = NFSPROC_##proc, \ | 1416 | .p_proc = NFSPROC_##proc, \ |
869 | .p_encode = (kxdrproc_t)nfs2_xdr_enc_##argtype, \ | 1417 | .p_encode = (kxdrproc_t)nfs2_xdr_enc_##argtype, \ |
870 | .p_decode = (kxdrproc_t) nfs_xdr_##restype, \ | 1418 | .p_decode = (kxdrproc_t)nfs2_xdr_dec_##restype, \ |
871 | .p_arglen = NFS_##argtype##_sz, \ | 1419 | .p_arglen = NFS_##argtype##_sz, \ |
872 | .p_replen = NFS_##restype##_sz, \ | 1420 | .p_replen = NFS_##restype##_sz, \ |
873 | .p_timer = timer, \ | 1421 | .p_timer = timer, \ |