Fix febootstrap-supermin-helper for dual boot case (RHBZ#594021).
authorRichard Jones <rjones@redhat.com>
Fri, 21 May 2010 08:35:05 +0000 (09:35 +0100)
committerRichard Jones <rjones@redhat.com>
Fri, 21 May 2010 08:35:05 +0000 (09:35 +0100)
Jinxin Zheng found a case where libguestfs-supermin-helper
(now febootstrap-supermin-helper) would fail to locate a
suitable kernel on a dual boot RHEL 5 + 6 system.

The issue was that febootstrap-supermin-helper will first look
for kernels with an <arch> field in the name (as is the case
for RHEL 6 kernels).  If none of these are found, it would look
for kernels without an <arch> field (RHEL 5 kernels).  Then it
would look for corresponding module directories.

This fails if the dual boot system is booted into RHEL 5 because
the first step will find only RHEL 6 kernels, and then the third
step will fail to find any module directories (step two having
been skipped in this case).

This changes the code to look for module directories when
searching for kernels, and discard any kernels which don't
have module directories first.  Thus all RHEL 6 kernels will
be discarded, and we will proceed to step two and find some
RHEL 5 kernels.

febootstrap-supermin-helper.c

index 1a43cf2..a81cf61 100644 (file)
@@ -233,6 +233,7 @@ print_timestamped_message (const char *fs, ...)
 }
 
 static char **read_dir (const char *dir);
+static char **filter (char **strings, int (*)(const char *));
 static char **filter_fnmatch (char **strings, const char *patt, int flags);
 static char **filter_notmatching_substring (char **strings, const char *sub);
 static void sort (char **strings, int (*compare) (const void *, const void *));
@@ -248,6 +249,48 @@ reverse_filevercmp (const void *p1, const void *p2)
   return filevercmp (s2, s1);
 }
 
+static char *
+get_modpath (const char *kernel_name)
+{
+  /* Ignore "vmlinuz-" at the beginning of the kernel name. */
+  const char *version = &kernel_name[8];
+
+  /* /lib/modules/<version> */
+  char *modpath = xasprintf (MODULESDIR "/%s", version);
+  if (!modpath) {
+    perror ("xasprintf");
+    exit (EXIT_FAILURE);
+  }
+
+  return modpath;
+}
+
+/* kernel_name is "vmlinuz-*".  Check if there is a corresponding
+ * module path in /lib/modules.
+ */
+static int
+has_modpath (const char *kernel_name)
+{
+  char *modpath = get_modpath (kernel_name);
+
+  if (verbose)
+    fprintf (stderr, "checking modpath %s is a directory\n", modpath);
+
+  int r = isdir (modpath);
+
+  if (r) {
+    if (verbose)
+      fprintf (stderr, "picked %s because modpath %s exists\n",
+               kernel_name, modpath);
+    free (modpath);
+    return 1;
+  }
+  else {
+    free (modpath);
+    return 0;
+  }
+}
+
 /* Create the kernel.  This chooses an appropriate kernel and makes a
  * symlink to it.
  *
@@ -278,12 +321,14 @@ create_kernel (const char *hostcpu, const char *kernel)
   char **candidates;
   candidates = filter_fnmatch (all_files, patt, FNM_NOESCAPE);
   candidates = filter_notmatching_substring (candidates, "xen");
+  candidates = filter (candidates, has_modpath);
 
   if (candidates[0] == NULL) {
     /* In original: ls -1dvr /boot/vmlinuz-* 2>/dev/null | grep -v xen */
     patt = "vmlinuz-*";
     candidates = filter_fnmatch (all_files, patt, FNM_NOESCAPE);
     candidates = filter_notmatching_substring (candidates, "xen");
+    candidates = filter (candidates, has_modpath);
 
     if (candidates[0] == NULL)
       goto no_kernels;
@@ -291,41 +336,18 @@ create_kernel (const char *hostcpu, const char *kernel)
 
   sort (candidates, reverse_filevercmp);
 
-  /* Choose the first candidate which has a corresponding /lib/modules
-   * directory.
-   */
-  int i;
-  for (i = 0; candidates[i] != NULL; ++i) {
-    if (verbose >= 2)
-      fprintf (stderr, "candidate kernel: " KERNELDIR "/%s\n", candidates[i]);
-
-    /* Ignore "vmlinuz-" at the beginning of the kernel name. */
-    const char *version = &candidates[i][8];
+  /* Choose the first candidate. */
+  char *tmp = xasprintf (KERNELDIR "/%s", candidates[0]);
 
-    /* /lib/modules/<version> */
-    char *modpath = xasprintf (MODULESDIR "/%s", version);
-
-    if (verbose >= 2)
-      fprintf (stderr, "checking modpath %s is a directory\n", modpath);
-
-    if (isdir (modpath)) {
-      if (verbose >= 2)
-        fprintf (stderr, "picked %s because modpath %s exists\n",
-                 candidates[i], modpath);
-
-      char *tmp = xasprintf (KERNELDIR "/%s", candidates[i]);
-
-      if (verbose >= 2)
-        fprintf (stderr, "creating symlink %s -> %s\n", kernel, tmp);
+  if (verbose)
+    fprintf (stderr, "creating symlink %s -> %s\n", kernel, tmp);
 
-      if (symlink (tmp, kernel) == -1)
-        error (EXIT_FAILURE, errno, "symlink kernel");
+  if (symlink (tmp, kernel) == -1)
+    error (EXIT_FAILURE, errno, "symlink kernel");
 
-      free (tmp);
+  free (tmp);
 
-      return modpath;
-    }
-  }
+  return get_modpath (candidates[0]);
 
   /* Print more diagnostics here than the old script did. */
  no_kernels:
@@ -716,6 +738,23 @@ read_dir (const char *name)
   return files;
 }
 
+/* Filter a list of strings, returning only those where f(s) != 0. */
+static char **
+filter (char **strings, int (*f) (const char *))
+{
+  char **out = NULL;
+  size_t n_used = 0, n_alloc = 0;
+
+  int i;
+  for (i = 0; strings[i] != NULL; ++i) {
+    if (f (strings[i]) != 0)
+      add_string (&out, &n_used, &n_alloc, strings[i]);
+  }
+
+  add_string (&out, &n_used, &n_alloc, NULL);
+  return out;
+}
+
 /* Filter a list of strings and return only those matching the wildcard. */
 static char **
 filter_fnmatch (char **strings, const char *patt, int flags)