diff options
author | Bryan Schumaker <bjschuma@netapp.com> | 2010-10-20 15:44:29 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-10-23 15:27:33 -0400 |
commit | babddc72a9468884ce1a23db3c3d54b0afa299f0 (patch) | |
tree | b176e5795b47c73c47543acdc546da0c38619ddc /fs | |
parent | ba8e452a4fe64a51b74d43761e14d99f0666cc45 (diff) |
NFS: decode_dirent should use an xdr_stream
Convert nfs*xdr.c to use an xdr stream in decode_dirent. This will prevent a
kernel oops that has been occuring when reading a vmapped page.
Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/dir.c | 29 | ||||
-rw-r--r-- | fs/nfs/internal.h | 6 | ||||
-rw-r--r-- | fs/nfs/nfs2xdr.c | 39 | ||||
-rw-r--r-- | fs/nfs/nfs3xdr.c | 93 | ||||
-rw-r--r-- | fs/nfs/nfs4_fs.h | 2 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 71 |
6 files changed, 201 insertions, 39 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index fd30f185ec01..88cbcda76856 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -171,7 +171,7 @@ struct nfs_cache_array { | |||
171 | 171 | ||
172 | #define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry)) | 172 | #define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry)) |
173 | 173 | ||
174 | typedef __be32 * (*decode_dirent_t)(__be32 *, struct nfs_entry *, int); | 174 | typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, int); |
175 | typedef struct { | 175 | typedef struct { |
176 | struct file *file; | 176 | struct file *file; |
177 | struct page *page; | 177 | struct page *page; |
@@ -357,13 +357,11 @@ error: | |||
357 | 357 | ||
358 | /* Fill in an entry based on the xdr code stored in desc->page */ | 358 | /* Fill in an entry based on the xdr code stored in desc->page */ |
359 | static | 359 | static |
360 | int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, __be32 **ptr) | 360 | int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, struct xdr_stream *stream) |
361 | { | 361 | { |
362 | __be32 *p = *ptr; | 362 | __be32 *p = desc->decode(stream, entry, desc->plus); |
363 | p = desc->decode(p, entry, desc->plus); | ||
364 | if (IS_ERR(p)) | 363 | if (IS_ERR(p)) |
365 | return PTR_ERR(p); | 364 | return PTR_ERR(p); |
366 | *ptr = p; | ||
367 | 365 | ||
368 | entry->fattr->time_start = desc->timestamp; | 366 | entry->fattr->time_start = desc->timestamp; |
369 | entry->fattr->gencount = desc->gencount; | 367 | entry->fattr->gencount = desc->gencount; |
@@ -438,10 +436,23 @@ out: | |||
438 | /* Perform conversion from xdr to cache array */ | 436 | /* Perform conversion from xdr to cache array */ |
439 | static | 437 | static |
440 | void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, | 438 | void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, |
441 | struct page *xdr_page, struct page *page) | 439 | struct page *xdr_page, struct page *page, unsigned int buflen) |
442 | { | 440 | { |
441 | struct xdr_stream stream; | ||
442 | struct xdr_buf buf; | ||
443 | __be32 *ptr = kmap(xdr_page); | 443 | __be32 *ptr = kmap(xdr_page); |
444 | while (xdr_decode(desc, entry, &ptr) == 0) { | 444 | |
445 | buf.head->iov_base = xdr_page; | ||
446 | buf.head->iov_len = buflen; | ||
447 | buf.tail->iov_len = 0; | ||
448 | buf.page_base = 0; | ||
449 | buf.page_len = 0; | ||
450 | buf.buflen = buf.head->iov_len; | ||
451 | buf.len = buf.head->iov_len; | ||
452 | |||
453 | xdr_init_decode(&stream, &buf, ptr); | ||
454 | |||
455 | while (xdr_decode(desc, entry, &stream) == 0) { | ||
445 | if (nfs_readdir_add_to_array(entry, page) == -1) | 456 | if (nfs_readdir_add_to_array(entry, page) == -1) |
446 | break; | 457 | break; |
447 | if (desc->plus == 1) | 458 | if (desc->plus == 1) |
@@ -458,6 +469,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, | |||
458 | struct file *file = desc->file; | 469 | struct file *file = desc->file; |
459 | struct nfs_cache_array *array; | 470 | struct nfs_cache_array *array; |
460 | int status = 0; | 471 | int status = 0; |
472 | unsigned int array_size = 1; | ||
461 | 473 | ||
462 | entry.prev_cookie = 0; | 474 | entry.prev_cookie = 0; |
463 | entry.cookie = *desc->dir_cookie; | 475 | entry.cookie = *desc->dir_cookie; |
@@ -476,9 +488,10 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, | |||
476 | goto out_release_array; | 488 | goto out_release_array; |
477 | do { | 489 | do { |
478 | status = nfs_readdir_xdr_filler(xdr_page, desc, &entry, file, inode); | 490 | status = nfs_readdir_xdr_filler(xdr_page, desc, &entry, file, inode); |
491 | |||
479 | if (status < 0) | 492 | if (status < 0) |
480 | break; | 493 | break; |
481 | nfs_readdir_page_filler(desc, &entry, xdr_page, page); | 494 | nfs_readdir_page_filler(desc, &entry, xdr_page, page, array_size * PAGE_SIZE); |
482 | } while (array->eof_index < 0 && array->size < MAX_READDIR_ARRAY); | 495 | } while (array->eof_index < 0 && array->size < MAX_READDIR_ARRAY); |
483 | 496 | ||
484 | put_page(xdr_page); | 497 | put_page(xdr_page); |
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index c961bc92c107..74b015598a43 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
@@ -181,15 +181,15 @@ extern void nfs_destroy_directcache(void); | |||
181 | /* nfs2xdr.c */ | 181 | /* nfs2xdr.c */ |
182 | extern int nfs_stat_to_errno(int); | 182 | extern int nfs_stat_to_errno(int); |
183 | extern struct rpc_procinfo nfs_procedures[]; | 183 | extern struct rpc_procinfo nfs_procedures[]; |
184 | extern __be32 * nfs_decode_dirent(__be32 *, struct nfs_entry *, int); | 184 | extern __be32 *nfs_decode_dirent(struct xdr_stream *, struct nfs_entry *, int); |
185 | 185 | ||
186 | /* nfs3xdr.c */ | 186 | /* nfs3xdr.c */ |
187 | extern struct rpc_procinfo nfs3_procedures[]; | 187 | extern struct rpc_procinfo nfs3_procedures[]; |
188 | extern __be32 *nfs3_decode_dirent(__be32 *, struct nfs_entry *, int); | 188 | extern __be32 *nfs3_decode_dirent(struct xdr_stream *, struct nfs_entry *, int); |
189 | 189 | ||
190 | /* nfs4xdr.c */ | 190 | /* nfs4xdr.c */ |
191 | #ifdef CONFIG_NFS_V4 | 191 | #ifdef CONFIG_NFS_V4 |
192 | extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus); | 192 | extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *entry, int plus); |
193 | #endif | 193 | #endif |
194 | #ifdef CONFIG_NFS_V4_1 | 194 | #ifdef CONFIG_NFS_V4_1 |
195 | extern const u32 nfs41_maxread_overhead; | 195 | extern const u32 nfs41_maxread_overhead; |
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 79c74387a2fe..0210c752e743 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c | |||
@@ -500,25 +500,56 @@ err_unmap: | |||
500 | goto out; | 500 | goto out; |
501 | } | 501 | } |
502 | 502 | ||
503 | static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) | ||
504 | { | ||
505 | dprintk("nfs: %s: prematurely hit end of receive buffer. " | ||
506 | "Remaining buffer length is %tu words.\n", | ||
507 | func, xdr->end - xdr->p); | ||
508 | } | ||
509 | |||
503 | __be32 * | 510 | __be32 * |
504 | nfs_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | 511 | nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus) |
505 | { | 512 | { |
506 | if (!*p++) { | 513 | __be32 *p; |
507 | if (!*p) | 514 | p = xdr_inline_decode(xdr, 4); |
515 | if (unlikely(!p)) | ||
516 | goto out_overflow; | ||
517 | if (!ntohl(*p++)) { | ||
518 | p = xdr_inline_decode(xdr, 4); | ||
519 | if (unlikely(!p)) | ||
520 | goto out_overflow; | ||
521 | if (!ntohl(*p++)) | ||
508 | return ERR_PTR(-EAGAIN); | 522 | return ERR_PTR(-EAGAIN); |
509 | entry->eof = 1; | 523 | entry->eof = 1; |
510 | return ERR_PTR(-EBADCOOKIE); | 524 | return ERR_PTR(-EBADCOOKIE); |
511 | } | 525 | } |
512 | 526 | ||
527 | p = xdr_inline_decode(xdr, 8); | ||
528 | if (unlikely(!p)) | ||
529 | goto out_overflow; | ||
530 | |||
513 | entry->ino = ntohl(*p++); | 531 | entry->ino = ntohl(*p++); |
514 | entry->len = ntohl(*p++); | 532 | entry->len = ntohl(*p++); |
533 | |||
534 | p = xdr_inline_decode(xdr, entry->len + 4); | ||
535 | if (unlikely(!p)) | ||
536 | goto out_overflow; | ||
515 | entry->name = (const char *) p; | 537 | entry->name = (const char *) p; |
516 | p += XDR_QUADLEN(entry->len); | 538 | p += XDR_QUADLEN(entry->len); |
517 | entry->prev_cookie = entry->cookie; | 539 | entry->prev_cookie = entry->cookie; |
518 | entry->cookie = ntohl(*p++); | 540 | entry->cookie = ntohl(*p++); |
519 | entry->eof = !p[0] && p[1]; | 541 | |
542 | p = xdr_inline_peek(xdr, 8); | ||
543 | if (p != NULL) | ||
544 | entry->eof = !p[0] && p[1]; | ||
545 | else | ||
546 | entry->eof = 0; | ||
520 | 547 | ||
521 | return p; | 548 | return p; |
549 | |||
550 | out_overflow: | ||
551 | print_overflow_msg(__func__, xdr); | ||
552 | return ERR_PTR(-EIO); | ||
522 | } | 553 | } |
523 | 554 | ||
524 | /* | 555 | /* |
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 52b2fda66e63..d562c8d9d56e 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c | |||
@@ -100,6 +100,13 @@ static const umode_t nfs_type2fmt[] = { | |||
100 | [NF3FIFO] = S_IFIFO, | 100 | [NF3FIFO] = S_IFIFO, |
101 | }; | 101 | }; |
102 | 102 | ||
103 | static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) | ||
104 | { | ||
105 | dprintk("nfs: %s: prematurely hit end of receive buffer. " | ||
106 | "Remaining buffer length is %tu words.\n", | ||
107 | func, xdr->end - xdr->p); | ||
108 | } | ||
109 | |||
103 | /* | 110 | /* |
104 | * Common NFS XDR functions as inlines | 111 | * Common NFS XDR functions as inlines |
105 | */ | 112 | */ |
@@ -119,6 +126,29 @@ xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh) | |||
119 | return NULL; | 126 | return NULL; |
120 | } | 127 | } |
121 | 128 | ||
129 | static inline __be32 * | ||
130 | xdr_decode_fhandle_stream(struct xdr_stream *xdr, struct nfs_fh *fh) | ||
131 | { | ||
132 | __be32 *p; | ||
133 | p = xdr_inline_decode(xdr, 4); | ||
134 | if (unlikely(!p)) | ||
135 | goto out_overflow; | ||
136 | fh->size = ntohl(*p++); | ||
137 | |||
138 | if (fh->size <= NFS3_FHSIZE) { | ||
139 | p = xdr_inline_decode(xdr, fh->size); | ||
140 | if (unlikely(!p)) | ||
141 | goto out_overflow; | ||
142 | memcpy(fh->data, p, fh->size); | ||
143 | return p + XDR_QUADLEN(fh->size); | ||
144 | } | ||
145 | return NULL; | ||
146 | |||
147 | out_overflow: | ||
148 | print_overflow_msg(__func__, xdr); | ||
149 | return ERR_PTR(-EIO); | ||
150 | } | ||
151 | |||
122 | /* | 152 | /* |
123 | * Encode/decode time. | 153 | * Encode/decode time. |
124 | */ | 154 | */ |
@@ -241,6 +271,26 @@ xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr) | |||
241 | } | 271 | } |
242 | 272 | ||
243 | static inline __be32 * | 273 | static inline __be32 * |
274 | xdr_decode_post_op_attr_stream(struct xdr_stream *xdr, struct nfs_fattr *fattr) | ||
275 | { | ||
276 | __be32 *p; | ||
277 | |||
278 | p = xdr_inline_decode(xdr, 4); | ||
279 | if (unlikely(!p)) | ||
280 | goto out_overflow; | ||
281 | if (ntohl(*p++)) { | ||
282 | p = xdr_inline_decode(xdr, 84); | ||
283 | if (unlikely(!p)) | ||
284 | goto out_overflow; | ||
285 | p = xdr_decode_fattr(p, fattr); | ||
286 | } | ||
287 | return p; | ||
288 | out_overflow: | ||
289 | print_overflow_msg(__func__, xdr); | ||
290 | return ERR_PTR(-EIO); | ||
291 | } | ||
292 | |||
293 | static inline __be32 * | ||
244 | xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr) | 294 | xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr) |
245 | { | 295 | { |
246 | if (*p++) | 296 | if (*p++) |
@@ -616,19 +666,33 @@ err_unmap: | |||
616 | } | 666 | } |
617 | 667 | ||
618 | __be32 * | 668 | __be32 * |
619 | nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | 669 | nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus) |
620 | { | 670 | { |
671 | __be32 *p; | ||
621 | struct nfs_entry old = *entry; | 672 | struct nfs_entry old = *entry; |
622 | 673 | ||
623 | if (!*p++) { | 674 | p = xdr_inline_decode(xdr, 4); |
624 | if (!*p) | 675 | if (unlikely(!p)) |
676 | goto out_overflow; | ||
677 | if (!ntohl(*p++)) { | ||
678 | p = xdr_inline_decode(xdr, 4); | ||
679 | if (unlikely(!p)) | ||
680 | goto out_overflow; | ||
681 | if (!ntohl(*p++)) | ||
625 | return ERR_PTR(-EAGAIN); | 682 | return ERR_PTR(-EAGAIN); |
626 | entry->eof = 1; | 683 | entry->eof = 1; |
627 | return ERR_PTR(-EBADCOOKIE); | 684 | return ERR_PTR(-EBADCOOKIE); |
628 | } | 685 | } |
629 | 686 | ||
687 | p = xdr_inline_decode(xdr, 12); | ||
688 | if (unlikely(!p)) | ||
689 | goto out_overflow; | ||
630 | p = xdr_decode_hyper(p, &entry->ino); | 690 | p = xdr_decode_hyper(p, &entry->ino); |
631 | entry->len = ntohl(*p++); | 691 | entry->len = ntohl(*p++); |
692 | |||
693 | p = xdr_inline_decode(xdr, entry->len + 8); | ||
694 | if (unlikely(!p)) | ||
695 | goto out_overflow; | ||
632 | entry->name = (const char *) p; | 696 | entry->name = (const char *) p; |
633 | p += XDR_QUADLEN(entry->len); | 697 | p += XDR_QUADLEN(entry->len); |
634 | entry->prev_cookie = entry->cookie; | 698 | entry->prev_cookie = entry->cookie; |
@@ -636,10 +700,17 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | |||
636 | 700 | ||
637 | if (plus) { | 701 | if (plus) { |
638 | entry->fattr->valid = 0; | 702 | entry->fattr->valid = 0; |
639 | p = xdr_decode_post_op_attr(p, entry->fattr); | 703 | p = xdr_decode_post_op_attr_stream(xdr, entry->fattr); |
704 | if (IS_ERR(p)) | ||
705 | goto out_overflow_exit; | ||
640 | /* In fact, a post_op_fh3: */ | 706 | /* In fact, a post_op_fh3: */ |
707 | p = xdr_inline_decode(xdr, 4); | ||
708 | if (unlikely(!p)) | ||
709 | goto out_overflow; | ||
641 | if (*p++) { | 710 | if (*p++) { |
642 | p = xdr_decode_fhandle(p, entry->fh); | 711 | p = xdr_decode_fhandle_stream(xdr, entry->fh); |
712 | if (IS_ERR(p)) | ||
713 | goto out_overflow_exit; | ||
643 | /* Ugh -- server reply was truncated */ | 714 | /* Ugh -- server reply was truncated */ |
644 | if (p == NULL) { | 715 | if (p == NULL) { |
645 | dprintk("NFS: FH truncated\n"); | 716 | dprintk("NFS: FH truncated\n"); |
@@ -650,8 +721,18 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | |||
650 | memset((u8*)(entry->fh), 0, sizeof(*entry->fh)); | 721 | memset((u8*)(entry->fh), 0, sizeof(*entry->fh)); |
651 | } | 722 | } |
652 | 723 | ||
653 | entry->eof = !p[0] && p[1]; | 724 | p = xdr_inline_peek(xdr, 8); |
725 | if (p != NULL) | ||
726 | entry->eof = !p[0] && p[1]; | ||
727 | else | ||
728 | entry->eof = 0; | ||
729 | |||
654 | return p; | 730 | return p; |
731 | |||
732 | out_overflow: | ||
733 | print_overflow_msg(__func__, xdr); | ||
734 | out_overflow_exit: | ||
735 | return ERR_PTR(-EIO); | ||
655 | } | 736 | } |
656 | 737 | ||
657 | /* | 738 | /* |
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index d24a8e07b5e2..c58ea6377506 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
@@ -331,7 +331,7 @@ extern void nfs_free_seqid(struct nfs_seqid *seqid); | |||
331 | extern const nfs4_stateid zero_stateid; | 331 | extern const nfs4_stateid zero_stateid; |
332 | 332 | ||
333 | /* nfs4xdr.c */ | 333 | /* nfs4xdr.c */ |
334 | extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus); | 334 | extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *entry, int plus); |
335 | extern struct rpc_procinfo nfs4_procedures[]; | 335 | extern struct rpc_procinfo nfs4_procedures[]; |
336 | 336 | ||
337 | struct nfs4_mount_data; | 337 | struct nfs4_mount_data; |
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 6ea5c9392fe4..a4919e999354 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c | |||
@@ -3950,13 +3950,13 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl) | |||
3950 | __be32 *p; | 3950 | __be32 *p; |
3951 | uint32_t namelen, type; | 3951 | uint32_t namelen, type; |
3952 | 3952 | ||
3953 | p = xdr_inline_decode(xdr, 32); | 3953 | p = xdr_inline_decode(xdr, 32); /* read 32 bytes */ |
3954 | if (unlikely(!p)) | 3954 | if (unlikely(!p)) |
3955 | goto out_overflow; | 3955 | goto out_overflow; |
3956 | p = xdr_decode_hyper(p, &offset); | 3956 | p = xdr_decode_hyper(p, &offset); /* read 2 8-byte long words */ |
3957 | p = xdr_decode_hyper(p, &length); | 3957 | p = xdr_decode_hyper(p, &length); |
3958 | type = be32_to_cpup(p++); | 3958 | type = be32_to_cpup(p++); /* 4 byte read */ |
3959 | if (fl != NULL) { | 3959 | if (fl != NULL) { /* manipulate file lock */ |
3960 | fl->fl_start = (loff_t)offset; | 3960 | fl->fl_start = (loff_t)offset; |
3961 | fl->fl_end = fl->fl_start + (loff_t)length - 1; | 3961 | fl->fl_end = fl->fl_start + (loff_t)length - 1; |
3962 | if (length == ~(uint64_t)0) | 3962 | if (length == ~(uint64_t)0) |
@@ -3966,9 +3966,9 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl) | |||
3966 | fl->fl_type = F_RDLCK; | 3966 | fl->fl_type = F_RDLCK; |
3967 | fl->fl_pid = 0; | 3967 | fl->fl_pid = 0; |
3968 | } | 3968 | } |
3969 | p = xdr_decode_hyper(p, &clientid); | 3969 | p = xdr_decode_hyper(p, &clientid); /* read 8 bytes */ |
3970 | namelen = be32_to_cpup(p); | 3970 | namelen = be32_to_cpup(p); /* read 4 bytes */ /* have read all 32 bytes now */ |
3971 | p = xdr_inline_decode(xdr, namelen); | 3971 | p = xdr_inline_decode(xdr, namelen); /* variable size field */ |
3972 | if (likely(p)) | 3972 | if (likely(p)) |
3973 | return -NFS4ERR_DENIED; | 3973 | return -NFS4ERR_DENIED; |
3974 | out_overflow: | 3974 | out_overflow: |
@@ -5755,21 +5755,33 @@ static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp, uint32_t *p, | |||
5755 | } | 5755 | } |
5756 | #endif /* CONFIG_NFS_V4_1 */ | 5756 | #endif /* CONFIG_NFS_V4_1 */ |
5757 | 5757 | ||
5758 | __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | 5758 | __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus) |
5759 | { | 5759 | { |
5760 | uint32_t bitmap[2] = {0}; | 5760 | uint32_t bitmap[2] = {0}; |
5761 | uint32_t len; | 5761 | uint32_t len; |
5762 | 5762 | __be32 *p = xdr_inline_decode(xdr, 4); | |
5763 | if (!*p++) { | 5763 | if (unlikely(!p)) |
5764 | if (!*p) | 5764 | goto out_overflow; |
5765 | if (!ntohl(*p++)) { | ||
5766 | p = xdr_inline_decode(xdr, 4); | ||
5767 | if (unlikely(!p)) | ||
5768 | goto out_overflow; | ||
5769 | if (!ntohl(*p++)) | ||
5765 | return ERR_PTR(-EAGAIN); | 5770 | return ERR_PTR(-EAGAIN); |
5766 | entry->eof = 1; | 5771 | entry->eof = 1; |
5767 | return ERR_PTR(-EBADCOOKIE); | 5772 | return ERR_PTR(-EBADCOOKIE); |
5768 | } | 5773 | } |
5769 | 5774 | ||
5775 | p = xdr_inline_decode(xdr, 12); | ||
5776 | if (unlikely(!p)) | ||
5777 | goto out_overflow; | ||
5770 | entry->prev_cookie = entry->cookie; | 5778 | entry->prev_cookie = entry->cookie; |
5771 | p = xdr_decode_hyper(p, &entry->cookie); | 5779 | p = xdr_decode_hyper(p, &entry->cookie); |
5772 | entry->len = ntohl(*p++); | 5780 | entry->len = ntohl(*p++); |
5781 | |||
5782 | p = xdr_inline_decode(xdr, entry->len + 4); | ||
5783 | if (unlikely(!p)) | ||
5784 | goto out_overflow; | ||
5773 | entry->name = (const char *) p; | 5785 | entry->name = (const char *) p; |
5774 | p += XDR_QUADLEN(entry->len); | 5786 | p += XDR_QUADLEN(entry->len); |
5775 | 5787 | ||
@@ -5782,29 +5794,54 @@ __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | |||
5782 | 5794 | ||
5783 | len = ntohl(*p++); /* bitmap length */ | 5795 | len = ntohl(*p++); /* bitmap length */ |
5784 | if (len-- > 0) { | 5796 | if (len-- > 0) { |
5797 | p = xdr_inline_decode(xdr, 4); | ||
5798 | if (unlikely(!p)) | ||
5799 | goto out_overflow; | ||
5785 | bitmap[0] = ntohl(*p++); | 5800 | bitmap[0] = ntohl(*p++); |
5786 | if (len-- > 0) { | 5801 | if (len-- > 0) { |
5802 | p = xdr_inline_decode(xdr, 4); | ||
5803 | if (unlikely(!p)) | ||
5804 | goto out_overflow; | ||
5787 | bitmap[1] = ntohl(*p++); | 5805 | bitmap[1] = ntohl(*p++); |
5788 | p += len; | 5806 | p += len; |
5789 | } | 5807 | } |
5790 | } | 5808 | } |
5809 | p = xdr_inline_decode(xdr, 4); | ||
5810 | if (unlikely(!p)) | ||
5811 | goto out_overflow; | ||
5791 | len = XDR_QUADLEN(ntohl(*p++)); /* attribute buffer length */ | 5812 | len = XDR_QUADLEN(ntohl(*p++)); /* attribute buffer length */ |
5792 | if (len > 0) { | 5813 | if (len > 0) { |
5793 | if (bitmap[0] & FATTR4_WORD0_RDATTR_ERROR) { | 5814 | if (bitmap[0] & FATTR4_WORD0_RDATTR_ERROR) { |
5794 | bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR; | 5815 | bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR; |
5795 | /* Ignore the return value of rdattr_error for now */ | 5816 | /* Ignore the return value of rdattr_error for now */ |
5796 | p++; | 5817 | p = xdr_inline_decode(xdr, 4); |
5797 | len--; | 5818 | if (unlikely(!p)) |
5819 | goto out_overflow; | ||
5798 | } | 5820 | } |
5799 | if (bitmap[0] == 0 && bitmap[1] == FATTR4_WORD1_MOUNTED_ON_FILEID) | 5821 | if (bitmap[0] == 0 && bitmap[1] == FATTR4_WORD1_MOUNTED_ON_FILEID) { |
5822 | p = xdr_inline_decode(xdr, 8); | ||
5823 | if (unlikely(!p)) | ||
5824 | goto out_overflow; | ||
5800 | xdr_decode_hyper(p, &entry->ino); | 5825 | xdr_decode_hyper(p, &entry->ino); |
5801 | else if (bitmap[0] == FATTR4_WORD0_FILEID) | 5826 | } else if (bitmap[0] == FATTR4_WORD0_FILEID) { |
5827 | p = xdr_inline_decode(xdr, 8); | ||
5828 | if (unlikely(!p)) | ||
5829 | goto out_overflow; | ||
5802 | xdr_decode_hyper(p, &entry->ino); | 5830 | xdr_decode_hyper(p, &entry->ino); |
5803 | p += len; | 5831 | } |
5804 | } | 5832 | } |
5805 | 5833 | ||
5806 | entry->eof = !p[0] && p[1]; | 5834 | p = xdr_inline_peek(xdr, 8); |
5835 | if (p != NULL) | ||
5836 | entry->eof = !p[0] && p[1]; | ||
5837 | else | ||
5838 | entry->eof = 0; | ||
5839 | |||
5807 | return p; | 5840 | return p; |
5841 | |||
5842 | out_overflow: | ||
5843 | print_overflow_msg(__func__, xdr); | ||
5844 | return ERR_PTR(-EIO); | ||
5808 | } | 5845 | } |
5809 | 5846 | ||
5810 | /* | 5847 | /* |