From: Vladimir 'phcoder' Serbinenko Date: Thu, 3 May 2012 18:29:10 +0000 (+0200) Subject: Fix B-tree search in BFS, especially in presence of non-ASCII X-Git-Tag: 2.00~266 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aa01499d052e3e609ca915f67ea6dca32d950686;p=thirdparty%2Fgrub.git 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. --- diff --git a/ChangeLog b/ChangeLog index 030df5fc5..c8f003edb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2012-05-03 Vladimir Serbinenko + + 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 * util/grub-fstest.c (cmd_cmp): Avoid comparing devices, pipes diff --git a/grub-core/fs/bfs.c b/grub-core/fs/bfs.c index ab7f76abf..ead4dfac4 100644 --- a/grub-core/fs/bfs.c +++ b/grub-core/fs/bfs.c @@ -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; }