summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Jones <rjones@redhat.com>2010-01-19 12:22:10 +0000
committerRichard Jones <rjones@redhat.com>2010-01-28 11:25:30 +0000
commit09d988e8f1cfa42ef9a5abe5c9c151962aa1d2cc (patch)
treec8d5e6403f67a1b6aebf149033087afc621ee4d6
parenta96877f75061ba35efa9fa5dde9fdcf7a1eeaf2a (diff)
downloadlibguestfs-09d988e8f1cfa42ef9a5abe5c9c151962aa1d2cc.tar.gz
libguestfs-09d988e8f1cfa42ef9a5abe5c9c151962aa1d2cc.tar.xz
libguestfs-09d988e8f1cfa42ef9a5abe5c9c151962aa1d2cc.zip
hivex: Modify children/values functions to return intermediate blocks.
Modify the functions that return child subnodes and values so they can also be used to return a list of the intermediate blocks. This is so we can delete those intermediate blocks (in a later commit). We also introduce an offset_list structure which is used for collecting lists of offsets, ie. lists of nodes, values or blocks. Note that this commit should not change the semantics of the code.
-rw-r--r--hivex/hivex.c238
1 files changed, 168 insertions, 70 deletions
diff --git a/hivex/hivex.c b/hivex/hivex.c
index f7dbd5fe..e1df96a5 100644
--- a/hivex/hivex.c
+++ b/hivex/hivex.c
@@ -620,34 +620,97 @@ hivex_node_classname (hive_h *h, hive_node_h node)
}
#endif
-hive_node_h *
-hivex_node_children (hive_h *h, hive_node_h node)
+/* Structure for returning 0-terminated lists of offsets (nodes,
+ * values, etc).
+ */
+struct offset_list {
+ size_t *offsets;
+ size_t len;
+ size_t alloc;
+};
+
+static void
+init_offset_list (struct offset_list *list)
+{
+ list->len = 0;
+ list->alloc = 0;
+ list->offsets = NULL;
+}
+
+#define INIT_OFFSET_LIST(name) \
+ struct offset_list name; \
+ init_offset_list (&name)
+
+/* Preallocates the offset_list, but doesn't make the contents longer. */
+static int
+grow_offset_list (struct offset_list *list, size_t alloc)
+{
+ assert (alloc >= list->len);
+ size_t *p = realloc (list->offsets, alloc * sizeof (size_t));
+ if (p == NULL)
+ return -1;
+ list->offsets = p;
+ list->alloc = alloc;
+ return 0;
+}
+
+static int
+add_to_offset_list (struct offset_list *list, size_t offset)
+{
+ if (list->len >= list->alloc) {
+ if (grow_offset_list (list, list->alloc ? list->alloc * 2 : 4) == -1)
+ return -1;
+ }
+ list->offsets[list->len] = offset;
+ list->len++;
+ return 0;
+}
+
+static void
+free_offset_list (struct offset_list *list)
+{
+ free (list->offsets);
+}
+
+static size_t *
+return_offset_list (struct offset_list *list)
+{
+ if (add_to_offset_list (list, 0) == -1)
+ return NULL;
+ return list->offsets; /* caller frees */
+}
+
+/* Iterate over children, returning child nodes and intermediate blocks. */
+static int
+get_children (hive_h *h, hive_node_h node,
+ hive_node_h **children_ret, size_t **blocks_ret)
{
if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) {
errno = EINVAL;
- return NULL;
+ return -1;
}
struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
size_t nr_subkeys_in_nk = le32toh (nk->nr_subkeys);
+ INIT_OFFSET_LIST (children);
+ INIT_OFFSET_LIST (blocks);
+
/* Deal with the common "no subkeys" case quickly. */
- hive_node_h *ret;
- if (nr_subkeys_in_nk == 0) {
- ret = malloc (sizeof (hive_node_h));
- if (ret == NULL)
- return NULL;
- ret[0] = 0;
- return ret;
- }
+ if (nr_subkeys_in_nk == 0)
+ goto ok;
/* Arbitrarily limit the number of subkeys we will ever deal with. */
if (nr_subkeys_in_nk > 1000000) {
errno = ERANGE;
- return NULL;
+ goto error;
}
+ /* Preallocate space for the children. */
+ if (grow_offset_list (&children, nr_subkeys_in_nk) == -1)
+ goto error;
+
/* The subkey_lf field can point either to an lf-record, which is
* the common case, or if there are lots of subkeys, to an
* ri-record.
@@ -659,9 +722,12 @@ hivex_node_children (hive_h *h, hive_node_h node)
fprintf (stderr, "hivex_node_children: returning EFAULT because subkey_lf is not a valid block (%zu)\n",
subkey_lf);
errno = EFAULT;
- return NULL;
+ goto error;
}
+ if (add_to_offset_list (&blocks, subkey_lf) == -1)
+ goto error;
+
struct ntreg_hbin_block *block =
(struct ntreg_hbin_block *) (h->addr + subkey_lf);
@@ -682,7 +748,7 @@ hivex_node_children (hive_h *h, hive_node_h node)
if (nr_subkeys_in_nk != nr_subkeys_in_lf) {
errno = ENOTSUP;
- return NULL;
+ goto error;
}
size_t len = block_len (h, subkey_lf, NULL);
@@ -691,16 +757,9 @@ hivex_node_children (hive_h *h, hive_node_h node)
fprintf (stderr, "hivex_node_children: returning EFAULT because too many subkeys (%zu, %zu)\n",
nr_subkeys_in_lf, len);
errno = EFAULT;
- return NULL;
+ goto error;
}
- /* Allocate space for the returned values. Note that
- * nr_subkeys_in_lf is limited to a 16 bit value.
- */
- ret = malloc ((1 + nr_subkeys_in_lf) * sizeof (hive_node_h));
- if (ret == NULL)
- return NULL;
-
size_t i;
for (i = 0; i < nr_subkeys_in_lf; ++i) {
hive_node_h subkey = lf->keys[i].offset;
@@ -710,13 +769,12 @@ hivex_node_children (hive_h *h, hive_node_h node)
fprintf (stderr, "hivex_node_children: returning EFAULT because subkey is not a valid block (0x%zx)\n",
subkey);
errno = EFAULT;
- free (ret);
- return NULL;
+ goto error;
}
- ret[i] = subkey;
+ if (add_to_offset_list (&children, subkey) == -1)
+ goto error;
}
- ret[i] = 0;
- return ret;
+ goto ok;
}
/* Points to ri-record? */
else if (block->id[0] == 'r' && block->id[1] == 'i') {
@@ -734,13 +792,16 @@ hivex_node_children (hive_h *h, hive_node_h node)
fprintf (stderr, "hivex_node_children: returning EFAULT because ri-offset is not a valid block (0x%zx)\n",
offset);
errno = EFAULT;
- return NULL;
+ goto error;
}
if (!BLOCK_ID_EQ (h, offset, "lf") && !BLOCK_ID_EQ (h, offset, "lh")) {
errno = ENOTSUP;
- return NULL;
+ goto error;
}
+ if (add_to_offset_list (&blocks, offset) == -1)
+ goto error;
+
struct ntreg_lf_record *lf =
(struct ntreg_lf_record *) (h->addr + offset);
@@ -753,17 +814,12 @@ hivex_node_children (hive_h *h, hive_node_h node)
if (nr_subkeys_in_nk != count) {
errno = ENOTSUP;
- return NULL;
+ goto error;
}
/* Copy list of children. Note nr_subkeys_in_nk is limited to
* something reasonable above.
*/
- ret = malloc ((1 + nr_subkeys_in_nk) * sizeof (hive_node_h));
- if (ret == NULL)
- return NULL;
-
- count = 0;
for (i = 0; i < nr_offsets; ++i) {
hive_node_h offset = ri->offset[i];
offset += 0x1000;
@@ -772,11 +828,11 @@ hivex_node_children (hive_h *h, hive_node_h node)
fprintf (stderr, "hivex_node_children: returning EFAULT because ri-offset is not a valid block (0x%zx)\n",
offset);
errno = EFAULT;
- return NULL;
+ goto error;
}
if (!BLOCK_ID_EQ (h, offset, "lf") && !BLOCK_ID_EQ (h, offset, "lh")) {
errno = ENOTSUP;
- return NULL;
+ goto error;
}
struct ntreg_lf_record *lf =
@@ -791,20 +847,40 @@ hivex_node_children (hive_h *h, hive_node_h node)
fprintf (stderr, "hivex_node_children: returning EFAULT because indirect subkey is not a valid block (0x%zx)\n",
subkey);
errno = EFAULT;
- free (ret);
- return NULL;
+ goto error;
}
- ret[count++] = subkey;
+ if (add_to_offset_list (&children, subkey) == -1)
+ goto error;
}
}
- ret[count] = 0;
-
- return ret;
+ goto ok;
}
- else {
- errno = ENOTSUP;
+ /* else not supported, set errno and fall through */
+ errno = ENOTSUP;
+ error:
+ free_offset_list (&children);
+ free_offset_list (&blocks);
+ return -1;
+
+ ok:
+ *children_ret = return_offset_list (&children);
+ *blocks_ret = return_offset_list (&blocks);
+ if (!*children_ret || !*blocks_ret)
+ goto error;
+ return 0;
+}
+
+hive_node_h *
+hivex_node_children (hive_h *h, hive_node_h node)
+{
+ hive_node_h *children;
+ size_t *blocks;
+
+ if (get_children (h, node, &children, &blocks) == -1)
return NULL;
- }
+
+ free (blocks);
+ return children;
}
/* Very inefficient, but at least having a separate API call
@@ -859,12 +935,13 @@ hivex_node_parent (hive_h *h, hive_node_h node)
return ret;
}
-hive_value_h *
-hivex_node_values (hive_h *h, hive_node_h node)
+static int
+get_values (hive_h *h, hive_node_h node,
+ hive_value_h **values_ret, size_t **blocks_ret)
{
if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) {
errno = EINVAL;
- return 0;
+ return -1;
}
struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
@@ -874,22 +951,23 @@ hivex_node_values (hive_h *h, hive_node_h node)
if (h->msglvl >= 2)
fprintf (stderr, "hivex_node_values: nr_values = %zu\n", nr_values);
+ INIT_OFFSET_LIST (values);
+ INIT_OFFSET_LIST (blocks);
+
/* Deal with the common "no values" case quickly. */
- hive_node_h *ret;
- if (nr_values == 0) {
- ret = malloc (sizeof (hive_node_h));
- if (ret == NULL)
- return NULL;
- ret[0] = 0;
- return ret;
- }
+ if (nr_values == 0)
+ goto ok;
/* Arbitrarily limit the number of values we will ever deal with. */
if (nr_values > 100000) {
errno = ERANGE;
- return NULL;
+ goto error;
}
+ /* Preallocate space for the values. */
+ if (grow_offset_list (&values, nr_values) == -1)
+ goto error;
+
/* Get the value list and check it looks reasonable. */
size_t vlist_offset = le32toh (nk->vallist);
vlist_offset += 0x1000;
@@ -898,9 +976,12 @@ hivex_node_values (hive_h *h, hive_node_h node)
fprintf (stderr, "hivex_node_values: returning EFAULT because value list is not a valid block (0x%zx)\n",
vlist_offset);
errno = EFAULT;
- return NULL;
+ goto error;
}
+ if (add_to_offset_list (&blocks, vlist_offset) == -1)
+ goto error;
+
struct ntreg_value_list *vlist =
(struct ntreg_value_list *) (h->addr + vlist_offset);
@@ -910,14 +991,9 @@ hivex_node_values (hive_h *h, hive_node_h node)
fprintf (stderr, "hivex_node_values: returning EFAULT because value list is too long (%zu, %zu)\n",
nr_values, len);
errno = EFAULT;
- return NULL;
+ goto error;
}
- /* Allocate return array and copy values in. */
- ret = malloc ((1 + nr_values) * sizeof (hive_node_h));
- if (ret == NULL)
- return NULL;
-
size_t i;
for (i = 0; i < nr_values; ++i) {
hive_node_h value = vlist->offset[i];
@@ -927,14 +1003,36 @@ hivex_node_values (hive_h *h, hive_node_h node)
fprintf (stderr, "hivex_node_values: returning EFAULT because value is not a valid block (0x%zx)\n",
value);
errno = EFAULT;
- free (ret);
- return NULL;
+ goto error;
}
- ret[i] = value;
+ if (add_to_offset_list (&values, value) == -1)
+ goto error;
}
- ret[i] = 0;
- return ret;
+ ok:
+ *values_ret = return_offset_list (&values);
+ *blocks_ret = return_offset_list (&blocks);
+ if (!*values_ret || !*blocks_ret)
+ goto error;
+ return 0;
+
+ error:
+ free_offset_list (&values);
+ free_offset_list (&blocks);
+ return -1;
+}
+
+hive_value_h *
+hivex_node_values (hive_h *h, hive_node_h node)
+{
+ hive_value_h *values;
+ size_t *blocks;
+
+ if (get_values (h, node, &values, &blocks) == -1)
+ return NULL;
+
+ free (blocks);
+ return values;
}
/* Very inefficient, but at least having a separate API call