|
@@ -140,7 +140,8 @@ struct dx_frame
|
|
struct dx_map_entry
|
|
struct dx_map_entry
|
|
{
|
|
{
|
|
u32 hash;
|
|
u32 hash;
|
|
- u32 offs;
|
|
|
|
|
|
+ u16 offs;
|
|
|
|
+ u16 size;
|
|
};
|
|
};
|
|
|
|
|
|
#ifdef CONFIG_EXT3_INDEX
|
|
#ifdef CONFIG_EXT3_INDEX
|
|
@@ -697,6 +698,10 @@ errout:
|
|
* Directory block splitting, compacting
|
|
* Directory block splitting, compacting
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Create map of hash values, offsets, and sizes, stored at end of block.
|
|
|
|
+ * Returns number of entries mapped.
|
|
|
|
+ */
|
|
static int dx_make_map (struct ext3_dir_entry_2 *de, int size,
|
|
static int dx_make_map (struct ext3_dir_entry_2 *de, int size,
|
|
struct dx_hash_info *hinfo, struct dx_map_entry *map_tail)
|
|
struct dx_hash_info *hinfo, struct dx_map_entry *map_tail)
|
|
{
|
|
{
|
|
@@ -710,7 +715,8 @@ static int dx_make_map (struct ext3_dir_entry_2 *de, int size,
|
|
ext3fs_dirhash(de->name, de->name_len, &h);
|
|
ext3fs_dirhash(de->name, de->name_len, &h);
|
|
map_tail--;
|
|
map_tail--;
|
|
map_tail->hash = h.hash;
|
|
map_tail->hash = h.hash;
|
|
- map_tail->offs = (u32) ((char *) de - base);
|
|
|
|
|
|
+ map_tail->offs = (u16) ((char *) de - base);
|
|
|
|
+ map_tail->size = le16_to_cpu(de->rec_len);
|
|
count++;
|
|
count++;
|
|
cond_resched();
|
|
cond_resched();
|
|
}
|
|
}
|
|
@@ -720,6 +726,7 @@ static int dx_make_map (struct ext3_dir_entry_2 *de, int size,
|
|
return count;
|
|
return count;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* Sort map by hash value */
|
|
static void dx_sort_map (struct dx_map_entry *map, unsigned count)
|
|
static void dx_sort_map (struct dx_map_entry *map, unsigned count)
|
|
{
|
|
{
|
|
struct dx_map_entry *p, *q, *top = map + count - 1;
|
|
struct dx_map_entry *p, *q, *top = map + count - 1;
|
|
@@ -1117,6 +1124,10 @@ static inline void ext3_set_de_type(struct super_block *sb,
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_EXT3_INDEX
|
|
#ifdef CONFIG_EXT3_INDEX
|
|
|
|
+/*
|
|
|
|
+ * Move count entries from end of map between two memory locations.
|
|
|
|
+ * Returns pointer to last entry moved.
|
|
|
|
+ */
|
|
static struct ext3_dir_entry_2 *
|
|
static struct ext3_dir_entry_2 *
|
|
dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count)
|
|
dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count)
|
|
{
|
|
{
|
|
@@ -1135,6 +1146,10 @@ dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count)
|
|
return (struct ext3_dir_entry_2 *) (to - rec_len);
|
|
return (struct ext3_dir_entry_2 *) (to - rec_len);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Compact each dir entry in the range to the minimal rec_len.
|
|
|
|
+ * Returns pointer to last entry in range.
|
|
|
|
+ */
|
|
static struct ext3_dir_entry_2* dx_pack_dirents(char *base, int size)
|
|
static struct ext3_dir_entry_2* dx_pack_dirents(char *base, int size)
|
|
{
|
|
{
|
|
struct ext3_dir_entry_2 *next, *to, *prev, *de = (struct ext3_dir_entry_2 *) base;
|
|
struct ext3_dir_entry_2 *next, *to, *prev, *de = (struct ext3_dir_entry_2 *) base;
|
|
@@ -1157,6 +1172,11 @@ static struct ext3_dir_entry_2* dx_pack_dirents(char *base, int size)
|
|
return prev;
|
|
return prev;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Split a full leaf block to make room for a new dir entry.
|
|
|
|
+ * Allocate a new block, and move entries so that they are approx. equally full.
|
|
|
|
+ * Returns pointer to de in block into which the new entry will be inserted.
|
|
|
|
+ */
|
|
static struct ext3_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
|
|
static struct ext3_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
|
|
struct buffer_head **bh,struct dx_frame *frame,
|
|
struct buffer_head **bh,struct dx_frame *frame,
|
|
struct dx_hash_info *hinfo, int *error)
|
|
struct dx_hash_info *hinfo, int *error)
|
|
@@ -1168,7 +1188,7 @@ static struct ext3_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
|
|
u32 hash2;
|
|
u32 hash2;
|
|
struct dx_map_entry *map;
|
|
struct dx_map_entry *map;
|
|
char *data1 = (*bh)->b_data, *data2;
|
|
char *data1 = (*bh)->b_data, *data2;
|
|
- unsigned split;
|
|
|
|
|
|
+ unsigned split, move, size, i;
|
|
struct ext3_dir_entry_2 *de = NULL, *de2;
|
|
struct ext3_dir_entry_2 *de = NULL, *de2;
|
|
int err = 0;
|
|
int err = 0;
|
|
|
|
|
|
@@ -1196,8 +1216,19 @@ static struct ext3_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
|
|
count = dx_make_map ((struct ext3_dir_entry_2 *) data1,
|
|
count = dx_make_map ((struct ext3_dir_entry_2 *) data1,
|
|
blocksize, hinfo, map);
|
|
blocksize, hinfo, map);
|
|
map -= count;
|
|
map -= count;
|
|
- split = count/2; // need to adjust to actual middle
|
|
|
|
dx_sort_map (map, count);
|
|
dx_sort_map (map, count);
|
|
|
|
+ /* Split the existing block in the middle, size-wise */
|
|
|
|
+ size = 0;
|
|
|
|
+ move = 0;
|
|
|
|
+ for (i = count-1; i >= 0; i--) {
|
|
|
|
+ /* is more than half of this entry in 2nd half of the block? */
|
|
|
|
+ if (size + map[i].size/2 > blocksize/2)
|
|
|
|
+ break;
|
|
|
|
+ size += map[i].size;
|
|
|
|
+ move++;
|
|
|
|
+ }
|
|
|
|
+ /* map index at which we will split */
|
|
|
|
+ split = count - move;
|
|
hash2 = map[split].hash;
|
|
hash2 = map[split].hash;
|
|
continued = hash2 == map[split - 1].hash;
|
|
continued = hash2 == map[split - 1].hash;
|
|
dxtrace(printk("Split block %i at %x, %i/%i\n",
|
|
dxtrace(printk("Split block %i at %x, %i/%i\n",
|