Add hivex_set_value API call, and ocaml and perl bindings, and tests.
authorConrad Meyer <cemeyer@cs.washington.edu>
Thu, 8 Jul 2010 10:12:42 +0000 (11:12 +0100)
committerRichard Jones <rjones@redhat.com>
Thu, 8 Jul 2010 10:12:42 +0000 (11:12 +0100)
generator/generator.ml
lib/hivex.c
perl/t/210-setvalue.t [new file with mode: 0644]

index 5a0ab6e..03f8c20 100755 (executable)
@@ -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_values
@@ -304,8 +305,16 @@ 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 replace a single (key, value) pair
+stored in C<node>. If the key does not already exist, then a
+new key is added. Key matching is case insensitive.
 
 C<node> is the node to modify.";
 ]
@@ -459,6 +468,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 +816,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 +948,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 +1494,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 *)
@@ -1548,6 +1565,7 @@ caml_raise_with_args (value tag, int nargs, value args[])
 #define Hiveh_val(v) (*((hive_h **)Data_custom_val(v)))
 static value Val_hiveh (hive_h *);
 static int HiveOpenFlags_val (value);
+static hive_set_value *HiveSetValue_val (value);
 static hive_set_value *HiveSetValues_val (value);
 static hive_type HiveType_val (value);
 static value Val_hive_type (hive_type);
@@ -1621,6 +1639,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";
 
@@ -1688,6 +1708,9 @@ static void raise_closed (const char *) Noreturn;
         | ASetValues ->
             pr "  free (values);\n";
             pr "\n";
+       | ASetValue ->
+           pr "  free (val);\n";
+           pr "\n";
       ) (snd style);
 
       (* Check for errors. *)
@@ -1750,6 +1773,19 @@ HiveOpenFlags_val (value v)
 }
 
 static hive_set_value *
+HiveSetValue_val (value v)
+{
+  hive_set_value *val = malloc (sizeof (hive_set_value));
+
+  val->key = String_val (Field (v, 0));
+  val->t = HiveType_val (Field (v, 1));
+  val->len = caml_string_length (Field (v, 2));
+  val->value = String_val (Field (v, 2));
+
+  return val;
+}
+
+static hive_set_value *
 HiveSetValues_val (value v)
 {
   size_t nr_values = Wosize_val (v);
@@ -2113,6 +2149,7 @@ and generate_perl_prototype name style =
       | AOpenFlags -> pr "[flags]"
       | AUnusedFlags -> assert false
       | ASetValues -> pr "\\@values"
+      | ASetValue -> pr "$val"
   ) args;
 
   pr ")"
@@ -2243,6 +2280,39 @@ unpack_pl_set_values (SV *sv)
   return ret;
 }
 
+static hive_set_value *
+unpack_set_value (SV *sv)
+{
+  hive_set_value *ret;
+
+  if (!sv || !SvROK (sv) || SvTYPE (SvRV (sv)) != SVt_PVHV)
+    croak (\"not a hash ref\");
+
+  ret = malloc (sizeof (hive_set_value));
+  if (ret == NULL)
+    croak (\"malloc failed\");
+
+  HV *hv = (HV *)SvRV(sv);
+
+  SV **svp;
+  svp = hv_fetch (hv, \"key\", 3, 0);
+  if (!svp || !*svp)
+    croak (\"missing 'key' in hash\");
+  ret->key = SvPV_nolen (*svp);
+
+  svp = hv_fetch (hv, \"t\", 1, 0);
+  if (!svp || !*svp)
+    croak (\"missing 't' in hash\");
+  ret->t = SvIV (*svp);
+
+  svp = hv_fetch (hv, \"value\", 5, 0);
+  if (!svp || !*svp)
+    croak (\"missing 'value' in hash\");
+  ret->value = SvPV (*svp, ret->len);
+
+  return ret;
+}
+
 MODULE = Win::Hivex  PACKAGE = Win::Hivex
 
 PROTOTYPES: ENABLE
@@ -2319,6 +2389,8 @@ DESTROY (h)
            | AUnusedFlags -> ()
            | ASetValues ->
                pr "      pl_set_values values = unpack_pl_set_values (ST(%d));\n" i
+           | ASetValue ->
+               pr "      hive_set_value *val = unpack_set_value (ST(%d));\n" i
        ) (snd style);
 
        let free_args () =
@@ -2326,6 +2398,8 @@ DESTROY (h)
            function
            | ASetValues ->
                pr "      free (values.values);\n"
+           | ASetValue ->
+               pr "      free (val);\n"
            | AHive | ANode _ | AValue _ | AString _ | AStringNullable _
            | AOpenFlags | AUnusedFlags -> ()
          ) (snd style)
index 74a7f55..c65f6de 100644 (file)
@@ -2606,3 +2606,83 @@ 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)
+{
+  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;
+  hive_value_h *prev_val;
+  for (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 (STRCASEEQ (valkey, val->key))
+      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;
+}
diff --git a/perl/t/210-setvalue.t b/perl/t/210-setvalue.t
new file mode 100644 (file)
index 0000000..8e24121
--- /dev/null
@@ -0,0 +1,65 @@
+# hivex Perl bindings -*- perl -*-
+# Copyright (C) 2010 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+use strict;
+use warnings;
+use Test::More tests => 11;
+
+use Win::Hivex;
+
+my $srcdir = $ENV{srcdir} || ".";
+
+my $h = Win::Hivex->open ("$srcdir/../images/minimal", write => 1);
+ok ($h);
+
+my $root = $h->root ();
+ok ($root);
+
+$h->node_add_child ($root, "B");
+ok (1);
+
+my $b = $h->node_get_child ($root, "B");
+ok ($b);
+
+my $values = [
+    { key => "Key1", t => 3, value => "ABC" },
+    { key => "Key2", t => 3, value => "DEF" }
+    ];
+$h->node_set_values ($b, $values);
+ok (1);
+
+my $value1 = { key => "Key3", t => 3, value => "GHI" };
+$h->node_set_value ($b, $value1);
+ok (1);
+
+my $value2 = { key => "Key1", t => 3, value => "JKL" };
+$h->node_set_value ($b, $value2);
+ok (1);
+
+my ($val, $t, $data);
+$val = $h->node_get_value ($b, "Key1");
+($t, $data) = $h->value_value ($val);
+ok ($t == 3);
+ok ($data eq "JKL");
+
+$val = $h->node_get_value ($b, "Key3");
+($t, $data) = $h->value_value ($val);
+ok ($t == 3);
+ok ($data eq "GHI");
+
+# don't commit because that would overwrite the original file
+# $h->commit ();