mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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
220
src/dbdump.c
Normal 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) */
|
||||
@@ -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);
|
||||
|
||||
@@ -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 *
|
||||
|
||||
Reference in New Issue
Block a user