[Libguestfs] [PATCH hivex 19/19] lib: node_add_child: Handle li-records as intermediate notes (RHBZ#987463).

Richard W.M. Jones rjones at redhat.com
Thu Jul 25 10:39:01 UTC 2013


From: "Richard W.M. Jones" <rjones at redhat.com>

This commit implements adding a child to a registry key that uses
li-records for intermediate nodes.
---
 lib/write.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 114 insertions(+), 25 deletions(-)

diff --git a/lib/write.c b/lib/write.c
index 371b1ab..076cf8c 100644
--- a/lib/write.c
+++ b/lib/write.c
@@ -385,6 +385,57 @@ insert_lf_record (hive_h *h, size_t old_offs, size_t posn,
   return new_offs;
 }
 
+/* Insert node into existing li-record at position.  Pretty much the
+ * same as insert_lf_record above, but the record layout is a bit
+ * different.
+ */
+static size_t
+insert_li_record (hive_h *h, size_t old_offs, size_t posn,
+                  const char *name, hive_node_h node)
+{
+  assert (IS_VALID_BLOCK (h, old_offs));
+  assert (BLOCK_ID_EQ (h, old_offs, "li"));
+
+  struct ntreg_ri_record *old_li =
+    (struct ntreg_ri_record *) ((char *) h->addr + old_offs);
+  size_t nr_offsets = le16toh (old_li->nr_offsets);
+
+  nr_offsets++; /* in new record ... */
+
+  size_t seg_len = sizeof (struct ntreg_ri_record) + (nr_offsets-1) * 4;
+
+  /* Copy the old_li->id in case it moves during allocate_block. */
+  char id[2];
+  memcpy (id, old_li->id, sizeof id);
+
+  size_t new_offs = allocate_block (h, seg_len, id);
+  if (new_offs == 0)
+    return 0;
+
+  /* old_li could have been invalidated by allocate_block. */
+  old_li = (struct ntreg_ri_record *) ((char *) h->addr + old_offs);
+
+  struct ntreg_ri_record *new_li =
+    (struct ntreg_ri_record *) ((char *) h->addr + new_offs);
+  new_li->nr_offsets = htole16 (nr_offsets);
+
+  /* Copy the offsets until we reach posn, insert the new offset
+   * there, then copy the remaining offsets.
+   */
+  size_t i;
+  for (i = 0; i < posn; ++i)
+    new_li->offset[i] = old_li->offset[i];
+
+  new_li->offset[i] = htole32 (node - 0x1000);
+
+  for (i = posn+1; i < nr_offsets; ++i)
+    new_li->offset[i] = old_li->offset[i-1];
+
+  /* Old block is unused, return new block. */
+  mark_block_unused (h, old_offs);
+  return new_offs;
+}
+
 /* Compare name with name in nk-record. */
 static int
 compare_name_with_nk_name (hive_h *h, const char *name, hive_node_h nk_offs)
@@ -407,19 +458,34 @@ compare_name_with_nk_name (hive_h *h, const char *name, hive_node_h nk_offs)
   return r;
 }
 
