+/* Calculate the hash for a lf or lh record offset.
+ */
+static void
+calc_hash (const char *type, const char *name, char *ret)
+{
+ size_t len = strlen (name);
+
+ if (STRPREFIX (type, "lf"))
+ /* Old-style, not used in current registries. */
+ memcpy (ret, name, len < 4 ? len : 4);
+ else {
+ /* New-style for lh-records. */
+ size_t i, c;
+ uint32_t h = 0;
+ for (i = 0; i < len; ++i) {
+ c = c_toupper (name[i]);
+ h *= 37;
+ h += c;
+ }
+ *((uint32_t *) ret) = htole32 (h);
+ }
+}
+
+/* Create a completely new lh-record containing just the single node. */
+static size_t
+new_lh_record (hive_h *h, const char *name, hive_node_h node)
+{
+ static const char id[2] = { 'l', 'h' };
+ size_t seg_len = sizeof (struct ntreg_lf_record);
+ size_t offset = allocate_block (h, seg_len, id);
+ if (offset == 0)
+ return 0;
+
+ struct ntreg_lf_record *lh = (struct ntreg_lf_record *) (h->addr + offset);
+ lh->nr_keys = htole16 (1);
+ lh->keys[0].offset = htole32 (node - 0x1000);
+ calc_hash ("lh", name, lh->keys[0].hash);
+
+ return offset;
+}
+
+/* Insert node into existing lf/lh-record at position.
+ * This allocates a new record and marks the old one as unused.
+ */
+static size_t
+insert_lf_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));
+
+ /* Work around C stupidity.
+ * http://www.redhat.com/archives/libguestfs/2010-February/msg00056.html
+ */
+ int test = BLOCK_ID_EQ (h, old_offs, "lf") || BLOCK_ID_EQ (h, old_offs, "lh");
+ assert (test);
+
+ struct ntreg_lf_record *old_lf =
+ (struct ntreg_lf_record *) (h->addr + old_offs);
+ size_t nr_keys = le16toh (old_lf->nr_keys);
+
+ nr_keys++; /* in new record ... */
+
+ size_t seg_len = sizeof (struct ntreg_lf_record) + (nr_keys-1) * 8;
+ size_t new_offs = allocate_block (h, seg_len, old_lf->id);
+ if (new_offs == 0)
+ return 0;
+
+ struct ntreg_lf_record *new_lf =
+ (struct ntreg_lf_record *) (h->addr + new_offs);
+ new_lf->nr_keys = htole16 (nr_keys);
+
+ /* Copy the keys until we reach posn, insert the new key there, then
+ * copy the remaining keys.
+ */
+ size_t i;
+ for (i = 0; i < posn; ++i)
+ new_lf->keys[i] = old_lf->keys[i];
+
+ new_lf->keys[i].offset = htole32 (node - 0x1000);
+ calc_hash (new_lf->id, name, new_lf->keys[i].hash);
+
+ for (i = posn+1; i < nr_keys; ++i)
+ new_lf->keys[i] = old_lf->keys[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)
+{
+ assert (IS_VALID_BLOCK (h, nk_offs));
+ assert (BLOCK_ID_EQ (h, nk_offs, "nk"));
+
+ /* Name in nk is not necessarily nul-terminated. */
+ char *nname = hivex_node_name (h, nk_offs);
+
+ /* Unfortunately we don't have a way to return errors here. */
+ if (!nname) {
+ perror ("compare_name_with_nk_name");
+ return 0;
+ }
+
+ int r = strcasecmp (name, nname);
+ free (nname);
+
+ return r;
+}
+