From 65125379903231f1b0c2ccd0929123317c80bcc2 Mon Sep 17 00:00:00 2001 From: Erik Nolte Date: Thu, 6 Oct 2011 14:47:26 -0600 Subject: Replace ArchLinux module detection with generic method. ArchLinux used to keep its kernel in /boot/vmlinuz26 but, with Linux 3.0, now uses /boot/vmlinuz-linux. Instead of just changing the kernel filename and module directory, this change removes the ArchLinux specific code, lets febootstrap find a kernel, and then computes the module directory from the version string extracted from the actual kernel file. --- helper/kernel.c | 156 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 96 insertions(+), 60 deletions(-) diff --git a/helper/kernel.c b/helper/kernel.c index 6a68e55..88dc940 100644 --- a/helper/kernel.c +++ b/helper/kernel.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "error.h" #include "xvasprintf.h" @@ -36,6 +37,9 @@ #define KERNELDIR "/boot" #define MODULESDIR "/lib/modules" +static char* get_kernel_version (char* filename); +static const char *create_kernel_from_env (const char *hostcpu, const char *kernel, const char *kernel_env, const char *modpath_env); + static char * get_modpath (const char *kernel_name) { @@ -49,6 +53,27 @@ get_modpath (const char *kernel_name) exit (EXIT_FAILURE); } + if (! isdir (modpath)) { + char* path; + char* version; + path = xasprintf (KERNELDIR "/%s", kernel_name); + if (!path) { + perror ("xasprintf"); + exit (EXIT_FAILURE); + } + version = get_kernel_version (path); + free (path); + if (version != NULL) { + free (modpath); + modpath = xasprintf (MODULESDIR "/%s", version); + free (version); + if (!path) { + perror ("xasprintf"); + exit (EXIT_FAILURE); + } + } + } + return modpath; } @@ -78,9 +103,6 @@ has_modpath (const char *kernel_name) } } -static const char *create_kernel_archlinux (const char *hostcpu, const char *kernel); -static const char *create_kernel_from_env (const char *hostcpu, const char *kernel, const char *kernel_env, const char *modpath_env); - /* Create the kernel. This chooses an appropriate kernel and makes a * symlink to it. * @@ -105,10 +127,6 @@ create_kernel (const char *hostcpu, const char *kernel) return create_kernel_from_env (hostcpu, kernel, kernel_env, modpath_env); } - /* In ArchLinux, kernel is always named /boot/vmlinuz26. */ - if (access ("/boot/vmlinuz26", F_OK) == 0) - return create_kernel_archlinux (hostcpu, kernel); - /* In original: ls -1dvr /boot/vmlinuz-*.$arch* 2>/dev/null | grep -v xen */ const char *patt; if (hostcpu[0] == 'i' && hostcpu[2] == '8' && hostcpu[3] == '6' && @@ -163,59 +181,6 @@ create_kernel (const char *hostcpu, const char *kernel) exit (EXIT_FAILURE); } -/* In ArchLinux, kernel is always named /boot/vmlinuz26, and we have - * to use the 'file' command to work out what version it is. - */ -static const char * -create_kernel_archlinux (const char *hostcpu, const char *kernel) -{ - const char *file_cmd = "file /boot/vmlinuz26 | awk '{print $9}'"; - FILE *pp; - char modversion[256]; - char *modpath; - size_t len; - - pp = popen (file_cmd, "r"); - if (pp == NULL) { - error: - fprintf (stderr, "febootstrap-supermin-helper: %s: command failed\n", - file_cmd); - exit (EXIT_FAILURE); - } - - if (fgets (modversion, sizeof modversion, pp) == NULL) - goto error; - - if (pclose (pp) == -1) - goto error; - - /* Chomp final \n */ - len = strlen (modversion); - if (len > 0 && modversion[len-1] == '\n') { - modversion[len-1] = '\0'; - len--; - } - - /* Generate module path. */ - modpath = xasprintf (MODULESDIR "/%s", modversion); - - /* Check module path is a directory. */ - if (!isdir (modpath)) { - fprintf (stderr, "febootstrap-supermin-helper: /boot/vmlinuz26 kernel exists but %s is not a valid module path\n", - modpath); - exit (EXIT_FAILURE); - } - - if (kernel) { - /* Symlink from kernel to /boot/vmlinuz26. */ - if (symlink ("/boot/vmlinuz26", kernel) == -1) - error (EXIT_FAILURE, errno, "symlink kernel"); - } - - /* Return module path. */ - return modpath; -} - /* Select the kernel from environment variables set by the user. * modpath_env may be NULL, in which case we attempt to work it out * from kernel_env. @@ -276,3 +241,74 @@ create_kernel_from_env (const char *hostcpu, const char *kernel, return modpath_env; } + +/* Read an unsigned little endian short at a specified offset in a file. + * Returns a non-negative int on success or -1 on failure. + */ +static int +read_leshort (FILE* fp, int offset) +{ + char buf[2]; + if (fseek (fp, offset, SEEK_SET) != 0 || + fread (buf, sizeof(char), 2, fp) != 2) + { + return -1; + } + return ((buf[1] & 0xFF) << 8) | (buf[0] & 0xFF); +} + +/* Extract the kernel version from a Linux kernel file. + * Returns a malloc'd string containing the version or NULL if the + * file can't be read, is not a Linux kernel, or the version can't + * be found. + * + * See ftp://ftp.astron.com/pub/file/file-.tar.gz + * (file-/magic/Magdir/linux) for the rules used to find the + * version number: + * 514 string HdrS Linux kernel + * >518 leshort >0x1ff + * >>(526.s+0x200) string >\0 version %s, + * + * Bugs: probably limited to x86 kernels. + */ +static char* +get_kernel_version (char* filename) +{ + FILE* fp; + int size = 132; + char buf[size]; + int offset; + + fp = fopen (filename, "rb"); + + if (fseek (fp, 514, SEEK_SET) != 0 || + fgets (buf, size, fp) == NULL || + strncmp (buf, "HdrS", 4) != 0 || + read_leshort (fp, 518) < 0x1FF) + { + /* not a Linux kernel */ + fclose (fp); + return NULL; + } + + offset = read_leshort (fp, 526); + if (offset == -1) + { + /* can't read version offset */ + fclose (fp); + return NULL; + } + + if (fseek (fp, offset + 0x200, SEEK_SET) != 0 || + fgets (buf, size, fp) == NULL) + { + /* can't read version string */ + fclose (fp); + return NULL; + } + + fclose (fp); + + buf[strcspn (buf, " \t\n")] = '\0'; + return strdup (buf); +} -- cgit