123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- /*
- * Common modutils related functions for busybox
- *
- * Copyright (C) 2008 by Timo Teras <timo.teras@iki.fi>
- *
- * Licensed under GPLv2 or later, see file LICENSE in this source tree.
- */
- #include "modutils.h"
- #include <sys/syscall.h>
- #define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
- #if defined(__NR_finit_module)
- # define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags)
- #endif
- #define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
- static module_entry *helper_get_module(module_db *db, const char *module, int create)
- {
- char modname[MODULE_NAME_LEN];
- struct module_entry *e;
- unsigned i, hash;
- filename2modname(module, modname);
- hash = 0;
- for (i = 0; modname[i]; i++)
- hash = ((hash << 5) + hash) + modname[i];
- hash %= MODULE_HASH_SIZE;
- for (e = db->buckets[hash]; e; e = e->next)
- if (strcmp(e->modname, modname) == 0)
- return e;
- if (!create)
- return NULL;
- e = xzalloc(sizeof(*e));
- e->modname = xstrdup(modname);
- e->next = db->buckets[hash];
- db->buckets[hash] = e;
- IF_DEPMOD(e->dnext = e->dprev = e;)
- return e;
- }
- module_entry* FAST_FUNC moddb_get(module_db *db, const char *module)
- {
- return helper_get_module(db, module, 0);
- }
- module_entry* FAST_FUNC moddb_get_or_create(module_db *db, const char *module)
- {
- return helper_get_module(db, module, 1);
- }
- void FAST_FUNC moddb_free(module_db *db)
- {
- module_entry *e, *n;
- unsigned i;
- for (i = 0; i < MODULE_HASH_SIZE; i++) {
- for (e = db->buckets[i]; e; e = n) {
- n = e->next;
- free(e->name);
- free(e->modname);
- free(e);
- }
- }
- }
- void FAST_FUNC replace(char *s, char what, char with)
- {
- while (*s) {
- if (what == *s)
- *s = with;
- ++s;
- }
- }
- int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim)
- {
- char *tok;
- int len = 0;
- while ((tok = strsep(&string, delim)) != NULL) {
- if (tok[0] == '\0')
- continue;
- llist_add_to_end(llist, xstrdup(tok));
- len += strlen(tok);
- }
- return len;
- }
- char* FAST_FUNC filename2modname(const char *filename, char *modname)
- {
- char local_modname[MODULE_NAME_LEN];
- int i;
- const char *from;
- if (filename == NULL)
- return NULL;
- if (modname == NULL)
- modname = local_modname;
- // Disabled since otherwise "modprobe dir/name" would work
- // as if it is "modprobe name". It is unclear why
- // 'basenamization' was here in the first place.
- //from = bb_get_last_path_component_nostrip(filename);
- from = filename;
- for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++)
- modname[i] = (from[i] == '-') ? '_' : from[i];
- modname[i] = '\0';
- if (modname == local_modname)
- return xstrdup(modname);
- return modname;
- }
- #if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS
- char* FAST_FUNC parse_cmdline_module_options(char **argv, int quote_spaces)
- {
- char *options;
- int optlen;
- options = xzalloc(1);
- optlen = 0;
- while (*++argv) {
- const char *fmt;
- const char *var;
- const char *val;
- var = *argv;
- options = xrealloc(options, optlen + 2 + strlen(var) + 2);
- fmt = "%.*s%s ";
- val = strchrnul(var, '=');
- if (quote_spaces) {
- /*
- * modprobe (module-init-tools version 3.11.1) compat:
- * quote only value:
- * var="val with spaces", not "var=val with spaces"
- * (note: var *name* is not checked for spaces!)
- */
- if (*val) { /* has var=val format. skip '=' */
- val++;
- if (strchr(val, ' '))
- fmt = "%.*s\"%s\" ";
- }
- }
- optlen += sprintf(options + optlen, fmt, (int)(val - var), var, val);
- }
- /* Remove trailing space. Disabled */
- /* if (optlen != 0) options[optlen-1] = '\0'; */
- return options;
- }
- #endif
- #if ENABLE_FEATURE_INSMOD_TRY_MMAP
- void* FAST_FUNC try_to_mmap_module(const char *filename, size_t *image_size_p)
- {
- /* We have user reports of failure to load 3MB module
- * on a 16MB RAM machine. Apparently even a transient
- * memory spike to 6MB during module load
- * is too big for that system. */
- void *image;
- struct stat st;
- int fd;
- fd = xopen(filename, O_RDONLY);
- fstat(fd, &st);
- image = NULL;
- /* st.st_size is off_t, we can't just pass it to mmap */
- if (st.st_size <= *image_size_p) {
- size_t image_size = st.st_size;
- image = mmap_read(fd, image_size);
- if (image == MAP_FAILED) {
- image = NULL;
- } else if (*(uint32_t*)image != SWAP_BE32(0x7f454C46)) {
- /* No ELF signature. Compressed module? */
- munmap(image, image_size);
- image = NULL;
- } else {
- /* Success. Report the size */
- *image_size_p = image_size;
- }
- }
- close(fd);
- return image;
- }
- #endif
- /* Return:
- * 0 on success,
- * -errno on open/read error,
- * errno on init_module() error
- */
- int FAST_FUNC bb_init_module(const char *filename, const char *options)
- {
- size_t image_size;
- char *image;
- int rc;
- bool mmaped;
- if (!options)
- options = "";
- //TODO: audit bb_init_module_24 to match error code convention
- #if ENABLE_FEATURE_2_4_MODULES
- if (get_linux_version_code() < KERNEL_VERSION(2,6,0))
- return bb_init_module_24(filename, options);
- #endif
- /*
- * First we try finit_module if available. Some kernels are configured
- * to only allow loading of modules off of secure storage (like a read-
- * only rootfs) which needs the finit_module call. If it fails, we fall
- * back to normal module loading to support compressed modules.
- */
- # ifdef __NR_finit_module
- {
- int fd = open(filename, O_RDONLY | O_CLOEXEC);
- if (fd >= 0) {
- rc = finit_module(fd, options, 0) != 0;
- close(fd);
- if (rc == 0)
- return rc;
- }
- }
- # endif
- image_size = INT_MAX - 4095;
- mmaped = 0;
- image = try_to_mmap_module(filename, &image_size);
- if (image) {
- mmaped = 1;
- } else {
- errno = ENOMEM; /* may be changed by e.g. open errors below */
- image = xmalloc_open_zipped_read_close(filename, &image_size);
- if (!image)
- return -errno;
- }
- errno = 0;
- init_module(image, image_size, options);
- rc = errno;
- if (mmaped)
- munmap(image, image_size);
- else
- free(image);
- return rc;
- }
- int FAST_FUNC bb_delete_module(const char *module, unsigned int flags)
- {
- errno = 0;
- delete_module(module, flags);
- return errno;
- }
- /* Note: not suitable for delete_module() errnos.
- * For them, probably only EWOULDBLOCK needs explaining:
- * "Other modules depend on us". So far we don't do such
- * translation and don't use moderror() for removal errors.
- */
- const char* FAST_FUNC moderror(int err)
- {
- switch (err) {
- case -1: /* btw: it's -EPERM */
- return "no such module";
- case ENOEXEC:
- return "invalid module format";
- case ENOENT:
- return "unknown symbol in module, or unknown parameter";
- case ESRCH:
- return "module has wrong symbol version";
- case ENOSYS:
- return "kernel does not support requested operation";
- }
- if (err < 0) /* should always be */
- err = -err;
- return strerror(err);
- }
|