diff --git a/.gitignore b/.gitignore index 7a4696b90..c8b53cac9 100644 --- a/.gitignore +++ b/.gitignore @@ -176,6 +176,7 @@ Makefile.in /daemon/optgroups.mli /daemon/parted.mli /daemon/realpath.mli +/daemon/rpm.mli /daemon/stamp-guestfsd.pod /daemon/statvfs.mli /daemon/structs-cleanups.c diff --git a/appliance/packagelist.in b/appliance/packagelist.in index 25f2645d2..15af4284a 100644 --- a/appliance/packagelist.in +++ b/appliance/packagelist.in @@ -41,6 +41,7 @@ ifelse(REDHAT,1, openssh-clients policycoreutils reiserfs-utils + rpm-libs syslinux-extlinux systemd dnl for /sbin/reboot and udevd vim-minimal @@ -71,6 +72,7 @@ dnl iproute has been renamed to iproute2 isc-dhcp-client ldmtool libc-bin + librpm9 linux-image dnl syslinux 'suggests' mtools, but in reality it's a hard dependency: mtools diff --git a/daemon/Makefile.am b/daemon/Makefile.am index df9dcc4ee..7f2d13414 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -57,6 +57,7 @@ generator_built = \ optgroups.mli \ parted.mli \ realpath.mli \ + rpm.mli \ statvfs.mli \ structs.ml \ structs.mli @@ -167,6 +168,7 @@ guestfsd_SOURCES = \ proto.c \ readdir.c \ rename.c \ + rpm-c.c \ rsync.c \ scrub.c \ selinux.c \ @@ -231,6 +233,7 @@ guestfsd_LDADD = \ $(LIB_CLOCK_GETTIME) \ $(LIBINTL) \ $(PCRE_LIBS) \ + $(LIBRPM_LIBS) \ $(TSK_LIBS) \ $(RPC_LIBS) \ $(YARA_LIBS) \ @@ -260,7 +263,8 @@ guestfsd_CFLAGS = \ $(HIVEX_CFLAGS) \ $(SD_JOURNAL_CFLAGS) \ $(JANSSON_CFLAGS) \ - $(PCRE_CFLAGS) + $(PCRE_CFLAGS) \ + $(LIBRPM_CFLAGS) # Parts of the daemon are written in OCaml. These are linked into a # library and then linked to the daemon. See @@ -298,6 +302,7 @@ SOURCES_MLI = \ optgroups.mli \ parted.mli \ realpath.mli \ + rpm.mli \ statvfs.mli \ structs.mli \ sysroot.mli \ @@ -338,6 +343,7 @@ SOURCES_ML = \ inspect_fs_windows.ml \ inspect_fs.ml \ inspect.ml \ + rpm.ml \ callbacks.ml \ daemon.ml diff --git a/daemon/rpm-c.c b/daemon/rpm-c.c new file mode 100644 index 000000000..92a3abf58 --- /dev/null +++ b/daemon/rpm-c.c @@ -0,0 +1,161 @@ +/* libguestfs - the guestfsd daemon + * Copyright (C) 2021 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_LIBRPM +#include +#include +#include +#include +#endif + +#include "daemon.h" +#include "actions.h" + +/* Very lightweight OCaml bindings for librpm. */ + +#pragma GCC diagnostic ignored "-Wimplicit-function-declaration" +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +#ifndef HAVE_LIBRPM + +value __attribute__((noreturn)) +guestfs_int_daemon_rpm_init (value unitv) +{ + CAMLparam1 (unitv); + caml_failwith ("no support for RPM guests because " + "librpm was missing at compile time"); +} + +value __attribute__((noreturn)) +guestfs_int_daemon_rpm_start_iterator (value unitv) +{ + guestfs_int_daemon_rpm_init (unitv); +} + +value __attribute__((noreturn)) +guestfs_int_daemon_rpm_next_application (value unitv) +{ + guestfs_int_daemon_rpm_init (unitv); +} + +value __attribute__((noreturn)) +guestfs_int_daemon_rpm_end_iterator (value unitv) +{ + guestfs_int_daemon_rpm_init (unitv); +} + +#else /* HAVE_LIBRPM */ + +value +guestfs_int_daemon_rpm_init (value unitv) +{ + CAMLparam1 (unitv); + rpmReadConfigFiles (NULL, NULL); + CAMLreturn (Val_unit); +} + +static rpmts ts; +static rpmdbMatchIterator iter; + +value +guestfs_int_daemon_rpm_start_iterator (value unitv) +{ + CAMLparam1 (unitv); + ts = rpmtsCreate (); + iter = rpmtsInitIterator (ts, RPMDBI_PACKAGES, NULL, 0); + CAMLreturn (Val_unit); +} + +value +guestfs_int_daemon_rpm_next_application (value unitv) +{ + CAMLparam1 (unitv); + CAMLlocal2 (rv, sv); + Header h; + guestfs_int_application2 app = { 0 }; + + h = rpmdbNextIterator (iter); + if (h == NULL) caml_raise_not_found (); + + h = headerLink (h); + app.app2_name = headerFormat (h, "%{NAME}", NULL); + // XXXapp.app2_epoch = headerFormat (h, "%{NAME}", NULL); + app.app2_version = headerFormat (h, "%{VERSION}", NULL); + app.app2_release = headerFormat (h, "%{RELEASE}", NULL); + app.app2_arch = headerFormat (h, "%{ARCH}", NULL); + app.app2_url = headerFormat (h, "%{URL}", NULL); + app.app2_summary = headerFormat (h, "%{SUMMARY}", NULL); + app.app2_description = headerFormat (h, "%{DESCRIPTION}", NULL); + headerFree (h); + + /* Convert this to an OCaml struct. Any NULL fields must be turned + * into empty string. + */ + rv = caml_alloc (17, 0); +#define TO_CAML_STRING(i, name) \ + sv = caml_copy_string (app.name ? app.name : ""); \ + Store_field (rv, i, sv); \ + free (app.name) + + TO_CAML_STRING (0, app2_name); + TO_CAML_STRING (1, app2_display_name); + sv = caml_copy_int32 (app.app2_epoch); + Store_field (rv, 2, sv); + TO_CAML_STRING (3, app2_version); + TO_CAML_STRING (4, app2_release); + TO_CAML_STRING (5, app2_arch); + TO_CAML_STRING (6, app2_install_path); + TO_CAML_STRING (7, app2_trans_path); + TO_CAML_STRING (8, app2_publisher); + TO_CAML_STRING (9, app2_url); + TO_CAML_STRING (10, app2_source_package); + TO_CAML_STRING (11, app2_summary); + TO_CAML_STRING (12, app2_description); + TO_CAML_STRING (13, app2_spare1); + TO_CAML_STRING (14, app2_spare2); + TO_CAML_STRING (15, app2_spare3); + TO_CAML_STRING (16, app2_spare4); +#undef TO_CAML_STRING + + CAMLreturn (rv); +} + +value +guestfs_int_daemon_rpm_end_iterator (value unitv) +{ + CAMLparam1 (unitv); + rpmdbFreeIterator (iter); + rpmtsFree (ts); + CAMLreturn (Val_unit); +} + +#endif /* HAVE_LIBRPM */ diff --git a/daemon/rpm.ml b/daemon/rpm.ml new file mode 100644 index 000000000..f61ce41c5 --- /dev/null +++ b/daemon/rpm.ml @@ -0,0 +1,58 @@ +(* guestfs-inspection + * Copyright (C) 2009-2021 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + *) + +open Printf + +open Std_utils + +external rpm_init : unit -> unit = "guestfs_int_daemon_rpm_init" +external rpm_start_iterator : unit -> unit = "guestfs_int_daemon_rpm_start_iterator" +external rpm_next_application : unit -> Structs.application2 = "guestfs_int_daemon_rpm_next_application" +external rpm_end_iterator : unit -> unit = "guestfs_int_daemon_rpm_end_iterator" + +(* librpm is troublesome when run from the main process. In + * particular it holds open some glibc NSS files. Therefore we fork + * before doing the chroot and any librpm operations. + * + * We could also consider in future limiting the time taken to run the + * subprocess since it's unclear that parsing RPM config files from + * the guest in particular is safe. + *) +let rec internal_list_rpm_applications () = + let chroot = Chroot.create ~name:"librpm" () in + let apps = Chroot.f chroot list_rpm_applications () in + eprintf "librpm returned %d installed packages\n%!" (List.length apps); + apps + +and list_rpm_applications () = + rpm_init (); + rpm_start_iterator (); + let ret = ref [] in + let rec loop () = + try + let app = rpm_next_application () in + List.push_front app ret; + loop () + with Not_found -> () + in + loop (); + rpm_end_iterator (); + List.sort + (fun { Structs.app2_name = n1 } { Structs.app2_name = n2 } -> + compare n1 n2) + !ret diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 831b7e25a..8b6aa8896 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -149,6 +149,7 @@ daemon/pingdaemon.c daemon/proto.c daemon/readdir.c daemon/rename.c +daemon/rpm-c.c daemon/rsync.c daemon/scrub.c daemon/selinux-relabel.c @@ -293,7 +294,6 @@ lib/command.c lib/conn-socket.c lib/copy-in-out.c lib/create.c -lib/dbdump.c lib/drives.c lib/errors.c lib/event-string.c diff --git a/docs/guestfs-building.pod b/docs/guestfs-building.pod index 28c761ce4..4d75772d0 100644 --- a/docs/guestfs-building.pod +++ b/docs/guestfs-building.pod @@ -210,11 +210,6 @@ eg. F. Optional. Used by the L to securely confine the appliance (sVirt). -=item Berkeley DB utils (db_dump, db_load, etc) - -Optional. Usually found in a package called C, -C, C etc. - =item systemtap Optional. For userspace probes. @@ -252,6 +247,10 @@ Optional. Render icons from guests. Optional. Render icons from Windows guests. +=item librpm + +Optional. To parse the list of applications from RPM-based guests. + =item Perl C Optional. Perl module used to test L. diff --git a/generator/actions_inspection.ml b/generator/actions_inspection.ml index e2098cb00..690afd460 100644 --- a/generator/actions_inspection.ml +++ b/generator/actions_inspection.ml @@ -607,6 +607,16 @@ Please read L for more details. See also C, C." }; + { defaults with + name = "internal_list_rpm_applications"; added = (1, 45, 3); + style = RStructList ("applications2", "application2"), [], []; + visibility = VInternal; + impl = OCaml "Rpm.internal_list_rpm_applications"; + shortdesc = "get applications from RPM guest"; + longdesc = "\ +This internal function is used by C +to list the applications for RPM guests."}; + ] let non_daemon_functions = [ diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml index 57976be36..6b6cb7353 100644 --- a/generator/proc_nr.ml +++ b/generator/proc_nr.ml @@ -514,6 +514,7 @@ let proc_nr = [ 507, "luks_uuid"; 508, "cryptsetup_open"; 509, "cryptsetup_close"; +510, "internal_list_rpm_applications"; ] (* End of list. If adding a new entry, add it at the end of the list diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR index 77afe238f..2bc4cd64b 100644 --- a/lib/MAX_PROC_NR +++ b/lib/MAX_PROC_NR @@ -1 +1 @@ -509 +510 diff --git a/lib/Makefile.am b/lib/Makefile.am index f9cc53df1..7f36ae515 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -81,7 +81,6 @@ libguestfs_la_SOURCES = \ conn-socket.c \ copy-in-out.c \ create.c \ - dbdump.c \ drives.c \ errors.c \ event-string.c \ diff --git a/lib/dbdump.c b/lib/dbdump.c deleted file mode 100644 index 7c17ce6b3..000000000 --- a/lib/dbdump.c +++ /dev/null @@ -1,229 +0,0 @@ -/* libguestfs - * Copyright (C) 2010-2012 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 - -#include -#include -#include -#include -#include - -#ifdef HAVE_ENDIAN_H -#include -#endif - -#include "guestfs.h" -#include "guestfs-internal.h" - -#if defined(DB_DUMP) - -static void read_db_dump_line (guestfs_h *g, void *datav, const char *line, size_t len); -static unsigned char *convert_hex_to_binary (guestfs_h *g, const char *hex, size_t hexlen, size_t *binlen_rtn); - -struct cb_data { - guestfs_int_db_dump_callback callback; - void *opaque; - enum { reading_header, - reading_key, reading_value, - reading_finished, - reading_failed } state; - unsigned char *key; - size_t keylen; -}; - -/* 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. - */ -int -guestfs_int_read_db_dump (guestfs_h *g, - const char *dumpfile, void *opaque, - guestfs_int_db_dump_callback callback) -{ - struct cb_data data; - CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g); - int r; - - data.callback = callback; - data.opaque = opaque; - data.state = reading_header; - data.key = NULL; - - guestfs_int_cmd_add_arg (cmd, DB_DUMP); - guestfs_int_cmd_add_arg (cmd, "-k"); - guestfs_int_cmd_add_arg (cmd, dumpfile); - guestfs_int_cmd_set_stdout_callback (cmd, read_db_dump_line, &data, 0); - - r = guestfs_int_cmd_run (cmd); - free (data.key); - - if (r == -1) - return -1; - if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { - guestfs_int_external_command_failed (g, r, DB_DUMP, NULL); - return -1; - } - if (data.state != reading_finished) { - error (g, _("%s: unexpected error or end of output"), DB_DUMP); - return -1; - } - - return 0; -} - -static void -read_db_dump_line (guestfs_h *g, void *datav, const char *line, size_t len) -{ - struct cb_data *data = datav; - - switch (data->state) { - case reading_finished: - case reading_failed: - return; - - case reading_header: - /* Ignore everything to end-of-header marker. */ - if (STRPREFIX (line, "HEADER=END")) - data->state = reading_key; - return; - - /* Read the key, value pairs using a state machine. 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. - */ - case reading_key: - if (STRPREFIX (line, "DATA=END")) { - data->state = reading_finished; - return; - } - - if (len < 1 || line[0] != ' ') { - debug (g, _("unexpected line from db_dump command, no space prefix")); - data->state = reading_failed; - return; - } - - data->key = convert_hex_to_binary (g, &line[1], len-1, &data->keylen); - if (data->key == NULL) { - data->state = reading_failed; - return; - } - - data->state = reading_value; - return; - - case reading_value: { - CLEANUP_FREE unsigned char *value = NULL; - size_t valuelen; - - if (len < 1 || line[0] != ' ') { - debug (g, _("unexpected line from db_dump command, no space prefix")); - data->state = reading_failed; - return; - } - - value = convert_hex_to_binary (g, &line[1], len-1, &valuelen); - if (value == NULL) { - data->state = reading_failed; - return; - } - - if (data->callback (g, data->key, data->keylen, - value, valuelen, data->opaque) == -1) { - data->state = reading_failed; - return; - } - - free (data->key); - data->key = NULL; - - data->state = reading_key; - return; - } - } -} - -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(DB_DUMP) */ diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h index d7ec7215d..4799ee0a1 100644 --- a/lib/guestfs-internal.h +++ b/lib/guestfs-internal.h @@ -719,10 +719,6 @@ extern int guestfs_int_set_backend (guestfs_h *g, const char *method); /* inspect.c */ extern char *guestfs_int_download_to_tmp (guestfs_h *g, const char *filename, const char *extension, uint64_t max_size); -/* dbdump.c */ -typedef int (*guestfs_int_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_int_read_db_dump (guestfs_h *g, const char *dumpfile, void *opaque, guestfs_int_db_dump_callback callback); - /* lpj.c */ extern int guestfs_int_get_lpj (guestfs_h *g); diff --git a/lib/inspect-apps.c b/lib/inspect-apps.c index dbc9d968c..da0003672 100644 --- a/lib/inspect-apps.c +++ b/lib/inspect-apps.c @@ -47,22 +47,12 @@ /* Some limits on what the inspection code will read, for safety. */ -/* Maximum RPM 'Packages' file we will download to /tmp. This file - * can get very large: 70 MB is roughly the standard size for a new - * Fedora install, and after lots of package installation/removal - * I have seen well over 400 MB databases. - */ -#define MAX_RPM_PACKAGES_SIZE (500 * 1000 * 1000) -/* Maximum RPM 'Name' file we will download to /tmp. */ -#define MAX_RPM_NAME_SIZE (50 * 1000 * 1000) /* Maximum dpkg 'status' file we will download to /tmp. */ #define MAX_DPKG_STATUS_SIZE (50 * 1000 * 1000) /* Maximum APK 'installed' file we will download to /tmp. */ #define MAX_APK_INSTALLED_SIZE (50 * 1000 * 1000) -#ifdef DB_DUMP static struct guestfs_application2_list *list_applications_rpm (guestfs_h *g, const char *root); -#endif static struct guestfs_application2_list *list_applications_deb (guestfs_h *g, const char *root); static struct guestfs_application2_list *list_applications_pacman (guestfs_h *g, const char *root); static struct guestfs_application2_list *list_applications_apk (guestfs_h *g, const char *root); @@ -136,11 +126,9 @@ guestfs_impl_inspect_list_applications2 (guestfs_h *g, const char *root) if (STREQ (type, "linux") || STREQ (type, "hurd")) { if (STREQ (package_format, "rpm")) { -#ifdef DB_DUMP ret = list_applications_rpm (g, root); if (ret == NULL) return NULL; -#endif } else if (STREQ (package_format, "deb")) { ret = list_applications_deb (g, root); @@ -178,254 +166,15 @@ guestfs_impl_inspect_list_applications2 (guestfs_h *g, const char *root) return ret; } -#ifdef DB_DUMP - -/* This data comes from the Name database, and contains the application - * names and the first 4 bytes of each 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) -{ - size_t i; - - for (i = 0; i < list->len; ++i) - free (list->names[i].name); - free (list->names); -} - -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; - const unsigned char *link_p; - char *name; - - /* Ignore bogus entries. */ - if (keylen == 0 || valuelen < 4) - return 0; - - /* A name entry will have as many links as installed instances of - * that package. 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; -} - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-align" - -/* tag constants, see rpmtag.h in RPM for complete list */ -#define RPMTAG_VERSION 1001 -#define RPMTAG_RELEASE 1002 -#define RPMTAG_EPOCH 1003 -#define RPMTAG_ARCH 1022 -#define RPMTAG_URL 1020 -#define RPMTAG_SUMMARY 1004 -#define RPMTAG_DESCRIPTION 1005 - -static char * -get_rpm_header_tag (guestfs_h *g, const unsigned char *header_start, - size_t header_len, uint32_t tag, char type) -{ - uint32_t num_fields, offset; - const unsigned char *cursor = header_start + 8, *store, *header_end; - size_t max_len; - char iv[4]; - - /* 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; - max_len = header_end - (store + offset); - - switch (type) { - case 's': - return safe_strndup (g, (const char *) (store + offset), max_len); - - case 'i': - memset (iv, 0, sizeof iv); - memcpy (iv, (void *) (store + offset), - max_len > sizeof iv ? sizeof iv : max_len); - return safe_memdup (g, iv, sizeof iv); - - default: - abort (); - } - } - cursor += 16; - } - - return NULL; -} - -struct read_package_data { - struct rpm_names_list *list; - struct guestfs_application2_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; - CLEANUP_FREE char *version = NULL, *release = NULL, - *epoch_str = NULL, *arch = NULL, *url = NULL, *summary = NULL, - *description = NULL; - int32_t epoch; - - /* 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 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. */ - if (keylen < 4 || valuelen == 0) - return 0; - - /* 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. - */ - - version = get_rpm_header_tag (g, value, valuelen, RPMTAG_VERSION, 's'); - release = get_rpm_header_tag (g, value, valuelen, RPMTAG_RELEASE, 's'); - epoch_str = get_rpm_header_tag (g, value, valuelen, RPMTAG_EPOCH, 'i'); - arch = get_rpm_header_tag (g, value, valuelen, RPMTAG_ARCH, 's'); - url = get_rpm_header_tag (g, value, valuelen, RPMTAG_URL, 's'); - summary = get_rpm_header_tag (g, value, valuelen, RPMTAG_SUMMARY, 's'); - description = get_rpm_header_tag (g, value, valuelen, RPMTAG_DESCRIPTION, 's'); - - /* The epoch is stored as big-endian integer. */ - if (epoch_str) - epoch = be32toh (*(int32_t *) epoch_str); - else - epoch = 0; - - /* Add the application and what we know. */ - if (version && release) - add_application (g, data->apps, entry->name, "", epoch, version, release, - arch ? arch : "", "", "", url ? : "", "", - summary ? : "", description ? : ""); - - return 0; -} - -#pragma GCC diagnostic pop - static struct guestfs_application2_list * list_applications_rpm (guestfs_h *g, const char *root) { - CLEANUP_FREE char *Name = NULL, *Packages = NULL; - struct rpm_names_list list = { .names = NULL, .len = 0 }; - struct guestfs_application2_list *apps = NULL; - struct read_package_data data; - - Name = guestfs_int_download_to_tmp (g, "/var/lib/rpm/Name", NULL, - MAX_RPM_NAME_SIZE); - if (Name == NULL) - goto error; - - Packages = guestfs_int_download_to_tmp (g, "/var/lib/rpm/Packages", NULL, - MAX_RPM_PACKAGES_SIZE); - if (Packages == NULL) - goto error; - - /* Read Name database. */ - if (guestfs_int_read_db_dump (g, Name, &list, read_rpm_name) == -1) - goto error; - - /* Sort the names by link field for fast searching. */ - qsort (list.names, list.len, sizeof (struct rpm_name), compare_links); - - /* Allocate 'apps' list. */ - apps = safe_malloc (g, sizeof *apps); - apps->len = 0; - apps->val = NULL; - - /* Read Packages database. */ - data.list = &list; - data.apps = apps; - if (guestfs_int_read_db_dump (g, Packages, &data, read_package) == -1) - goto error; - - free_rpm_names_list (&list); - - return apps; - - error: - free_rpm_names_list (&list); - guestfs_free_application2_list (apps); - - return NULL; + /* We don't need the ‘root’ parameter here. The caller is supposed + * to have mounted the guest up before calling the public API. + */ + return guestfs_internal_list_rpm_applications (g); } -#endif /* defined DB_DUMP */ - static struct guestfs_application2_list * list_applications_deb (guestfs_h *g, const char *root) { diff --git a/m4/guestfs-daemon.m4 b/m4/guestfs-daemon.m4 index aa90268b4..1728249a5 100644 --- a/m4/guestfs-daemon.m4 +++ b/m4/guestfs-daemon.m4 @@ -102,6 +102,14 @@ PKG_CHECK_MODULES([HIVEX], [hivex],[ [AC_MSG_FAILURE([hivex library is required])]) AM_CONDITIONAL([HAVE_HIVEX],[test "x$HIVEX_LIBS" != "x"]) +dnl librpm library (optional) +PKG_CHECK_MODULES([LIBRPM], [rpm >= 4.6.0],[ + AC_SUBST([LIBRPM_CFLAGS]) + AC_SUBST([LIBRPM_LIBS]) + AC_DEFINE([HAVE_LIBRPM],[1],[librpm library found at compile time.]) +],[AC_MSG_WARN([librpm library not found])] +) + dnl systemd journal library (optional) PKG_CHECK_MODULES([SD_JOURNAL], [libsystemd],[ AC_SUBST([SD_JOURNAL_CFLAGS]) diff --git a/m4/guestfs-progs.m4 b/m4/guestfs-progs.m4 index bf1f83c9d..f90bda04e 100644 --- a/m4/guestfs-progs.m4 +++ b/m4/guestfs-progs.m4 @@ -63,16 +63,6 @@ AC_CHECK_PROG([PO4A_GETTEXTIZE],[po4a-gettextize],[po4a-gettextize],[no]) AC_CHECK_PROG([PO4A_TRANSLATE],[po4a-translate],[po4a-translate],[no]) AM_CONDITIONAL([HAVE_PO4A], [test "x$PO4A_GETTEXTIZE" != "xno" && test "x$PO4A_TRANSLATE" != "xno"]) -dnl Check for db_dump, db_load (optional). -GUESTFS_FIND_DB_TOOL([DB_DUMP], [dump]) -GUESTFS_FIND_DB_TOOL([DB_LOAD], [load]) -if test "x$DB_DUMP" != "xno"; then - AC_DEFINE_UNQUOTED([DB_DUMP],["$DB_DUMP"],[Name of db_dump program.]) -fi -if test "x$DB_LOAD" != "xno"; then - AC_DEFINE_UNQUOTED([DB_LOAD],["$DB_LOAD"],[Name of db_load program.]) -fi - dnl Check for netpbm programs (optional). AC_PATH_PROGS([PBMTEXT],[pbmtext],[no]) AC_PATH_PROGS([PNMTOPNG],[pnmtopng],[no])