+ /* Create the new nk-record. */
+ static const char nk_id[2] = { 'n', 'k' };
+ size_t seg_len = sizeof (struct ntreg_nk_record) + strlen (name);
+ hive_node_h node = allocate_block (h, seg_len, nk_id);
+ if (node == 0)
+ return 0;
+
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_node_add_child: allocated new nk-record for child at 0x%zx\n", node);
+
+ struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
+ nk->flags = htole16 (0x0020); /* key is ASCII. */
+ nk->parent = htole32 (parent - 0x1000);
+ nk->subkey_lf = htole32 (0xffffffff);
+ nk->subkey_lf_volatile = htole32 (0xffffffff);
+ nk->vallist = htole32 (0xffffffff);
+ nk->classname = htole32 (0xffffffff);
+ nk->name_len = htole16 (strlen (name));
+ strcpy (nk->name, name);
+
+ /* Inherit parent sk. */
+ struct ntreg_nk_record *parent_nk =
+ (struct ntreg_nk_record *) (h->addr + parent);
+ size_t parent_sk_offset = le32toh (parent_nk->sk);
+ parent_sk_offset += 0x1000;
+ if (!IS_VALID_BLOCK (h, parent_sk_offset) ||
+ !BLOCK_ID_EQ (h, parent_sk_offset, "sk")) {
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_node_add_child: returning EFAULT because parent sk is not a valid block (%zu)\n",
+ parent_sk_offset);
+ errno = EFAULT;
+ return 0;
+ }
+ struct ntreg_sk_record *sk =
+ (struct ntreg_sk_record *) (h->addr + parent_sk_offset);
+ sk->refcount = htole32 (le32toh (sk->refcount) + 1);
+ nk->sk = htole32 (parent_sk_offset - 0x1000);
+
+ /* Inherit parent timestamp. */
+ memcpy (nk->timestamp, parent_nk->timestamp, sizeof (parent_nk->timestamp));
+
+ /* What I found out the hard way (not documented anywhere): the
+ * subkeys in lh-records must be kept sorted. If you just add a
+ * subkey in a non-sorted position (eg. just add it at the end) then
+ * 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.
+ *
+ * The only other case is the no-subkeys case, where we have to
+ * create a brand new lh-record.
+ */
+ hive_node_h *unused;
+ size_t *blocks;
+
+ if (get_children (h, parent, &unused, &blocks, 0) == -1)
+ return 0;
+ free (unused);
+
+ size_t i, j;
+ size_t nr_subkeys_in_parent_nk = le32toh (parent_nk->nr_subkeys);
+ if (nr_subkeys_in_parent_nk == 0) { /* No subkeys case. */
+ /* Free up any existing intermediate blocks. */
+ for (i = 0; blocks[i] != 0; ++i)
+ mark_block_unused (h, blocks[i]);
+ size_t lh_offs = new_lh_record (h, name, node);
+ if (lh_offs == 0) {
+ free (blocks);
+ return 0;
+ }
+
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_node_add_child: no keys, allocated new lh-record at 0x%zx\n", lh_offs);
+
+ parent_nk->subkey_lf = htole32 (lh_offs - 0x1000);
+ }
+ else { /* Insert subkeys case. */
+ size_t old_offs = 0, new_offs = 0;
+ struct ntreg_lf_record *old_lf = NULL;
+
+ /* Find lf/lh key name just after the one we are inserting. */
+ 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 *) (h->addr + old_offs);
+ for (j = 0; j < le16toh (old_lf->nr_keys); ++j) {
+ hive_node_h nk_offs = le32toh (old_lf->keys[j].offset);
+ 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);
+
+ /* Insert it. */
+ insert_it:
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_node_add_child: insert key in existing lh-record at 0x%zx, posn %zu\n", old_offs, j);
+
+ new_offs = insert_lf_record (h, old_offs, j, name, node);
+ if (new_offs == 0) {
+ free (blocks);
+ return 0;
+ }
+
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_node_add_child: new lh-record at 0x%zx\n",
+ new_offs);
+
+ /* If the lf/lh-record was directly referenced by the parent nk,
+ * then update the parent nk.
+ */
+ 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")) {
+ struct ntreg_ri_record *ri =
+ (struct ntreg_ri_record *) (h->addr + blocks[i]);
+ for (j = 0; j < le16toh (ri->nr_offsets); ++j)
+ if (le32toh (ri->offset[j] + 0x1000) == old_offs) {
+ ri->offset[j] = htole32 (new_offs - 0x1000);
+ goto found_it;
+ }
+ }
+ }