From cfc87a581d1200b3e2082e242258b68639c31d75 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Oct 07 2014 13:27:35 +0000 Subject: Update to current master --- diff --git a/0001-ext2-fix-small-memory-leak-on-error.patch b/0001-ext2-fix-small-memory-leak-on-error.patch new file mode 100644 index 0000000..f4b5643 --- /dev/null +++ b/0001-ext2-fix-small-memory-leak-on-error.patch @@ -0,0 +1,26 @@ +From b84a7d38902199382a7264cc17be509384940020 Mon Sep 17 00:00:00 2001 +From: Pino Toscano +Date: Tue, 9 Sep 2014 14:43:02 +0200 +Subject: [PATCH] ext2: fix small memory leak on error + +When failing to read the output of `readlink -f`, free the memory buffer +used for it. +--- + src/ext2fs-c.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/ext2fs-c.c b/src/ext2fs-c.c +index 9e0770a..f3ca7dc 100644 +--- a/src/ext2fs-c.c ++++ b/src/ext2fs-c.c +@@ -631,6 +631,7 @@ ext2_copy_file (struct ext2_data *data, const char *src, const char *dest) + } + if (fgets (new_dirname, PATH_MAX, fp) == NULL) { + pclose (fp); ++ free (new_dirname); + goto cont; + } + pclose (fp); +-- +1.9.3 + diff --git a/0002-build-Add-some-more-debug-messages.patch b/0002-build-Add-some-more-debug-messages.patch new file mode 100644 index 0000000..1f0eeb8 --- /dev/null +++ b/0002-build-Add-some-more-debug-messages.patch @@ -0,0 +1,36 @@ +From c11daf4e7b254bcb56af4871e0a1b8d27b828486 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Sat, 13 Sep 2014 14:41:29 +0100 +Subject: [PATCH] build: Add some more debug messages. + +Allows us to use 'annotate-output supermin -v ...' in order to find +out how long the RPM dependency checking takes. +--- + src/build.ml | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/build.ml b/src/build.ml +index 8ff574b..7011731 100644 +--- a/src/build.ml ++++ b/src/build.ml +@@ -86,11 +86,17 @@ let rec build debug + (* Read the supermin appliance, ie. the input files and/or + * directories that make up the appliance. + *) ++ if debug >= 1 then ++ printf "supermin: reading the supermin appliance\n%!"; + let appliance = read_appliance debug basedir empty_appliance inputs in + + (* Resolve dependencies in the list of packages. *) + let ph = get_package_handler () in ++ if debug >= 1 then ++ printf "supermin: mapping package names to installed packages\n%!"; + let packages = filter_map ph.ph_package_of_string appliance.packages in ++ if debug >= 1 then ++ printf "supermin: resolving full list of package dependencies\n%!"; + let packages = + let packages = package_set_of_list packages in + get_all_requires packages in +-- +1.9.3 + diff --git a/0003-rpm-Don-t-both-verifying-signatures-and-digests-when.patch b/0003-rpm-Don-t-both-verifying-signatures-and-digests-when.patch new file mode 100644 index 0000000..7cf193d --- /dev/null +++ b/0003-rpm-Don-t-both-verifying-signatures-and-digests-when.patch @@ -0,0 +1,61 @@ +From 7c3984e45b91f8a78530efe6ddfc7a83556892ec Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Mon, 15 Sep 2014 13:51:06 +0100 +Subject: [PATCH] rpm: Don't both verifying signatures and digests when reading + RPM metadata. + +supermin doesn't care, and there's a performance penalty for +doing it. + +Thanks: Panu Matilainen +--- + src/rpm.ml | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/src/rpm.ml b/src/rpm.ml +index 434f15b..1195948 100644 +--- a/src/rpm.ml ++++ b/src/rpm.ml +@@ -136,7 +136,7 @@ let rpm_package_of_string str = + * ourselves. *) + let parse_rpm str = + let cmd = +- sprintf "%s -q --qf '%%{name} %%{epoch} %%{version} %%{release} %%{arch}\\n' %s" ++ sprintf "%s --nosignature --nodigest -q --qf '%%{name} %%{epoch} %%{version} %%{release} %%{arch}\\n' %s" + Config.rpm + (quote str) in + let lines = run_command_get_lines cmd in +@@ -177,7 +177,8 @@ let rpm_package_of_string str = + + (* Check if an RPM is installed. *) + and check_rpm_installed name = +- let cmd = sprintf "%s -q %s >/dev/null" Config.rpm (quote name) in ++ let cmd = sprintf "%s --nosignature --nodigest -q %s >/dev/null" ++ Config.rpm (quote name) in + 0 = Sys.command cmd + in + +@@ -227,9 +228,9 @@ let rpm_get_package_database_mtime () = + let rpm_get_all_requires pkgs = + let get pkgs = + let cmd = sprintf "\ +- %s -qR %s | ++ %s --nosignature --nodigest -qR %s | + awk '{print $1}' | +- xargs rpm -q --qf '%%{name}\\n' --whatprovides | ++ xargs rpm --nosignature --nodigest -q --qf '%%{name}\\n' --whatprovides | + grep -v 'no package provides' | + sort -u" + Config.rpm +@@ -251,7 +252,7 @@ let rpm_get_all_requires pkgs = + + let rpm_get_all_files pkgs = + let cmd = sprintf "\ +- %s -q --qf '[%%{FILENAMES}\\t%%{FILEFLAGS:fflags}\\n]' %s | ++ %s --nosignature --nodigest -q --qf '[%%{FILENAMES}\\t%%{FILEFLAGS:fflags}\\n]' %s | + grep '^/' | + sort -u" + Config.rpm +-- +1.9.3 + diff --git a/0004-Use-open_process_full-in-compressed-file-reading.patch b/0004-Use-open_process_full-in-compressed-file-reading.patch new file mode 100644 index 0000000..f61d72a --- /dev/null +++ b/0004-Use-open_process_full-in-compressed-file-reading.patch @@ -0,0 +1,37 @@ +From 68bc5e82e4892f981b1d5409ec217518749c4b61 Mon Sep 17 00:00:00 2001 +From: Pino Toscano +Date: Wed, 17 Sep 2014 12:59:54 +0200 +Subject: [PATCH] Use open_process_full in compressed file reading + +Since only few bytes of the compressed file are read, closing the stdout +of the process will cause it to complain about that. +Switch to open_process_full instead of open_process_in, so we can close +also stderr and avoid that harmless error message. +--- + src/build.ml | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/build.ml b/src/build.ml +index 7011731..500ce33 100644 +--- a/src/build.ml ++++ b/src/build.ml +@@ -322,13 +322,13 @@ and get_file_content file buf len = + + and get_compressed_file_content zcat file = + let cmd = sprintf "%s %s" zcat (quote file) in +- let chan = open_process_in cmd in ++ let chan_out, chan_in, chan_err = open_process_full cmd [||] in + let buf = String.create 512 in +- let len = input chan buf 0 (String.length buf) in ++ let len = input chan_out buf 0 (String.length buf) in + (* We're expecting the subprocess to fail because we close the pipe + * early, so: + *) +- ignore (close_process_in chan); ++ ignore (Unix.close_process_full (chan_out, chan_in, chan_err)); + + get_file_content file buf len + +-- +1.9.3 + diff --git a/0005-package-handlers-add-possibility-for-final-teardown.patch b/0005-package-handlers-add-possibility-for-final-teardown.patch new file mode 100644 index 0000000..4de1814 --- /dev/null +++ b/0005-package-handlers-add-possibility-for-final-teardown.patch @@ -0,0 +1,120 @@ +From 57cb5a62439cc0e4b59d4d19ee1d7e0ffdb4ba26 Mon Sep 17 00:00:00 2001 +From: Pino Toscano +Date: Wed, 17 Sep 2014 13:07:24 +0200 +Subject: [PATCH] package handlers: add possibility for final teardown + +Add a ph_fini callback to package handlers, so they can do teardown +operations, if needed, at the very end of the supermin run. + +Currently all of the current package handlers do nothing. +--- + src/dpkg.ml | 1 + + src/package_handler.ml | 5 +++++ + src/package_handler.mli | 7 +++++++ + src/pacman.ml | 1 + + src/rpm.ml | 1 + + src/supermin.ml | 4 +++- + 6 files changed, 18 insertions(+), 1 deletion(-) + +diff --git a/src/dpkg.ml b/src/dpkg.ml +index 1bb3f7f..ddfb03a 100644 +--- a/src/dpkg.ml ++++ b/src/dpkg.ml +@@ -191,6 +191,7 @@ let () = + let ph = { + ph_detect = dpkg_detect; + ph_init = dpkg_init; ++ ph_fini = (fun () -> ()); + ph_package_of_string = dpkg_package_of_string; + ph_package_to_string = dpkg_package_to_string; + ph_package_name = dpkg_package_name; +diff --git a/src/package_handler.ml b/src/package_handler.ml +index b1dffc0..64b8f66 100644 +--- a/src/package_handler.ml ++++ b/src/package_handler.ml +@@ -59,6 +59,7 @@ let file_source file = + type package_handler = { + ph_detect : unit -> bool; + ph_init : settings -> unit; ++ ph_fini : unit -> unit; + ph_package_of_string : string -> package option; + ph_package_to_string : package -> string; + ph_package_name : package -> string; +@@ -140,6 +141,10 @@ let rec get_package_handler_name () = + | Some (system, packager, _) -> sprintf "%s/%s" system packager + | None -> assert false + ++let package_handler_shutdown () = ++ let ph = get_package_handler () in ++ ph.ph_fini () ++ + let get_all_requires pkgs = + let ph = get_package_handler () in + match ph.ph_get_requires with +diff --git a/src/package_handler.mli b/src/package_handler.mli +index 7e17981..43b0c08 100644 +--- a/src/package_handler.mli ++++ b/src/package_handler.mli +@@ -99,6 +99,11 @@ type package_handler = { + initializes. The [settings] parameter is a struct of general + settings and configuration. *) + ++ ph_fini : unit -> unit; ++ (** This is called at the end of the supermin processing. It can ++ be used to do teardown operations for the package handler, ++ when no more package-related operations are going to be done. *) ++ + ph_package_of_string : string -> package option; + (** Convert a string (from user input) into a package object. If + the package is not installed or the string is otherwise +@@ -172,6 +177,8 @@ val list_package_handlers : unit -> unit + + val check_system : settings -> unit + ++val package_handler_shutdown : unit -> unit ++ + val get_package_handler : unit -> package_handler + + val get_package_handler_name : unit -> string +diff --git a/src/pacman.ml b/src/pacman.ml +index 8b11ba8..45fb393 100644 +--- a/src/pacman.ml ++++ b/src/pacman.ml +@@ -227,6 +227,7 @@ let () = + let ph = { + ph_detect = pacman_detect; + ph_init = pacman_init; ++ ph_fini = (fun () -> ()); + ph_package_of_string = pacman_package_of_string; + ph_package_to_string = pacman_package_to_string; + ph_package_name = pacman_package_name; +diff --git a/src/rpm.ml b/src/rpm.ml +index 1195948..1bd81f4 100644 +--- a/src/rpm.ml ++++ b/src/rpm.ml +@@ -394,6 +394,7 @@ let () = + let fedora = { + ph_detect = fedora_detect; + ph_init = rpm_init; ++ ph_fini = (fun () -> ()); + ph_package_of_string = rpm_package_of_string; + ph_package_to_string = rpm_package_to_string; + ph_package_name = rpm_package_name; +diff --git a/src/supermin.ml b/src/supermin.ml +index 0153977..2ee61a9 100644 +--- a/src/supermin.ml ++++ b/src/supermin.ml +@@ -261,7 +261,9 @@ let main () = + *) + sprintf "( chmod -R +w %s ; rm -rf %s ) 2>/dev/null &" + (quote old_outputdir) (quote old_outputdir) in +- ignore (Sys.command cmd) ++ ignore (Sys.command cmd); ++ ++ package_handler_shutdown () + + let () = + try main () +-- +1.9.3 + diff --git a/0006-rpm-use-the-rpm-library-instead-of-invoking-rpm.patch b/0006-rpm-use-the-rpm-library-instead-of-invoking-rpm.patch new file mode 100644 index 0000000..4255692 --- /dev/null +++ b/0006-rpm-use-the-rpm-library-instead-of-invoking-rpm.patch @@ -0,0 +1,1011 @@ +From 5e85a311a866e691715fe4450b53f5655e4d5ae4 Mon Sep 17 00:00:00 2001 +From: Pino Toscano +Date: Wed, 17 Sep 2014 17:00:11 +0200 +Subject: [PATCH] rpm: use the rpm library instead of invoking rpm + +Look for the rpm library, and use it to query for the information +needed, such as: + - the list of installed packages + - the list of requires for a specified package + - the providers of a specified capability + - the list of files of a package + +Also, rework the dependency resolution, using a queue to iterate on the +packages not resolved yet (thus resolving each package just once), and +caching the provider of each capability. +--- + configure.ac | 4 + + src/Makefile.am | 7 +- + src/librpm-c.c | 483 ++++++++++++++++++++++++++++++++++++++++++++++++ + src/librpm.ml | 51 +++++ + src/librpm.mli | 48 +++++ + src/rpm.ml | 215 +++++++++++---------- + src/supermin-link.sh.in | 2 +- + 7 files changed, 698 insertions(+), 112 deletions(-) + create mode 100644 src/librpm-c.c + create mode 100644 src/librpm.ml + create mode 100644 src/librpm.mli + +diff --git a/configure.ac b/configure.ac +index 65dab78..e604ea2 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -95,6 +95,10 @@ dnl For yum-rpm handler. + AC_PATH_PROG(RPM,[rpm],[no]) + AC_PATH_PROG(RPM2CPIO,[rpm2cpio],[no]) + AC_PATH_PROG(YUMDOWNLOADER,[yumdownloader],[no]) ++PKG_CHECK_MODULES([LIBRPM], [rpm], [librpm=yes], [:]) ++if test "x$librpm" = "xyes"; then ++ AC_DEFINE([HAVE_LIBRPM], [1], [Define if you have librpm]) ++fi + + dnl For Zypper handler. + AC_PATH_PROG(ZYPPER,[zypper],[no]) +diff --git a/src/Makefile.am b/src/Makefile.am +index 90aa773..6261c86 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -42,6 +42,9 @@ SOURCES = \ + realpath-c.c \ + realpath.ml \ + realpath.mli \ ++ librpm-c.c \ ++ librpm.ml \ ++ librpm.mli \ + config.ml \ + utils.ml \ + utils.mli \ +@@ -66,6 +69,7 @@ SOURCES_ML = \ + fnmatch.ml \ + glob.ml \ + realpath.ml \ ++ librpm.ml \ + config.ml \ + utils.ml \ + types.ml \ +@@ -86,6 +90,7 @@ SOURCES_C = \ + ext2init-c.c \ + fnmatch-c.c \ + glob-c.c \ ++ librpm-c.c \ + realpath-c.c + + CLEANFILES = *~ *.cmi *.cmo *.cmx *.o supermin +@@ -98,7 +103,7 @@ bin_PROGRAMS = supermin + supermin_SOURCES = $(SOURCES_C) + supermin_CFLAGS = \ + -I$(shell $(OCAMLC) -where) \ +- $(EXT2FS_CFLAGS) $(COM_ERR_CFLAGS) \ ++ $(EXT2FS_CFLAGS) $(COM_ERR_CFLAGS) $(LIBRPM_CFLAGS) \ + -Wall $(WERROR_CFLAGS) \ + -I$(top_srcdir)/lib -I../lib + +diff --git a/src/librpm-c.c b/src/librpm-c.c +new file mode 100644 +index 0000000..7131de9 +--- /dev/null ++++ b/src/librpm-c.c +@@ -0,0 +1,483 @@ ++/* supermin 5 ++ * Copyright (C) 2014 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 ++#include ++#include ++ ++#ifdef HAVE_LIBRPM ++ ++#include ++#include ++#include ++#include ++#include ++ ++static rpmlogCallback old_log_callback; ++ ++static int ++supermin_rpm_log_callback (rpmlogRec rec, rpmlogCallbackData data) ++{ ++ fprintf (stderr, "supermin: rpm: lib: %s%s", ++ rpmlogLevelPrefix (rpmlogRecPriority (rec)), ++ rpmlogRecMessage (rec)); ++ return 0; ++} ++ ++struct librpm_data ++{ ++ rpmts ts; ++ int debug; ++}; ++ ++static void librpm_handle_closed (void) __attribute__((noreturn)); ++ ++static void ++librpm_handle_closed (void) ++{ ++ caml_failwith ("librpm: function called on a closed handle"); ++} ++ ++static void ++librpm_raise_multiple_matches (int occurrences) ++{ ++ caml_raise_with_arg (*caml_named_value ("librpm_multiple_matches"), ++ Val_int (occurrences)); ++} ++ ++#define Librpm_val(v) (*((struct librpm_data *)Data_custom_val(v))) ++#define Val_none Val_int(0) ++#define Some_val(v) Field(v,0) ++ ++static void ++librpm_finalize (value rpmv) ++{ ++ struct librpm_data data = Librpm_val (rpmv); ++ ++ if (data.ts) { ++ rpmtsFree (data.ts); ++ ++ rpmlogSetCallback (old_log_callback, NULL); ++ } ++} ++ ++static struct custom_operations librpm_custom_operations = { ++ (char *) "librpm_custom_operations", ++ librpm_finalize, ++ custom_compare_default, ++ custom_hash_default, ++ custom_serialize_default, ++ custom_deserialize_default ++}; ++ ++static value ++Val_librpm (struct librpm_data *data) ++{ ++ CAMLparam0 (); ++ CAMLlocal1 (rpmv); ++ ++ rpmv = caml_alloc_custom (&librpm_custom_operations, ++ sizeof (struct librpm_data), 0, 1); ++ Librpm_val (rpmv) = *data; ++ CAMLreturn (rpmv); ++} ++ ++value ++supermin_rpm_is_available (value unit) ++{ ++ return Val_true; ++} ++ ++value ++supermin_rpm_version (value unit) ++{ ++ return caml_copy_string (RPMVERSION); ++} ++ ++value ++supermin_rpm_open (value debugv) ++{ ++ CAMLparam1 (debugv); ++ CAMLlocal1 (rpmv); ++ struct librpm_data data; ++ int res; ++ rpmlogLvl lvl; ++ ++ data.debug = debugv == Val_none ? 0 : Int_val (Some_val (debugv)); ++ ++ switch (data.debug) { ++ case 3: ++ lvl = RPMLOG_INFO; ++ break; ++ case 2: ++ lvl = RPMLOG_NOTICE; ++ break; ++ case 1: ++ lvl = RPMLOG_WARNING; ++ break; ++ case 0: ++ default: ++ lvl = RPMLOG_ERR; ++ break; ++ } ++ ++ rpmSetVerbosity (lvl); ++ old_log_callback = rpmlogSetCallback (supermin_rpm_log_callback, NULL); ++ ++ res = rpmReadConfigFiles (NULL, NULL); ++ if (res == -1) ++ caml_failwith ("rpm_open: rpmReadConfigFiles failed"); ++ ++ data.ts = rpmtsCreate (); ++ if (data.ts == NULL) ++ caml_failwith ("rpm_open: rpmtsCreate failed"); ++ ++ rpmv = Val_librpm (&data); ++ CAMLreturn (rpmv); ++} ++ ++value ++supermin_rpm_close (value rpmv) ++{ ++ CAMLparam1 (rpmv); ++ ++ librpm_finalize (rpmv); ++ ++ /* So we don't double-free in the finalizer. */ ++ Librpm_val (rpmv).ts = NULL; ++ ++ CAMLreturn (Val_unit); ++} ++ ++value ++supermin_rpm_installed (value rpmv, value pkgv) ++{ ++ CAMLparam2 (rpmv, pkgv); ++ CAMLlocal2 (rv, v); ++ struct librpm_data data; ++ rpmdbMatchIterator iter; ++ int count, i; ++ Header h; ++ ++ data = Librpm_val (rpmv); ++ if (data.ts == NULL) ++ librpm_handle_closed (); ++ ++ iter = rpmtsInitIterator (data.ts, RPMTAG_NAME, String_val (pkgv), 0); ++ if (iter == NULL) ++ caml_raise_not_found (); ++ ++ count = rpmdbGetIteratorCount (iter); ++ if (data.debug >= 2) ++ printf ("supermin: rpm: installed: %d occurrences for '%s'\n", count, String_val (pkgv)); ++ ++ rv = caml_alloc (count, 0); ++ i = 0; ++ ++ while ((h = rpmdbNextIterator (iter)) != NULL) { ++ HeaderIterator hi; ++ rpmtd td; ++ uint32_t *val; ++ bool stored_vals[5] = { false }; ++ ++ v = caml_alloc (5, 0); ++ hi = headerInitIterator (h); ++ td = rpmtdNew (); ++ while (headerNext (hi, td) == 1) { ++ switch (rpmtdTag (td)) { ++ case RPMTAG_NAME: ++ Store_field (v, 0, caml_copy_string (rpmtdGetString (td))); ++ stored_vals[0] = true; ++ break; ++ case RPMTAG_EPOCH: ++ val = rpmtdGetUint32 (td); ++ Store_field (v, 1, Val_int ((int) *val)); ++ stored_vals[1] = true; ++ break; ++ case RPMTAG_VERSION: ++ Store_field (v, 2, caml_copy_string (rpmtdGetString (td))); ++ stored_vals[2] = true; ++ break; ++ case RPMTAG_RELEASE: ++ Store_field (v, 3, caml_copy_string (rpmtdGetString (td))); ++ stored_vals[3] = true; ++ break; ++ case RPMTAG_ARCH: ++ Store_field (v, 4, caml_copy_string (rpmtdGetString (td))); ++ stored_vals[4] = true; ++ break; ++ } ++ rpmtdFreeData (td); ++ } ++ /* Make sure to properly initialize all the fields of the returned ++ * rmp_t, even if some tags are missing in the RPM header. ++ */ ++ if (!stored_vals[0]) ++ Store_field (v, 0, caml_copy_string (String_val (pkgv))); ++ if (!stored_vals[1]) ++ Store_field (v, 1, Val_int (0)); ++ if (!stored_vals[2]) ++ Store_field (v, 2, caml_copy_string ("0")); ++ if (!stored_vals[3]) ++ Store_field (v, 3, caml_copy_string ("unknown")); ++ if (!stored_vals[4]) ++ Store_field (v, 4, caml_copy_string ("unknown")); ++ Store_field (rv, i, v); ++ ++ rpmtdFree (td); ++ headerFreeIterator (hi); ++ ++i; ++ } ++ ++ rpmdbFreeIterator (iter); ++ ++ CAMLreturn (rv); ++} ++ ++value ++supermin_rpm_pkg_requires (value rpmv, value pkgv) ++{ ++ CAMLparam2 (rpmv, pkgv); ++ CAMLlocal1 (rv); ++ struct librpm_data data; ++ rpmdbMatchIterator iter; ++ int count, i; ++ Header h; ++ rpmtd td; ++ ++ data = Librpm_val (rpmv); ++ if (data.ts == NULL) ++ librpm_handle_closed (); ++ ++ iter = rpmtsInitIterator (data.ts, RPMDBI_LABEL, String_val (pkgv), 0); ++ if (iter == NULL) ++ caml_raise_not_found (); ++ ++ count = rpmdbGetIteratorCount (iter); ++ if (data.debug >= 2) ++ printf ("supermin: rpm: pkg_requires: %d occurrences for '%s'\n", count, String_val (pkgv)); ++ if (count != 1) ++ librpm_raise_multiple_matches (count); ++ ++ h = rpmdbNextIterator (iter); ++ assert (h != NULL); ++ ++ td = rpmtdNew (); ++ i = headerGet (h, RPMTAG_REQUIRENAME, td, HEADERGET_MINMEM); ++ if (i != 1) ++ caml_failwith ("rpm_pkg_requires: headerGet failed"); ++ ++ rv = caml_alloc (rpmtdCount (td), 0); ++ for (i = 0; i < rpmtdCount (td); ++i) ++ Store_field (rv, i, caml_copy_string (rpmtdNextString (td))); ++ ++ rpmtdFreeData (td); ++ rpmtdFree (td); ++ ++ rpmdbFreeIterator (iter); ++ ++ CAMLreturn (rv); ++} ++ ++static rpmdbMatchIterator ++createProvidesIterator (rpmts ts, const char *what) ++{ ++ rpmdbMatchIterator mi = NULL; ++ ++ if (what[0] != '/') { ++ mi = rpmtsInitIterator(ts, RPMDBI_PROVIDENAME, what, 0); ++ if (mi != NULL) ++ return mi; ++ } ++ mi = rpmtsInitIterator(ts, RPMDBI_INSTFILENAMES, what, 0); ++ if (mi != NULL) ++ return mi; ++ ++ mi = rpmtsInitIterator(ts, RPMDBI_PROVIDENAME, what, 0); ++ ++ return mi; ++} ++ ++value ++supermin_rpm_pkg_whatprovides (value rpmv, value pkgv) ++{ ++ CAMLparam2 (rpmv, pkgv); ++ CAMLlocal1 (rv); ++ struct librpm_data data; ++ rpmdbMatchIterator iter; ++ int count, i; ++ Header h; ++ ++ data = Librpm_val (rpmv); ++ if (data.ts == NULL) ++ librpm_handle_closed (); ++ ++ iter = createProvidesIterator (data.ts, String_val (pkgv)); ++ if (iter == NULL) ++ caml_raise_not_found (); ++ ++ count = rpmdbGetIteratorCount (iter); ++ if (data.debug >= 2) ++ printf ("supermin: rpm: pkg_whatprovides: %d occurrences for '%s'\n", count, String_val (pkgv)); ++ ++ rv = caml_alloc (count, 0); ++ i = 0; ++ ++ while ((h = rpmdbNextIterator (iter)) != NULL) { ++ rpmtd td; ++ int ret; ++ ++ td = rpmtdNew (); ++ ret = headerGet (h, RPMTAG_NAME, td, HEADERGET_MINMEM); ++ if (ret != 1) ++ caml_failwith ("rpm_pkg_whatprovides: headerGet failed"); ++ ++ Store_field (rv, i, caml_copy_string (rpmtdGetString (td))); ++ ++ rpmtdFreeData (td); ++ rpmtdFree (td); ++ ++i; ++ } ++ ++ rpmdbFreeIterator (iter); ++ ++ CAMLreturn (rv); ++} ++ ++value ++supermin_rpm_pkg_filelist (value rpmv, value pkgv) ++{ ++ CAMLparam2 (rpmv, pkgv); ++ CAMLlocal2 (rv, v); ++ struct librpm_data data; ++ rpmdbMatchIterator iter; ++ int count, i; ++ Header h; ++ rpmfi fi; ++ const rpmfiFlags fiflags = RPMFI_NOHEADER | RPMFI_FLAGS_QUERY | RPMFI_NOFILEDIGESTS; ++ ++ data = Librpm_val (rpmv); ++ if (data.ts == NULL) ++ librpm_handle_closed (); ++ ++ iter = rpmtsInitIterator (data.ts, RPMDBI_LABEL, String_val (pkgv), 0); ++ if (iter == NULL) ++ caml_raise_not_found (); ++ ++ count = rpmdbGetIteratorCount (iter); ++ if (data.debug >= 2) ++ printf ("supermin: rpm: pkg_filelist: %d occurrences for '%s'\n", count, String_val (pkgv)); ++ if (count != 1) ++ librpm_raise_multiple_matches (count); ++ ++ h = rpmdbNextIterator (iter); ++ assert (h != NULL); ++ ++ fi = rpmfiNew (data.ts, h, RPMTAG_BASENAMES, fiflags); ++ ++ count = rpmfiFC (fi); ++ if (count < 0) ++ count = 0; ++ ++ rv = caml_alloc (count, 0); ++ i = 0; ++ ++ fi = rpmfiInit (fi, 0); ++ while (rpmfiNext (fi) >= 0) { ++ const char *fn; ++ ++ v = caml_alloc (2, 0); ++ fn = rpmfiFN(fi); ++ Store_field (v, 0, caml_copy_string (fn)); ++ if (rpmfiFFlags (fi) & RPMFILE_CONFIG) ++ Store_field (v, 1, Val_long (1)); /* FileConfig */ ++ else ++ Store_field (v, 1, Val_long (0)); /* FileNormal */ ++ Store_field (rv, i, v); ++ ++i; ++ } ++ rpmfiFree(fi); ++ ++ rpmdbFreeIterator (iter); ++ ++ CAMLreturn (rv); ++} ++ ++#else ++ ++value ++supermin_rpm_is_available (value unit) ++{ ++ return Val_false; ++} ++ ++value ++supermin_rpm_version (value unit) ++{ ++ abort (); ++} ++ ++value ++supermin_rpm_open (value debugv) ++{ ++ abort (); ++} ++ ++value ++supermin_rpm_close (value rpmv) ++{ ++ abort (); ++} ++ ++value ++supermin_rpm_installed (value rpmv, value pkgv) ++{ ++ abort (); ++} ++ ++value ++supermin_rpm_pkg_required (value rpmv, value pkgv) ++{ ++ abort (); ++} ++ ++value ++supermin_rpm_pkg_whatprovides (value rpmv, value pkgv) ++{ ++ abort (); ++} ++ ++value ++supermin_rpm_pkg_filelist (value rpmv, value pkgv) ++{ ++ abort (); ++} ++ ++#endif +diff --git a/src/librpm.ml b/src/librpm.ml +new file mode 100644 +index 0000000..aa8d367 +--- /dev/null ++++ b/src/librpm.ml +@@ -0,0 +1,51 @@ ++(* supermin 5 ++ * Copyright (C) 2014 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 ++ *) ++ ++external rpm_is_available : unit -> bool = "supermin_rpm_is_available" ++ ++external rpm_version : unit -> string = "supermin_rpm_version" ++ ++type t ++ ++exception Multiple_matches of int ++ ++external rpm_open : ?debug:int -> t = "supermin_rpm_open" ++external rpm_close : t -> unit = "supermin_rpm_close" ++ ++type rpm_t = { ++ name : string; ++ epoch : int; ++ version : string; ++ release : string; ++ arch : string; ++} ++ ++type rpmfile_t = { ++ filepath : string; ++ filetype : rpmfiletype_t; ++} and rpmfiletype_t = ++ | FileNormal ++ | FileConfig ++ ++external rpm_installed : t -> string -> rpm_t array = "supermin_rpm_installed" ++external rpm_pkg_requires : t -> string -> string array = "supermin_rpm_pkg_requires" ++external rpm_pkg_whatprovides : t -> string -> string array = "supermin_rpm_pkg_whatprovides" ++external rpm_pkg_filelist : t -> string -> rpmfile_t array = "supermin_rpm_pkg_filelist" ++ ++let () = ++ Callback.register_exception "librpm_multiple_matches" (Multiple_matches 0) +diff --git a/src/librpm.mli b/src/librpm.mli +new file mode 100644 +index 0000000..880a038 +--- /dev/null ++++ b/src/librpm.mli +@@ -0,0 +1,48 @@ ++(* supermin 5 ++ * Copyright (C) 2014 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 ++ *) ++ ++val rpm_is_available : unit -> bool ++ ++val rpm_version : unit -> string ++ ++type t ++ ++exception Multiple_matches of int ++ ++val rpm_open : ?debug:int -> t ++val rpm_close : t -> unit ++ ++type rpm_t = { ++ name : string; ++ epoch : int; ++ version : string; ++ release : string; ++ arch : string; ++} ++ ++type rpmfile_t = { ++ filepath : string; ++ filetype : rpmfiletype_t; ++} and rpmfiletype_t = ++ | FileNormal ++ | FileConfig ++ ++val rpm_installed : t -> string -> rpm_t array ++val rpm_pkg_requires : t -> string -> string array ++val rpm_pkg_whatprovides : t -> string -> string array ++val rpm_pkg_filelist : t -> string -> rpmfile_t array +diff --git a/src/rpm.ml b/src/rpm.ml +index 1bd81f4..3a75ea0 100644 +--- a/src/rpm.ml ++++ b/src/rpm.ml +@@ -21,9 +21,15 @@ open Printf + + open Utils + open Package_handler ++open Librpm ++ ++module StringSet = Set.Make (String) ++ ++let stringset_of_list pkgs = ++ List.fold_left (fun set elem -> StringSet.add elem set) StringSet.empty pkgs + + let fedora_detect () = +- Config.rpm <> "no" && Config.rpm2cpio <> "no" && ++ Config.rpm <> "no" && Config.rpm2cpio <> "no" && rpm_is_available () && + Config.yumdownloader <> "no" && + try + (stat "/etc/redhat-release").st_kind = S_REG || +@@ -31,12 +37,12 @@ let fedora_detect () = + with Unix_error _ -> false + + let opensuse_detect () = +- Config.rpm <> "no" && Config.rpm2cpio <> "no" && ++ Config.rpm <> "no" && Config.rpm2cpio <> "no" && rpm_is_available () && + Config.zypper <> "no" && + try (stat "/etc/SuSE-release").st_kind = S_REG with Unix_error _ -> false + + let mageia_detect () = +- Config.rpm <> "no" && Config.rpm2cpio <> "no" && ++ Config.rpm <> "no" && Config.rpm2cpio <> "no" && rpm_is_available () && + Config.urpmi <> "no" && + Config.fakeroot <> "no" && + try (stat "/etc/mageia-release").st_kind = S_REG with Unix_error _ -> false +@@ -44,6 +50,14 @@ let mageia_detect () = + let settings = ref no_settings + let rpm_major, rpm_minor = ref 0, ref 0 + let zypper_major, zypper_minor, zypper_patch = ref 0, ref 0, ref 0 ++let t = ref None ++ ++let get_rpm () = ++ match !t with ++ | None -> ++ eprintf "supermin: rpm: get_rpm called too early"; ++ exit 1 ++ | Some t -> t + + let rec rpm_init s = + settings := s; +@@ -51,31 +65,26 @@ let rec rpm_init s = + (* Get RPM version. We have to adjust some RPM commands based on + * the version. + *) +- let cmd = sprintf "%s --version | awk '{print $3}'" Config.rpm in +- let lines = run_command_get_lines cmd in ++ let version = rpm_version () in + let major, minor = +- match lines with ++ match string_split "." version with + | [] -> +- eprintf "supermin: rpm --version command had no output\n"; ++ eprintf "supermin: unable to parse empty rpm version string\n"; + exit 1 +- | line :: _ -> +- let line = string_split "." line in +- match line with +- | [] -> +- eprintf "supermin: unable to parse empty output of rpm --version\n"; +- exit 1 +- | [x] -> +- eprintf "supermin: unable to parse output of rpm --version: %s\n" x; +- exit 1 +- | major :: minor :: _ -> +- try int_of_string major, int_of_string minor +- with Failure "int_of_string" -> +- eprintf "supermin: unable to parse output of rpm --version: non-numeric\n"; +- exit 1 in ++ | [x] -> ++ eprintf "supermin: unable to parse rpm version string: %s\n" x; ++ exit 1 ++ | major :: minor :: _ -> ++ try int_of_string major, int_of_string minor ++ with Failure "int_of_string" -> ++ eprintf "supermin: unable to parse rpm version string: non-numeric, %s\n" version; ++ exit 1 in + rpm_major := major; + rpm_minor := minor; + if !settings.debug >= 1 then +- printf "supermin: rpm: detected RPM version %d.%d\n" major minor ++ printf "supermin: rpm: detected RPM version %d.%d\n" major minor; ++ ++ t := Some (rpm_open ~debug:!settings.debug) + + and opensuse_init s = + rpm_init s; +@@ -115,13 +124,10 @@ and opensuse_init s = + if !settings.debug >= 1 then + printf "supermin: rpm: detected zypper version %d.%d.%d\n" major minor patch + +-type rpm_t = { +- name : string; +- epoch : int32; +- version : string; +- release : string; +- arch : string; +-} ++let rpm_fini () = ++ match !t with ++ | None -> () ++ | Some t -> rpm_close t + + (* Memo from package type to internal rpm_t. *) + let rpm_of_pkg, pkg_of_rpm = get_memo_functions () +@@ -130,38 +136,8 @@ let rpm_of_pkg, pkg_of_rpm = get_memo_functions () + let rpmh = Hashtbl.create 13 + + let rpm_package_of_string str = +- (* Parse an RPM name into the fields like name and version. Since +- * the package is installed (see check below), it's easier to use RPM +- * itself to do this parsing rather than haphazardly parsing it +- * ourselves. *) +- let parse_rpm str = +- let cmd = +- sprintf "%s --nosignature --nodigest -q --qf '%%{name} %%{epoch} %%{version} %%{release} %%{arch}\\n' %s" +- Config.rpm +- (quote str) in +- let lines = run_command_get_lines cmd in +- let lines = List.map (string_split " ") lines in +- let rpms = filter_map ( +- function +- | [ name; ("0"|"(none)"); version; release; arch ] -> +- Some { name = name; +- epoch = 0_l; +- version = version; release = release; arch = arch } +- | [ name; epoch; version; release; arch ] -> +- Some { name = name; +- epoch = Int32.of_string epoch; +- version = version; release = release; arch = arch } +- | xs -> +- (* grrr, RPM doesn't send errors to stderr *) +- None +- ) lines in +- +- if rpms = [] then ( +- eprintf "supermin: no output from rpm command could be parsed when searching for '%s'\nThe command was:\n %s\n" +- str cmd; +- exit 1 +- ); +- ++ let query rpm = ++ let rpms = Array.to_list (rpm_installed (get_rpm ()) str) in + (* RPM will return multiple hits when either multiple versions or + * multiple arches are installed at the same time. We are only + * interested in the highest version with the best +@@ -174,12 +150,6 @@ let rpm_package_of_string str = + in + let rpms = List.sort cmp rpms in + List.hd rpms +- +- (* Check if an RPM is installed. *) +- and check_rpm_installed name = +- let cmd = sprintf "%s --nosignature --nodigest -q %s >/dev/null" +- Config.rpm (quote name) in +- 0 = Sys.command cmd + in + + try +@@ -187,11 +157,8 @@ let rpm_package_of_string str = + with + Not_found -> + let r = +- if check_rpm_installed str then ( +- let rpm = parse_rpm str in +- Some (pkg_of_rpm rpm) +- ) +- else None in ++ try Some (pkg_of_rpm (query str)) ++ with Not_found -> None in + Hashtbl.add rpmh str r; + r + +@@ -212,10 +179,10 @@ let rpm_package_to_string pkg = + !rpm_major < 4 || (!rpm_major = 4 && !rpm_minor < 11) in + + let rpm = rpm_of_pkg pkg in +- if is_rpm_lt_4_11 || rpm.epoch = 0_l then ++ if is_rpm_lt_4_11 || rpm.epoch = 0 then + sprintf "%s-%s-%s.%s" rpm.name rpm.version rpm.release rpm.arch + else +- sprintf "%s-%ld:%s-%s.%s" ++ sprintf "%s-%d:%s-%s.%s" + rpm.name rpm.epoch rpm.version rpm.release rpm.arch + + let rpm_package_name pkg = +@@ -225,47 +192,75 @@ let rpm_package_name pkg = + let rpm_get_package_database_mtime () = + (lstat "/var/lib/rpm/Packages").st_mtime + ++(* Memo of resolved provides. *) ++let rpm_providers = Hashtbl.create 13 ++ + let rpm_get_all_requires pkgs = +- let get pkgs = +- let cmd = sprintf "\ +- %s --nosignature --nodigest -qR %s | +- awk '{print $1}' | +- xargs rpm --nosignature --nodigest -q --qf '%%{name}\\n' --whatprovides | +- grep -v 'no package provides' | +- sort -u" +- Config.rpm +- (quoted_list (List.map rpm_package_to_string +- (PackageSet.elements pkgs))) in +- let lines = run_command_get_lines cmd in +- let lines = filter_map rpm_package_of_string lines in +- PackageSet.union pkgs (package_set_of_list lines) +- in +- (* The command above only gets one level of dependencies. We need +- * to keep iterating until we reach a fixpoint. +- *) +- let rec loop pkgs = +- let pkgs' = get pkgs in +- if PackageSet.equal pkgs pkgs' then pkgs +- else loop pkgs' ++ let get pkg = ++ let reqs = ++ try ++ rpm_pkg_requires (get_rpm ()) pkg ++ with ++ Multiple_matches _ as ex -> ++ match rpm_package_of_string pkg with ++ | None -> raise ex ++ | Some pkg -> ++ rpm_pkg_requires (get_rpm ()) (rpm_package_to_string pkg) in ++ let pkgs' = Array.fold_left ( ++ fun set x -> ++ try ++ let provides = ++ try Hashtbl.find rpm_providers x ++ with Not_found -> rpm_pkg_whatprovides (get_rpm ()) x in ++ let newset = Array.fold_left ( ++ fun newset p -> ++ match rpm_package_of_string p with ++ | None -> newset ++ | Some x -> StringSet.add p newset ++ ) StringSet.empty provides in ++ StringSet.union set newset ++ with Not_found -> set ++ ) StringSet.empty reqs in ++ pkgs' + in +- loop pkgs ++ let queue = Queue.create () in ++ let final = ref (stringset_of_list ++ (List.map rpm_package_name ++ (PackageSet.elements pkgs))) in ++ StringSet.iter (fun x -> Queue.push x queue) !final; ++ let resolved = ref StringSet.empty in ++ while not (Queue.is_empty queue) do ++ let current = Queue.pop queue in ++ if not (StringSet.mem current !resolved) then ( ++ try ++ let expanded = get current in ++ let diff = StringSet.diff expanded !final in ++ if not (StringSet.is_empty diff) then ( ++ final := StringSet.union !final diff; ++ StringSet.iter (fun x -> Queue.push x queue) diff; ++ ) ++ with Not_found -> (); ++ resolved := StringSet.add current !resolved ++ ) ++ done; ++ let pkgs' = filter_map rpm_package_of_string (StringSet.elements !final) in ++ package_set_of_list pkgs' + + let rpm_get_all_files pkgs = +- let cmd = sprintf "\ +- %s --nosignature --nodigest -q --qf '[%%{FILENAMES}\\t%%{FILEFLAGS:fflags}\\n]' %s | +- grep '^/' | +- sort -u" +- Config.rpm +- (quoted_list (List.map rpm_package_to_string (PackageSet.elements pkgs))) in +- let lines = run_command_get_lines cmd in +- let lines = List.map (string_split "\t") lines in ++ let files_compare { filepath = a } { filepath = b } = ++ compare a b in ++ let files = List.map rpm_package_to_string (PackageSet.elements pkgs) in ++ let files = List.fold_right ( ++ fun pkg xs -> ++ let files = Array.to_list (rpm_pkg_filelist (get_rpm ()) pkg) in ++ files @ xs ++ ) files [] in ++ let files = sort_uniq ~cmp:files_compare files in + List.map ( +- function +- | [ path; flags ] -> +- let config = String.contains flags 'c' in ++ fun { filepath = path; filetype = flags } -> ++ let config = flags = FileConfig in + { ft_path = path; ft_source_path = path; ft_config = config } +- | _ -> assert false +- ) lines ++ ) files + + let rec fedora_download_all_packages pkgs dir = + let tdir = !settings.tmpdir // string_random8 () in +@@ -394,7 +389,7 @@ let () = + let fedora = { + ph_detect = fedora_detect; + ph_init = rpm_init; +- ph_fini = (fun () -> ()); ++ ph_fini = rpm_fini; + ph_package_of_string = rpm_package_of_string; + ph_package_to_string = rpm_package_to_string; + ph_package_name = rpm_package_name; +diff --git a/src/supermin-link.sh.in b/src/supermin-link.sh.in +index b2d71d9..29b84a1 100644 +--- a/src/supermin-link.sh.in ++++ b/src/supermin-link.sh.in +@@ -21,4 +21,4 @@ + # Hack automake to link 'supermin' binary properly. There is no other + # way to add the -cclib parameter to the end of the command line. + +-exec "$@" -linkpkg -cclib '@EXT2FS_LIBS@ @COM_ERR_LIBS@' ++exec "$@" -linkpkg -cclib '@EXT2FS_LIBS@ @COM_ERR_LIBS@ @LIBRPM_LIBS@' +-- +1.9.3 + diff --git a/0007-rpm-fix-typo-in-non-librpm-code.patch b/0007-rpm-fix-typo-in-non-librpm-code.patch new file mode 100644 index 0000000..a88b22a --- /dev/null +++ b/0007-rpm-fix-typo-in-non-librpm-code.patch @@ -0,0 +1,27 @@ +From 906fedeed83fe8f1a4e4fd7ec2656e548b83b9c5 Mon Sep 17 00:00:00 2001 +From: Pino Toscano +Date: Wed, 17 Sep 2014 17:34:59 +0200 +Subject: [PATCH] rpm: fix typo in non-librpm code + +Properly spell the name of a stub function, so it builds fine also with +no rpm library. +--- + src/librpm-c.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/librpm-c.c b/src/librpm-c.c +index 7131de9..e5079b1 100644 +--- a/src/librpm-c.c ++++ b/src/librpm-c.c +@@ -463,7 +463,7 @@ supermin_rpm_installed (value rpmv, value pkgv) + } + + value +-supermin_rpm_pkg_required (value rpmv, value pkgv) ++supermin_rpm_pkg_requires (value rpmv, value pkgv) + { + abort (); + } +-- +1.9.3 + diff --git a/0008-rpm-reuse-the-rpmtd-when-possible.patch b/0008-rpm-reuse-the-rpmtd-when-possible.patch new file mode 100644 index 0000000..5fceae1 --- /dev/null +++ b/0008-rpm-reuse-the-rpmtd-when-possible.patch @@ -0,0 +1,91 @@ +From 3a967a746008001fb9b7ade5451d19a1f1956601 Mon Sep 17 00:00:00 2001 +From: Pino Toscano +Date: Thu, 18 Sep 2014 16:37:48 +0200 +Subject: [PATCH] rpm: reuse the rpmtd when possible + +Use a single rpmtd object per operation, making sure it is properly +cleaned when needed. This slightly reduces the number of malloc/free's. +--- + src/librpm-c.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/src/librpm-c.c b/src/librpm-c.c +index e5079b1..1ae3bad 100644 +--- a/src/librpm-c.c ++++ b/src/librpm-c.c +@@ -185,6 +185,7 @@ supermin_rpm_installed (value rpmv, value pkgv) + rpmdbMatchIterator iter; + int count, i; + Header h; ++ rpmtd td; + + data = Librpm_val (rpmv); + if (data.ts == NULL) +@@ -200,16 +201,15 @@ supermin_rpm_installed (value rpmv, value pkgv) + + rv = caml_alloc (count, 0); + i = 0; ++ td = rpmtdNew (); + + while ((h = rpmdbNextIterator (iter)) != NULL) { + HeaderIterator hi; +- rpmtd td; + uint32_t *val; + bool stored_vals[5] = { false }; + + v = caml_alloc (5, 0); + hi = headerInitIterator (h); +- td = rpmtdNew (); + while (headerNext (hi, td) == 1) { + switch (rpmtdTag (td)) { + case RPMTAG_NAME: +@@ -251,11 +251,11 @@ supermin_rpm_installed (value rpmv, value pkgv) + Store_field (v, 4, caml_copy_string ("unknown")); + Store_field (rv, i, v); + +- rpmtdFree (td); + headerFreeIterator (hi); + ++i; + } + ++ rpmtdFree (td); + rpmdbFreeIterator (iter); + + CAMLreturn (rv); +@@ -334,6 +334,7 @@ supermin_rpm_pkg_whatprovides (value rpmv, value pkgv) + rpmdbMatchIterator iter; + int count, i; + Header h; ++ rpmtd td; + + data = Librpm_val (rpmv); + if (data.ts == NULL) +@@ -349,12 +350,11 @@ supermin_rpm_pkg_whatprovides (value rpmv, value pkgv) + + rv = caml_alloc (count, 0); + i = 0; ++ td = rpmtdNew (); + + while ((h = rpmdbNextIterator (iter)) != NULL) { +- rpmtd td; + int ret; + +- td = rpmtdNew (); + ret = headerGet (h, RPMTAG_NAME, td, HEADERGET_MINMEM); + if (ret != 1) + caml_failwith ("rpm_pkg_whatprovides: headerGet failed"); +@@ -362,10 +362,10 @@ supermin_rpm_pkg_whatprovides (value rpmv, value pkgv) + Store_field (rv, i, caml_copy_string (rpmtdGetString (td))); + + rpmtdFreeData (td); +- rpmtdFree (td); + ++i; + } + ++ rpmtdFree (td); + rpmdbFreeIterator (iter); + + CAMLreturn (rv); +-- +1.9.3 + diff --git a/0009-rpm-reuse-the-same-iteration-set.patch b/0009-rpm-reuse-the-same-iteration-set.patch new file mode 100644 index 0000000..ab0a773 --- /dev/null +++ b/0009-rpm-reuse-the-same-iteration-set.patch @@ -0,0 +1,35 @@ +From c3638240da5f1b56a807cdf802495704bd395cc2 Mon Sep 17 00:00:00 2001 +From: Pino Toscano +Date: Thu, 18 Sep 2014 16:58:54 +0200 +Subject: [PATCH] rpm: reuse the same iteration set + +When resolving the requirements of a package, use the same set with the +results, instead of creating a new set and merging it with the results +set. +--- + src/rpm.ml | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/src/rpm.ml b/src/rpm.ml +index 3a75ea0..4c2156a 100644 +--- a/src/rpm.ml ++++ b/src/rpm.ml +@@ -212,13 +212,12 @@ let rpm_get_all_requires pkgs = + let provides = + try Hashtbl.find rpm_providers x + with Not_found -> rpm_pkg_whatprovides (get_rpm ()) x in +- let newset = Array.fold_left ( ++ Array.fold_left ( + fun newset p -> + match rpm_package_of_string p with + | None -> newset + | Some x -> StringSet.add p newset +- ) StringSet.empty provides in +- StringSet.union set newset ++ ) set provides + with Not_found -> set + ) StringSet.empty reqs in + pkgs' +-- +1.9.3 + diff --git a/0010-chroot-fix-quoting-in-cp-invocation.patch b/0010-chroot-fix-quoting-in-cp-invocation.patch new file mode 100644 index 0000000..08f2b9c --- /dev/null +++ b/0010-chroot-fix-quoting-in-cp-invocation.patch @@ -0,0 +1,27 @@ +From 73e10157dc90309e9d2cea63e1be3c74ed4aa635 Mon Sep 17 00:00:00 2001 +From: Pino Toscano +Date: Mon, 29 Sep 2014 16:53:55 +0200 +Subject: [PATCH] chroot: fix quoting in cp invocation + +Make sure to quote source and destination, to avoid failures when +dealing with paths with e.g. spaces, brackets, etc. +--- + src/chroot.ml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/chroot.ml b/src/chroot.ml +index 63a5a79..d0ee4c3 100644 +--- a/src/chroot.ml ++++ b/src/chroot.ml +@@ -60,7 +60,7 @@ let build_chroot debug files outputdir = + + | S_REG | S_CHR | S_BLK | S_FIFO | S_SOCK -> + if debug >= 2 then printf "supermin: chroot: copy %s\n%!" opath; +- let cmd = sprintf "cp -p %s %s" path opath in ++ let cmd = sprintf "cp -p %s %s" (quote path) (quote opath) in + ignore (Sys.command cmd) + with Unix_error _ -> () + ) files; +-- +1.9.3 + diff --git a/0011-rpm-use-the-proper-parameter.patch b/0011-rpm-use-the-proper-parameter.patch new file mode 100644 index 0000000..8835e7f --- /dev/null +++ b/0011-rpm-use-the-proper-parameter.patch @@ -0,0 +1,25 @@ +From 17317277c8c7a7051502c975d3aa03d9863ca552 Mon Sep 17 00:00:00 2001 +From: Pino Toscano +Date: Tue, 7 Oct 2014 10:51:01 +0200 +Subject: [PATCH] rpm: use the proper parameter + +--- + src/rpm.ml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/rpm.ml b/src/rpm.ml +index 4c2156a..b88ac73 100644 +--- a/src/rpm.ml ++++ b/src/rpm.ml +@@ -137,7 +137,7 @@ let rpmh = Hashtbl.create 13 + + let rpm_package_of_string str = + let query rpm = +- let rpms = Array.to_list (rpm_installed (get_rpm ()) str) in ++ let rpms = Array.to_list (rpm_installed (get_rpm ()) rpm) in + (* RPM will return multiple hits when either multiple versions or + * multiple arches are installed at the same time. We are only + * interested in the highest version with the best +-- +1.9.3 + diff --git a/0012-rpm-fix-caching-of-provides.patch b/0012-rpm-fix-caching-of-provides.patch new file mode 100644 index 0000000..48c06ea --- /dev/null +++ b/0012-rpm-fix-caching-of-provides.patch @@ -0,0 +1,29 @@ +From 46f987c48296ebd35c153c04488fcf7ee97a41bd Mon Sep 17 00:00:00 2001 +From: Pino Toscano +Date: Tue, 7 Oct 2014 11:09:10 +0200 +Subject: [PATCH] rpm: fix caching of provides + +Now each provide is resolved really once. +--- + src/rpm.ml | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/rpm.ml b/src/rpm.ml +index b88ac73..71cdab2 100644 +--- a/src/rpm.ml ++++ b/src/rpm.ml +@@ -211,7 +211,10 @@ let rpm_get_all_requires pkgs = + try + let provides = + try Hashtbl.find rpm_providers x +- with Not_found -> rpm_pkg_whatprovides (get_rpm ()) x in ++ with Not_found -> ++ let p = rpm_pkg_whatprovides (get_rpm ()) x in ++ Hashtbl.add rpm_providers x p; ++ p in + Array.fold_left ( + fun newset p -> + match rpm_package_of_string p with +-- +1.9.3 + diff --git a/0013-rpm-check-for-providers-for-not-found-package.patch b/0013-rpm-check-for-providers-for-not-found-package.patch new file mode 100644 index 0000000..5a0ccc7 --- /dev/null +++ b/0013-rpm-check-for-providers-for-not-found-package.patch @@ -0,0 +1,38 @@ +From d78c898c7e2bc5f12cbebef98b95a7908d9120f1 Mon Sep 17 00:00:00 2001 +From: Pino Toscano +Date: Tue, 7 Oct 2014 11:51:15 +0200 +Subject: [PATCH] rpm: check for providers for not found package + +If a package is not found among the ones installed, check whether there +is any other package providing it (only a single one). +--- + src/rpm.ml | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/src/rpm.ml b/src/rpm.ml +index 71cdab2..b5c5ff5 100644 +--- a/src/rpm.ml ++++ b/src/rpm.ml +@@ -158,7 +158,18 @@ let rpm_package_of_string str = + Not_found -> + let r = + try Some (pkg_of_rpm (query str)) +- with Not_found -> None in ++ with Not_found -> ++ try ++ let p = rpm_pkg_whatprovides (get_rpm ()) str in ++ (* Pick only a provided package when there is just one of them, ++ * otherwise there is no reliable way to know which one to pick ++ * if there are multiple providers. ++ *) ++ if Array.length p = 1 then ++ Some (pkg_of_rpm (query p.(0))) ++ else ++ None ++ with Not_found -> None in + Hashtbl.add rpmh str r; + r + +-- +1.9.3 + diff --git a/supermin.spec b/supermin.spec index 1fb4965..d37039d 100644 --- a/supermin.spec +++ b/supermin.spec @@ -1,7 +1,7 @@ Summary: Tool for creating supermin appliances Name: supermin Version: 5.1.10 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ %if 0%{?rhel} >= 7 @@ -11,8 +11,23 @@ ExclusiveArch: x86_64 URL: http://people.redhat.com/~rjones/supermin/ Source0: http://libguestfs.org/download/supermin/%{name}-%{version}.tar.gz +Patch1: 0001-ext2-fix-small-memory-leak-on-error.patch +Patch2: 0002-build-Add-some-more-debug-messages.patch +Patch3: 0003-rpm-Don-t-both-verifying-signatures-and-digests-when.patch +Patch4: 0004-Use-open_process_full-in-compressed-file-reading.patch +Patch5: 0005-package-handlers-add-possibility-for-final-teardown.patch +Patch6: 0006-rpm-use-the-rpm-library-instead-of-invoking-rpm.patch +Patch7: 0007-rpm-fix-typo-in-non-librpm-code.patch +Patch8: 0008-rpm-reuse-the-rpmtd-when-possible.patch +Patch9: 0009-rpm-reuse-the-same-iteration-set.patch +Patch10: 0010-chroot-fix-quoting-in-cp-invocation.patch +Patch11: 0011-rpm-use-the-proper-parameter.patch +Patch12: 0012-rpm-fix-caching-of-provides.patch +Patch13: 0013-rpm-check-for-providers-for-not-found-package.patch + BuildRequires: /usr/bin/pod2man BuildRequires: rpm +BuildRequires: rpm-devel BuildRequires: yum-utils BuildRequires: /usr/sbin/mke2fs BuildRequires: e2fsprogs-devel @@ -21,6 +36,9 @@ BuildRequires: glibc-static, zlib-static BuildRequires: xz-static, xz-devel BuildRequires: ocaml, ocaml-findlib-devel +# autoreconf during build, since the patches change configure.ac +BuildRequires: automake, autoconf + # These are required only to run the tests. We could patch out the # tests to not require these packages. BuildRequires: augeas hivex kernel tar @@ -52,6 +70,10 @@ second when you need to boot one of them. %prep %setup -q +%autopatch -p1 + +autoreconf -i + %build %configure --disable-network-tests @@ -84,6 +106,10 @@ make check || { %changelog +* Tue Oct 7 2014 Pino Toscano - 5.1.10-2 +- Update to upstream commit d78c898c7e2bc5f12cbebef98b95a7908d9120f1. +- BR rpm-devel, since it is now used instead of invoking rpm. + * Thu Sep 4 2014 Richard W.M. Jones - 5.1.10-1 - New upstream version 5.1.10. - Remove patch which is now included upstream.