inspect: Abstract out db_dump code for listing RPM applications.
[libguestfs.git] / src / dbdump.c
diff --git a/src/dbdump.c b/src/dbdump.c
new file mode 100644 (file)
index 0000000..f9d06aa
--- /dev/null
@@ -0,0 +1,220 @@
+/* libguestfs
+ * Copyright (C) 2010-2011 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <endian.h>
+
+#ifdef HAVE_PCRE
+#include <pcre.h>
+#endif
+
+#ifdef HAVE_HIVEX
+#include <hivex.h>
+#endif
+
+#include "c-ctype.h"
+#include "ignore-value.h"
+
+#include "guestfs.h"
+#include "guestfs-internal.h"
+
+#if defined(HAVE_PCRE) && defined(HAVE_HIVEX) && defined(DB_DUMP)
+
+static unsigned char *convert_hex_to_binary (guestfs_h *g, const char *hex, size_t hexlen, size_t *binlen_rtn);
+
+/* This helper function is specialized to just reading the hash-format
+ * output from db_dump/db4_dump.  It's just enough to support the RPM
+ * database format.  Note that the filename must not contain any shell
+ * characters (this is guaranteed by the caller).
+ */
+int
+guestfs___read_db_dump (guestfs_h *g,
+                        const char *dumpfile, void *opaque,
+                        guestfs___db_dump_callback callback)
+{
+#define cmd_len (strlen (dumpfile) + 64)
+  char cmd[cmd_len];
+  FILE *pp = NULL;
+  char *line = NULL;
+  size_t len = 0;
+  ssize_t linelen;
+  unsigned char *key = NULL, *value = NULL;
+  size_t keylen, valuelen;
+  int ret = -1;
+
+  snprintf (cmd, cmd_len, DB_DUMP " -k '%s'", dumpfile);
+
+  debug (g, "read_db_dump command: %s", cmd);
+
+  pp = popen (cmd, "r");
+  if (pp == NULL) {
+    perrorf (g, "popen: %s", cmd);
+    goto out;
+  }
+
+  /* Ignore everything to end-of-header marker. */
+  while ((linelen = getline (&line, &len, pp)) != -1) {
+    if (STRPREFIX (line, "HEADER=END"))
+      break;
+  }
+
+  if (linelen == -1) {
+    error (g, _("unexpected end of output from db_dump command before end of header"));
+    goto out;
+  }
+
+  /* Now read the key, value pairs.  They are prefixed with a space and
+   * printed as hex strings, so convert those strings to binary.  Pass
+   * the strings up to the callback function.
+   */
+  while ((linelen = getline (&line, &len, pp)) != -1) {
+    if (STRPREFIX (line, "DATA=END"))
+      break;
+
+    if (linelen < 1 || line[0] != ' ') {
+      error (g, _("unexpected line from db_dump command, no space prefix"));
+      goto out;
+    }
+
+    if ((key = convert_hex_to_binary (g, &line[1], linelen-1,
+                                      &keylen)) == NULL)
+      goto out;
+
+    if ((linelen = getline (&line, &len, pp)) == -1)
+      break;
+
+    if (linelen < 1 || line[0] != ' ') {
+      error (g, _("unexpected line from db_dump command, no space prefix"));
+      goto out;
+    }
+
+    if ((value = convert_hex_to_binary (g, &line[1], linelen-1,
+                                        &valuelen)) == NULL)
+      goto out;
+
+    if (callback (g, key, keylen, value, valuelen, opaque) == -1)
+      goto out;
+
+    free (key);
+    free (value);
+    key = value = NULL;
+  }
+
+  if (linelen == -1) {
+    error (g, _("unexpected end of output from db_dump command before end of data"));
+    goto out;
+  }
+
+  /* Catch errors from the db_dump command. */
+  if (pclose (pp) == -1) {
+    perrorf (g, "pclose: %s", cmd);
+    goto out;
+  }
+  pp = NULL;
+
+  ret = 0;
+
+ out:
+  if (pp)
+    pclose (pp);
+
+  free (line);
+  free (key);
+  free (value);
+
+  return ret;
+#undef cmd_len
+}
+
+static int
+convert_hex_octet (const char *h)
+{
+  int r;
+
+  switch (h[0]) {
+  case 'a'...'f':
+    r = (h[0] - 'a' + 10) << 4;
+    break;
+  case 'A'...'F':
+    r = (h[0] - 'A' + 10) << 4;
+    break;
+  case '0'...'9':
+    r = (h[0] - '0') << 4;
+    break;
+  default:
+    return -1;
+  }
+
+  switch (h[1]) {
+  case 'a'...'f':
+    r |= h[1] - 'a' + 10;
+    break;
+  case 'A'...'F':
+    r |= h[1] - 'A' + 10;
+    break;
+  case '0'...'9':
+    r |= h[1] - '0';
+    break;
+  default:
+    return -1;
+  }
+
+  return r;
+}
+
+static unsigned char *
+convert_hex_to_binary (guestfs_h *g, const char *hex, size_t hexlen,
+                       size_t *binlen_rtn)
+{
+  unsigned char *bin;
+  size_t binlen;
+  size_t i, o;
+  int b;
+
+  if (hexlen > 0 && hex[hexlen-1] == '\n')
+    hexlen--;
+
+  binlen = hexlen / 2;
+  bin = safe_malloc (g, binlen);
+
+  for (i = o = 0; i+1 < hexlen && o < binlen; i += 2, ++o) {
+    b = convert_hex_octet (&hex[i]);
+    if (b >= 0)
+      bin[o] = b;
+    else {
+      error (g, _("unexpected non-hex digits in output of db_dump command"));
+      free (bin);
+      return NULL;
+    }
+  }
+
+  *binlen_rtn = binlen;
+  return bin;
+}
+
+#endif /* defined(HAVE_PCRE) && defined(HAVE_HIVEX) && defined(DB_DUMP) */