--- /dev/null
+/* 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) */
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);
}
#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)
{
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 *