diff options
author | Jon Masters <jcm@jonmasters.org> | 2010-03-10 01:04:57 -0500 |
---|---|---|
committer | Jon Masters <jcm@jonmasters.org> | 2010-03-10 01:04:57 -0500 |
commit | 74efce3e06313f2a57171f75c514b2eef2a9e755 (patch) | |
tree | d478585ff211eda2fa2402488f12ee81bed1795f | |
parent | d99d7ddee568f7f10ad71d4f1d66ff6530d47646 (diff) | |
parent | 70ce1505e1d9b73060828552b695d0d4a4b57515 (diff) | |
download | module-init-tools-74efce3e06313f2a57171f75c514b2eef2a9e755.tar.gz |
Merge branch 'alan-softdep-merge' into dev
-rw-r--r-- | depmod.c | 38 | ||||
-rw-r--r-- | doc/modprobe.conf.sgml | 31 | ||||
-rw-r--r-- | elfops.c | 39 | ||||
-rw-r--r-- | elfops.h | 3 | ||||
-rw-r--r-- | elfops_core.c | 11 | ||||
-rw-r--r-- | index.c | 15 | ||||
-rw-r--r-- | insmod.c | 13 | ||||
-rw-r--r-- | modinfo.c | 2 | ||||
-rw-r--r-- | modprobe.c | 694 | ||||
-rw-r--r-- | tests/test-modprobe/28softdep.sh | 67 | ||||
-rwxr-xr-x | tests/valgrind/valgrindme.sh | 2 | ||||
-rw-r--r-- | zlibsupport.c | 17 |
12 files changed, 627 insertions, 305 deletions
@@ -877,13 +877,13 @@ static int output_aliases(struct module *modules, FILE *out, char *dirname) filename2modname(modname, i->pathname); /* Grab from old-style .modalias section. */ - tbl = file->ops->load_strings(file, ".modalias", NULL, fatal); + tbl = file->ops->load_strings(file, ".modalias", NULL); for (j = 0; tbl && j < tbl->cnt; j++) fprintf(out, "alias %s %s\n", tbl->str[j], modname); strtbl_free(tbl); /* Grab from new-style .modinfo section. */ - tbl = file->ops->load_strings(file, ".modinfo", NULL, fatal); + tbl = file->ops->load_strings(file, ".modinfo", NULL); for (j = 0; tbl && j < tbl->cnt; j++) { const char *p = tbl->str[j]; if (strstarts(p, "alias=")) @@ -914,7 +914,7 @@ static int output_aliases_bin(struct module *modules, FILE *out, char *dirname) filename2modname(modname, i->pathname); /* Grab from old-style .modalias section. */ - tbl = file->ops->load_strings(file, ".modalias", NULL, fatal); + tbl = file->ops->load_strings(file, ".modalias", NULL); for (j = 0; tbl && j < tbl->cnt; j++) { alias = NOFAIL(strdup(tbl->str[j])); underscores(alias); @@ -927,7 +927,7 @@ static int output_aliases_bin(struct module *modules, FILE *out, char *dirname) strtbl_free(tbl); /* Grab from new-style .modinfo section. */ - tbl = file->ops->load_strings(file, ".modinfo", NULL, fatal); + tbl = file->ops->load_strings(file, ".modinfo", NULL); for (j = 0; tbl && j < tbl->cnt; j++) { const char *p = tbl->str[j]; if (strstarts(p, "alias=")) { @@ -949,6 +949,35 @@ static int output_aliases_bin(struct module *modules, FILE *out, char *dirname) return 1; } +static int output_softdeps(struct module *modules, FILE *out, char *dirname) +{ + struct module *i; + struct elf_file *file; + struct string_table *tbl; + int j; + + fprintf(out, "# Soft dependencies extracted from modules themselves.\n"); + fprintf(out, "# Copy, with a .conf extension, to /etc/modprobe.d to use " + "it with modprobe.\n"); + for (i = modules; i; i = i->next) { + char modname[strlen(i->pathname)+1]; + + file = i->file; + filename2modname(modname, i->pathname); + + /* Grab from new-style .modinfo section. */ + tbl = file->ops->load_strings(file, ".modinfo", NULL); + for (j = 0; tbl && j < tbl->cnt; j++) { + const char *p = tbl->str[j]; + if (strstarts(p, "softdep=")) + fprintf(out, "softdep %s %s\n", + modname, p + strlen("softdep=")); + } + strtbl_free(tbl); + } + return 1; +} + struct depfile { char *name; int (*func)(struct module *, FILE *, char *dirname); @@ -968,6 +997,7 @@ static struct depfile depfiles[] = { { "modules.seriomap", output_serio_table, 1 }, { "modules.alias", output_aliases, 0 }, { "modules.alias.bin", output_aliases_bin, 0 }, + { "modules.softdep", output_softdeps, 0 }, { "modules.symbols", output_symbols, 0 }, { "modules.symbols.bin", output_symbols_bin, 0 }, { "modules.builtin.bin", output_builtin_bin, 0 }, diff --git a/doc/modprobe.conf.sgml b/doc/modprobe.conf.sgml index 6fab7d0..b1e19fa 100644 --- a/doc/modprobe.conf.sgml +++ b/doc/modprobe.conf.sgml @@ -173,6 +173,37 @@ </para> </listitem> </varlistentry> + <varlistentry> + <term>softdep <replaceable>modulename</replaceable> [pre: <replaceable>pre-deps...</replaceable>] [post: <replaceable>post-deps...</replaceable>] + </term> + <listitem> + <para> + The softdep command allows you to specify soft, or optional, module + dependencies. <replaceable>modulename</replaceable> can be used + without these optional modules installed, but usually with some + features missing. + </para> + <para> + pre-deps and post-deps are lists of names and/or aliases of other + modules that modprobe will attempt to install (or remove) in order + before and after the main module given in the + <replaceable>modulename</replaceable> argument. + </para> + <para> + Example: Assume "softdep c pre: a b post: d e" is provided in the + configuration. Running "modprobe c" is now equivalent to + "modprobe a b c d e" without the softdep. + Flags such as --use-blacklist are applied to all the specified + modules, while module parameters only apply to module c. + </para> + <para> + Note: if there are <command>install</command> or + <command>remove</command> commands with the same + <replaceable>modulename</replaceable> argument, + <command>softdep</command> takes precedence. + </para> + </listitem> + </varlistentry> </variablelist> </refsect1> <refsect1> @@ -62,46 +62,21 @@ static int elf_ident(void *file, unsigned long fsize, int *conv) */ struct elf_file *grab_elf_file(const char *pathname) { - int fd; - int err; - struct elf_file *file; - - fd = open(pathname, O_RDONLY, 0); - if (fd < 0) - return NULL; - file = grab_elf_file_fd(pathname, fd); - - err = errno; - close(fd); - errno = err; - return file; -} - -/* - * grab_elf_file_fd - read ELF file from file descriptor into memory - * @pathame: name of file to load - * @fd: file descriptor of file to load - * - * Returns NULL, and errno set on error. - */ -struct elf_file *grab_elf_file_fd(const char *pathname, int fd) -{ struct elf_file *file; file = malloc(sizeof(*file)); if (!file) { errno = ENOMEM; - return NULL; + goto fail; } file->pathname = strdup(pathname); if (!file->pathname) { - free(file); errno = ENOMEM; - return NULL; + goto fail_free_file; } - file->data = grab_fd(fd, &file->len); + file->data = grab_file(pathname, &file->len); if (!file->data) - goto fail; + goto fail_free_pathname; switch (elf_ident(file->data, file->len, &file->conv)) { case ELFCLASS32: @@ -117,8 +92,12 @@ struct elf_file *grab_elf_file_fd(const char *pathname, int fd) goto fail; } return file; + +fail_free_pathname: + free(file->pathname); +fail_free_file: + free(file); fail: - release_elf_file(file); return NULL; } @@ -63,7 +63,7 @@ struct module_ops void *(*load_section)(struct elf_file *module, const char *secname, unsigned long *secsize); struct string_table *(*load_strings)(struct elf_file *module, - const char *secname, struct string_table *tbl, errfn_t error); + const char *secname, struct string_table *tbl); struct string_table *(*load_symbols)(struct elf_file *module, uint64_t **versions); struct string_table *(*load_dep_syms)(struct elf_file *module, @@ -79,7 +79,6 @@ struct module_ops extern struct module_ops mod_ops32, mod_ops64; struct elf_file *grab_elf_file(const char *pathname); -struct elf_file *grab_elf_file_fd(const char *pathname, int fd); void release_elf_file(struct elf_file *file); #endif /* MODINITTOOLS_MODULEOPS_H */ diff --git a/elfops_core.c b/elfops_core.c index 1495f68..58a7943 100644 --- a/elfops_core.c +++ b/elfops_core.c @@ -80,14 +80,18 @@ static void *PERBIT(load_section)(struct elf_file *module, static struct string_table *PERBIT(load_strings)(struct elf_file *module, const char *secname, - struct string_table *tbl, - errfn_t error) + struct string_table *tbl) { unsigned long size; const char *strings; strings = PERBIT(load_section)(module, secname, &size); if (strings) { + if (strings[size-1] != 0) { + warn("%s may be corrupt; an unterminated string" + " was found at the end of section %s\n", + module->pathname, secname); + } /* Skip any zero padding. */ while (!strings[0]) { strings++; @@ -147,8 +151,7 @@ static struct string_table *PERBIT(load_symbols)(struct elf_file *module, return symtbl; } fallback: - return PERBIT(load_strings)(module, "__ksymtab_strings", symtbl, - fatal); + return PERBIT(load_strings)(module, "__ksymtab_strings", symtbl); } static char *PERBIT(get_aliases)(struct elf_file *module, unsigned long *size) @@ -624,6 +624,7 @@ char *index_search(struct index_file *in, const char *key) static char *index_search__node(struct index_node_f *node, const char *key, int i) { + char *value; struct index_node_f *child; int ch; int j; @@ -640,10 +641,13 @@ static char *index_search__node(struct index_node_f *node, const char *key, int i += j; if (key[i] == '\0') { - if (node->values) - return strdup(node->values[0].value); - else + if (node->values) { + value = strdup(node->values[0].value); + index_close(node); + return value; + } else { return NULL; + } } child = index_readchild(node, key[i]); @@ -784,9 +788,10 @@ static void index_searchwild__all(struct index_node_f *node, int j, if (node->values) { if (fnmatch(buf_str(buf), subkey, 0) == 0) index_searchwild__allvalues(node, out); + } else { + index_close(node); } - index_close(node); buf_popchars(buf, pushed); } @@ -797,4 +802,6 @@ static void index_searchwild__allvalues(struct index_node_f *node, for (v = node->values; v != NULL; v = v->next) add_value(out, v->value, v->priority); + + index_close(node); } @@ -60,9 +60,7 @@ static void *grab_file(const char *filename, unsigned long *size) { unsigned int max = 16384; int ret, fd, err_save; - void *buffer = malloc(max); - if (!buffer) - return NULL; + void *buffer; if (streq(filename, "-")) fd = dup(STDIN_FILENO); @@ -72,6 +70,10 @@ static void *grab_file(const char *filename, unsigned long *size) if (fd < 0) return NULL; + buffer = malloc(max); + if (!buffer) + goto out_error; + *size = 0; while ((ret = read(fd, buffer + *size, max - *size)) > 0) { *size += ret; @@ -158,7 +160,10 @@ int main(int argc, char *argv[]) if (ret != 0) { fprintf(stderr, "insmod: error inserting '%s': %li %s\n", filename, ret, moderror(errno)); - exit(1); } + free(file); + + if (ret != 0) + exit(1); exit(0); } @@ -307,7 +307,7 @@ int main(int argc, char *argv[]) ret = 1; continue; } - tags = mod->ops->load_strings(mod, ".modinfo", NULL, error); + tags = mod->ops->load_strings(mod, ".modinfo", NULL); if (!tags) { release_elf_file(mod); continue; @@ -51,6 +51,13 @@ int use_binary_indexes = 1; /* default to enabled. */ +/* Limit do_softdep/do_modprobe recursion. + * This is a simple way to handle dependency loops + * caused by poorly written softdep commands. + */ +static int recursion_depth = 0; +const int MAX_RECURSION = 50; /* Arbitrary choice */ + extern long init_module(void *, unsigned long, const char *); extern long delete_module(const char *, unsigned int); @@ -68,9 +75,10 @@ typedef enum mit_use_blacklist = 8, mit_ignore_commands = 16, mit_ignore_loaded = 32, - mit_strip_vermagic = 64, - mit_strip_modversion = 128, - mit_resolve_alias = 256 + mit_quiet_inuse = 64, + mit_strip_vermagic = 128, + mit_strip_modversion = 256, + mit_resolve_alias = 512 } modprobe_flags_t; @@ -99,24 +107,6 @@ static struct module *find_module(const char *filename, struct list_head *list) return NULL; } -/* We used to lock with a write flock but that allows regular users to block - * module load by having a read lock on the module file (no way to bust the - * existing locks without killing the offending process). Instead, we now - * do the system call/init_module and allow the kernel to fail us instead. - */ -static int open_file(const char *filename) -{ - int fd = open(filename, O_RDONLY, 0); - - return fd; -} - -static void close_file(int fd) -{ - /* Valgrind is picky... */ - close(fd); -} - static void add_module(char *filename, int namelen, struct list_head *list) { struct module *mod; @@ -138,6 +128,12 @@ static void add_module(char *filename, int namelen, struct list_head *list) list_add_tail(&mod->list, list); } +static void free_module(struct module *mod) +{ + free(mod->modname); + free(mod); +} + /* Compare len chars of a to b, with _ and - equivalent. */ static int modname_equal(const char *a, const char *b, unsigned int len) { @@ -345,7 +341,7 @@ static void clear_magic(struct elf_file *module) module->ops->strip_section(module, "__vermagic"); /* New-style: in .modinfo section */ - tbl = module->ops->load_strings(module, ".modinfo", NULL, fatal); + tbl = module->ops->load_strings(module, ".modinfo", NULL); for (j = 0; tbl && j < tbl->cnt; j++) { const char *p = tbl->str[j]; if (strstarts(p, "vermagic=")) { @@ -372,6 +368,7 @@ struct module_command struct module_alias { struct module_alias *next; + char *aliasname; char *module; }; @@ -381,6 +378,25 @@ struct module_blacklist char *modulename; }; +struct module_softdep +{ + struct module_softdep *next; + char *buf; + /* The modname and string tables point to buf. */ + char *modname; + struct string_table *pre; + struct string_table *post; +}; + +struct modprobe_conf +{ + struct module_options *options; + struct module_command *commands; + struct module_alias *aliases; + struct module_blacklist *blacklist; + struct module_softdep *softdeps; +}; + /* Link in a new option line from the config file. */ static struct module_options * add_options(const char *modname, @@ -417,16 +433,48 @@ add_command(const char *modname, /* Link in a new alias line from the config file. */ static struct module_alias * -add_alias(const char *modname, struct module_alias *aliases) +add_alias(const char *aliasname, const char *modname, struct module_alias *aliases) { struct module_alias *new; new = NOFAIL(malloc(sizeof(*new))); + new->aliasname = NOFAIL(strdup(aliasname)); new->module = NOFAIL(strdup(modname)); new->next = aliases; return new; } + +/* Return a list of matching aliases */ +static struct module_alias * +find_aliases(const struct module_alias *aliases, + const char *name) +{ + struct module_alias *result = NULL; + while (aliases) { + char *aliasname = aliases->aliasname; + char *modname = aliases->module; + if (fnmatch(aliasname, name, 0) == 0) + result = add_alias(aliasname, modname, result); + aliases = aliases->next; + } + return result; +} + +static void free_aliases(struct module_alias *alias_list) +{ + while (alias_list) { + struct module_alias *alias; + + alias = alias_list; + alias_list = alias_list->next; + + free(alias->aliasname); + free(alias->module); + free(alias); + } +} + /* Link in a new blacklist line from the config file. */ static struct module_blacklist * add_blacklist(const char *modname, struct module_blacklist *blacklist) @@ -440,7 +488,7 @@ add_blacklist(const char *modname, struct module_blacklist *blacklist) } /* Find blacklist commands if any. */ -static int +static int find_blacklist(const char *modname, const struct module_blacklist *blacklist) { while (blacklist) { @@ -451,19 +499,21 @@ find_blacklist(const char *modname, const struct module_blacklist *blacklist) return 0; } -/* return a new alias list, with backlisted elems filtered out */ -static struct module_alias * -apply_blacklist(const struct module_alias *aliases, +/* delete backlisted elems from a list of aliases */ +static void +apply_blacklist(struct module_alias **aliases, const struct module_blacklist *blacklist) { struct module_alias *result = NULL; - while (aliases) { - char *modname = aliases->module; + struct module_alias *alias = *aliases; + while (alias) { + char *modname = alias->module; if (!find_blacklist(modname, blacklist)) - result = add_alias(modname, result); - aliases = aliases->next; + result = add_alias(alias->aliasname, modname, result); + alias = alias->next; } - return result; + free_aliases(*aliases); + *aliases = result; } /* Find install commands if any. */ @@ -478,6 +528,18 @@ static const char *find_command(const char *modname, return NULL; } +/* Find soft dependencies, if any. */ +static const struct module_softdep * +find_softdep(const char *modname, const struct module_softdep *softdeps) +{ + while (softdeps) { + if (fnmatch(softdeps->modname, modname, 0) == 0) + return softdeps; + softdeps = softdeps->next; + } + return NULL; +} + static char *append_option(char *options, const char *newoption) { options = NOFAIL(realloc(options, strlen(options) + 1 @@ -509,15 +571,17 @@ static char *prepend_option(char *options, const char *newoption) /* Add to options */ static char *add_extra_options(const char *modname, - char *optstring, + const char *optstring, const struct module_options *options) { + char *opts = NOFAIL(strdup(optstring)); + while (options) { if (streq(options->modulename, modname)) - optstring = prepend_option(optstring, options->options); + opts = prepend_option(opts, options->options); options = options->next; } - return optstring; + return opts; } /* Is module in /proc/modules? If so, fill in usecount if not NULL. @@ -609,7 +673,7 @@ static int module_builtin(const char *dirname, const char *modname) return value ? 1 : 0; } -/* Is module in /sys/module? If so, fill in usecount if not NULL. +/* Is module in /sys/module? If so, fill in usecount if not NULL. 0 means no, 1 means yes, -1 means unknown. */ static int module_in_sysfs(const char *modname, unsigned int *usecount) @@ -767,27 +831,24 @@ static char *strsep_skipspace(char **string, char *delim) } static int parse_config_scan(const char *filename, - const char *name, + struct modprobe_conf *conf, int dump_only, - int removing, - struct module_options **options, - struct module_command **commands, - struct module_alias **alias, - struct module_blacklist **blacklist); + int removing); static int parse_config_file(const char *filename, - const char *name, + struct modprobe_conf *conf, int dump_only, - int removing, - struct module_options **options, - struct module_command **commands, - struct module_alias **aliases, - struct module_blacklist **blacklist) + int removing) { char *line; unsigned int linenum = 0; FILE *cfile; + struct module_options **options = &conf->options; + struct module_command **commands = &conf->commands; + struct module_alias **aliases = &conf->aliases; + struct module_blacklist **blacklist = &conf->blacklist; + cfile = fopen(filename, "r"); if (!cfile) return 0; @@ -808,52 +869,51 @@ static int parse_config_file(const char *filename, if (streq(cmd, "alias")) { char *wildcard = strsep_skipspace(&ptr, "\t "); char *realname = strsep_skipspace(&ptr, "\t "); - if (!wildcard || !realname) - grammar(cmd, filename, linenum); - else if (fnmatch(underscores(wildcard),name,0) == 0) - *aliases = add_alias(underscores(realname), *aliases); + goto syntax_error; + *aliases = add_alias(underscores(wildcard), + underscores(realname), + *aliases); } else if (streq(cmd, "include")) { - struct module_alias *newalias = NULL; + struct modprobe_conf newconf = *conf; + newconf.aliases = NULL; char *newfilename; - newfilename = strsep_skipspace(&ptr, "\t "); - if (!newfilename) { - grammar(cmd, filename, linenum); + if (!newfilename) + goto syntax_error; + + warn("\"include %s\" is deprecated, " + "please use /etc/modprobe.d\n", newfilename); + if (strstarts(newfilename, "/etc/modprobe.d")) { + warn("\"include /etc/modprobe.d\" is " + "the default, ignored\n"); } else { - warn("\"include %s\" is deprecated, " - "please use /etc/modprobe.d\n", newfilename); - if (strstarts(newfilename, "/etc/modprobe.d")) { - warn("\"include /etc/modprobe.d\" is " - "the default, ignored\n"); - } else { - if (!parse_config_scan(newfilename, name, - dump_only, removing, - options, commands, &newalias, - blacklist)) - warn("Failed to open included" - " config file %s: %s\n", - newfilename, strerror(errno)); - } - /* Files included override aliases, - etc that was already set ... */ - if (newalias) - *aliases = newalias; + if (!parse_config_scan(newfilename, + &newconf, dump_only, + removing)) + warn("Failed to open included" + " config file %s: %s\n", + newfilename, strerror(errno)); } + /* Files included override aliases, + etc that was already set ... */ + if (newconf.aliases) + *aliases = newconf.aliases; + } else if (streq(cmd, "options")) { modname = strsep_skipspace(&ptr, "\t "); if (!modname || !ptr) - grammar(cmd, filename, linenum); - else { - ptr += strspn(ptr, "\t "); - *options = add_options(underscores(modname), - ptr, *options); - } + goto syntax_error; + + ptr += strspn(ptr, "\t "); + *options = add_options(underscores(modname), + ptr, *options); + } else if (streq(cmd, "install")) { modname = strsep_skipspace(&ptr, "\t "); if (!modname || !ptr) - grammar(cmd, filename, linenum); - else if (!removing) { + goto syntax_error; + if (!removing) { ptr += strspn(ptr, "\t "); *commands = add_command(underscores(modname), ptr, *commands); @@ -861,34 +921,73 @@ static int parse_config_file(const char *filename, } else if (streq(cmd, "blacklist")) { modname = strsep_skipspace(&ptr, "\t "); if (!modname) - grammar(cmd, filename, linenum); - else if (!removing) { + goto syntax_error; + if (!removing) { *blacklist = add_blacklist(underscores(modname), *blacklist); } } else if (streq(cmd, "remove")) { modname = strsep_skipspace(&ptr, "\t "); if (!modname || !ptr) - grammar(cmd, filename, linenum); - else if (removing) { + goto syntax_error; + if (removing) { ptr += strspn(ptr, "\t "); *commands = add_command(underscores(modname), ptr, *commands); } + } else if (streq(cmd, "softdep")) { + char *tk; + int pre = 0, post = 0; + struct string_table *pre_modnames = NULL; + struct string_table *post_modnames = NULL; + struct module_softdep *new; + + modname = underscores(strsep_skipspace(&ptr, "\t ")); + if (!modname || !ptr) + goto syntax_error; + while ((tk = strsep_skipspace(&ptr, "\t ")) != NULL) { + if (streq(tk, "pre:")) { + pre = 1; post = 0; + } else if (streq(tk, "post:")) { + pre = 0; post = 1; + } else if (pre) { + pre_modnames = NOFAIL( + strtbl_add(tk, pre_modnames)); + } else if (post) { + post_modnames = NOFAIL( + strtbl_add(tk, post_modnames)); + } else { + strtbl_free(pre_modnames); + strtbl_free(post_modnames); + goto syntax_error; + } + } + new = NOFAIL(malloc(sizeof(*new))); + new->buf = line; + new->modname = modname; + new->pre = pre_modnames; + new->post = post_modnames; + new->next = conf->softdeps; + conf->softdeps = new; + + line = NULL; /* Don't free() this line. */ + } else if (streq(cmd, "config")) { char *tmp = strsep_skipspace(&ptr, "\t "); if (!tmp) - grammar(cmd, filename, linenum); - else if (streq(tmp, "binary_indexes")) { + goto syntax_error; + if (streq(tmp, "binary_indexes")) { tmp = strsep_skipspace(&ptr, "\t "); if (streq(tmp, "yes")) use_binary_indexes = 1; if (streq(tmp, "no")) use_binary_indexes = 0; } - } else + } else { +syntax_error: grammar(cmd, filename, linenum); + } free(line); } @@ -896,29 +995,22 @@ static int parse_config_file(const char *filename, return 1; } -/* fallback to plain-text aliases file as necessary */ +/* Read binary index file containing aliases only */ static int read_aliases_file(const char *filename, const char *name, int dump_only, - int removing, - struct module_options **options, - struct module_command **commands, - struct module_alias **aliases, - struct module_blacklist **blacklist) + struct module_alias **aliases) { struct index_value *realnames; struct index_value *realname; char *binfile; struct index_file *index; - if (!use_binary_indexes) - goto plain_text; - nofail_asprintf(&binfile, "%s.bin", filename); index = index_file_open(binfile); if (!index) { free(binfile); - goto plain_text; + return 0; } if (dump_only) { @@ -930,26 +1022,69 @@ static int read_aliases_file(const char *filename, realnames = index_searchwild(index, name); for (realname = realnames; realname; realname = realname->next) - *aliases = add_alias(realname->value, *aliases); + *aliases = add_alias("*", realname->value, *aliases); index_values_free(realnames); free(binfile); index_file_close(index); return 1; +} + +/* fallback to plain-text aliases file if necessary */ +static int read_aliases(const char *filename, + const char *name, + int dump_only, + struct module_alias **aliases) +{ + char *line; + unsigned int linenum = 0; + FILE *cfile; -plain_text: - return parse_config_file(filename, name, dump_only, removing, - options, commands, aliases, blacklist); + if (use_binary_indexes) + if (read_aliases_file(filename, name, dump_only, aliases)) + return 1; + + cfile = fopen(filename, "r"); + if (!cfile) + return 0; + + while ((line = getline_wrapped(cfile, &linenum)) != NULL) { + char *ptr = line; + char *cmd; + + if (dump_only) + printf("%s\n", line); + + cmd = strsep_skipspace(&ptr, "\t "); + if (cmd == NULL || cmd[0] == '#' || cmd[0] == '\0') { + free(line); + continue; + } + + if (streq(cmd, "alias")) { + char *wildcard = strsep_skipspace(&ptr, "\t "); + char *realname = strsep_skipspace(&ptr, "\t "); + if (!wildcard || !realname) + goto syntax_error; + if (fnmatch(underscores(wildcard),name,0) == 0) + *aliases = add_alias(wildcard, + underscores(realname), + *aliases); + } else { +syntax_error: + grammar(cmd, filename, linenum); + } + + free(line); + } + fclose(cfile); + return 1; } static int parse_config_scan(const char *filename, - const char *name, + struct modprobe_conf *conf, int dump_only, - int removing, - struct module_options **options, - struct module_command **commands, - struct module_alias **aliases, - struct module_blacklist **blacklist) + int removing) { DIR *dir; int ret = 0; @@ -996,10 +1131,8 @@ static int parse_config_scan(const char *filename, char *cfgfile; nofail_asprintf(&cfgfile, "%s/%s", filename, fe->name); - if (!parse_config_file(cfgfile, name, - dump_only, removing, - options, commands, - aliases, blacklist)) + if (!parse_config_file(cfgfile, conf, + dump_only, removing)) warn("Failed to open config file " "%s: %s\n", fe->name, strerror(errno)); free(cfgfile); @@ -1009,40 +1142,32 @@ static int parse_config_scan(const char *filename, ret = 1; } else { - if (parse_config_file(filename, name, dump_only, removing, - options, commands, aliases, blacklist)) + if (parse_config_file(filename, conf, dump_only, removing)) ret = 1; } return ret; } -/* Read binary index file containing aliases only */ static void parse_toplevel_config(const char *filename, - const char *name, + struct modprobe_conf *conf, int dump_only, - int removing, - struct module_options **options, - struct module_command **commands, - struct module_alias **aliases, - struct module_blacklist **blacklist) + int removing) { if (filename) { - if (!parse_config_scan(filename, name, dump_only, removing, - options, commands, aliases, blacklist)) + if (!parse_config_scan(filename, conf, dump_only, removing)) fatal("Failed to open config file %s: %s\n", filename, strerror(errno)); return; } /* deprecated config file */ - if (parse_config_file("/etc/modprobe.conf", name, dump_only, removing, - options, commands, aliases, blacklist) > 0) + if (parse_config_file("/etc/modprobe.conf", conf, + dump_only, removing) > 0) warn("Deprecated config file /etc/modprobe.conf, " "all config files belong into /etc/modprobe.d/.\n"); /* default config */ - parse_config_scan("/etc/modprobe.d", name, dump_only, removing, - options, commands, aliases, blacklist); + parse_config_scan("/etc/modprobe.d", conf, dump_only, removing); } /* Read possible module arguments from the kernel command line. */ @@ -1093,6 +1218,7 @@ static void add_to_env_var(const char *option) char *newenv; nofail_asprintf(&newenv, "%s %s", oldenv, option); setenv("MODPROBE_OPTIONS", newenv, 1); + free(newenv); } else setenv("MODPROBE_OPTIONS", option, 1); } @@ -1171,31 +1297,94 @@ static void do_command(const char *modname, info("%s %s\n", type, replaced_cmd); if (dry_run) - return; + goto out; setenv("MODPROBE_MODULE", modname, 1); ret = system(replaced_cmd); if (ret == -1 || WEXITSTATUS(ret)) error("Error running %s command for %s\n", type, modname); + +out: free(replaced_cmd); } -/* Actually do the insert. Frees second arg. */ +/* Forward declaration */ +int do_modprobe(const char *modname, + const char *newname, + const char *cmdline_opts, + const struct modprobe_conf *conf, + const char *dirname, + errfn_t error, + modprobe_flags_t flags); + +static void do_softdep(const struct module_softdep *softdep, + const char *cmdline_opts, + const struct modprobe_conf *conf, + const char *dirname, + errfn_t error, + modprobe_flags_t flags) +{ + struct string_table *pre_modnames, *post_modnames; + int i, j; + + if (++recursion_depth >= MAX_RECURSION) + fatal("modprobe: softdep dependency loop encountered %s %s\n", + (flags & mit_remove) ? "removing" : "inserting", + softdep->modname); + + if (flags & mit_remove) { + /* Reverse module order if removing. */ + pre_modnames = softdep->post; + post_modnames = softdep->pre; + } else { + pre_modnames = softdep->pre; + post_modnames = softdep->post; + } + + /* Modprobe pre_modnames */ + + for (i = 0; pre_modnames && i < pre_modnames->cnt; i++) { + /* Reverse module order if removing. */ + j = (flags & mit_remove) ? pre_modnames->cnt-1 - i : i; + + do_modprobe(pre_modnames->str[j], NULL, "", + conf, dirname, warn, flags); + } + + /* Modprobe main module, passing cmdline_opts, ignoring softdep */ + + do_modprobe(softdep->modname, NULL, cmdline_opts, + conf, dirname, warn, flags | mit_ignore_commands); + + /* Modprobe post_modnames */ + + for (i = 0; post_modnames && i < post_modnames->cnt; i++) { + /* Reverse module order if removing. */ + j = (flags & mit_remove) ? post_modnames->cnt-1 - i : i; + + do_modprobe(post_modnames->str[j], NULL, "", conf, + dirname, warn, flags); + } +} + +/* Actually do the insert. */ static int insmod(struct list_head *list, - char *optstring, + const char *optstring, const char *newname, - const struct module_options *options, - const struct module_command *commands, const char *cmdline_opts, + const struct modprobe_conf *conf, + const char *dirname, errfn_t error, modprobe_flags_t flags) { - int ret, fd; + int ret; struct elf_file *module; + const struct module_softdep *softdep; const char *command; struct module *mod = list_entry(list->next, struct module, list); int rc = 0; int already_loaded; + char *opts = NULL; /* Take us off the list. */ list_del(&mod->list); @@ -1205,22 +1394,16 @@ static int insmod(struct list_head *list, modprobe_flags_t f = flags; f &= ~mit_first_time; f &= ~mit_ignore_commands; - if ((rc = insmod(list, NOFAIL(strdup("")), NULL, - options, commands, "", warn, f)) != 0) { + if ((rc = insmod(list, "", NULL, + "", conf, dirname, warn, f)) != 0) + { error("Error inserting %s (%s): %s\n", mod->modname, mod->filename, insert_moderror(errno)); - goto out_optstring; + goto out; } } - fd = open_file(mod->filename); - if (fd < 0) { - error("Could not open '%s': %s\n", - mod->filename, strerror(errno)); - goto out_optstring; - } - /* Don't do ANYTHING if already in kernel. */ already_loaded = module_in_kernel(newname ?: mod->modname, NULL); @@ -1228,10 +1411,17 @@ static int insmod(struct list_head *list, if (flags & mit_first_time) error("Module %s already in kernel.\n", newname ?: mod->modname); - goto out_unlock; + goto out; } - command = find_command(mod->modname, commands); + softdep = find_softdep(mod->modname, conf->softdeps); + if (softdep && !(flags & mit_ignore_commands)) { + do_softdep(softdep, cmdline_opts, conf, dirname, + error, flags & (mit_remove | mit_dry_run)); + goto out; + } + + command = find_command(mod->modname, conf->commands); if (command && !(flags & mit_ignore_commands)) { if (already_loaded == -1) { warn("/sys/module/ not present or too old," @@ -1240,19 +1430,18 @@ static int insmod(struct list_head *list, " in case it is already loaded.\n", newname ?: mod->modname); } else { - close_file(fd); do_command(mod->modname, command, flags & mit_dry_run, error, "install", cmdline_opts); - goto out_optstring; + goto out; } } - module = grab_elf_file_fd(mod->filename, fd); + module = grab_elf_file(mod->filename); if (!module) { error("Could not read '%s': %s\n", mod->filename, (errno == ENOEXEC) ? "Invalid module format" : strerror(errno)); - goto out_unlock; + goto out; } if (newname) rename_module(module, mod->modname, newname); @@ -1262,20 +1451,20 @@ static int insmod(struct list_head *list, clear_magic(module); /* Config file might have given more options */ - optstring = add_extra_options(mod->modname, optstring, options); + opts = add_extra_options(mod->modname, optstring, conf->options); - info("insmod %s %s\n", mod->filename, optstring); + info("insmod %s %s\n", mod->filename, opts); if (flags & mit_dry_run) - goto out; + goto out_elf_file; - ret = init_module(module->data, module->len, optstring); + ret = init_module(module->data, module->len, opts); if (ret != 0) { if (errno == EEXIST) { if (flags & mit_first_time) error("Module %s already in kernel.\n", newname ?: mod->modname); - goto out_unlock; + goto out_elf_file; } /* don't warn noisely if we're loading multiple aliases. */ /* one of the aliases may try to use hardware we don't have. */ @@ -1285,23 +1474,24 @@ static int insmod(struct list_head *list, insert_moderror(errno)); rc = 1; } - out: + out_elf_file: release_elf_file(module); - out_unlock: - close_file(fd); - out_optstring: - free(optstring); + free(opts); + out: + free_module(mod); return rc; } /* Do recursive removal. */ static void rmmod(struct list_head *list, const char *name, - struct module_command *commands, const char *cmdline_opts, + const struct modprobe_conf *conf, + const char *dirname, errfn_t error, modprobe_flags_t flags) { + const struct module_softdep *softdep; const char *command; unsigned int usecount = 0; struct module *mod = list_entry(list->next, struct module, list); @@ -1318,8 +1508,16 @@ static void rmmod(struct list_head *list, if (exists == 0) goto nonexistent_module; - /* Even if renamed, find commands to orig. name. */ - command = find_command(mod->modname, commands); + /* Even if renamed, find commands/softdeps to orig. name. */ + + softdep = find_softdep(mod->modname, conf->softdeps); + if (softdep && !(flags & mit_ignore_commands)) { + do_softdep(softdep, cmdline_opts, conf, dirname, + error, flags & (mit_remove | mit_dry_run)); + goto remove_rest; + } + + command = find_command(mod->modname, conf->commands); if (command && !(flags & mit_ignore_commands)) { if (exists == -1) { warn("/sys/module/ not present or too old," @@ -1335,7 +1533,7 @@ static void rmmod(struct list_head *list, } if (usecount != 0) { - if (!(flags & mit_ignore_loaded)) + if (!(flags & mit_quiet_inuse)) error("Module %s is in use.\n", name); goto remove_rest; } @@ -1358,10 +1556,11 @@ static void rmmod(struct list_head *list, if (!list_empty(list)) { flags &= ~mit_first_time; flags &= ~mit_ignore_commands; - flags |= mit_ignore_loaded; + flags |= mit_quiet_inuse; - rmmod(list, NULL, commands, "", warn, flags); + rmmod(list, NULL, "", conf, dirname, warn, flags); } + free_module(mod); return; nonexistent_module: @@ -1373,19 +1572,28 @@ nonexistent_module: static int handle_module(const char *modname, struct list_head *todo_list, const char *newname, - char *options, - struct module_options *modoptions, - struct module_command *commands, + const char *options, const char *cmdline_opts, + const struct modprobe_conf *conf, + const char *dirname, errfn_t error, modprobe_flags_t flags) { if (list_empty(todo_list)) { + const struct module_softdep *softdep; const char *command; /* The dependencies have to be real modules, but handle case where the first is completely bogus. */ - command = find_command(modname, commands); + + softdep = find_softdep(modname, conf->softdeps); + if (softdep && !(flags & mit_ignore_commands)) { + do_softdep(softdep, cmdline_opts, conf, dirname, + error, flags & (mit_remove | mit_dry_run)); + return 0; + } + + command = find_command(modname, conf->commands); if (command && !(flags & mit_ignore_commands)) { do_command(modname, command, flags & mit_dry_run, error, (flags & mit_remove) ? "remove":"install", cmdline_opts); @@ -1397,12 +1605,12 @@ static int handle_module(const char *modname, return 1; } - if (flags & mit_remove) { - flags &= ~mit_ignore_loaded; - rmmod(todo_list, newname, commands, cmdline_opts, error, flags); - } else - insmod(todo_list, NOFAIL(strdup(options)), newname, - modoptions, commands, cmdline_opts, error, flags); + if (flags & mit_remove) + rmmod(todo_list, newname, cmdline_opts, + conf, dirname, error, flags); + else + insmod(todo_list, options, newname, + cmdline_opts, conf, dirname, error, flags); return 0; } @@ -1424,93 +1632,97 @@ int handle_builtin_module(const char *modname, return 0; } -int do_modprobe(char *modname, - char *newname, - char *cmdline_opts, - const char *configname, +int do_modprobe(const char *modulename, + const char *newname, + const char *cmdline_opts, + const struct modprobe_conf *conf, const char *dirname, - const char *aliasfilename, - const char *symfilename, errfn_t error, modprobe_flags_t flags) { - struct module_command *commands = NULL; - struct module_options *modoptions = NULL; - struct module_alias *aliases = NULL; - struct module_blacklist *blacklist = NULL; + char *modname; + struct module_alias *matching_aliases; LIST_HEAD(list); int failed = 0; /* Convert name we are looking for */ - underscores(modname); - - /* Returns the resolved alias, options */ - parse_toplevel_config(configname, modname, 0, - flags & mit_remove, &modoptions, &commands, &aliases, &blacklist); + modname = underscores(NOFAIL(strdup(modulename))); - /* Read module options from kernel command line */ - parse_kcmdline(0, &modoptions); + matching_aliases = find_aliases(conf->aliases, modname); /* No luck? Try symbol names, if starts with symbol:. */ - if (!aliases && strstarts(modname, "symbol:")) { - parse_config_file(symfilename, modname, 0, - flags & mit_remove, &modoptions, &commands, - &aliases, &blacklist); + if (!matching_aliases && strstarts(modname, "symbol:")) { + char *symfilename; + + nofail_asprintf(&symfilename, "%s/modules.symbols", dirname); + read_aliases(symfilename, modname, 0, &matching_aliases); + free(symfilename); } - if (!aliases) { + if (!matching_aliases) { if(!strchr(modname, ':')) read_depends(dirname, modname, &list); /* We only use canned aliases as last resort. */ if (list_empty(&list) - && !find_command(modname, commands)) + && !find_softdep(modname, conf->softdeps) + && !find_command(modname, conf->commands)) { - read_aliases_file(aliasfilename, - modname, 0, flags & mit_remove, - &modoptions, &commands, - &aliases, &blacklist); + char *aliasfilename; + + nofail_asprintf(&aliasfilename, "%s/modules.alias", + dirname); + read_aliases(aliasfilename, modname, 0, + &matching_aliases); + free(aliasfilename); /* builtin module? */ - if (!aliases && module_builtin(dirname, modname) > 0) { - return handle_builtin_module(modname, error, - flags); + if (!matching_aliases && module_builtin(dirname, modname) > 0) { + failed |= handle_builtin_module(modname, error, + flags); + goto out; } } } - aliases = apply_blacklist(aliases, blacklist); + apply_blacklist(&matching_aliases, conf->blacklist); if(flags & mit_resolve_alias) { + struct module_alias *aliases = matching_aliases; + for(; aliases; aliases=aliases->next) printf("%s\n", aliases->module); - return 0; + goto out; } - if (aliases) { + if (matching_aliases) { errfn_t err = error; + struct module_alias *aliases = matching_aliases; /* More than one alias? Don't bail out on failure. */ if (aliases->next) err = warn; while (aliases) { /* Add the options for this alias. */ - char *opts = NOFAIL(strdup(cmdline_opts)); - opts = add_extra_options(modname, - opts, modoptions); + char *opts; + opts = add_extra_options(modname, cmdline_opts, conf->options); read_depends(dirname, aliases->module, &list); failed |= handle_module(aliases->module, - &list, newname, opts, modoptions, - commands, cmdline_opts, err, flags); + &list, newname, opts, cmdline_opts, + conf, dirname, err, flags); aliases = aliases->next; + free(opts); INIT_LIST_HEAD(&list); } } else { if (flags & mit_use_blacklist - && find_blacklist(modname, blacklist)) - return failed; + && find_blacklist(modname, conf->blacklist)) + goto out; failed |= handle_module(modname, &list, newname, cmdline_opts, - modoptions, commands, cmdline_opts, error, flags); + cmdline_opts, conf, dirname, error, flags); } +out: + free(modname); + free_aliases(matching_aliases); return failed; } @@ -1556,10 +1768,13 @@ int main(int argc, char *argv[]) char *basedir = ""; char *cmdline_opts = NULL; char *newname = NULL; - char *dirname, *aliasfilename, *symfilename; + char *dirname; errfn_t error = fatal; int failed = 0; modprobe_flags_t flags = 0; + struct modprobe_conf conf = {}; + + recursion_depth = 0; /* Prepend options from environment. */ argv = merge_args(getenv("MODPROBE_OPTIONS"), argv, &argc); @@ -1661,34 +1876,38 @@ int main(int argc, char *argv[]) print_usage(argv[0]); nofail_asprintf(&dirname, "%s%s/%s", basedir, MODULE_DIR, buf.release); - nofail_asprintf(&aliasfilename, "%s/modules.alias", dirname); - nofail_asprintf(&symfilename, "%s/modules.symbols", dirname); /* Old-style -t xxx wildcard? Only with -l. */ if (list_only) { if (optind+1 < argc) fatal("Can't have multiple wildcards\n"); /* fprintf(stderr, "man find\n"); return 1; */ - return do_wildcard(dirname, type, argv[optind]?:"*"); + failed = do_wildcard(dirname, type, argv[optind]?:"*"); + goto out; } if (type) fatal("-t only supported with -l"); + /* Read aliases, options etc. */ + parse_toplevel_config(configname, &conf, dump_config, flags & mit_remove); + + /* Read module options from kernel command line */ + parse_kcmdline(1, &conf.options); + if (dump_config) { - struct module_command *commands = NULL; - struct module_options *modoptions = NULL; - struct module_alias *aliases = NULL; - struct module_blacklist *blacklist = NULL; + char *aliasfilename, *symfilename; + struct modprobe_conf conf = {}; - parse_toplevel_config(configname, "", 1, 0, &modoptions, - &commands, &aliases, &blacklist); + nofail_asprintf(&aliasfilename, "%s/modules.alias", dirname); + nofail_asprintf(&symfilename, "%s/modules.symbols", dirname); + + parse_toplevel_config(configname, &conf, 1, 0); /* Read module options from kernel command line */ - parse_kcmdline(1, &modoptions); - parse_config_file(aliasfilename, "", 1, 0, &modoptions, - &commands, &aliases, &blacklist); - parse_config_file(symfilename, "", 1, 0, &modoptions, - &commands, &aliases, &blacklist); - exit(0); + parse_kcmdline(1, &conf.options); + read_aliases(aliasfilename, "", 1, &conf.aliases); + read_aliases(symfilename, "", 1, &conf.aliases); + + goto out; } if ((flags & mit_remove) || all) { @@ -1707,17 +1926,16 @@ int main(int argc, char *argv[]) dump_modversions(modname, error); else failed |= do_modprobe(modname, newname, cmdline_opts, - configname, dirname, aliasfilename, symfilename, - error, flags); + &conf, dirname, error, flags); } + +out: if (logging) closelog(); - free(dirname); - free(aliasfilename); - free(symfilename); free(cmdline_opts); + /* Don't bother to free conf */ exit(failed); } diff --git a/tests/test-modprobe/28softdep.sh b/tests/test-modprobe/28softdep.sh new file mode 100644 index 0000000..234a214 --- /dev/null +++ b/tests/test-modprobe/28softdep.sh @@ -0,0 +1,67 @@ +#! /bin/sh + +BITNESS=32 + +rm -rf tests/tmp/* + +MODULE_DIR=tests/tmp/lib/modules/$MODTEST_UNAME + +# Set up modules +mkdir -p $MODULE_DIR +ln tests/data/$BITNESS/normal/noexport_nodep-$BITNESS.ko $MODULE_DIR/a.ko +ln -s a.ko $MODULE_DIR/b.ko +ln -s a.ko $MODULE_DIR/c.ko +ln -s a.ko $MODULE_DIR/d.ko +ln -s a.ko $MODULE_DIR/e.ko +ln -s a.ko $MODULE_DIR/f.ko + +# Set up dependencies +cat > $MODULE_DIR/modules.dep << EOF +/lib/modules/$MODTEST_UNAME/a.ko: +/lib/modules/$MODTEST_UNAME/b.ko: +/lib/modules/$MODTEST_UNAME/c.ko: +/lib/modules/$MODTEST_UNAME/d.ko: +/lib/modules/$MODTEST_UNAME/e.ko: +/lib/modules/$MODTEST_UNAME/f.ko: +EOF + +# Test softdeps + +mkdir -p tests/tmp/etc/modprobe.d +cat > tests/tmp/etc/modprobe.d/modprobe.conf << EOF +alias a_alias a +alias c_alias c +softdep c pre: a_alias b post: d e +softdep e post: f +softdep f pre: a +EOF + +# Insert-test + +R1=`modprobe -v c_alias 2>&1 | \ + grep -v INIT_MODULE\: | \ + sed -e "s/\/lib\/modules\/$MODTEST_UNAME\///g"` +R2=`echo $R1` # remove newlines +[ "$R2" = "insmod a.ko insmod b.ko insmod c.ko insmod d.ko insmod e.ko insmod a.ko insmod f.ko" ] + +# Remove-test + +R1=`modprobe -v -r c_alias 2>&1 | \ + grep -v DELETE_MODULE\: | \ + sed -e "s/\/lib\/modules\/$MODTEST_UNAME\///g"` +R2=`echo $R1` # remove newlines +[ "$R2" = "rmmod f.ko rmmod a.ko rmmod e.ko rmmod d.ko rmmod c.ko rmmod b.ko rmmod a.ko" ] + +# Test loop detector + +mkdir -p tests/tmp/etc/modprobe.d +cat > tests/tmp/etc/modprobe.d/modprobe.conf << EOF +alias a_alias a +softdep a pre: b +softdep b pre: a +EOF + +R1=`modprobe -v a_alias 2>&1 | cat` # shell won't assign to R1 without no-op cat. +# This comparison will fail if MAX_RECURSION is odd. In that case: s/b/a/g +[ "$R1" = "FATAL: modprobe: softdep dependency loop encountered inserting b" ] + diff --git a/tests/valgrind/valgrindme.sh b/tests/valgrind/valgrindme.sh index 0ab8a86..5d4cade 100755 --- a/tests/valgrind/valgrindme.sh +++ b/tests/valgrind/valgrindme.sh @@ -2,7 +2,7 @@ # Make sure it finds the real one this time. PATH=`pwd`/tests/build:$PATH -valgrind -q --num-callers=8 --suppressions=tests/valgrind/suppressions --log-fd=3 `basename $0` "$@" 3>tests/tmp/valgrind +valgrind -q --num-callers=8 --leak-check=yes --suppressions=tests/valgrind/suppressions --log-fd=3 `basename $0` "$@" 3>tests/tmp/valgrind ret=$? if [ -s tests/tmp/valgrind ]; then diff --git a/zlibsupport.c b/zlibsupport.c index b159765..597820e 100644 --- a/zlibsupport.c +++ b/zlibsupport.c @@ -40,23 +40,6 @@ void *grab_contents(gzFile *gzfd, unsigned long *size) return buffer; } -void *grab_fd(int fd, unsigned long *size) -{ - gzFile gzfd; - - gzfd = gzdopen(fd, "rb"); - if (!gzfd) { - if (errno == ENOMEM) - fatal("Memory allocation failure in gzdopen\n"); - return NULL; - } - - /* gzclose(gzfd) would close fd, which would drop locks. - Don't blame zlib: POSIX locking semantics are so horribly - broken that they should be ripped out. */ - return grab_contents(gzfd, size); -} - /* gzopen handles uncompressed files transparently. */ void *grab_file(const char *filename, unsigned long *size) { |