diff options
Diffstat (limited to 'fs/ext3/namei.c')
-rw-r--r-- | fs/ext3/namei.c | 73 |
1 files changed, 65 insertions, 8 deletions
diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 1586807b8177..c1fa1908dba0 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c | |||
@@ -140,7 +140,8 @@ struct dx_frame | |||
140 | struct dx_map_entry | 140 | struct dx_map_entry |
141 | { | 141 | { |
142 | u32 hash; | 142 | u32 hash; |
143 | u32 offs; | 143 | u16 offs; |
144 | u16 size; | ||
144 | }; | 145 | }; |
145 | 146 | ||
146 | #ifdef CONFIG_EXT3_INDEX | 147 | #ifdef CONFIG_EXT3_INDEX |
@@ -379,13 +380,28 @@ dx_probe(struct dentry *dentry, struct inode *dir, | |||
379 | 380 | ||
380 | entries = (struct dx_entry *) (((char *)&root->info) + | 381 | entries = (struct dx_entry *) (((char *)&root->info) + |
381 | root->info.info_length); | 382 | root->info.info_length); |
382 | assert(dx_get_limit(entries) == dx_root_limit(dir, | 383 | |
383 | root->info.info_length)); | 384 | if (dx_get_limit(entries) != dx_root_limit(dir, |
385 | root->info.info_length)) { | ||
386 | ext3_warning(dir->i_sb, __FUNCTION__, | ||
387 | "dx entry: limit != root limit"); | ||
388 | brelse(bh); | ||
389 | *err = ERR_BAD_DX_DIR; | ||
390 | goto fail; | ||
391 | } | ||
392 | |||
384 | dxtrace (printk("Look up %x", hash)); | 393 | dxtrace (printk("Look up %x", hash)); |
385 | while (1) | 394 | while (1) |
386 | { | 395 | { |
387 | count = dx_get_count(entries); | 396 | count = dx_get_count(entries); |
388 | assert (count && count <= dx_get_limit(entries)); | 397 | if (!count || count > dx_get_limit(entries)) { |
398 | ext3_warning(dir->i_sb, __FUNCTION__, | ||
399 | "dx entry: no count or count > limit"); | ||
400 | brelse(bh); | ||
401 | *err = ERR_BAD_DX_DIR; | ||
402 | goto fail2; | ||
403 | } | ||
404 | |||
389 | p = entries + 1; | 405 | p = entries + 1; |
390 | q = entries + count - 1; | 406 | q = entries + count - 1; |
391 | while (p <= q) | 407 | while (p <= q) |
@@ -423,8 +439,15 @@ dx_probe(struct dentry *dentry, struct inode *dir, | |||
423 | if (!(bh = ext3_bread (NULL,dir, dx_get_block(at), 0, err))) | 439 | if (!(bh = ext3_bread (NULL,dir, dx_get_block(at), 0, err))) |
424 | goto fail2; | 440 | goto fail2; |
425 | at = entries = ((struct dx_node *) bh->b_data)->entries; | 441 | at = entries = ((struct dx_node *) bh->b_data)->entries; |
426 | assert (dx_get_limit(entries) == dx_node_limit (dir)); | 442 | if (dx_get_limit(entries) != dx_node_limit (dir)) { |
443 | ext3_warning(dir->i_sb, __FUNCTION__, | ||
444 | "dx entry: limit != node limit"); | ||
445 | brelse(bh); | ||
446 | *err = ERR_BAD_DX_DIR; | ||
447 | goto fail2; | ||
448 | } | ||
427 | frame++; | 449 | frame++; |
450 | frame->bh = NULL; | ||
428 | } | 451 | } |
429 | fail2: | 452 | fail2: |
430 | while (frame >= frame_in) { | 453 | while (frame >= frame_in) { |
@@ -432,6 +455,10 @@ fail2: | |||
432 | frame--; | 455 | frame--; |
433 | } | 456 | } |
434 | fail: | 457 | fail: |
458 | if (*err == ERR_BAD_DX_DIR) | ||
459 | ext3_warning(dir->i_sb, __FUNCTION__, | ||
460 | "Corrupt dir inode %ld, running e2fsck is " | ||
461 | "recommended.", dir->i_ino); | ||
435 | return NULL; | 462 | return NULL; |
436 | } | 463 | } |
437 | 464 | ||
@@ -671,6 +698,10 @@ errout: | |||
671 | * Directory block splitting, compacting | 698 | * Directory block splitting, compacting |
672 | */ | 699 | */ |
673 | 700 | ||
701 | /* | ||
702 | * Create map of hash values, offsets, and sizes, stored at end of block. | ||
703 | * Returns number of entries mapped. | ||
704 | */ | ||
674 | static int dx_make_map (struct ext3_dir_entry_2 *de, int size, | 705 | static int dx_make_map (struct ext3_dir_entry_2 *de, int size, |
675 | struct dx_hash_info *hinfo, struct dx_map_entry *map_tail) | 706 | struct dx_hash_info *hinfo, struct dx_map_entry *map_tail) |
676 | { | 707 | { |
@@ -684,7 +715,8 @@ static int dx_make_map (struct ext3_dir_entry_2 *de, int size, | |||
684 | ext3fs_dirhash(de->name, de->name_len, &h); | 715 | ext3fs_dirhash(de->name, de->name_len, &h); |
685 | map_tail--; | 716 | map_tail--; |
686 | map_tail->hash = h.hash; | 717 | map_tail->hash = h.hash; |
687 | map_tail->offs = (u32) ((char *) de - base); | 718 | map_tail->offs = (u16) ((char *) de - base); |
719 | map_tail->size = le16_to_cpu(de->rec_len); | ||
688 | count++; | 720 | count++; |
689 | cond_resched(); | 721 | cond_resched(); |
690 | } | 722 | } |
@@ -694,6 +726,7 @@ static int dx_make_map (struct ext3_dir_entry_2 *de, int size, | |||
694 | return count; | 726 | return count; |
695 | } | 727 | } |
696 | 728 | ||
729 | /* Sort map by hash value */ | ||
697 | static void dx_sort_map (struct dx_map_entry *map, unsigned count) | 730 | static void dx_sort_map (struct dx_map_entry *map, unsigned count) |
698 | { | 731 | { |
699 | struct dx_map_entry *p, *q, *top = map + count - 1; | 732 | struct dx_map_entry *p, *q, *top = map + count - 1; |
@@ -1091,6 +1124,10 @@ static inline void ext3_set_de_type(struct super_block *sb, | |||
1091 | } | 1124 | } |
1092 | 1125 | ||
1093 | #ifdef CONFIG_EXT3_INDEX | 1126 | #ifdef CONFIG_EXT3_INDEX |
1127 | /* | ||
1128 | * Move count entries from end of map between two memory locations. | ||
1129 | * Returns pointer to last entry moved. | ||
1130 | */ | ||
1094 | static struct ext3_dir_entry_2 * | 1131 | static struct ext3_dir_entry_2 * |
1095 | dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count) | 1132 | dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count) |
1096 | { | 1133 | { |
@@ -1109,6 +1146,10 @@ dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count) | |||
1109 | return (struct ext3_dir_entry_2 *) (to - rec_len); | 1146 | return (struct ext3_dir_entry_2 *) (to - rec_len); |
1110 | } | 1147 | } |
1111 | 1148 | ||
1149 | /* | ||
1150 | * Compact each dir entry in the range to the minimal rec_len. | ||
1151 | * Returns pointer to last entry in range. | ||
1152 | */ | ||
1112 | static struct ext3_dir_entry_2* dx_pack_dirents(char *base, int size) | 1153 | static struct ext3_dir_entry_2* dx_pack_dirents(char *base, int size) |
1113 | { | 1154 | { |
1114 | struct ext3_dir_entry_2 *next, *to, *prev, *de = (struct ext3_dir_entry_2 *) base; | 1155 | struct ext3_dir_entry_2 *next, *to, *prev, *de = (struct ext3_dir_entry_2 *) base; |
@@ -1131,6 +1172,11 @@ static struct ext3_dir_entry_2* dx_pack_dirents(char *base, int size) | |||
1131 | return prev; | 1172 | return prev; |
1132 | } | 1173 | } |
1133 | 1174 | ||
1175 | /* | ||
1176 | * Split a full leaf block to make room for a new dir entry. | ||
1177 | * Allocate a new block, and move entries so that they are approx. equally full. | ||
1178 | * Returns pointer to de in block into which the new entry will be inserted. | ||
1179 | */ | ||
1134 | static struct ext3_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, | 1180 | static struct ext3_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, |
1135 | struct buffer_head **bh,struct dx_frame *frame, | 1181 | struct buffer_head **bh,struct dx_frame *frame, |
1136 | struct dx_hash_info *hinfo, int *error) | 1182 | struct dx_hash_info *hinfo, int *error) |
@@ -1142,7 +1188,7 @@ static struct ext3_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, | |||
1142 | u32 hash2; | 1188 | u32 hash2; |
1143 | struct dx_map_entry *map; | 1189 | struct dx_map_entry *map; |
1144 | char *data1 = (*bh)->b_data, *data2; | 1190 | char *data1 = (*bh)->b_data, *data2; |
1145 | unsigned split; | 1191 | unsigned split, move, size, i; |
1146 | struct ext3_dir_entry_2 *de = NULL, *de2; | 1192 | struct ext3_dir_entry_2 *de = NULL, *de2; |
1147 | int err = 0; | 1193 | int err = 0; |
1148 | 1194 | ||
@@ -1170,8 +1216,19 @@ static struct ext3_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, | |||
1170 | count = dx_make_map ((struct ext3_dir_entry_2 *) data1, | 1216 | count = dx_make_map ((struct ext3_dir_entry_2 *) data1, |
1171 | blocksize, hinfo, map); | 1217 | blocksize, hinfo, map); |
1172 | map -= count; | 1218 | map -= count; |
1173 | split = count/2; // need to adjust to actual middle | ||
1174 | dx_sort_map (map, count); | 1219 | dx_sort_map (map, count); |
1220 | /* Split the existing block in the middle, size-wise */ | ||
1221 | size = 0; | ||
1222 | move = 0; | ||
1223 | for (i = count-1; i >= 0; i--) { | ||
1224 | /* is more than half of this entry in 2nd half of the block? */ | ||
1225 | if (size + map[i].size/2 > blocksize/2) | ||
1226 | break; | ||
1227 | size += map[i].size; | ||
1228 | move++; | ||
1229 | } | ||
1230 | /* map index at which we will split */ | ||
1231 | split = count - move; | ||
1175 | hash2 = map[split].hash; | 1232 | hash2 = map[split].hash; |
1176 | continued = hash2 == map[split - 1].hash; | 1233 | continued = hash2 == map[split - 1].hash; |
1177 | dxtrace(printk("Split block %i at %x, %i/%i\n", | 1234 | dxtrace(printk("Split block %i at %x, %i/%i\n", |