I'm not entirely sure the generator/generator.ml changes are as correct
as they could be. I'm not very familiar with Caml.
The hivex_node_set_value call builds up a list of hive_set_values by
walking the existing values at the node, adding or replacing the passed
hive_set_value as necessary, then shoving the list at
hivex_node_set_values.
Not included: Perl or OCaml binding glue.
I'm not on the list, please CC me to replies. Thanks!
---
generator/generator.ml | 29 +++++++++++++--
lib/hivex.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 115 insertions(+), 4 deletions(-)
diff --git a/generator/generator.ml b/generator/generator.ml
index 5a0ab6e..dbcbd2c 100755
--- a/generator/generator.ml
+++ b/generator/generator.ml
@@ -71,6 +71,7 @@ and argt = (* Note, cannot be NULL/0 unless
it
| AOpenFlags (* HIVEX_OPEN_* flags list. *)
| AUnusedFlags (* Flags arg that is always 0 *)
| ASetValues (* See hivex_node_set_values. *)
+ | ASetValue (* See hivex_node_set_value. *)
(* Hive types, from:
*
https://secure.wikimedia.org/wikipedia/en/wiki/Windows_Registry#Keys_and_...
@@ -304,8 +305,15 @@ subnodes become invalid. You cannot delete the root node.";
"set (key, value) pairs at a node",
"\
This call can be used to set all the (key, value) pairs
-stored in C<node>. Note that this library does not offer
-a way to modify just a single key at a node.
+stored in C<node>.
+
+C<node> is the node to modify.";
+
+ "node_set_value", (RErr, [AHive; ANode "node"; ASetValue;
AUnusedFlags]),
+ "set a single (key, value) pair at a given node",
+ "\
+This call can be used to set a single (key, value) pair
+stored in C<node>.
C<node> is the node to modify.";
]
@@ -459,6 +467,7 @@ let name_of_argt = function
| ANode n | AValue n | AString n | AStringNullable n -> n
| AOpenFlags | AUnusedFlags -> "flags"
| ASetValues -> "values"
+ | ASetValue -> "val"
(* Check function names etc. for consistency. *)
let check_functions () =
@@ -806,6 +815,7 @@ and generate_c_prototype ?(extern = false) name style =
| AString n | AStringNullable n -> pr "const char *%s" n
| AOpenFlags | AUnusedFlags -> pr "int flags"
| ASetValues -> pr "size_t nr_values, const hive_set_value *values"
+ | ASetValue -> pr "const hive_set_value *val"
) (snd style);
(match fst style with
| RLenType | RLenTypeVal -> pr ", hive_type *t, size_t *len"
@@ -937,6 +947,11 @@ Any existing values stored at the node are discarded, and their
C<hive_value_h> handles become invalid. Thus you can remove all
values stored at C<node> by passing C<nr_values = 0>.\n\n";
+ if List.mem ASetValue (snd style) then
+ pr "C<value> is a single (key, value) pair.
+
+Existing C<hive_value_h> handles become invalid.\n\n";
+
(match fst style with
| RErr ->
pr "\
@@ -1478,6 +1493,7 @@ and generate_ocaml_prototype ?(is_external = false) name style =
| AOpenFlags -> pr "open_flag list -> "
| AUnusedFlags -> ()
| ASetValues -> pr "set_value array -> "
+ | ASetValue -> pr "set_value -> "
) (snd style);
(match fst style with
| RErr -> pr "unit" (* all errors are turned into exceptions *)
@@ -1621,6 +1637,8 @@ static void raise_closed (const char *) Noreturn;
| ASetValues ->
pr " int nrvalues = Wosize_val (valuesv);\n";
pr " hive_set_value *values = HiveSetValues_val (valuesv);\n"
+ | ASetValue ->
+ pr " hive_set_value *val = HiveSetValue_val (valv);\n"
) (snd style);
pr "\n";
@@ -1684,7 +1702,7 @@ static void raise_closed (const char *) Noreturn;
List.iter (
function
| AHive | ANode _ | AValue _ | AString _ | AStringNullable _
- | AOpenFlags | AUnusedFlags -> ()
+ | AOpenFlags | AUnusedFlags | ASetValue -> ()
| ASetValues ->
pr " free (values);\n";
pr "\n";
@@ -2113,6 +2131,7 @@ and generate_perl_prototype name style =
| AOpenFlags -> pr "[flags]"
| AUnusedFlags -> assert false
| ASetValues -> pr "\\@values"
+ | ASetValue -> pr "$val"
) args;
pr ")"
@@ -2319,6 +2338,8 @@ DESTROY (h)
| AUnusedFlags -> ()
| ASetValues ->
pr " pl_set_values values = unpack_pl_set_values (ST(%d));\n" i
+ | ASetValue ->
+ pr " pl_set_value val = unpack_pl_set_value (ST(%d));\n" i
) (snd style);
let free_args () =
@@ -2327,7 +2348,7 @@ DESTROY (h)
| ASetValues ->
pr " free (values.values);\n"
| AHive | ANode _ | AValue _ | AString _ | AStringNullable _
- | AOpenFlags | AUnusedFlags -> ()
+ | AOpenFlags | AUnusedFlags | ASetValue -> ()
) (snd style)
in
diff --git a/lib/hivex.c b/lib/hivex.c
index 74a7f55..3879238 100644
--- a/lib/hivex.c
+++ b/lib/hivex.c
@@ -2606,3 +2606,93 @@ hivex_node_set_values (hive_h *h, hive_node_h node,
return 0;
}
+
+
+int
+hivex_node_set_value (hive_h *h, hive_node_h node,
+ const hive_set_value *val, int flags)
+{
+ if (!h->writable) {
+ errno = EROFS;
+ return -1;
+ }
+
+ if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ hive_value_h *prev_values = hivex_node_values (h, node);
+ if (prev_values == NULL)
+ return -1;
+
+ int retval = -1;
+
+ size_t nr_values = 0;
+ for (hive_value_h *itr = prev_values; *itr != 0; ++itr)
+ ++nr_values;
+
+ hive_set_value *values = malloc ((nr_values + 1) * (sizeof (hive_set_value)));
+ if (values == NULL)
+ goto leave_prev_values;
+
+ int alloc_ct = 0;
+ int idx_of_val = -1;
+ for (hive_value_h *prev_val = prev_values; *prev_val != 0; ++prev_val) {
+ size_t len;
+ hive_type t;
+
+ hive_set_value *value = &values[prev_val - prev_values];
+
+ char *valval = hivex_value_value (h, *prev_val, &t, &len);
+ if (valval == NULL) goto leave_partial;
+
+ ++alloc_ct;
+ value->value = valval;
+ value->t = t;
+ value->len = len;
+
+ char *valkey = hivex_value_key (h, *prev_val);
+ if (valkey == NULL) goto leave_partial;
+
+ ++alloc_ct;
+ value->key = valkey;
+
+ if (strcmp (valkey, val->key) == 0)
+ idx_of_val = prev_val - prev_values;
+ }
+
+ if (idx_of_val > -1) {
+ free (values[idx_of_val].key);
+ free (values[idx_of_val].value);
+ } else {
+ idx_of_val = nr_values;
+ ++nr_values;
+ }
+
+ hive_set_value *value = &values[idx_of_val];
+ *value = (hive_set_value){
+ .key = strdup (val->key),
+ .value = malloc (val->len),
+ .len = val->len,
+ .t = val->t
+ };
+
+ if (value->key == NULL || value->value == NULL) goto leave_partial;
+ memcpy (value->value, val->value, val->len);
+
+ retval = hivex_node_set_values (h, node, nr_values, values, 0);
+
+ leave_partial:
+ for (int i = 0; i < alloc_ct; i += 2) {
+ if (values[i / 2].value != NULL)
+ free (values[i / 2].value);
+ if (i + 1 < alloc_ct && values[i / 2].key != NULL)
+ free (values[i / 2].key);
+ }
+ free (values);
+
+ leave_prev_values:
+ free (prev_values);
+ return retval;
+}
--
1.7.1