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.
This commit is contained in:
Richard W.M. Jones
2011-04-14 13:28:02 +01:00
parent 3336b5448f
commit a986e8dadb
5 changed files with 252 additions and 81 deletions

View File

@@ -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

View File

@@ -124,6 +124,7 @@ libguestfs_la_SOURCES = \
actions.c \
appliance.c \
bindtests.c \
dbdump.c \
events.c \
filearch.c \
inspect.c \

220
src/dbdump.c Normal file
View File

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

View File

@@ -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);

View File

@@ -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 *