Fix B-tree search in BFS, especially in presence of non-ASCII

characters.

	* grub-core/fs/bfs.c (bfs_strcmp): New function.
	(find_in_b_tree): Use standard bsearch + btree algorithm.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2012-05-03 20:29:10 +02:00
parent 5700603549
commit aa01499d05
2 changed files with 70 additions and 13 deletions

View file

@ -1,3 +1,11 @@
2012-05-03 Vladimir Serbinenko <phcoder@gmail.com>
Fix B-tree search in BFS, especially in presence of non-ASCII
characters.
* grub-core/fs/bfs.c (bfs_strcmp): New function.
(find_in_b_tree): Use standard bsearch + btree algorithm.
2012-05-03 Vladimir Serbinenko <phcoder@gmail.com>
* util/grub-fstest.c (cmd_cmp): Avoid comparing devices, pipes

View file

@ -506,6 +506,25 @@ iterate_in_b_tree (grub_disk_t disk,
}
}
static int
bfs_strcmp (const char *a, const char *b, grub_size_t alen)
{
while (*b && alen)
{
if (*a != *b)
break;
a++;
b++;
alen--;
}
if (!alen)
return - (int) (grub_uint8_t) *b;
return (int) (grub_uint8_t) *a - (int) (grub_uint8_t) *b;
}
static grub_err_t
find_in_b_tree (grub_disk_t disk,
const struct grub_bfs_superblock *sb,
@ -537,7 +556,6 @@ find_in_b_tree (grub_disk_t disk,
grub_uint16_t keylen_idx[grub_bfs_to_cpu_treehead (node.count_keys)];
grub_uint64_t key_values[grub_bfs_to_cpu_treehead (node.count_keys)];
unsigned i;
grub_uint16_t start = 0, end = 0;
err =
read_bfs_file (disk, sb, ino, node_off + sizeof (node), key_data,
grub_bfs_to_cpu_treehead (node.total_key_len), 0);
@ -566,31 +584,62 @@ find_in_b_tree (grub_disk_t disk,
if (err)
return err;
for (i = 0; i < grub_bfs_to_cpu_treehead (node.count_keys); i++)
int lg, j;
for (lg = 0; grub_bfs_to_cpu_treehead (node.count_keys) >> lg; lg++);
i = 0;
for (j = lg - 1; j >= 0; j--)
{
int cmp;
char c;
start = end;
end = grub_bfs_to_cpu16 (keylen_idx[i]);
grub_uint16_t start = 0, end = 0;
if ((i | (1 << j)) >= grub_bfs_to_cpu_treehead (node.count_keys))
continue;
start = grub_bfs_to_cpu16 (keylen_idx[(i | (1 << j)) - 1]);
end = grub_bfs_to_cpu16 (keylen_idx[(i | (1 << j))]);
if (grub_bfs_to_cpu_treehead (node.total_key_len) <= end)
end = grub_bfs_to_cpu_treehead (node.total_key_len);
c = key_data[end];
key_data[end] = 0;
cmp = grub_strcmp (key_data + start, name);
key_data[end] = c;
cmp = bfs_strcmp (key_data + start, name, end - start);
if (cmp == 0 && level == 0)
{
*res = grub_bfs_to_cpu64 (key_values[i]);
*res = grub_bfs_to_cpu64 (key_values[i | (1 << j)]);
return GRUB_ERR_NONE;
}
if (cmp < 0)
i |= (1 << j);
}
if (i == 0)
{
grub_uint16_t end = 0;
int cmp;
end = grub_bfs_to_cpu16 (keylen_idx[0]);
if (grub_bfs_to_cpu_treehead (node.total_key_len) <= end)
end = grub_bfs_to_cpu_treehead (node.total_key_len);
cmp = bfs_strcmp (key_data, name, end);
if (cmp == 0 && level == 0)
{
*res = grub_bfs_to_cpu64 (key_values[0]);
return GRUB_ERR_NONE;
}
if (cmp >= 0 && level != 0)
{
node_off = grub_bfs_to_cpu64 (key_values[i]);
break;
node_off = grub_bfs_to_cpu64 (key_values[0]);
level--;
continue;
}
else if (level != 0
&& grub_bfs_to_cpu_treehead (node.count_keys) >= 2)
{
node_off = grub_bfs_to_cpu64 (key_values[1]);
level--;
continue;
}
}
if (i < grub_bfs_to_cpu_treehead (node.count_keys))
else if (level != 0
&& i + 1 < grub_bfs_to_cpu_treehead (node.count_keys))
{
node_off = grub_bfs_to_cpu64 (key_values[i + 1]);
level--;
continue;
}