]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Fix B-tree search in BFS, especially in presence of non-ASCII
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 3 May 2012 18:29:10 +0000 (20:29 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 3 May 2012 18:29:10 +0000 (20:29 +0200)
characters.

* grub-core/fs/bfs.c (bfs_strcmp): New function.
(find_in_b_tree): Use standard bsearch + btree algorithm.

ChangeLog
grub-core/fs/bfs.c

index 030df5fc5bcd8d1f5e8d1a267c5f7ed005b028cc..c8f003edb1c1f9b15f9b94bd27729dc7340fdf2d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
index ab7f76abfd947029b12e2c342f6ea78b3546f891..ead4dfac4219241c9710150593cabc6ca705c933 100644 (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;
          }