+/* See comment about keeping things sorted in hivex_node_add_child. */
 static int
 insert_subkey (hive_h *h, const char *name,
                size_t parent, size_t nkoffset, size_t *blocks)
 {
   size_t old_offs = 0, new_offs = 0;
-  size_t i, j;
+  size_t i, j = SIZE_MAX;
   struct ntreg_lf_record *old_lf = NULL;
+  struct ntreg_ri_record *old_li = NULL;
 
-  /* Find lf/lh key name just after the one we are inserting. */
+  /* The caller already dealt with the no subkeys case, so this should
+   * be true.
+   */
+  assert (blocks[0] != 0);
+
+  /* Find the intermediate block which contains a link to the key that
+   * is just after the one we are inserting.  This intermediate block
+   * might be an lf/lh-record or an li-record (but it won't be a
+   * ri-record so we can ignore those).  The lf/lh- and li-records
+   * have different formats.  If we cannot find the key after, then we
+   * end up at the final block and we have to insert the new key at
+   * the end.
+   */
   for (i = 0; blocks[i] != 0; ++i) {
     if (BLOCK_ID_EQ (h, blocks[i], "lf") || BLOCK_ID_EQ (h, blocks[i], "lh")) {
       old_offs = blocks[i];
       old_lf = (struct ntreg_lf_record *) ((char *) h->addr + old_offs);
+      old_li = NULL;
       for (j = 0; j < le16toh (old_lf->nr_keys); ++j) {
         hive_node_h nk_offs = le32toh (old_lf->keys[j].offset);
         nk_offs += 0x1000;
@@ -427,39 +493,62 @@ insert_subkey (hive_h *h, const char *name,
           goto insert_it;
       }
     }
+    else if (BLOCK_ID_EQ (h, blocks[i], "li")) {
+      old_offs = blocks[i];
+      old_lf = NULL;
+      old_li = (struct ntreg_ri_record *) ((char *) h->addr + old_offs);
+      for (j = 0; j < le16toh (old_li->nr_offsets); ++j) {
+        hive_node_h nk_offs = le32toh (old_li->offset[j]);
+        nk_offs += 0x1000;
+        if (compare_name_with_nk_name (h, name, nk_offs) < 0)
+          goto insert_it;
+      }
+    }
   }
 
-  /* Insert it at the end.
-   * old_offs points to the last lf record, set j.
-   */
-  assert (old_offs != 0);  /* should never happen if nr_subkeys > 0 */
-  j = le16toh (old_lf->nr_keys);
+  /* To insert it at the end, we fall through here. */
+  assert (j != SIZE_MAX);
 
-  /* Insert it. */
  insert_it:
-  DEBUG (2, "insert key in existing lh-record at 0x%zx, posn %zu",
-         old_offs, j);
+  /* Verify that the search worked. */
+  assert (old_lf || old_li);
+  assert (!(old_lf && old_li));
 
-  new_offs = insert_lf_record (h, old_offs, j, name, nkoffset);
-  if (new_offs == 0)
-    return -1;
+  if (old_lf) {
+    DEBUG (2, "insert key in existing lf/lh-record at 0x%zx, posn %zu",
+           old_offs, j);
 
-  DEBUG (2, "new lh-record at 0x%zx", new_offs);
+    new_offs = insert_lf_record (h, old_offs, j, name, nkoffset);
+    if (new_offs == 0)
+      return -1;
+
+    DEBUG (2, "new lf/lh-record at 0x%zx", new_offs);
+  }
+  else /* old_li */ {
+    DEBUG (2, "insert key in existing li-record at 0x%zx, posn %zu",
+           old_offs, j);
+
+    new_offs = insert_li_record (h, old_offs, j, name, nkoffset);
+    if (new_offs == 0)
+      return -1;
+
+    DEBUG (2, "new li-record at 0x%zx", new_offs);
+  }
 
   /* Recalculate pointers that could have been invalidated by
-   * previous call to allocate_block (via new_lh_record).
+   * previous call to allocate_block (via new_{lf,li}_record).
    */
   struct ntreg_nk_record *parent_nk =
     (struct ntreg_nk_record *) ((char *) h->addr + parent);
 
-  /* If the lf/lh-record was directly referenced by the parent nk,
-   * then update the parent nk.
+  /* Since the lf/lh/li-record has moved, now we have to find the old
+   * reference to it and update it.  It might be referenced directly
+   * from the parent_nk->subkey_lf, or it might be referenced
+   * indirectly from some ri-record in blocks[].  Since we can update
+   * either of these in-place, we don't need to do this recursively.
    */
   if (le32toh (parent_nk->subkey_lf) + 0x1000 == old_offs)
     parent_nk->subkey_lf = htole32 (new_offs - 0x1000);
-  /* Else we have to look for the intermediate ri-record and update
-   * that in-place.
-   */
   else {
     for (i = 0; blocks[i] != 0; ++i) {
       if (BLOCK_ID_EQ (h, blocks[i], "ri")) {
@@ -548,11 +637,11 @@ hivex_node_add_child (hive_h *h, hive_node_h parent, const char *name)
    * Windows won't see the subkey _and_ Windows will corrupt the hive
    * itself when it modifies or saves it.
    *
-   * So use get_children() to get a list of intermediate
-   * lf/lh-records.  get_children() returns these in reading order
-   * (which is sorted), so we look for the lf/lh-records in sequence
-   * until we find the key name just after the one we are inserting,
-   * and we insert the subkey just before it.
+   * So use get_children() to get a list of intermediate records.
+   * get_children() returns these in reading order (which is sorted),
+   * so we look for the lf/lh/li-records in sequence until we find the
+   * key name just after the one we are inserting, and we insert the
+   * subkey just before it.
    *
    * The only other case is the no-subkeys case, where we have to
    * create a brand new lh-record.
-- 
1.8.3.1




More information about the Libguestfs mailing list