1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
|
/*
* 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)
# ifndef MODULE_INIT_COMPRESSED_FILE
# define MODULE_INIT_COMPRESSED_FILE 4
# endif
#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) {
int flags = is_suffixed_with(filename, ".ko") ? 0 : MODULE_INIT_COMPRESSED_FILE;
for (;;) {
rc = finit_module(fd, options, flags);
if (rc == 0 || flags == 0)
break;
/* Loading non-.ko named uncompressed module? Not likely, but let's try it */
flags = 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);
}
|