+/* 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) */