busybox/modutils/modutils.c
<<
>>
Prefs
   1/*
   2 * Common modutils related functions for busybox
   3 *
   4 * Copyright (C) 2008 by Timo Teras <timo.teras@iki.fi>
   5 *
   6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   7 */
   8#include "modutils.h"
   9
  10#include <sys/syscall.h>
  11
  12#define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
  13#if defined(__NR_finit_module)
  14# define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags)
  15#endif
  16#define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
  17
  18static module_entry *helper_get_module(module_db *db, const char *module, int create)
  19{
  20        char modname[MODULE_NAME_LEN];
  21        struct module_entry *e;
  22        unsigned i, hash;
  23
  24        filename2modname(module, modname);
  25
  26        hash = 0;
  27        for (i = 0; modname[i]; i++)
  28                hash = ((hash << 5) + hash) + modname[i];
  29        hash %= MODULE_HASH_SIZE;
  30
  31        for (e = db->buckets[hash]; e; e = e->next)
  32                if (strcmp(e->modname, modname) == 0)
  33                        return e;
  34        if (!create)
  35                return NULL;
  36
  37        e = xzalloc(sizeof(*e));
  38        e->modname = xstrdup(modname);
  39        e->next = db->buckets[hash];
  40        db->buckets[hash] = e;
  41        IF_DEPMOD(e->dnext = e->dprev = e;)
  42
  43        return e;
  44}
  45module_entry* FAST_FUNC moddb_get(module_db *db, const char *module)
  46{
  47        return helper_get_module(db, module, 0);
  48}
  49module_entry* FAST_FUNC moddb_get_or_create(module_db *db, const char *module)
  50{
  51        return helper_get_module(db, module, 1);
  52}
  53
  54void FAST_FUNC moddb_free(module_db *db)
  55{
  56        module_entry *e, *n;
  57        unsigned i;
  58
  59        for (i = 0; i < MODULE_HASH_SIZE; i++) {
  60                for (e = db->buckets[i]; e; e = n) {
  61                        n = e->next;
  62                        free(e->name);
  63                        free(e->modname);
  64                        free(e);
  65                }
  66        }
  67}
  68
  69void FAST_FUNC replace(char *s, char what, char with)
  70{
  71        while (*s) {
  72                if (what == *s)
  73                        *s = with;
  74                ++s;
  75        }
  76}
  77
  78int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim)
  79{
  80        char *tok;
  81        int len = 0;
  82
  83        while ((tok = strsep(&string, delim)) != NULL) {
  84                if (tok[0] == '\0')
  85                        continue;
  86                llist_add_to_end(llist, xstrdup(tok));
  87                len += strlen(tok);
  88        }
  89        return len;
  90}
  91
  92char* FAST_FUNC filename2modname(const char *filename, char *modname)
  93{
  94        char local_modname[MODULE_NAME_LEN];
  95        int i;
  96        const char *from;
  97
  98        if (filename == NULL)
  99                return NULL;
 100        if (modname == NULL)
 101                modname = local_modname;
 102        // Disabled since otherwise "modprobe dir/name" would work
 103        // as if it is "modprobe name". It is unclear why
 104        // 'basenamization' was here in the first place.
 105        //from = bb_get_last_path_component_nostrip(filename);
 106        from = filename;
 107        for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++)
 108                modname[i] = (from[i] == '-') ? '_' : from[i];
 109        modname[i] = '\0';
 110
 111        if (modname == local_modname)
 112                return xstrdup(modname);
 113
 114        return modname;
 115}
 116
 117#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS
 118char* FAST_FUNC parse_cmdline_module_options(char **argv, int quote_spaces)
 119{
 120        char *options;
 121        int optlen;
 122
 123        options = xzalloc(1);
 124        optlen = 0;
 125        while (*++argv) {
 126                const char *fmt;
 127                const char *var;
 128                const char *val;
 129
 130                var = *argv;
 131                options = xrealloc(options, optlen + 2 + strlen(var) + 2);
 132                fmt = "%.*s%s ";
 133                val = strchrnul(var, '=');
 134                if (quote_spaces) {
 135                        /*
 136                         * modprobe (module-init-tools version 3.11.1) compat:
 137                         * quote only value:
 138                         * var="val with spaces", not "var=val with spaces"
 139                         * (note: var *name* is not checked for spaces!)
 140                         */
 141                        if (*val) { /* has var=val format. skip '=' */
 142                                val++;
 143                                if (strchr(val, ' '))
 144                                        fmt = "%.*s\"%s\" ";
 145                        }
 146                }
 147                optlen += sprintf(options + optlen, fmt, (int)(val - var), var, val);
 148        }
 149        /* Remove trailing space. Disabled */
 150        /* if (optlen != 0) options[optlen-1] = '\0'; */
 151        return options;
 152}
 153#endif
 154
 155#if ENABLE_FEATURE_INSMOD_TRY_MMAP
 156void* FAST_FUNC try_to_mmap_module(const char *filename, size_t *image_size_p)
 157{
 158        /* We have user reports of failure to load 3MB module
 159         * on a 16MB RAM machine. Apparently even a transient
 160         * memory spike to 6MB during module load
 161         * is too big for that system. */
 162        void *image;
 163        struct stat st;
 164        int fd;
 165
 166        fd = xopen(filename, O_RDONLY);
 167        fstat(fd, &st);
 168        image = NULL;
 169        /* st.st_size is off_t, we can't just pass it to mmap */
 170        if (st.st_size <= *image_size_p) {
 171                size_t image_size = st.st_size;
 172                image = mmap(NULL, image_size, PROT_READ, MAP_PRIVATE, fd, 0);
 173                if (image == MAP_FAILED) {
 174                        image = NULL;
 175                } else if (*(uint32_t*)image != SWAP_BE32(0x7f454C46)) {
 176                        /* No ELF signature. Compressed module? */
 177                        munmap(image, image_size);
 178                        image = NULL;
 179                } else {
 180                        /* Success. Report the size */
 181                        *image_size_p = image_size;
 182                }
 183        }
 184        close(fd);
 185        return image;
 186}
 187#endif
 188
 189/* Return:
 190 * 0 on success,
 191 * -errno on open/read error,
 192 * errno on init_module() error
 193 */
 194int FAST_FUNC bb_init_module(const char *filename, const char *options)
 195{
 196        size_t image_size;
 197        char *image;
 198        int rc;
 199        bool mmaped;
 200
 201        if (!options)
 202                options = "";
 203
 204//TODO: audit bb_init_module_24 to match error code convention
 205#if ENABLE_FEATURE_2_4_MODULES
 206        if (get_linux_version_code() < KERNEL_VERSION(2,6,0))
 207                return bb_init_module_24(filename, options);
 208#endif
 209
 210        /*
 211         * First we try finit_module if available.  Some kernels are configured
 212         * to only allow loading of modules off of secure storage (like a read-
 213         * only rootfs) which needs the finit_module call.  If it fails, we fall
 214         * back to normal module loading to support compressed modules.
 215         */
 216# ifdef __NR_finit_module
 217        {
 218                int fd = open(filename, O_RDONLY | O_CLOEXEC);
 219                if (fd >= 0) {
 220                        rc = finit_module(fd, options, 0) != 0;
 221                        close(fd);
 222                        if (rc == 0)
 223                                return rc;
 224                }
 225        }
 226# endif
 227
 228        image_size = INT_MAX - 4095;
 229        mmaped = 0;
 230        image = try_to_mmap_module(filename, &image_size);
 231        if (image) {
 232                mmaped = 1;
 233        } else {
 234                errno = ENOMEM; /* may be changed by e.g. open errors below */
 235                image = xmalloc_open_zipped_read_close(filename, &image_size);
 236                if (!image)
 237                        return -errno;
 238        }
 239
 240        errno = 0;
 241        init_module(image, image_size, options);
 242        rc = errno;
 243        if (mmaped)
 244                munmap(image, image_size);
 245        else
 246                free(image);
 247        return rc;
 248}
 249
 250int FAST_FUNC bb_delete_module(const char *module, unsigned int flags)
 251{
 252        errno = 0;
 253        delete_module(module, flags);
 254        return errno;
 255}
 256
 257/* Note: not suitable for delete_module() errnos.
 258 * For them, probably only EWOULDBLOCK needs explaining:
 259 * "Other modules depend on us". So far we don't do such
 260 * translation and don't use moderror() for removal errors.
 261 */
 262const char* FAST_FUNC moderror(int err)
 263{
 264        switch (err) {
 265        case -1: /* btw: it's -EPERM */
 266                return "no such module";
 267        case ENOEXEC:
 268                return "invalid module format";
 269        case ENOENT:
 270                return "unknown symbol in module, or unknown parameter";
 271        case ESRCH:
 272                return "module has wrong symbol version";
 273        case ENOSYS:
 274                return "kernel does not support requested operation";
 275        }
 276        if (err < 0) /* should always be */
 277                err = -err;
 278        return strerror(err);
 279}
 280