+/* Call @cb on each subkey cell of the key @nk. */
+static enum hive_status
+iterate_subkeys(const struct regf *regf, const struct nk *nk,
+ subkey_cb_t cb, void *cb_ctx)
+{
+ u32 num_subkeys = le32_to_cpu(nk->num_subkeys);
+ struct subkey_iteration_stats stats;
+ enum hive_status status;
+
+ if (num_subkeys == 0)
+ return HIVE_OK;
+
+ if (num_subkeys > MAX_SUBKEYS)
+ return HIVE_CORRUPT;
+
+ stats.levels_remaining = MAX_SUBKEY_LIST_LEVELS;
+ stats.subkey_lists_remaining = MAX_SUBKEY_LISTS;
+ stats.subkeys_remaining = num_subkeys;
+
+ status = iterate_subkeys_recursive(regf, nk->subkey_list_offset,
+ cb, cb_ctx, &stats);
+ if (stats.subkeys_remaining != 0 && status == HIVE_OK)
+ status = HIVE_CORRUPT;
+ return status;
+}
+
+struct lookup_subkey_ctx {
+ const utf16lechar *key_name;
+ size_t key_name_nchars;
+ const struct nk *result;
+};
+
+static enum hive_status
+lookup_subkey_cb(const struct nk *sub_nk, void *_ctx)
+{
+ struct lookup_subkey_ctx *ctx = _ctx;
+
+ if (names_equal(ctx->key_name, ctx->key_name_nchars,
+ sub_nk->name, le16_to_cpu(sub_nk->name_size),
+ (sub_nk->flags & NK_COMPRESSED_NAME)))
+ {
+ ctx->result = sub_nk;
+ return HIVE_ITERATION_STOPPED;
+ }
+
+ return HIVE_OK;
+}
+
+/*
+ * Given a registry key cell @nk, look up the next component of the key
+ * *key_namep. If found, return HIVE_OK, advance *key_namep past the key name
+ * component, and return the subkey cell in @sub_nk_ret. Otherwise, return
+ * another HIVE_* error code.
+ */
+static enum hive_status
+lookup_subkey(const struct regf *regf, const utf16lechar **key_namep,
+ const struct nk *nk, const struct nk **sub_nk_ret)
+{
+ const utf16lechar *key_name = *key_namep;
+ size_t key_name_nchars = 0;
+ struct lookup_subkey_ctx ctx;
+ enum hive_status status;
+
+ while (key_name[key_name_nchars] != cpu_to_le16('\0') &&
+ key_name[key_name_nchars] != cpu_to_le16('\\'))
+ key_name_nchars++;
+
+ ctx.key_name = key_name;
+ ctx.key_name_nchars = key_name_nchars;
+ ctx.result = NULL;
+
+ status = iterate_subkeys(regf, nk, lookup_subkey_cb, &ctx);
+ if (!ctx.result) {
+ if (status == HIVE_OK)
+ status = HIVE_KEY_NOT_FOUND;
+ return status;
+ }
+
+ key_name += key_name_nchars;
+ while (*key_name == cpu_to_le16('\\'))
+ key_name++;
+ *key_namep = key_name;
+ *sub_nk_ret = ctx.result;
+ return HIVE_OK;