diff options
| author | John Eckersberg <jeckersb@redhat.com> | 2012-10-29 16:22:00 -0400 |
|---|---|---|
| committer | Richard W.M. Jones <rjones@redhat.com> | 2012-10-29 21:56:45 +0000 |
| commit | 63a091e21dbb509923eaeeb310707447385c566c (patch) | |
| tree | 0eee95fd97a7f179c56090708b077c66bb72f36e /src | |
| parent | 269460770d9d472e882da7ec80a1e3a1ba4f8c3a (diff) | |
| download | libguestfs-63a091e21dbb509923eaeeb310707447385c566c.tar.gz libguestfs-63a091e21dbb509923eaeeb310707447385c566c.tar.xz libguestfs-63a091e21dbb509923eaeeb310707447385c566c.zip | |
lib: update inspect_list_applications to return all installed RPMs (RHBZ#859885).
Note that because of RHBZ#859949, this will return two identical
entries for RPMs which differ only by arch.
Diffstat (limited to 'src')
| -rw-r--r-- | src/inspect-apps.c | 117 |
1 files changed, 71 insertions, 46 deletions
diff --git a/src/inspect-apps.c b/src/inspect-apps.c index f65c70a5..23e68e49 100644 --- a/src/inspect-apps.c +++ b/src/inspect-apps.c @@ -125,7 +125,7 @@ guestfs__inspect_list_applications (guestfs_h *g, const char *root) #ifdef DB_DUMP /* This data comes from the Name database, and contains the application - * names and the first 4 bytes of the link field. + * names and the first 4 bytes of each link field. */ struct rpm_names_list { struct rpm_name *names; @@ -161,26 +161,76 @@ read_rpm_name (guestfs_h *g, void *listv) { struct rpm_names_list *list = listv; + const unsigned char *link_p; 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++; + /* A name entry will have as many links as installed instances of + * that pacakge. For example, if glibc.i686 and glibc.x86_64 are + * both installed, then there will be a link for each Packages + * entry. Add an entry onto list for all installed instances. + */ + for (link_p = value; link_p < value + valuelen; link_p += 8) { + name = safe_strndup (g, (const char *) key, keylen); + + 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, link_p, 4); + list->len++; + } return 0; } +/* tag constants, see rpmtag.h in RPM for complete list */ +#define RPMTAG_VERSION 1001 +#define RPMTAG_RELEASE 1002 + +static char * +get_rpm_header_tag (guestfs_h *g, const unsigned char *header_start, + size_t header_len, uint32_t tag) +{ + uint32_t num_fields, offset; + const unsigned char *cursor = header_start + 8, *store, *header_end; + + /* This function parses the RPM header structure to pull out various + * tag strings (version, release, arch, etc.). For more detail on the + * header format, see: + * http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html#S2-RPM-FILE-FORMAT-HEADER + */ + + /* The minimum header size that makes sense here is 24 bytes. Four + * bytes for number of fields, followed by four bytes denoting the + * size of the store, then 16 bytes for the first index entry. + */ + if (header_len < 24) + return NULL; + + num_fields = be32toh (*(uint32_t *) header_start); + store = header_start + 8 + (16 * num_fields); + + /* The first byte *after* the buffer. If you are here, you've gone + * too far! */ + header_end = header_start + header_len; + + while (cursor < store && cursor <= header_end - 16) { + if (be32toh (*(uint32_t *) cursor) == tag) { + offset = be32toh(*(uint32_t *) (cursor + 8)); + if (store + offset >= header_end) + return NULL; + return safe_strndup(g, (const char *) (store + offset), + header_end - (store + offset)); + } + cursor += 16; + } + + return NULL; +} + struct read_package_data { struct rpm_names_list *list; struct guestfs_application_list *apps; @@ -194,16 +244,13 @@ read_package (guestfs_h *g, { 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; + char *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). + * value is a long binary string, but we can extract the header data + * from it as below. First we have to look up the link field in the + * list of links (which is sorted by link field). */ /* Ignore bogus entries. */ @@ -219,38 +266,16 @@ read_package (guestfs_h *g, /* 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. + * application out of the binary value string. */ - /* 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; - - /* 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 (version); - p += len + 1; - max = valuelen - (p - (char *) value); - if (max < 0) - max = 0; - release = safe_strndup (g, p, max); + version = get_rpm_header_tag (g, value, valuelen, RPMTAG_VERSION); + release = get_rpm_header_tag (g, value, valuelen, RPMTAG_RELEASE); /* Add the application and what we know. */ - add_application (g, data->apps, entry->name, "", 0, version, release, - "", "", "", ""); + if (version && release) + add_application (g, data->apps, entry->name, "", 0, version, release, + "", "", "", ""); free (version); free (release); |
