diff options
Diffstat (limited to 'src/dbdump.c')
-rw-r--r-- | src/dbdump.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/dbdump.c b/src/dbdump.c new file mode 100644 index 00000000..f9d06aa8 --- /dev/null +++ b/src/dbdump.c @@ -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) */ |