ldlinux: Automatically lookup file extension
authorMatt Fleming <matt.fleming@intel.com>
Fri, 30 Mar 2012 09:57:22 +0000 (10:57 +0100)
committerMatt Fleming <matt.fleming@intel.com>
Mon, 16 Apr 2012 14:23:46 +0000 (15:23 +0100)
The old kernel loader would automatically calculate valid file
extensions if one wasn't supplied by the user. For example, if a user
types 'ls' we want to run the ls.c32 module. Add support for this to
the new code in ldlinux.

Reported-by: H. Peter Anvin <hpa@zytor.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
com32/elflink/ldlinux/ldlinux.c

index 6c306ad..fcb3070 100644 (file)
@@ -1,7 +1,9 @@
 #include <linux/list.h>
 #include <sys/times.h>
+#include <fcntl.h>
 #include <stdbool.h>
 #include <core.h>
+#include <fs.h>
 #include "cli.h"
 #include "console.h"
 #include "com32.h"
 
 #include <sys/module.h>
 
-static enum kernel_type parse_kernel_type(char *kernel)
-{
+struct file_ext {
+       const char *name;
        enum kernel_type type;
+};
+
+static const struct file_ext file_extensions[] = {
+       { ".com", KT_COMBOOT },
+       { ".cbt", KT_COMBOOT },
+       { ".c32", KT_COM32 },
+       { ".img", KT_FDIMAGE },
+       { ".bss", KT_BSS },
+       { ".bin", KT_BOOT },
+       { ".bs", KT_BOOT },
+       { ".0", KT_PXE },
+       { NULL, KT_NONE },
+};
+
+/*
+ * Return a pointer to one byte after the last character of the
+ * command.
+ */
+static inline const char *find_command(const char *str)
+{
        const char *p;
-       int len;
 
-       /* Find the end of the command */
-       p = kernel;
+       p = str;
        while (*p && !my_isspace(*p))
                p++;
+       return p;
+}
 
+static enum kernel_type parse_kernel_type(char *kernel)
+{
+       const struct file_ext *ext;
+       const char *p;
+       int len;
+
+       /* Find the end of the command */
+       p = find_command(kernel);
        len = p - kernel;
 
-       if (!strncmp(kernel + len - 4, ".c32", 4)) {
-               type = KT_COM32;
-       } else if (!strncmp(kernel + len - 2, ".0", 2)) {
-               type = KT_PXE;
-       } else if (!strncmp(kernel + len - 3, ".bs", 3)) {
-               type = KT_BOOT;
-       } else if (!strncmp(kernel + len - 4, ".img", 4)) {
-               type = KT_FDIMAGE;
-       } else if (!strncmp(kernel + len - 4, ".bin", 4)) {
-               type = KT_BOOT;
-       } else if (!strncmp(kernel + len - 4, ".bss", 4)) {
-               type = KT_BSS;
-       } else if (!strncmp(kernel + len - 4, ".com", 4) ||
-                  !strncmp(kernel + len - 4, ".cbt", 4)) {
-               type = KT_COMBOOT;
+       for (ext = file_extensions; ext->name; ext++) {
+               int elen = strlen(ext->name);
+
+               if (!strncmp(kernel + len - elen, ext->name, elen))
+                       return ext->type;
        }
+
        /* use KT_KERNEL as default */
-       else
-               type = KT_KERNEL;
+       return KT_KERNEL;
+}
+
+/*
+ * Returns the kernel name with file extension if one wasn't present.
+ */
+static const char *get_extension(const char *kernel)
+{
+       const struct file_ext *ext;
+       const char *p;
+       int len;
+
+       /* Find the end of the command */
+       p = find_command(kernel);
+       len = p - kernel;
+
+       for (ext = file_extensions; ext->name; ext++) {
+               char *str;
+               int elen = strlen(ext->name);
+               int fd;
+
+               str = malloc(len + elen + 1);
 
-       return type;
+               strncpy(str, kernel, len);
+               strncpy(str + len, ext->name, elen);
+               str[len + elen] = '\0';
+
+               fd = searchdir(str);
+               free(str);
+
+               if (fd >= 0)
+                       return ext->name;
+       }
+
+       return NULL;
+}
+
+static const char *apply_extension(const char *kernel, const char *ext)
+{
+       const char *p;
+       char *k;
+       int len = strlen(kernel);
+       int elen = strlen(ext);
+
+       k = malloc(len + elen + 1);
+       if (!k)
+               return NULL;
+
+       p = find_command(kernel);
+
+       len = p - kernel;
+
+       /* Copy just the kernel name */
+       memcpy(k, kernel, len);
+
+       /* Append the extension */
+       memcpy(k + len, ext, elen);
+
+       /* Copy the rest of the command line */
+       strcpy(k + len + elen, p);
+
+       k[len + elen] = '\0';
+
+       return k;
 }
 
 /*
@@ -54,11 +135,16 @@ static enum kernel_type parse_kernel_type(char *kernel)
  * the the kernel. If we return the caller should call enter_cmdline()
  * so that the user can help us out.
  */
-static void load_kernel(const char *kernel)
+static void load_kernel(const char *command_line)
 {
        struct menu_entry *me;
        enum kernel_type type;
        const char *cmdline;
+       const char *kernel;
+
+       kernel = strdup(command_line);
+       if (!kernel)
+               goto bad_kernel;
 
        /* Virtual kernel? */
        me = find_label(kernel);
@@ -78,7 +164,30 @@ static void load_kernel(const char *kernel)
                goto bad_implicit;
 
        type = parse_kernel_type(kernel);
+       if (type == KT_KERNEL) {
+               const char *ext;
+
+               /*
+                * Automatically lookup the extension if one wasn't
+                * supplied by the user.
+                */
+               ext = get_extension(kernel);
+               if (ext) {
+                       const char *k;
+
+                       k = apply_extension(kernel, ext);
+                       if (!k)
+                               goto bad_kernel;
+
+                       free((void *)kernel);
+                       kernel = k;
+
+                       type = parse_kernel_type(kernel);
+               }
+       }
+
        execute(kernel, type);
+       free((void *)kernel);
 
 bad_implicit:
 bad_kernel: