From a986e8dadb0c70634f6d1d89dd3e7bb5d9af3078 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Thu, 14 Apr 2011 13:28:02 +0100 Subject: [PATCH] inspect: Abstract out db_dump code for listing RPM applications. There are two changes here: (1) The code for listing RPM applications ran db_dump and parsed the output. We abstract out that parsing code into a separate reusable module (src/dbdump.c). (2) The old db_dump parsing code used db_dump -p (printable) format. Instead use db_dump -k (hex) format so we can read binary fields. --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/dbdump.c | 220 +++++++++++++++++++++++++++++++++++++++++++++++++ src/guestfs-internal.h | 2 + src/inspect_apps.c | 109 +++++++----------------- 5 files changed, 252 insertions(+), 81 deletions(-) create mode 100644 src/dbdump.c diff --git a/po/POTFILES.in b/po/POTFILES.in index 3077ebe..663490c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -132,6 +132,7 @@ ruby/ext/guestfs/_guestfs.c src/actions.c src/appliance.c src/bindtests.c +src/dbdump.c src/errnostring.c src/errnostring_gperf.c src/events.c diff --git a/src/Makefile.am b/src/Makefile.am index 6aea6cc..32ac7df 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -124,6 +124,7 @@ libguestfs_la_SOURCES = \ actions.c \ appliance.c \ bindtests.c \ + dbdump.c \ events.c \ filearch.c \ inspect.c \ diff --git a/src/dbdump.c b/src/dbdump.c new file mode 100644 index 0000000..f9d06aa --- /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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_PCRE +#include +#endif + +#ifdef HAVE_HIVEX +#include +#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) */ diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index b75aee8..1730f68 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -355,6 +355,8 @@ extern int guestfs___parse_unsigned_int_ignore_trailing (guestfs_h *g, const cha extern int guestfs___parse_major_minor (guestfs_h *g, struct inspect_fs *fs); extern char *guestfs___first_line_of_file (guestfs_h *g, const char *filename); extern int guestfs___first_egrep_of_file (guestfs_h *g, const char *filename, const char *eregex, int iflag, char **ret); +typedef int (*guestfs___db_dump_callback) (guestfs_h *g, const unsigned char *key, size_t keylen, const unsigned char *value, size_t valuelen, void *opaque); +extern int guestfs___read_db_dump (guestfs_h *g, const char *dumpfile, void *opaque, guestfs___db_dump_callback callback); extern int guestfs___check_installer_root (guestfs_h *g, struct inspect_fs *fs); extern int guestfs___check_linux_root (guestfs_h *g, struct inspect_fs *fs); extern int guestfs___check_freebsd_root (guestfs_h *g, struct inspect_fs *fs); diff --git a/src/inspect_apps.c b/src/inspect_apps.c index df99df9..8ce09ab 100644 --- a/src/inspect_apps.c +++ b/src/inspect_apps.c @@ -126,6 +126,28 @@ guestfs__inspect_list_applications (guestfs_h *g, const char *root) } #ifdef DB_DUMP + +static int +read_rpm_name (guestfs_h *g, + const unsigned char *key, size_t keylen, + const unsigned char *value, size_t valuelen, + void *appsv) +{ + struct guestfs_application_list *apps = appsv; + char *name; + + /* The name (key) field won't be NUL-terminated, so we must do that. */ + name = safe_malloc (g, keylen+1); + memcpy (name, key, keylen); + name[keylen] = '\0'; + + add_application (g, apps, name, "", 0, "", "", "", "", "", ""); + + free (name); + + return 0; +} + static struct guestfs_application_list * list_applications_rpm (guestfs_h *g, struct inspect_fs *fs) { @@ -138,95 +160,20 @@ list_applications_rpm (guestfs_h *g, struct inspect_fs *fs) MAX_PKG_DB_SIZE) == -1) return NULL; - struct guestfs_application_list *apps = NULL, *ret = NULL; -#define cmd_len (strlen (tmpdir_basename) + 64) - char cmd[cmd_len]; - FILE *pp = NULL; - char line[1024]; - size_t len; - - snprintf (cmd, cmd_len, DB_DUMP " -p '%s'", tmpdir_basename); - - debug (g, "list_applications_rpm: %s", cmd); - - pp = popen (cmd, "r"); - if (pp == NULL) { - perrorf (g, "popen: %s", cmd); - goto out; - } - - /* Ignore everything to end-of-header marker. */ - for (;;) { - if (fgets (line, sizeof line, pp) == NULL) { - error (g, _("unexpected end of output from db_dump command")); - goto out; - } - - len = strlen (line); - if (len > 0 && line[len-1] == '\n') { - line[len-1] = '\0'; - len--; - } - - if (STREQ (line, "HEADER=END")) - break; - } - /* Allocate 'apps' list. */ + struct guestfs_application_list *apps; apps = safe_malloc (g, sizeof *apps); apps->len = 0; apps->val = NULL; - /* Read alternate lines until end of data marker. */ - for (;;) { - if (fgets (line, sizeof line, pp) == NULL) { - error (g, _("unexpected end of output from db_dump command")); - goto out; - } - - len = strlen (line); - if (len > 0 && line[len-1] == '\n') { - line[len-1] = '\0'; - len--; - } - - if (STREQ (line, "DATA=END")) - break; - - char *p = line; - if (len > 0 && line[0] == ' ') - p = line+1; - /* Ignore any application name that contains non-printable chars. - * In the db_dump output these would be escaped with backslash, so - * we can just ignore any such line. - */ - if (strchr (p, '\\') == NULL) - add_application (g, apps, p, "", 0, "", "", "", "", "", ""); - - /* Discard next line. */ - if (fgets (line, sizeof line, pp) == NULL) { - error (g, _("unexpected end of output from db_dump command")); - goto out; - } - } - - /* Catch errors from the db_dump command. */ - if (pclose (pp) == -1) { - perrorf (g, "pclose: %s", cmd); - goto out; - } - pp = NULL; - - ret = apps; - - out: - if (ret == NULL && apps != NULL) + if (guestfs___read_db_dump (g, tmpdir_basename, apps, read_rpm_name) == -1) { guestfs_free_application_list (apps); - if (pp) - pclose (pp); + return NULL; + } - return ret; + return apps; } + #endif /* defined DB_DUMP */ static struct guestfs_application_list * -- 1.8.3.1