tftp: do not risk invoking Sorcerer's Apprentice syndrome
[oweals/busybox.git] / modutils / modprobe-small.c
index cf9be47a6a05d27bf869804e396d8320336f5f55..3fd7bf5adaec76df5b1f1a9b57a1c4045e8a067e 100644 (file)
@@ -3,13 +3,12 @@
  * simplified modprobe
  *
  * Copyright (c) 2008 Vladimir Dronnikov
- * Copyright (c) 2008 Bernhard Fischer (initial depmod code)
+ * Copyright (c) 2008 Bernhard Reutner-Fischer (initial depmod code)
  *
  * Licensed under GPLv2, see file LICENSE in this tarball for details.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
 
 #include <sys/utsname.h> /* uname() */
 #include <fnmatch.h>
@@ -44,6 +43,7 @@ struct globals {
        module_info *modinfo;
        char *module_load_options;
        smallint dep_bb_seen;
+       smallint wrote_dep_bb_ok;
        int module_count;
        int module_found_idx;
        int stringbuf_idx;
@@ -53,6 +53,7 @@ struct globals {
 #define G (*ptr_to_globals)
 #define modinfo             (G.modinfo            )
 #define dep_bb_seen         (G.dep_bb_seen        )
+#define wrote_dep_bb_ok     (G.wrote_dep_bb_ok    )
 #define module_count        (G.module_count       )
 #define module_found_idx    (G.module_found_idx   )
 #define module_load_options (G.module_load_options)
@@ -99,7 +100,7 @@ static char* find_keyword(char *ptr, size_t len, const char *word)
 {
        int wlen;
 
-       if (!ptr) /* happens if read_module cannot read it */
+       if (!ptr) /* happens if xmalloc_open_zipped_read_close cannot read it */
                return NULL;
 
        wlen = strlen(word);
@@ -140,40 +141,6 @@ static char* str_2_list(const char *str)
        return dst;
 }
 
-#if ENABLE_FEATURE_MODPROBE_SMALL_ZIPPED
-static char *xmalloc_open_zipped_read_close(const char *fname, size_t *sizep)
-{
-       size_t len;
-       char *image;
-       char *suffix;
-
-       int fd = open_or_warn(fname, O_RDONLY);
-       if (fd < 0)
-               return NULL;
-
-       suffix = strrchr(fname, '.');
-       if (suffix) {
-               if (strcmp(suffix, ".gz") == 0)
-                       fd = open_transformer(fd, unpack_gz_stream, "gunzip");
-               else if (strcmp(suffix, ".bz2") == 0)
-                       fd = open_transformer(fd, unpack_bz2_stream, "bunzip2");
-       }
-
-       len = (sizep) ? *sizep : 64 * 1024 * 1024;
-       image = xmalloc_read(fd, &len);
-       if (!image)
-               bb_perror_msg("read error from '%s'", fname);
-       close(fd);
-
-       if (sizep)
-               *sizep = len;
-       return image;
-}
-# define read_module xmalloc_open_zipped_read_close
-#else
-# define read_module xmalloc_open_read_close
-#endif
-
 /* We use error numbers in a loose translation... */
 static const char *moderror(int err)
 {
@@ -200,7 +167,7 @@ static int load_module(const char *fname, const char *options)
        char *module_image;
        dbg1_error_msg("load_module('%s','%s')", fname, options);
 
-       module_image = read_module(fname, &len);
+       module_image = xmalloc_open_zipped_read_close(fname, &len);
        r = (!module_image || init_module(module_image, len, options ? options : "") != 0);
        free(module_image);
        dbg1_error_msg("load_module:%d", r);
@@ -222,7 +189,7 @@ static void parse_module(module_info *info, const char *pathname)
 
        /* Read (possibly compressed) module */
        len = 64 * 1024 * 1024; /* 64 Mb at most */
-       module_image = read_module(pathname, &len);
+       module_image = xmalloc_open_zipped_read_close(pathname, &len);
 //TODO: optimize redundant module body reads
 
        /* "alias1 symbol:sym1 alias2 symbol:sym2" */
@@ -236,15 +203,16 @@ static void parse_module(module_info *info, const char *pathname)
                                break;
                        /* DOCME: __ksymtab_gpl and __ksymtab_strings occur
                         * in many modules. What do they mean? */
-                       if (strcmp(ptr, "gpl") != 0 && strcmp(ptr, "strings") != 0) {
-                               dbg2_error_msg("alias:'symbol:%s'", ptr);
-                               append("symbol:");
-                       }
+                       if (strcmp(ptr, "gpl") == 0 || strcmp(ptr, "strings") == 0)
+                               goto skip;
+                       dbg2_error_msg("alias:'symbol:%s'", ptr);
+                       append("symbol:");
                } else {
                        dbg2_error_msg("alias:'%s'", ptr);
                }
                append(ptr);
                appendc(' ');
+ skip:
                pos = (ptr - module_image);
        }
        bksp(); /* remove last ' ' */
@@ -296,10 +264,9 @@ static FAST_FUNC int fileAction(const char *pathname,
 
        cur = module_count++;
        modinfo = xrealloc_vector(modinfo, 12, cur);
-//TODO: use zeroing version of xrealloc_vector?
        modinfo[cur].pathname = xstrdup(pathname);
-       modinfo[cur].aliases = NULL;
-       modinfo[cur+1].pathname = NULL;
+       /*modinfo[cur].aliases = NULL; - xrealloc_vector did it */
+       /*modinfo[cur+1].pathname = NULL;*/
 
        if (!pathname_matches_modname(fname, modname_to_match)) {
                dbg1_error_msg("'%s' module name doesn't match", pathname);
@@ -325,7 +292,7 @@ static FAST_FUNC int fileAction(const char *pathname,
 static int load_dep_bb(void)
 {
        char *line;
-       FILE *fp = fopen(DEPFILE_BB, "r");
+       FILE *fp = fopen_for_read(DEPFILE_BB);
 
        if (!fp)
                return 0;
@@ -356,8 +323,7 @@ static int load_dep_bb(void)
                space = strchrnul(line, ' ');
                cur = module_count++;
                modinfo = xrealloc_vector(modinfo, 12, cur);
-//TODO: use zeroing version of xrealloc_vector?
-               modinfo[cur+1].pathname = NULL;
+               /*modinfo[cur+1].pathname = NULL; - xrealloc_vector did it */
                modinfo[cur].pathname = line; /* we take ownership of malloced block here */
                if (*space)
                        *space++ = '\0';
@@ -379,6 +345,10 @@ static int start_dep_bb_writeout(void)
 {
        int fd;
 
+       /* depmod -n: write result to stdout */
+       if (applet_name[0] == 'd' && (option_mask32 & 1))
+               return STDOUT_FILENO;
+
        fd = open(DEPFILE_BB".new", O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
        if (fd < 0) {
                if (errno == EEXIST) {
@@ -421,13 +391,19 @@ static void write_out_dep_bb(int fd)
        }
        /* Badly formatted depfile is a no-no. Be paranoid. */
        errno = 0;
-       if (ferror(fp) | fclose(fp))
+       if (ferror(fp) | fclose(fp)) /* | instead of || is intended */
                goto err;
+
+       if (fd == STDOUT_FILENO) /* it was depmod -n */
+               goto ok;
+
        if (rename(DEPFILE_BB".new", DEPFILE_BB) != 0) {
  err:
                bb_perror_msg("can't create %s", DEPFILE_BB);
                unlink(DEPFILE_BB".new");
        } else {
+ ok:
+               wrote_dep_bb_ok = 1;
                dbg1_error_msg("created "DEPFILE_BB);
        }
 }
@@ -468,8 +444,10 @@ static module_info* find_alias(const char *alias)
                if (!modinfo[i].aliases) {
                        parse_module(&modinfo[i], modinfo[i].pathname);
                }
-               if (result)
+               if (result) {
+                       i++;
                        continue;
+               }
                /* "alias1 symbol:sym1 alias2 symbol:sym2" */
                desc = str_2_list(modinfo[i].aliases);
                /* Does matching substring exist? */
@@ -501,23 +479,19 @@ static module_info* find_alias(const char *alias)
 }
 
 #if ENABLE_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED
+// TODO: open only once, invent config_rewind()
 static int already_loaded(const char *name)
 {
        int ret = 0;
-       int len = strlen(name);
-       char *line;
-       FILE* modules;
-
-       modules = xfopen("/proc/modules", "r");
-       while ((line = xmalloc_fgets(modules)) != NULL) {
-               if (strncmp(line, name, len) == 0 && line[len] == ' ') {
-                       free(line);
+       char *s;
+       parser_t *parser = config_open2("/proc/modules", xfopen_for_read);
+       while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) {
+               if (strcmp(s, name) == 0) {
                        ret = 1;
                        break;
                }
-               free(line);
        }
-       fclose(modules);
+       config_close(parser);
        return ret;
 }
 #else
@@ -525,8 +499,10 @@ static int already_loaded(const char *name)
 #endif
 
 /*
- Given modules definition and module name (or alias, or symbol)
- load/remove the module respecting dependencies
+ * Given modules definition and module name (or alias, or symbol)
+ * load/remove the module respecting dependencies.
+ * NB: also called by depmod with bogus name "/",
+ * just in order to force modprobe.dep.bb creation.
 */
 #if !ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
 #define process_module(a,b) process_module(a)
@@ -605,8 +581,10 @@ static void process_module(char *name, const char *cmdline_options)
                 * will try to remove them, too." */
        }
 
-       if (!info) { /* both dirscan and find_alias found nothing */
-               bb_error_msg("module '%s' not found", name);
+       if (!info) {
+               /* both dirscan and find_alias found nothing */
+               if (applet_name[0] != 'd') /* it wasn't depmod */
+                       bb_error_msg("module '%s' not found", name);
 //TODO: _and_die()?
                goto ret;
        }
@@ -621,19 +599,23 @@ static void process_module(char *name, const char *cmdline_options)
        }
        free(deps);
 
-       /* insmod -> load it */
+       /* modprobe -> load it */
        if (!is_rmmod) {
-               errno = 0;
-               if (load_module(info->pathname, options) != 0) {
-                       if (EEXIST != errno) {
-                               bb_error_msg("'%s': %s",
+               if (!options || strstr(options, "blacklist") == NULL) {
+                       errno = 0;
+                       if (load_module(info->pathname, options) != 0) {
+                               if (EEXIST != errno) {
+                                       bb_error_msg("'%s': %s",
                                                info->pathname,
                                                moderror(errno));
-                       } else {
-                               dbg1_error_msg("'%s': %s",
+                               } else {
+                                       dbg1_error_msg("'%s': %s",
                                                info->pathname,
                                                moderror(errno));
+                               }
                        }
+               } else {
+                       dbg1_error_msg("'%s': blacklisted", info->pathname);
                }
        }
  ret:
@@ -644,39 +626,52 @@ static void process_module(char *name, const char *cmdline_options)
 #undef cmdline_options
 
 
-/* For reference, module-init-tools-0.9.15-pre2 options:
+/* For reference, module-init-tools v3.4 options:
 
 # insmod
 Usage: insmod filename [args]
 
 # rmmod --help
 Usage: rmmod [-fhswvV] modulename ...
- -f (or --force) forces a module unload, and may crash your machine.
+ -f (or --force) forces a module unload, and may crash your
+    machine. This requires the Forced Module Removal option
+    when the kernel was compiled.
+ -h (or --help) prints this help text
  -s (or --syslog) says use syslog, not stderr
  -v (or --verbose) enables more messages
- -w (or --wait) begins a module removal even if it is used
+ -V (or --version) prints the version code
+ -w (or --wait) begins module removal even if it is used
     and will stop new users from accessing the module (so it
     should eventually fall to zero).
 
 # modprobe
-Usage: modprobe [--verbose|--version|--config|--remove] filename [options]
+Usage: modprobe [-v] [-V] [-C config-file] [-n] [-i] [-q] [-b]
+    [-o <modname>] [ --dump-modversions ] <modname> [parameters...]
+modprobe -r [-n] [-i] [-v] <modulename> ...
+modprobe -l -t <dirname> [ -a <modulename> ...]
 
 # depmod --help
-depmod 0.9.15-pre2 -- part of module-init-tools
-depmod -[aA] [-n -e -v -q -V -r -u] [-b basedirectory] [forced_version]
-depmod [-n -e -v -q -r -u] [-F kernelsyms] module1.o module2.o ...
-If no arguments (except options) are given, "depmod -a" is assumed
-
+depmod 3.4 -- part of module-init-tools
+depmod -[aA] [-n -e -v -q -V -r -u]
+      [-b basedirectory] [forced_version]
+depmod [-n -e -v -q -r -u] [-F kernelsyms] module1.ko module2.ko ...
+If no arguments (except options) are given, "depmod -a" is assumed.
 depmod will output a dependancy list suitable for the modprobe utility.
-
 Options:
-        -a, --all               Probe all modules
-        -n, --show              Write the dependency file on stdout only
-        -b basedirectory
-        --basedir basedirectory Use an image of a module tree.
-        -F kernelsyms
-        --filesyms kernelsyms   Use the file instead of the
-                                current kernel symbols.
+    -a, --all           Probe all modules
+    -A, --quick         Only does the work if there's a new module
+    -n, --show          Write the dependency file on stdout only
+    -e, --errsyms       Report not supplied symbols
+    -V, --version       Print the release version
+    -v, --verbose       Enable verbose mode
+    -h, --help          Print this usage message
+The following options are useful for people managing distributions:
+    -b basedirectory
+    --basedir basedirectory
+                        Use an image of a module tree
+    -F kernelsyms
+    --filesyms kernelsyms
+                        Use the file instead of the current kernel symbols
 */
 
 int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -684,20 +679,57 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
 {
        struct utsname uts;
        char applet0 = applet_name[0];
-       USE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(char *options;)
-
-       /* depmod is a stub */
-       if ('d' == applet0)
-               return EXIT_SUCCESS;
+       IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(char *options;)
 
        /* are we lsmod? -> just dump /proc/modules */
        if ('l' == applet0) {
-               xprint_and_close_file(xfopen("/proc/modules", "r"));
+               xprint_and_close_file(xfopen_for_read("/proc/modules"));
                return EXIT_SUCCESS;
        }
 
        INIT_G();
 
+       /* Prevent ugly corner cases with no modules at all */
+       modinfo = xzalloc(sizeof(modinfo[0]));
+
+       if ('i' != applet0) { /* not insmod */
+               /* Goto modules directory */
+               xchdir(CONFIG_DEFAULT_MODULES_DIR);
+       }
+       uname(&uts); /* never fails */
+
+       /* depmod? */
+       if ('d' == applet0) {
+               /* Supported:
+                * -n: print result to stdout
+                * -a: process all modules (default)
+                * optional VERSION parameter
+                * Ignored:
+                * -A: do work only if a module is newer than depfile
+                * -e: report any symbols which a module needs
+                *  which are not supplied by other modules or the kernel
+                * -F FILE: System.map (symbols for -e)
+                * -q, -r, -u: noop?
+                * Not supported:
+                * -b BASEDIR: (TODO!) modules are in
+                *  $BASEDIR/lib/modules/$VERSION
+                * -v: human readable deps to stdout
+                * -V: version (don't want to support it - people may depend
+                *  on it as an indicator of "standard" depmod)
+                * -h: help (well duh)
+                * module1.o module2.o parameters (just ignored for now)
+                */
+               getopt32(argv, "na" "AeF:qru" /* "b:vV", NULL */, NULL);
+               argv += optind;
+               /* if (argv[0] && argv[1]) bb_show_usage(); */
+               /* Goto $VERSION directory */
+               xchdir(argv[0] ? argv[0] : uts.release);
+               /* Force full module scan by asking to find a bogus module.
+                * This will generate modules.dep.bb as a side effect. */
+               process_module((char*)"/", NULL);
+               return !wrote_dep_bb_ok;
+       }
+
        /* insmod, modprobe, rmmod require at least one argument */
        opt_complementary = "-1";
        /* only -q (quiet) and -r (rmmod),
@@ -710,10 +742,10 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
                option_mask32 |= OPT_r;
        }
 
-       /* goto modules directory */
-       xchdir(CONFIG_DEFAULT_MODULES_DIR);
-       uname(&uts); /* never fails */
-       xchdir(uts.release);
+       if ('i' != applet0) { /* not insmod */
+               /* Goto $VERSION directory */
+               xchdir(uts.release);
+       }
 
 #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
        /* If not rmmod, parse possible module options given on command line.
@@ -734,20 +766,32 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
                argv[1] = NULL;
 #endif
 
-       /* Prevent ugly corner cases with no modules at all */
-       modinfo = xzalloc(sizeof(modinfo[0]));
+       if ('i' == applet0) { /* insmod */
+               size_t len;
+               void *map;
+
+               len = MAXINT(ssize_t);
+               map = xmalloc_xopen_read_close(*argv, &len);
+               if (init_module(map, len,
+                       IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(options ? options : "")
+                       IF_NOT_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE("")
+                               ) != 0)
+                       bb_error_msg_and_die("can't insert '%s': %s",
+                                       *argv, moderror(errno));
+               return 0;
+       }
 
        /* Try to load modprobe.dep.bb */
        load_dep_bb();
 
        /* Load/remove modules.
-        * Only rmmod loops here, insmod/modprobe has only argv[0] */
+        * Only rmmod loops here, modprobe has only argv[0] */
        do {
                process_module(*argv++, options);
        } while (*argv);
 
        if (ENABLE_FEATURE_CLEAN_UP) {
-               USE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(free(options);)
+               IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(free(options);)
        }
        return EXIT_SUCCESS;
 }