diff options
Diffstat (limited to 'fs/gfs2/dir.c')
-rw-r--r-- | fs/gfs2/dir.c | 92 |
1 files changed, 44 insertions, 48 deletions
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 2023dc4ea306..d74a52bda540 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c | |||
@@ -81,9 +81,10 @@ | |||
81 | #define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1) | 81 | #define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1) |
82 | #define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1)) | 82 | #define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1)) |
83 | 83 | ||
84 | typedef int (*leaf_call_t) (struct gfs2_inode *dip, | 84 | typedef int (*leaf_call_t) (struct gfs2_inode *dip, u32 index, u32 len, |
85 | u32 index, u32 len, u64 leaf_no, | 85 | u64 leaf_no, void *data); |
86 | void *data); | 86 | typedef int (*gfs2_dscan_t)(const struct gfs2_dirent *dent, |
87 | const struct qstr *name, void *opaque); | ||
87 | 88 | ||
88 | 89 | ||
89 | int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, u64 block, | 90 | int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, u64 block, |
@@ -348,17 +349,13 @@ fail: | |||
348 | return (copied) ? copied : error; | 349 | return (copied) ? copied : error; |
349 | } | 350 | } |
350 | 351 | ||
351 | typedef int (*gfs2_dscan_t)(const struct gfs2_dirent *dent, | ||
352 | const struct qstr *name, | ||
353 | void *opaque); | ||
354 | |||
355 | static inline int __gfs2_dirent_find(const struct gfs2_dirent *dent, | 352 | static inline int __gfs2_dirent_find(const struct gfs2_dirent *dent, |
356 | const struct qstr *name, int ret) | 353 | const struct qstr *name, int ret) |
357 | { | 354 | { |
358 | if (dent->de_inum.no_addr != 0 && | 355 | if (dent->de_inum.no_addr != 0 && |
359 | be32_to_cpu(dent->de_hash) == name->hash && | 356 | be32_to_cpu(dent->de_hash) == name->hash && |
360 | be16_to_cpu(dent->de_name_len) == name->len && | 357 | be16_to_cpu(dent->de_name_len) == name->len && |
361 | memcmp((char *)(dent+1), name->name, name->len) == 0) | 358 | memcmp(dent+1, name->name, name->len) == 0) |
362 | return ret; | 359 | return ret; |
363 | return 0; | 360 | return 0; |
364 | } | 361 | } |
@@ -483,8 +480,7 @@ wrong_type: | |||
483 | return -1; | 480 | return -1; |
484 | } | 481 | } |
485 | 482 | ||
486 | static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode, | 483 | static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode, void *buf, |
487 | void *buf, | ||
488 | unsigned int len, gfs2_dscan_t scan, | 484 | unsigned int len, gfs2_dscan_t scan, |
489 | const struct qstr *name, | 485 | const struct qstr *name, |
490 | void *opaque) | 486 | void *opaque) |
@@ -500,7 +496,7 @@ static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode, | |||
500 | 496 | ||
501 | offset = ret; | 497 | offset = ret; |
502 | prev = NULL; | 498 | prev = NULL; |
503 | dent = (struct gfs2_dirent *)(buf + offset); | 499 | dent = buf + offset; |
504 | size = be16_to_cpu(dent->de_rec_len); | 500 | size = be16_to_cpu(dent->de_rec_len); |
505 | if (gfs2_check_dirent(dent, offset, size, len, 1)) | 501 | if (gfs2_check_dirent(dent, offset, size, len, 1)) |
506 | goto consist_inode; | 502 | goto consist_inode; |
@@ -512,7 +508,7 @@ static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode, | |||
512 | if (offset == len) | 508 | if (offset == len) |
513 | break; | 509 | break; |
514 | prev = dent; | 510 | prev = dent; |
515 | dent = (struct gfs2_dirent *)(buf + offset); | 511 | dent = buf + offset; |
516 | size = be16_to_cpu(dent->de_rec_len); | 512 | size = be16_to_cpu(dent->de_rec_len); |
517 | if (gfs2_check_dirent(dent, offset, size, len, 0)) | 513 | if (gfs2_check_dirent(dent, offset, size, len, 0)) |
518 | goto consist_inode; | 514 | goto consist_inode; |
@@ -567,6 +563,24 @@ static int dirent_first(struct gfs2_inode *dip, struct buffer_head *bh, | |||
567 | } | 563 | } |
568 | } | 564 | } |
569 | 565 | ||
566 | static int dirent_check_reclen(struct gfs2_inode *dip, | ||
567 | const struct gfs2_dirent *d, const void *end_p) | ||
568 | { | ||
569 | const void *ptr = d; | ||
570 | u16 rec_len = be16_to_cpu(d->de_rec_len); | ||
571 | |||
572 | if (unlikely(rec_len < sizeof(struct gfs2_dirent))) | ||
573 | goto broken; | ||
574 | ptr += rec_len; | ||
575 | if (ptr < end_p) | ||
576 | return rec_len; | ||
577 | if (ptr == end_p) | ||
578 | return -ENOENT; | ||
579 | broken: | ||
580 | gfs2_consist_inode(dip); | ||
581 | return -EIO; | ||
582 | } | ||
583 | |||
570 | /** | 584 | /** |
571 | * dirent_next - Next dirent | 585 | * dirent_next - Next dirent |
572 | * @dip: the directory | 586 | * @dip: the directory |
@@ -579,33 +593,18 @@ static int dirent_first(struct gfs2_inode *dip, struct buffer_head *bh, | |||
579 | static int dirent_next(struct gfs2_inode *dip, struct buffer_head *bh, | 593 | static int dirent_next(struct gfs2_inode *dip, struct buffer_head *bh, |
580 | struct gfs2_dirent **dent) | 594 | struct gfs2_dirent **dent) |
581 | { | 595 | { |
582 | struct gfs2_dirent *tmp, *cur; | 596 | struct gfs2_dirent *cur = *dent, *tmp; |
583 | char *bh_end; | 597 | char *bh_end = bh->b_data + bh->b_size; |
584 | u16 cur_rec_len; | 598 | int ret; |
585 | |||
586 | cur = *dent; | ||
587 | bh_end = bh->b_data + bh->b_size; | ||
588 | cur_rec_len = be16_to_cpu(cur->de_rec_len); | ||
589 | |||
590 | if ((char *)cur + cur_rec_len >= bh_end) { | ||
591 | if ((char *)cur + cur_rec_len > bh_end) { | ||
592 | gfs2_consist_inode(dip); | ||
593 | return -EIO; | ||
594 | } | ||
595 | return -ENOENT; | ||
596 | } | ||
597 | |||
598 | tmp = (struct gfs2_dirent *)((char *)cur + cur_rec_len); | ||
599 | 599 | ||
600 | if ((char *)tmp + be16_to_cpu(tmp->de_rec_len) > bh_end) { | 600 | ret = dirent_check_reclen(dip, cur, bh_end); |
601 | gfs2_consist_inode(dip); | 601 | if (ret < 0) |
602 | return -EIO; | 602 | return ret; |
603 | } | ||
604 | 603 | ||
605 | if (cur_rec_len == 0) { | 604 | tmp = (void *)cur + ret; |
606 | gfs2_consist_inode(dip); | 605 | ret = dirent_check_reclen(dip, tmp, bh_end); |
607 | return -EIO; | 606 | if (ret == -EIO) |
608 | } | 607 | return ret; |
609 | 608 | ||
610 | /* Only the first dent could ever have de_inum.no_addr == 0 */ | 609 | /* Only the first dent could ever have de_inum.no_addr == 0 */ |
611 | if (!tmp->de_inum.no_addr) { | 610 | if (!tmp->de_inum.no_addr) { |
@@ -614,7 +613,6 @@ static int dirent_next(struct gfs2_inode *dip, struct buffer_head *bh, | |||
614 | } | 613 | } |
615 | 614 | ||
616 | *dent = tmp; | 615 | *dent = tmp; |
617 | |||
618 | return 0; | 616 | return 0; |
619 | } | 617 | } |
620 | 618 | ||
@@ -821,9 +819,9 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh, | |||
821 | gfs2_metatype_set(bh, GFS2_METATYPE_LF, GFS2_FORMAT_LF); | 819 | gfs2_metatype_set(bh, GFS2_METATYPE_LF, GFS2_FORMAT_LF); |
822 | leaf = (struct gfs2_leaf *)bh->b_data; | 820 | leaf = (struct gfs2_leaf *)bh->b_data; |
823 | leaf->lf_depth = cpu_to_be16(depth); | 821 | leaf->lf_depth = cpu_to_be16(depth); |
824 | leaf->lf_entries = cpu_to_be16(0); | 822 | leaf->lf_entries = 0; |
825 | leaf->lf_dirent_format = cpu_to_be16(GFS2_FORMAT_DE); | 823 | leaf->lf_dirent_format = cpu_to_be16(GFS2_FORMAT_DE); |
826 | leaf->lf_next = cpu_to_be64(0); | 824 | leaf->lf_next = 0; |
827 | memset(leaf->lf_reserved, 0, sizeof(leaf->lf_reserved)); | 825 | memset(leaf->lf_reserved, 0, sizeof(leaf->lf_reserved)); |
828 | dent = (struct gfs2_dirent *)(leaf+1); | 826 | dent = (struct gfs2_dirent *)(leaf+1); |
829 | gfs2_qstr2dirent(&name, bh->b_size - sizeof(struct gfs2_leaf), dent); | 827 | gfs2_qstr2dirent(&name, bh->b_size - sizeof(struct gfs2_leaf), dent); |
@@ -1152,14 +1150,14 @@ fail: | |||
1152 | 1150 | ||
1153 | static int compare_dents(const void *a, const void *b) | 1151 | static int compare_dents(const void *a, const void *b) |
1154 | { | 1152 | { |
1155 | struct gfs2_dirent *dent_a, *dent_b; | 1153 | const struct gfs2_dirent *dent_a, *dent_b; |
1156 | u32 hash_a, hash_b; | 1154 | u32 hash_a, hash_b; |
1157 | int ret = 0; | 1155 | int ret = 0; |
1158 | 1156 | ||
1159 | dent_a = *(struct gfs2_dirent **)a; | 1157 | dent_a = *(const struct gfs2_dirent **)a; |
1160 | hash_a = be32_to_cpu(dent_a->de_hash); | 1158 | hash_a = be32_to_cpu(dent_a->de_hash); |
1161 | 1159 | ||
1162 | dent_b = *(struct gfs2_dirent **)b; | 1160 | dent_b = *(const struct gfs2_dirent **)b; |
1163 | hash_b = be32_to_cpu(dent_b->de_hash); | 1161 | hash_b = be32_to_cpu(dent_b->de_hash); |
1164 | 1162 | ||
1165 | if (hash_a > hash_b) | 1163 | if (hash_a > hash_b) |
@@ -1175,9 +1173,7 @@ static int compare_dents(const void *a, const void *b) | |||
1175 | else if (len_a < len_b) | 1173 | else if (len_a < len_b) |
1176 | ret = -1; | 1174 | ret = -1; |
1177 | else | 1175 | else |
1178 | ret = memcmp((char *)(dent_a + 1), | 1176 | ret = memcmp(dent_a + 1, dent_b + 1, len_a); |
1179 | (char *)(dent_b + 1), | ||
1180 | len_a); | ||
1181 | } | 1177 | } |
1182 | 1178 | ||
1183 | return ret; | 1179 | return ret; |
@@ -1246,7 +1242,7 @@ static int do_filldir_main(struct gfs2_inode *dip, u64 *offset, | |||
1246 | 1242 | ||
1247 | gfs2_inum_in(&inum, (char *)&dent->de_inum); | 1243 | gfs2_inum_in(&inum, (char *)&dent->de_inum); |
1248 | 1244 | ||
1249 | error = filldir(opaque, (char *)(dent + 1), | 1245 | error = filldir(opaque, (const char *)(dent + 1), |
1250 | be16_to_cpu(dent->de_name_len), | 1246 | be16_to_cpu(dent->de_name_len), |
1251 | off, &inum, | 1247 | off, &inum, |
1252 | be16_to_cpu(dent->de_type)); | 1248 | be16_to_cpu(dent->de_type)); |
@@ -1298,7 +1294,7 @@ static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, | |||
1298 | return 0; | 1294 | return 0; |
1299 | 1295 | ||
1300 | error = -ENOMEM; | 1296 | error = -ENOMEM; |
1301 | larr = vmalloc((leaves + entries) * sizeof(void*)); | 1297 | larr = vmalloc((leaves + entries) * sizeof(void *)); |
1302 | if (!larr) | 1298 | if (!larr) |
1303 | goto out; | 1299 | goto out; |
1304 | darr = (const struct gfs2_dirent **)(larr + leaves); | 1300 | darr = (const struct gfs2_dirent **)(larr + leaves); |