#include <errno.h>
#include <endian.h>
-#ifdef HAVE_PCRE
#include <pcre.h>
-#endif
#ifdef HAVE_HIVEX
#include <hivex.h>
#include "guestfs-internal-actions.h"
#include "guestfs_protocol.h"
-#if defined(HAVE_PCRE) && defined(HAVE_HIVEX)
+#if defined(HAVE_HIVEX)
#ifdef DB_DUMP
static struct guestfs_application_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs);
}
#ifdef DB_DUMP
-static struct guestfs_application_list *
-list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
+
+/* This data comes from the Name database, and contains the application
+ * names and the first 4 bytes of the link field.
+ */
+struct rpm_names_list {
+ struct rpm_name *names;
+ size_t len;
+};
+struct rpm_name {
+ char *name;
+ char link[4];
+};
+
+static void
+free_rpm_names_list (struct rpm_names_list *list)
{
- const char *basename = "rpm_Name";
- char tmpdir_basename[strlen (g->tmpdir) + strlen (basename) + 2];
- snprintf (tmpdir_basename, sizeof tmpdir_basename, "%s/%s",
- g->tmpdir, basename);
+ size_t i;
- if (guestfs___download_to_tmp (g, "/var/lib/rpm/Name", basename,
- MAX_PKG_DB_SIZE) == -1)
- return NULL;
+ for (i = 0; i < list->len; ++i)
+ free (list->names[i].name);
+ free (list->names);
+}
- 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];
+static int
+compare_links (const void *av, const void *bv)
+{
+ const struct rpm_name *a = av;
+ const struct rpm_name *b = bv;
+ return memcmp (a->link, b->link, 4);
+}
+
+static int
+read_rpm_name (guestfs_h *g,
+ const unsigned char *key, size_t keylen,
+ const unsigned char *value, size_t valuelen,
+ void *listv)
+{
+ struct rpm_names_list *list = listv;
+ char *name;
+
+ /* Ignore bogus entries. */
+ if (keylen == 0 || valuelen < 4)
+ return 0;
+
+ /* 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';
+
+ list->names = safe_realloc (g, list->names,
+ (list->len + 1) * sizeof (struct rpm_name));
+ list->names[list->len].name = name;
+ memcpy (list->names[list->len].link, value, 4);
+ list->len++;
+
+ return 0;
+}
+
+struct read_package_data {
+ struct rpm_names_list *list;
+ struct guestfs_application_list *apps;
+};
+
+static int
+read_package (guestfs_h *g,
+ const unsigned char *key, size_t keylen,
+ const unsigned char *value, size_t valuelen,
+ void *datav)
+{
+ struct read_package_data *data = datav;
+ struct rpm_name nkey, *entry;
+ char *p;
size_t len;
+ ssize_t max;
+ char *nul_name_nul, *version, *release;
+
+ /* This function reads one (key, value) pair from the Packages
+ * database. The key is the link field (see struct rpm_name). The
+ * value is a long binary string, but we can extract the version
+ * number from it as below. First we have to look up the link field
+ * in the list of links (which is sorted by link field).
+ */
- snprintf (cmd, cmd_len, DB_DUMP " -p '%s'", tmpdir_basename);
+ /* Ignore bogus entries. */
+ if (keylen < 4 || valuelen == 0)
+ return 0;
- debug (g, "list_applications_rpm: %s", cmd);
+ /* Look up the link (key) in the list. */
+ memcpy (nkey.link, key, 4);
+ entry = bsearch (&nkey, data->list->names, data->list->len,
+ sizeof (struct rpm_name), compare_links);
+ if (!entry)
+ return 0; /* Not found - ignore it. */
+
+ /* We found a matching link entry, so that gives us the application
+ * name (entry->name). Now we can get other data for this
+ * application out of the binary value string. XXX This is a real
+ * hack.
+ */
- pp = popen (cmd, "r");
- if (pp == NULL) {
- perrorf (g, "popen: %s", cmd);
- goto out;
- }
+ /* Look for \0<name>\0 */
+ len = strlen (entry->name);
+ nul_name_nul = safe_malloc (g, len + 2);
+ nul_name_nul[0] = '\0';
+ memcpy (&nul_name_nul[1], entry->name, len);
+ nul_name_nul[len+1] = '\0';
+ p = memmem (value, valuelen, nul_name_nul, len+2);
+ free (nul_name_nul);
+ if (!p)
+ return 0;
- /* 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;
- }
+ /* Following that are \0-delimited version and release fields. */
+ p += len + 2; /* Note we have to skip \0 + name + \0. */
+ max = valuelen - (p - (char *) value);
+ if (max < 0)
+ max = 0;
+ version = safe_strndup (g, p, max);
- len = strlen (line);
- if (len > 0 && line[len-1] == '\n') {
- line[len-1] = '\0';
- len--;
- }
+ len = strlen (version);
+ p += len + 1;
+ max = valuelen - (p - (char *) value);
+ if (max < 0)
+ max = 0;
+ release = safe_strndup (g, p, max);
- if (STREQ (line, "HEADER=END"))
- break;
- }
+ /* Add the application and what we know. */
+ add_application (g, data->apps, entry->name, "", 0, version, release,
+ "", "", "", "");
- /* Allocate 'apps' list. */
- apps = safe_malloc (g, sizeof *apps);
- apps->len = 0;
- apps->val = NULL;
+ free (version);
+ free (release);
- /* 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;
- }
+ return 0;
+}
- len = strlen (line);
- if (len > 0 && line[len-1] == '\n') {
- line[len-1] = '\0';
- len--;
- }
+static struct guestfs_application_list *
+list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
+{
+ char *Name = NULL, *Packages = NULL;
+ struct rpm_names_list list = { .names = NULL, .len = 0 };
+ struct guestfs_application_list *apps = NULL;
- if (STREQ (line, "DATA=END"))
- break;
+ Name = guestfs___download_to_tmp (g, fs,
+ "/var/lib/rpm/Name", "rpm_Name",
+ MAX_PKG_DB_SIZE);
+ if (Name == NULL)
+ goto error;
- 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, "", "", "", "", "", "");
+ Packages = guestfs___download_to_tmp (g, fs,
+ "/var/lib/rpm/Packages", "rpm_Packages",
+ MAX_PKG_DB_SIZE);
+ if (Packages == NULL)
+ goto error;
- /* Discard next line. */
- if (fgets (line, sizeof line, pp) == NULL) {
- error (g, _("unexpected end of output from db_dump command"));
- goto out;
- }
- }
+ /* Read Name database. */
+ if (guestfs___read_db_dump (g, Name, &list, read_rpm_name) == -1)
+ goto error;
- /* Catch errors from the db_dump command. */
- if (pclose (pp) == -1) {
- perrorf (g, "pclose: %s", cmd);
- goto out;
- }
- pp = NULL;
+ /* Sort the names by link field for fast searching. */
+ qsort (list.names, list.len, sizeof (struct rpm_name), compare_links);
- ret = apps;
+ /* Allocate 'apps' list. */
+ apps = safe_malloc (g, sizeof *apps);
+ apps->len = 0;
+ apps->val = NULL;
- out:
- if (ret == NULL && apps != NULL)
+ /* Read Packages database. */
+ struct read_package_data data = { .list = &list, .apps = apps };
+ if (guestfs___read_db_dump (g, Packages, &data, read_package) == -1)
+ goto error;
+
+ free (Name);
+ free (Packages);
+ free_rpm_names_list (&list);
+
+ return apps;
+
+ error:
+ free (Name);
+ free (Packages);
+ free_rpm_names_list (&list);
+ if (apps != NULL)
guestfs_free_application_list (apps);
- if (pp)
- pclose (pp);
- return ret;
+ return NULL;
}
+
#endif /* defined DB_DUMP */
static struct guestfs_application_list *
list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
{
- const char *basename = "deb_status";
- char tmpdir_basename[strlen (g->tmpdir) + strlen (basename) + 2];
- snprintf (tmpdir_basename, sizeof tmpdir_basename, "%s/%s",
- g->tmpdir, basename);
-
- if (guestfs___download_to_tmp (g, "/var/lib/dpkg/status", basename,
- MAX_PKG_DB_SIZE) == -1)
+ char *status = NULL;
+ status = guestfs___download_to_tmp (g, fs, "/var/lib/dpkg/status", "status",
+ MAX_PKG_DB_SIZE);
+ if (status == NULL)
return NULL;
struct guestfs_application_list *apps = NULL, *ret = NULL;
char *name = NULL, *version = NULL, *release = NULL;
int installed_flag = 0;
- fp = fopen (tmpdir_basename, "r");
+ fp = fopen (status, "r");
if (fp == NULL) {
- perrorf (g, "fopen: %s", tmpdir_basename);
+ perrorf (g, "fopen: %s", status);
goto out;
}
}
if (fclose (fp) == -1) {
- perrorf (g, "fclose: %s", tmpdir_basename);
+ perrorf (g, "fclose: %s", status);
goto out;
}
fp = NULL;
free (name);
free (version);
free (release);
+ free (status);
return ret;
}
static struct guestfs_application_list *
list_applications_windows (guestfs_h *g, struct inspect_fs *fs)
{
- const char *basename = "software";
- char tmpdir_basename[strlen (g->tmpdir) + strlen (basename) + 2];
- snprintf (tmpdir_basename, sizeof tmpdir_basename, "%s/%s",
- g->tmpdir, basename);
-
size_t len = strlen (fs->windows_systemroot) + 64;
char software[len];
snprintf (software, len, "%s/system32/config/software",
fs->windows_systemroot);
char *software_path = guestfs___case_sensitive_path_silently (g, software);
- if (!software_path)
- /* If the software hive doesn't exist, just accept that we cannot
- * list windows apps.
- */
- return 0;
+ if (!software_path) {
+ /* Missing software hive is a problem. */
+ error (g, "no HKLM\\SOFTWARE hive found in the guest");
+ return NULL;
+ }
+ char *software_hive = NULL;
struct guestfs_application_list *ret = NULL;
hive_h *h = NULL;
- if (guestfs___download_to_tmp (g, software_path, basename,
- MAX_REGISTRY_SIZE) == -1)
+ software_hive = guestfs___download_to_tmp (g, fs, software_path, "software",
+ MAX_REGISTRY_SIZE);
+ if (software_hive == NULL)
goto out;
free (software_path);
software_path = NULL;
- h = hivex_open (tmpdir_basename, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
+ h = hivex_open (software_hive, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
if (h == NULL) {
perrorf (g, "hivex_open");
goto out;
out:
if (h) hivex_close (h);
free (software_path);
+ free (software_hive);
return ret;
}
compare_applications);
}
-#else /* no PCRE or hivex at compile time */
+#else /* no hivex at compile time */
/* XXX These functions should be in an optgroup. */
#define NOT_IMPL(r) \
- error (g, _("inspection API not available since this version of libguestfs was compiled without PCRE or hivex libraries")); \
+ error (g, _("inspection API not available since this version of libguestfs was compiled without the hivex library")); \
return r
struct guestfs_application_list *
NOT_IMPL(NULL);
}
-#endif /* no PCRE or hivex at compile time */
+#endif /* no hivex at compile time */