enable scrub on Debian
[libguestfs.git] / hivex / hivexsh.c
index 847954c..332b773 100644 (file)
 //#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0)
 //#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0)
 //#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0)
-//#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
+#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
 
 #include "c-ctype.h"
+#include "xstrtol.h"
 
 #include "hivex.h"
+#include "byte_conversions.h"
+
+#define HIVEX_MAX_VALUES         1000
 
 static int quit = 0;
 static int is_tty;
@@ -72,18 +76,23 @@ static void cleanup_readline (void);
 static void add_history_line (const char *);
 static char *rl_gets (const char *prompt_string);
 static void sort_strings (char **strings, int len);
+static int get_xdigit (char c);
 static int dispatch (char *cmd, char *args);
+static int cmd_add (char *name);
 static int cmd_cd (char *path);
 static int cmd_close (char *path);
+static int cmd_commit (char *path);
+static int cmd_del (char *args);
 static int cmd_help (char *args);
 static int cmd_load (char *hivefile);
 static int cmd_ls (char *args);
 static int cmd_lsval (char *args);
+static int cmd_setval (char *args);
 
 static void
 usage (void)
 {
-  fprintf (stderr, "hivexsh [-df] [hivefile]\n");
+  fprintf (stderr, "hivexsh [-dfw] [hivefile]\n");
   exit (EXIT_FAILURE);
 }
 
@@ -99,7 +108,7 @@ main (int argc, char *argv[])
 
   set_prompt_string ();
 
-  while ((c = getopt (argc, argv, "df")) != EOF) {
+  while ((c = getopt (argc, argv, "dfw")) != EOF) {
     switch (c) {
     case 'd':
       open_flags |= HIVEX_OPEN_DEBUG;
@@ -107,6 +116,9 @@ main (int argc, char *argv[])
     case 'f':
       filename = optarg;
       break;
+    case 'w':
+      open_flags |= HIVEX_OPEN_WRITE;
+      break;
     default:
       usage ();
     }
@@ -172,7 +184,6 @@ main (int argc, char *argv[])
 
     char *cmd = buf;
     char *args;
-    size_t i = 0;
 
     if (buf[len] == '\0') {
       /* This is mostly safe.  Although the cmd_* functions do sometimes
@@ -220,7 +231,7 @@ set_prompt_string (void)
   fp = open_memstream (&ptr, &size);
   if (fp == NULL) {
     perror ("open_memstream");
-    exit (1);
+    exit (EXIT_FAILURE);
   }
 
   if (h) {
@@ -370,6 +381,17 @@ sort_strings (char **strings, int len)
 }
 
 static int
+get_xdigit (char c)
+{
+  switch (c) {
+  case '0'...'9': return c - '0';
+  case 'a'...'f': return c - 'a' + 10;
+  case 'A'...'F': return c - 'A' + 10;
+  default: return -1;
+  }
+}
+
+static int
 dispatch (char *cmd, char *args)
 {
   if (STRCASEEQ (cmd, "help"))
@@ -391,14 +413,22 @@ dispatch (char *cmd, char *args)
     return -1;
   }
 
-  if (STRCASEEQ (cmd, "cd"))
+  if (STRCASEEQ (cmd, "add"))
+    return cmd_add (args);
+  else if (STRCASEEQ (cmd, "cd"))
     return cmd_cd (args);
   else if (STRCASEEQ (cmd, "close") || STRCASEEQ (cmd, "unload"))
     return cmd_close (args);
+  else if (STRCASEEQ (cmd, "commit"))
+    return cmd_commit (args);
+  else if (STRCASEEQ (cmd, "del"))
+    return cmd_del (args);
   else if (STRCASEEQ (cmd, "ls"))
     return cmd_ls (args);
   else if (STRCASEEQ (cmd, "lsval"))
     return cmd_lsval (args);
+  else if (STRCASEEQ (cmd, "setval"))
+    return cmd_setval (args);
   else {
     fprintf (stderr, _("hivexsh: unknown command '%s', use 'help' for help summary\n"),
              cmd);
@@ -478,6 +508,20 @@ cmd_close (char *args)
 }
 
 static int
+cmd_commit (char *path)
+{
+  if (STREQ (path, ""))
+    path = NULL;
+
+  if (hivex_commit (h, path, 0) == -1) {
+    perror ("hivexsh: commit");
+    return -1;
+  }
+
+  return 0;
+}
+
+static int
 cmd_cd (char *path)
 {
   if (STREQ (path, "")) {
@@ -519,10 +563,14 @@ cmd_cd (char *path)
       continue;
     }
 
+    errno = 0;
     new_cwd = hivex_node_get_child (h, new_cwd, elem);
     if (new_cwd == 0) {
-      fprintf (stderr, _("hivexsh: cd: subkey '%s' not found\n"),
-               elem);
+      if (errno)
+        perror ("hivexsh: cd");
+      else
+        fprintf (stderr, _("hivexsh: cd: subkey '%s' not found\n"),
+                 elem);
       return -1;
     }
   }
@@ -570,7 +618,7 @@ cmd_ls (char *args)
   char **names = calloc (len, sizeof (char *));
   if (names == NULL) {
     perror ("malloc");
-    exit (1);
+    exit (EXIT_FAILURE);
   }
 
   int ret = -1;
@@ -740,7 +788,7 @@ cmd_lsval (char *key)
       case hive_t_dword:
       case hive_t_dword_be: {
         int32_t j = hivex_value_dword (h, values[i]);
-        printf ("dword:%08" PRIx32 "\"", j);
+        printf ("dword:%08" PRIx32, j);
         break;
       }
 
@@ -752,7 +800,8 @@ cmd_lsval (char *key)
       case hive_t_full_resource_description:
       case hive_t_resource_requirements_list:
       default: {
-        char *data = hivex_value_value (h, values[i], &t, &len);
+        unsigned char *data =
+          (unsigned char *) hivex_value_value (h, values[i], &t, &len);
         if (!data)
           goto error;
 
@@ -779,3 +828,267 @@ cmd_lsval (char *key)
   perror ("hivexsh: lsval");
   return -1;
 }
+
+static int
+cmd_setval (char *nrvals_str)
+{
+  strtol_error xerr;
+
+  /* Parse number of values. */
+  long nrvals;
+  xerr = xstrtol (nrvals_str, NULL, 0, &nrvals, "");
+  if (xerr != LONGINT_OK) {
+    fprintf (stderr, _("%s: %s: invalid integer parameter (%s returned %d)\n"),
+             "setval", "nrvals", "xstrtol", xerr);
+    return -1;
+  }
+  if (nrvals < 0 || nrvals > HIVEX_MAX_VALUES) {
+    fprintf (stderr, _("%s: %s: integer out of range\n"),
+             "setval", "nrvals");
+    return -1;
+  }
+
+  struct hive_set_value *values =
+    calloc (nrvals, sizeof (struct hive_set_value));
+  if (values == NULL) {
+    perror ("calloc");
+    exit (EXIT_FAILURE);
+  }
+
+  int ret = -1;
+
+  /* Read nrvals * 2 lines of input, nrvals * (key, value) pairs, as
+   * explained in the man page.
+   */
+  int i, j;
+  for (i = 0; i < nrvals; ++i) {
+    /* Read key. */
+    char *buf = rl_gets ("  key> ");
+    if (!buf) {
+      fprintf (stderr, _("hivexsh: setval: unexpected end of input\n"));
+      quit = 1;
+      goto error;
+    }
+
+    /* Note that buf will be overwritten by the next call to rl_gets. */
+    if (STREQ (buf, "@"))
+      values[i].key = strdup ("");
+    else
+      values[i].key = strdup (buf);
+    if (values[i].key == NULL) {
+      perror ("strdup");
+      exit (EXIT_FAILURE);
+    }
+
+    /* Read value. */
+    buf = rl_gets ("value> ");
+    if (!buf) {
+      fprintf (stderr, _("hivexsh: setval: unexpected end of input\n"));
+      quit = 1;
+      goto error;
+    }
+
+    if (STREQ (buf, "none")) {
+      values[i].t = hive_t_none;
+      values[i].len = 0;
+    }
+    else if (STRPREFIX (buf, "string:")) {
+      buf += 7;
+      values[i].t = hive_t_string;
+      int nr_chars = strlen (buf);
+      values[i].len = 2 * (nr_chars + 1);
+      values[i].value = malloc (values[i].len);
+      if (!values[i].value) {
+        perror ("malloc");
+        exit (EXIT_FAILURE);
+      }
+      for (j = 0; j <= /* sic */ nr_chars; ++j) {
+        if (buf[j] & 0x80) {
+          fprintf (stderr, _("hivexsh: string(utf16le): only 7 bit ASCII strings are supported for input\n"));
+          goto error;
+        }
+        values[i].value[2*j] = buf[j];
+        values[i].value[2*j+1] = '\0';
+      }
+    }
+    else if (STRPREFIX (buf, "expandstring:")) {
+      buf += 13;
+      values[i].t = hive_t_expand_string;
+      int nr_chars = strlen (buf);
+      values[i].len = 2 * (nr_chars + 1);
+      values[i].value = malloc (values[i].len);
+      if (!values[i].value) {
+        perror ("malloc");
+        exit (EXIT_FAILURE);
+      }
+      for (j = 0; j <= /* sic */ nr_chars; ++j) {
+        if (buf[j] & 0x80) {
+          fprintf (stderr, _("hivexsh: string(utf16le): only 7 bit ASCII strings are supported for input\n"));
+          goto error;
+        }
+        values[i].value[2*j] = buf[j];
+        values[i].value[2*j+1] = '\0';
+      }
+    }
+    else if (STRPREFIX (buf, "dword:")) {
+      buf += 6;
+      values[i].t = hive_t_dword;
+      values[i].len = 4;
+      values[i].value = malloc (4);
+      if (!values[i].value) {
+        perror ("malloc");
+        exit (EXIT_FAILURE);
+      }
+      long n;
+      xerr = xstrtol (buf, NULL, 0, &n, "");
+      if (xerr != LONGINT_OK) {
+        fprintf (stderr, _("%s: %s: invalid integer parameter (%s returned %d)\n"),
+                 "setval", "dword", "xstrtol", xerr);
+        goto error;
+      }
+      if (n < 0 || n > UINT32_MAX) {
+        fprintf (stderr, _("%s: %s: integer out of range\n"),
+                 "setval", "dword");
+        goto error;
+      }
+      uint32_t u32 = htole32 (n);
+      memcpy (values[i].value, &u32, 4);
+    }
+    else if (STRPREFIX (buf, "qword:")) {
+      buf += 6;
+      values[i].t = hive_t_qword;
+      values[i].len = 8;
+      values[i].value = malloc (8);
+      if (!values[i].value) {
+        perror ("malloc");
+        exit (EXIT_FAILURE);
+      }
+      long long n;
+      xerr = xstrtoll (buf, NULL, 0, &n, "");
+      if (xerr != LONGINT_OK) {
+        fprintf (stderr, _("%s: %s: invalid integer parameter (%s returned %d)\n"),
+                 "setval", "dword", "xstrtoll", xerr);
+        goto error;
+      }
+#if 0
+      if (n < 0 || n > UINT64_MAX) {
+        fprintf (stderr, _("%s: %s: integer out of range\n"),
+                 "setval", "dword");
+        goto error;
+      }
+#endif
+      uint64_t u64 = htole64 (n);
+      memcpy (values[i].value, &u64, 4);
+    }
+    else if (STRPREFIX (buf, "hex:")) {
+      /* Read the type. */
+      buf += 4;
+      size_t len = strcspn (buf, ":");
+      char *nextbuf;
+      if (buf[len] == '\0')     /* "hex:t" */
+        nextbuf = &buf[len];
+      else {                    /* "hex:t:..." */
+        buf[len] = '\0';
+        nextbuf = &buf[len+1];
+      }
+
+      long t;
+      xerr = xstrtol (buf, NULL, 0, &t, "");
+      if (xerr != LONGINT_OK) {
+        fprintf (stderr, _("%s: %s: invalid integer parameter (%s returned %d)\n"),
+                 "setval", "hex", "xstrtol", xerr);
+        goto error;
+      }
+      if (t < 0 || t > UINT32_MAX) {
+        fprintf (stderr, _("%s: %s: integer out of range\n"),
+                 "setval", "hex");
+        goto error;
+      }
+      values[i].t = t;
+
+      /* Read the hex data. */
+      buf = nextbuf;
+
+      /* The allocation length is an overestimate, but it doesn't matter. */
+      values[i].value = malloc (1 + strlen (buf) / 2);
+      if (!values[i].value) {
+        perror ("malloc");
+        exit (EXIT_FAILURE);
+      }
+      values[i].len = 0;
+
+      while (*buf) {
+        int c = 0;
+
+        for (j = 0; *buf && j < 2; buf++) {
+          if (c_isxdigit (*buf)) { /* NB: ignore non-hex digits. */
+            c <<= 4;
+            c |= get_xdigit (*buf);
+            j++;
+          }
+        }
+
+        if (j == 2) values[i].value[values[i].len++] = c;
+        else if (j == 1) {
+          fprintf (stderr, _("hivexsh: setval: trailing garbage after hex string\n"));
+          goto error;
+        }
+      }
+    }
+    else {
+      fprintf (stderr,
+               _("hivexsh: setval: cannot parse value string, please refer to the man page hivexsh(1) for help: %s\n"),
+               buf);
+      goto error;
+    }
+  }
+
+  ret = hivex_node_set_values (h, cwd, nrvals, values, 0);
+
+ error:
+  /* Free values array. */
+  for (i = 0; i < nrvals; ++i) {
+    free (values[i].key);
+    free (values[i].value);
+  }
+  free (values);
+
+  return ret;
+}
+
+static int
+cmd_del (char *args)
+{
+  if (STRNEQ (args, "")) {
+    fprintf (stderr, _("hivexsh: '%s' command should not be given arguments\n"),
+             "del");
+    return -1;
+  }
+
+  if (cwd == hivex_root (h)) {
+    fprintf (stderr, _("hivexsh: del: the root node cannot be deleted\n"));
+    return -1;
+  }
+
+  hive_node_h new_cwd = hivex_node_parent (h, cwd);
+
+  if (hivex_node_delete_child (h, cwd) == -1) {
+    perror ("hivexsh: del");
+    return -1;
+  }
+
+  cwd = new_cwd;
+  set_prompt_string ();
+  return 0;
+}
+
+static int
+cmd_add (char *name)
+{
+  hive_node_h node = hivex_node_add_child (h, cwd, name);
+  if (node == 0) {
+    perror ("hivexsh: add");
+    return -1;
+  }
+  return 0;
+}