138465Smsmith/*-
238465Smsmith * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
338465Smsmith * All rights reserved.
438465Smsmith *
538465Smsmith * Redistribution and use in source and binary forms, with or without
638465Smsmith * modification, are permitted provided that the following conditions
738465Smsmith * are met:
838465Smsmith * 1. Redistributions of source code must retain the above copyright
938465Smsmith *    notice, this list of conditions and the following disclaimer.
1038465Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1138465Smsmith *    notice, this list of conditions and the following disclaimer in the
1238465Smsmith *    documentation and/or other materials provided with the distribution.
1338465Smsmith *
1438465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538465Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638465Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738465Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838465Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938465Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038465Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238465Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438465Smsmith * SUCH DAMAGE.
2538465Smsmith */
2638465Smsmith
27119483Sobrien#include <sys/cdefs.h>
28119483Sobrien__FBSDID("$FreeBSD: stable/11/stand/common/module.c 329010 2018-02-08 02:44:21Z kevans $");
29119483Sobrien
3038465Smsmith/*
3159854Sbp * file/module function dispatcher, support, etc.
3238465Smsmith */
3338465Smsmith
3438465Smsmith#include <stand.h>
3538465Smsmith#include <string.h>
3640141Speter#include <sys/param.h>
3740141Speter#include <sys/linker.h>
3883321Speter#include <sys/module.h>
3983321Speter#include <sys/queue.h>
40269614Smarcel#include <sys/stdint.h>
4138465Smsmith
4238465Smsmith#include "bootstrap.h"
4338465Smsmith
4483321Speter#define	MDIR_REMOVED	0x0001
4583321Speter#define	MDIR_NOHINTS	0x0002
4683321Speter
4783321Speterstruct moduledir {
4883321Speter	char	*d_path;	/* path of modules directory */
4983321Speter	u_char	*d_hints;	/* content of linker.hints file */
5083321Speter	int	d_hintsz;	/* size of hints data */
5183321Speter	int	d_flags;
5283321Speter	STAILQ_ENTRY(moduledir) d_link;
5383321Speter};
5483321Speter
5559854Sbpstatic int			file_load(char *filename, vm_offset_t dest, struct preloaded_file **result);
5686469Siedowsestatic int			file_load_dependencies(struct preloaded_file *base_mod);
5783321Speterstatic char *			file_search(const char *name, char **extlist);
5883321Speterstatic struct kernel_module *	file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo);
5983321Speterstatic int			file_havepath(const char *name);
6083321Speterstatic char			*mod_searchmodule(char *name, struct mod_depend *verinfo);
6159854Sbpstatic void			file_insert_tail(struct preloaded_file *mp);
6259854Sbpstruct file_metadata*		metadata_next(struct file_metadata *base_mp, int type);
6383321Speterstatic void			moduledir_readhints(struct moduledir *mdp);
6483321Speterstatic void			moduledir_rebuild(void);
6538764Smsmith
6639178Smsmith/* load address should be tweaked by first module loaded (kernel) */
6738465Smsmithstatic vm_offset_t	loadaddr = 0;
6838465Smsmith
69271054Sian#if defined(LOADER_FDT_SUPPORT)
70271054Sianstatic const char	*default_searchpath =
71271054Sian    "/boot/kernel;/boot/modules;/boot/dtb";
72271054Sian#else
73111852Srustatic const char	*default_searchpath ="/boot/kernel;/boot/modules";
74271054Sian#endif
7539178Smsmith
7683321Speterstatic STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list);
7783321Speter
7859854Sbpstruct preloaded_file *preloaded_files = NULL;
7938465Smsmith
8083321Speterstatic char *kld_ext_list[] = {
8183321Speter    ".ko",
8283321Speter    "",
83172445Sobrien    ".debug",
8483321Speter    NULL
8583321Speter};
8683321Speter
8783321Speter
8838764Smsmith/*
8938764Smsmith * load an object, either a disk file or code module.
9038764Smsmith *
9138764Smsmith * To load a file, the syntax is:
9238764Smsmith *
9338764Smsmith * load -t <type> <path>
9438764Smsmith *
9538764Smsmith * code modules are loaded as:
9638764Smsmith *
9738764Smsmith * load <path> <options>
9838764Smsmith */
9938764Smsmith
10038465SmsmithCOMMAND_SET(load, "load", "load a kernel or module", command_load);
10138465Smsmith
10238465Smsmithstatic int
10338465Smsmithcommand_load(int argc, char *argv[])
10438465Smsmith{
105286234Strasz    struct preloaded_file *fp;
10638764Smsmith    char	*typestr;
10783321Speter    int		dofile, dokld, ch, error;
108299499Spfg
10983321Speter    dokld = dofile = 0;
11038764Smsmith    optind = 1;
11142512Smsmith    optreset = 1;
11238764Smsmith    typestr = NULL;
11353993Sdcs    if (argc == 1) {
11453993Sdcs	command_errmsg = "no filename specified";
115293835Ssmh	return (CMD_CRIT);
11653993Sdcs    }
11783321Speter    while ((ch = getopt(argc, argv, "kt:")) != -1) {
11838764Smsmith	switch(ch) {
11983321Speter	case 'k':
12083321Speter	    dokld = 1;
12183321Speter	    break;
12238764Smsmith	case 't':
12338764Smsmith	    typestr = optarg;
12438764Smsmith	    dofile = 1;
12538764Smsmith	    break;
12638764Smsmith	case '?':
12738764Smsmith	default:
12838764Smsmith	    /* getopt has already reported an error */
129293835Ssmh	    return (CMD_OK);
13038764Smsmith	}
13138764Smsmith    }
13238764Smsmith    argv += (optind - 1);
13338764Smsmith    argc -= (optind - 1);
13438764Smsmith
13538764Smsmith    /*
13638764Smsmith     * Request to load a raw file?
13738764Smsmith     */
13838764Smsmith    if (dofile) {
13977971Sjesper	if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) {
14038764Smsmith	    command_errmsg = "invalid load type";
141293835Ssmh	    return (CMD_CRIT);
14238764Smsmith	}
143286234Strasz
144286234Strasz	fp = file_findfile(argv[1], typestr);
145286234Strasz	if (fp) {
146329010Skevans		snprintf(command_errbuf, sizeof(command_errbuf),
147329010Skevans		    "warning: file '%s' already loaded", argv[1]);
148293835Ssmh		return (CMD_WARN);
149286234Strasz	}
150286234Strasz
151293835Ssmh	if (file_loadraw(argv[1], typestr, 1) != NULL)
152293835Ssmh		return (CMD_OK);
153293835Ssmh
154293835Ssmh	/* Failing to load mfs_root is never going to end well! */
155293835Ssmh	if (strcmp("mfs_root", typestr) == 0)
156293835Ssmh		return (CMD_FATAL);
157293835Ssmh
158293835Ssmh	return (CMD_ERROR);
15938764Smsmith    }
16038764Smsmith    /*
16183321Speter     * Do we have explicit KLD load ?
16283321Speter     */
16383321Speter    if (dokld || file_havepath(argv[1])) {
16483321Speter	error = mod_loadkld(argv[1], argc - 2, argv + 2);
165293835Ssmh	if (error == EEXIST) {
166329010Skevans	    snprintf(command_errbuf, sizeof(command_errbuf),
167329010Skevans		"warning: KLD '%s' already loaded", argv[1]);
168293835Ssmh	    return (CMD_WARN);
169293835Ssmh	}
170293835Ssmh
171293835Ssmh	return (error == 0 ? CMD_OK : CMD_CRIT);
17283321Speter    }
17383321Speter    /*
17438764Smsmith     * Looks like a request for a module.
17538764Smsmith     */
17683321Speter    error = mod_load(argv[1], NULL, argc - 2, argv + 2);
177293835Ssmh    if (error == EEXIST) {
178329010Skevans	snprintf(command_errbuf, sizeof(command_errbuf),
179329010Skevans	    "warning: module '%s' already loaded", argv[1]);
180293835Ssmh	return (CMD_WARN);
181293835Ssmh    }
182293835Ssmh
183293835Ssmh    return (error == 0 ? CMD_OK : CMD_CRIT);
18438465Smsmith}
18538465Smsmith
186321232Sngie#ifdef LOADER_GELI_SUPPORT
187188666SthompsaCOMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli);
188188666Sthompsa
189188666Sthompsastatic int
190188666Sthompsacommand_load_geli(int argc, char *argv[])
191188666Sthompsa{
192188666Sthompsa    char	typestr[80];
193188666Sthompsa    char	*cp;
194188666Sthompsa    int		ch, num;
195188666Sthompsa
196188666Sthompsa    if (argc < 3) {
197188666Sthompsa	    command_errmsg = "usage is [-n key#] <prov> <file>";
198188666Sthompsa	    return(CMD_ERROR);
199188666Sthompsa    }
200188666Sthompsa
201188666Sthompsa    num = 0;
202188666Sthompsa    optind = 1;
203188666Sthompsa    optreset = 1;
204188666Sthompsa    while ((ch = getopt(argc, argv, "n:")) != -1) {
205188666Sthompsa	switch(ch) {
206188666Sthompsa	case 'n':
207188666Sthompsa	    num = strtol(optarg, &cp, 0);
208188666Sthompsa	    if (cp == optarg) {
209329010Skevans		    snprintf(command_errbuf, sizeof(command_errbuf),
210329010Skevans			"bad key index '%s'", optarg);
211188666Sthompsa		    return(CMD_ERROR);
212188666Sthompsa	    }
213188666Sthompsa	    break;
214188666Sthompsa	case '?':
215188666Sthompsa	default:
216188666Sthompsa	    /* getopt has already reported an error */
217188666Sthompsa	    return(CMD_OK);
218188666Sthompsa	}
219188666Sthompsa    }
220188666Sthompsa    argv += (optind - 1);
221188666Sthompsa    argc -= (optind - 1);
222188666Sthompsa    sprintf(typestr, "%s:geli_keyfile%d", argv[1], num);
223277215Sroyger    return (file_loadraw(argv[2], typestr, 1) ? CMD_OK : CMD_ERROR);
224188666Sthompsa}
225321232Sngie#endif
226188666Sthompsa
227269618Smarcelvoid
228269618Smarcelunload(void)
229269618Smarcel{
230269618Smarcel    struct preloaded_file *fp;
23138712Smsmith
23259854Sbp    while (preloaded_files != NULL) {
23359854Sbp	fp = preloaded_files;
23459854Sbp	preloaded_files = preloaded_files->f_next;
23559854Sbp	file_discard(fp);
23638712Smsmith    }
23738712Smsmith    loadaddr = 0;
23865613Sdcs    unsetenv("kernelname");
239269618Smarcel}
240269618Smarcel
241269618SmarcelCOMMAND_SET(unload, "unload", "unload all modules", command_unload);
242269618Smarcel
243269618Smarcelstatic int
244269618Smarcelcommand_unload(int argc, char *argv[])
245269618Smarcel{
246269618Smarcel    unload();
24738712Smsmith    return(CMD_OK);
24838712Smsmith}
24938712Smsmith
25038465SmsmithCOMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
25138465Smsmith
25238465Smsmithstatic int
25338465Smsmithcommand_lsmod(int argc, char *argv[])
25438465Smsmith{
25559854Sbp    struct preloaded_file	*fp;
25659854Sbp    struct kernel_module	*mp;
25759854Sbp    struct file_metadata	*md;
25838465Smsmith    char			lbuf[80];
259328889Skevans    int				ch, verbose, ret = 0;
26038764Smsmith
26138764Smsmith    verbose = 0;
26238764Smsmith    optind = 1;
26342512Smsmith    optreset = 1;
26438764Smsmith    while ((ch = getopt(argc, argv, "v")) != -1) {
26538764Smsmith	switch(ch) {
26638764Smsmith	case 'v':
26738764Smsmith	    verbose = 1;
26838764Smsmith	    break;
26938764Smsmith	case '?':
27038764Smsmith	default:
27138764Smsmith	    /* getopt has already reported an error */
27238764Smsmith	    return(CMD_OK);
27338764Smsmith	}
27438764Smsmith    }
27538764Smsmith
27638465Smsmith    pager_open();
27759854Sbp    for (fp = preloaded_files; fp; fp = fp->f_next) {
278328889Skevans	snprintf(lbuf, sizeof(lbuf), " %p: ", (void *) fp->f_addr);
27938465Smsmith	pager_output(lbuf);
280269616Smarcel	pager_output(fp->f_name);
281328889Skevans	snprintf(lbuf, sizeof(lbuf), " (%s, 0x%lx)\n", fp->f_type,
282328889Skevans	    (long)fp->f_size);
283328889Skevans	if (pager_output(lbuf))
284328889Skevans	    break;
28559854Sbp	if (fp->f_args != NULL) {
28638465Smsmith	    pager_output("    args: ");
28759854Sbp	    pager_output(fp->f_args);
288300117Simp	    if (pager_output("\n"))
289300117Simp		    break;
29038465Smsmith	}
29159854Sbp	if (fp->f_modules) {
29259854Sbp	    pager_output("  modules: ");
29359854Sbp	    for (mp = fp->f_modules; mp; mp = mp->m_next) {
294328889Skevans		snprintf(lbuf, sizeof(lbuf), "%s.%d ", mp->m_name,
295328889Skevans		    mp->m_version);
29659854Sbp		pager_output(lbuf);
29759854Sbp	    }
298300117Simp	    if (pager_output("\n"))
299300117Simp		    break;
300300117Simp	    	}
30159854Sbp	if (verbose) {
30238764Smsmith	    /* XXX could add some formatting smarts here to display some better */
30359854Sbp	    for (md = fp->f_metadata; md != NULL; md = md->md_next) {
304328889Skevans		snprintf(lbuf, sizeof(lbuf), "      0x%04x, 0x%lx\n",
305328889Skevans		    md->md_type, (long) md->md_size);
306300117Simp		if (pager_output(lbuf))
307300117Simp			break;
30838764Smsmith	    }
30959854Sbp	}
310328889Skevans	if (ret)
311328889Skevans	    break;
31238465Smsmith    }
31338465Smsmith    pager_close();
31438465Smsmith    return(CMD_OK);
31538465Smsmith}
31638465Smsmith
31738764Smsmith/*
31859854Sbp * File level interface, functions file_*
31938764Smsmith */
32038465Smsmithint
32159854Sbpfile_load(char *filename, vm_offset_t dest, struct preloaded_file **result)
32238465Smsmith{
323241069Sae    static int last_file_format = 0;
32459854Sbp    struct preloaded_file *fp;
32559854Sbp    int error;
32659854Sbp    int i;
32738764Smsmith
328220311Smarcel    if (archsw.arch_loadaddr != NULL)
329220311Smarcel	dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest);
330220311Smarcel
33159854Sbp    error = EFTYPE;
332241069Sae    for (i = last_file_format, fp = NULL;
333241069Sae	file_formats[i] && fp == NULL; i++) {
334220332Smarcel	error = (file_formats[i]->l_load)(filename, dest, &fp);
33559854Sbp	if (error == 0) {
336241069Sae	    fp->f_loader = last_file_format = i; /* remember the loader */
33759854Sbp	    *result = fp;
33859854Sbp	    break;
339241069Sae	} else if (last_file_format == i && i != 0) {
340241070Sae	    /* Restart from the beginning */
341249719Sae	    i = -1;
342249719Sae	    last_file_format = 0;
343241070Sae	    fp = NULL;
344241070Sae	    continue;
34559854Sbp	}
34659854Sbp	if (error == EFTYPE)
34759854Sbp	    continue;		/* Unknown to this handler? */
34859854Sbp	if (error) {
349329010Skevans	    snprintf(command_errbuf, sizeof(command_errbuf),
350329010Skevans		"can't load file '%s': %s", filename, strerror(error));
35159854Sbp	    break;
35259854Sbp	}
35359854Sbp    }
35459854Sbp    return (error);
35559854Sbp}
35638465Smsmith
35759854Sbpstatic int
358207854Simpfile_load_dependencies(struct preloaded_file *base_file)
359207854Simp{
36059854Sbp    struct file_metadata *md;
36159854Sbp    struct preloaded_file *fp;
36283321Speter    struct mod_depend *verinfo;
36383321Speter    struct kernel_module *mp;
36459854Sbp    char *dmodname;
36559854Sbp    int error;
36659854Sbp
36759854Sbp    md = file_findmetadata(base_file, MODINFOMD_DEPLIST);
36859854Sbp    if (md == NULL)
36959854Sbp	return (0);
37059854Sbp    error = 0;
37159854Sbp    do {
37283321Speter	verinfo = (struct mod_depend*)md->md_data;
37383321Speter	dmodname = (char *)(verinfo + 1);
37483321Speter	if (file_findmodule(NULL, dmodname, verinfo) == NULL) {
37559854Sbp	    printf("loading required module '%s'\n", dmodname);
37683321Speter	    error = mod_load(dmodname, verinfo, 0, NULL);
37759854Sbp	    if (error)
37859854Sbp		break;
37983321Speter	    /*
38083321Speter	     * If module loaded via kld name which isn't listed
38183321Speter	     * in the linker.hints file, we should check if it have
38283321Speter	     * required version.
38383321Speter	     */
38483321Speter	    mp = file_findmodule(NULL, dmodname, verinfo);
38583321Speter	    if (mp == NULL) {
386329010Skevans		snprintf(command_errbuf, sizeof(command_errbuf),
387329010Skevans		    "module '%s' exists but with wrong version", dmodname);
38883321Speter		error = ENOENT;
38983321Speter		break;
39083321Speter	    }
39159854Sbp	}
39259854Sbp	md = metadata_next(md, MODINFOMD_DEPLIST);
39359854Sbp    } while (md);
39457468Sbp    if (!error)
39557468Sbp	return (0);
39657468Sbp    /* Load failed; discard everything */
39759854Sbp    while (base_file != NULL) {
39859854Sbp        fp = base_file;
39959854Sbp        base_file = base_file->f_next;
40059854Sbp        file_discard(fp);
40138764Smsmith    }
40257468Sbp    return (error);
40338764Smsmith}
404246953Skientzle
40538764Smsmith/*
406294058Ssmh * We've been asked to load (fname) as (type), so just suck it in,
40738764Smsmith * no arguments or anything.
40838764Smsmith */
409262345Sianstruct preloaded_file *
410294058Ssmhfile_loadraw(const char *fname, char *type, int insert)
41138764Smsmith{
41259854Sbp    struct preloaded_file	*fp;
413294058Ssmh    char			*name;
41438764Smsmith    int				fd, got;
41538764Smsmith    vm_offset_t			laddr;
41638764Smsmith
41738764Smsmith    /* We can't load first */
41859854Sbp    if ((file_findfile(NULL, NULL)) == NULL) {
41938764Smsmith	command_errmsg = "can't load file before kernel";
420262345Sian	return(NULL);
42138764Smsmith    }
42238764Smsmith
42339178Smsmith    /* locate the file on the load path */
424294058Ssmh    name = file_search(fname, NULL);
425294058Ssmh    if (name == NULL) {
426329010Skevans	snprintf(command_errbuf, sizeof(command_errbuf),
427329010Skevans	    "can't find '%s'", fname);
428262345Sian	return(NULL);
42939178Smsmith    }
430220311Smarcel
43138764Smsmith    if ((fd = open(name, O_RDONLY)) < 0) {
432329010Skevans	snprintf(command_errbuf, sizeof(command_errbuf),
433329010Skevans	    "can't open '%s': %s", name, strerror(errno));
43444570Sdcs	free(name);
435262345Sian	return(NULL);
43638764Smsmith    }
43738764Smsmith
438220311Smarcel    if (archsw.arch_loadaddr != NULL)
439220311Smarcel	loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr);
440201340Snyan
441269614Smarcel    printf("%s ", name);
442269614Smarcel
44338764Smsmith    laddr = loadaddr;
44438764Smsmith    for (;;) {
44538764Smsmith	/* read in 4k chunks; size is not really important */
44638764Smsmith	got = archsw.arch_readin(fd, laddr, 4096);
44738764Smsmith	if (got == 0)				/* end of file */
44838764Smsmith	    break;
44938764Smsmith	if (got < 0) {				/* error */
450329010Skevans	    snprintf(command_errbuf, sizeof(command_errbuf),
451329010Skevans		"error reading '%s': %s", name, strerror(errno));
45244210Sdcs	    free(name);
45357269Smsmith	    close(fd);
454262345Sian	    return(NULL);
45538764Smsmith	}
45638764Smsmith	laddr += got;
45738764Smsmith    }
458269614Smarcel
459269614Smarcel    printf("size=%#jx\n", (uintmax_t)(laddr - loadaddr));
460269614Smarcel
46138764Smsmith    /* Looks OK so far; create & populate control structure */
46259854Sbp    fp = file_alloc();
463240342Savg    fp->f_name = strdup(name);
46459854Sbp    fp->f_type = strdup(type);
46559854Sbp    fp->f_args = NULL;
46659854Sbp    fp->f_metadata = NULL;
46759854Sbp    fp->f_loader = -1;
46859854Sbp    fp->f_addr = loadaddr;
46959854Sbp    fp->f_size = laddr - loadaddr;
47038764Smsmith
47138764Smsmith    /* recognise space consumption */
47238764Smsmith    loadaddr = laddr;
47338764Smsmith
47459854Sbp    /* Add to the list of loaded files */
475277215Sroyger    if (insert != 0)
476277215Sroyger    	file_insert_tail(fp);
47757269Smsmith    close(fd);
478262345Sian    return(fp);
47938764Smsmith}
48038764Smsmith
48138764Smsmith/*
48259854Sbp * Load the module (name), pass it (argc),(argv), add container file
48359854Sbp * to the list of loaded files.
48459854Sbp * If module is already loaded just assign new argc/argv.
48538764Smsmith */
48659854Sbpint
48783321Spetermod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[])
48838764Smsmith{
48959854Sbp    struct kernel_module	*mp;
49059854Sbp    int				err;
49159854Sbp    char			*filename;
49238764Smsmith
49383321Speter    if (file_havepath(modname)) {
49483321Speter	printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname);
49583321Speter	return (mod_loadkld(modname, argc, argv));
49683321Speter    }
49757468Sbp    /* see if module is already loaded */
49883321Speter    mp = file_findmodule(NULL, modname, verinfo);
49957468Sbp    if (mp) {
50059854Sbp#ifdef moduleargs
50159854Sbp	if (mp->m_args)
50259854Sbp	    free(mp->m_args);
50359854Sbp	mp->m_args = unargv(argc, argv);
50459854Sbp#endif
505329010Skevans	snprintf(command_errbuf, sizeof(command_errbuf),
506329010Skevans	    "warning: module '%s' already loaded", mp->m_name);
50759854Sbp	return (0);
50857468Sbp    }
50959854Sbp    /* locate file with the module on the search path */
51083321Speter    filename = mod_searchmodule(modname, verinfo);
51159854Sbp    if (filename == NULL) {
512329010Skevans	snprintf(command_errbuf, sizeof(command_errbuf),
513329010Skevans	    "can't find '%s'", modname);
51459854Sbp	return (ENOENT);
51559854Sbp    }
51683321Speter    err = mod_loadkld(filename, argc, argv);
51783321Speter    return (err);
51883321Speter}
51983321Speter
52083321Speter/*
52183321Speter * Load specified KLD. If path is omitted, then try to locate it via
52283321Speter * search path.
52383321Speter */
52483321Speterint
52583321Spetermod_loadkld(const char *kldname, int argc, char *argv[])
52683321Speter{
52783321Speter    struct preloaded_file	*fp, *last_file;
52883321Speter    int				err;
52983321Speter    char			*filename;
53083321Speter
53183321Speter    /*
53283321Speter     * Get fully qualified KLD name
53383321Speter     */
53483321Speter    filename = file_search(kldname, kld_ext_list);
53583321Speter    if (filename == NULL) {
536329010Skevans	snprintf(command_errbuf, sizeof(command_errbuf),
537329010Skevans	    "can't find '%s'", kldname);
53883321Speter	return (ENOENT);
53983321Speter    }
540299499Spfg    /*
54183321Speter     * Check if KLD already loaded
54283321Speter     */
54383321Speter    fp = file_findfile(filename, NULL);
54483321Speter    if (fp) {
545329010Skevans	snprintf(command_errbuf, sizeof(command_errbuf),
546329010Skevans	    "warning: KLD '%s' already loaded", filename);
54783321Speter	free(filename);
54883321Speter	return (0);
54983321Speter    }
550299499Spfg    for (last_file = preloaded_files;
55159854Sbp	 last_file != NULL && last_file->f_next != NULL;
55259854Sbp	 last_file = last_file->f_next)
55359854Sbp	;
55457468Sbp
55559854Sbp    do {
55659854Sbp	err = file_load(filename, loadaddr, &fp);
55759854Sbp	if (err)
55838764Smsmith	    break;
55959854Sbp	fp->f_args = unargv(argc, argv);
56059854Sbp	loadaddr = fp->f_addr + fp->f_size;
56159854Sbp	file_insert_tail(fp);		/* Add to the list of loaded files */
56286469Siedowse	if (file_load_dependencies(fp) != 0) {
56359854Sbp	    err = ENOENT;
56459854Sbp	    last_file->f_next = NULL;
56559854Sbp	    loadaddr = last_file->f_addr + last_file->f_size;
56659854Sbp	    fp = NULL;
56759854Sbp	    break;
56859854Sbp	}
56959854Sbp    } while(0);
570329010Skevans    if (err == EFTYPE) {
571329010Skevans	snprintf(command_errbuf, sizeof(command_errbuf),
572329010Skevans	    "don't know how to load module '%s'", filename);
573329010Skevans    }
57459854Sbp    if (err && fp)
57559854Sbp	file_discard(fp);
57659854Sbp    free(filename);
57757468Sbp    return (err);
57838764Smsmith}
57938764Smsmith
58059854Sbp/*
58159854Sbp * Find a file matching (name) and (type).
58259854Sbp * NULL may be passed as a wildcard to either.
58359854Sbp */
58459854Sbpstruct preloaded_file *
585277215Sroygerfile_findfile(const char *name, const char *type)
58638764Smsmith{
58759854Sbp    struct preloaded_file *fp;
58838764Smsmith
58959854Sbp    for (fp = preloaded_files; fp != NULL; fp = fp->f_next) {
59059854Sbp	if (((name == NULL) || !strcmp(name, fp->f_name)) &&
59159854Sbp	    ((type == NULL) || !strcmp(type, fp->f_type)))
59259854Sbp	    break;
59359854Sbp    }
59459854Sbp    return (fp);
59538465Smsmith}
59638465Smsmith
59738764Smsmith/*
59859854Sbp * Find a module matching (name) inside of given file.
59959854Sbp * NULL may be passed as a wildcard.
60038764Smsmith */
60159854Sbpstruct kernel_module *
60283321Speterfile_findmodule(struct preloaded_file *fp, char *modname,
60383321Speter	struct mod_depend *verinfo)
60438465Smsmith{
60583321Speter    struct kernel_module *mp, *best;
60683321Speter    int bestver, mver;
60759854Sbp
60859854Sbp    if (fp == NULL) {
60959854Sbp	for (fp = preloaded_files; fp; fp = fp->f_next) {
61083321Speter	    mp = file_findmodule(fp, modname, verinfo);
611299499Spfg	    if (mp)
61283321Speter		return (mp);
61359854Sbp	}
61459854Sbp	return (NULL);
61538465Smsmith    }
61683321Speter    best = NULL;
61783321Speter    bestver = 0;
61859854Sbp    for (mp = fp->f_modules; mp; mp = mp->m_next) {
61983321Speter        if (strcmp(modname, mp->m_name) == 0) {
62083321Speter	    if (verinfo == NULL)
62183321Speter		return (mp);
62283321Speter	    mver = mp->m_version;
62383321Speter	    if (mver == verinfo->md_ver_preferred)
62483321Speter		return (mp);
625299499Spfg	    if (mver >= verinfo->md_ver_minimum &&
62683321Speter		mver <= verinfo->md_ver_maximum &&
62783321Speter		mver > bestver) {
62883321Speter		best = mp;
62983321Speter		bestver = mver;
63083321Speter	    }
63183321Speter	}
63259854Sbp    }
63383321Speter    return (best);
63438465Smsmith}
63538764Smsmith/*
63638764Smsmith * Make a copy of (size) bytes of data from (p), and associate them as
63738764Smsmith * metadata of (type) to the module (mp).
63838764Smsmith */
63938712Smsmithvoid
64059854Sbpfile_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p)
64138712Smsmith{
64259854Sbp    struct file_metadata	*md;
64338712Smsmith
64464187Sjhb    md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size);
64538712Smsmith    md->md_size = size;
64638712Smsmith    md->md_type = type;
64738712Smsmith    bcopy(p, md->md_data, size);
64859854Sbp    md->md_next = fp->f_metadata;
64959854Sbp    fp->f_metadata = md;
65038712Smsmith}
65138712Smsmith
65238764Smsmith/*
65359854Sbp * Find a metadata object of (type) associated with the file (fp)
65438764Smsmith */
65559854Sbpstruct file_metadata *
65659854Sbpfile_findmetadata(struct preloaded_file *fp, int type)
65738712Smsmith{
65859854Sbp    struct file_metadata *md;
65938712Smsmith
66059854Sbp    for (md = fp->f_metadata; md != NULL; md = md->md_next)
66138712Smsmith	if (md->md_type == type)
66238712Smsmith	    break;
66338712Smsmith    return(md);
66438712Smsmith}
66538764Smsmith
666318019Sroyger/*
667318019Sroyger * Remove all metadata from the file.
668318019Sroyger */
669318019Sroygervoid
670318019Sroygerfile_removemetadata(struct preloaded_file *fp)
671318019Sroyger{
672318019Sroyger    struct file_metadata *md, *next;
673318019Sroyger
674318019Sroyger    for (md = fp->f_metadata; md != NULL; md = next)
675318019Sroyger    {
676318019Sroyger	next = md->md_next;
677318019Sroyger	free(md);
678318019Sroyger    }
679318019Sroyger    fp->f_metadata = NULL;
680318019Sroyger}
681318019Sroyger
68259854Sbpstruct file_metadata *
68359854Sbpmetadata_next(struct file_metadata *md, int type)
68457468Sbp{
68557468Sbp    if (md == NULL)
68657468Sbp	return (NULL);
68757468Sbp    while((md = md->md_next) != NULL)
68857468Sbp	if (md->md_type == type)
68957468Sbp	    break;
69057468Sbp    return (md);
69157468Sbp}
69257468Sbp
69383321Speterstatic char *emptyextlist[] = { "", NULL };
69483321Speter
69538764Smsmith/*
69683321Speter * Check if the given file is in place and return full path to it.
69783321Speter */
69883321Speterstatic char *
69983321Speterfile_lookup(const char *path, const char *name, int namelen, char **extlist)
70083321Speter{
70183321Speter    struct stat	st;
70283321Speter    char	*result, *cp, **cpp;
70383321Speter    int		pathlen, extlen, len;
70483321Speter
70583321Speter    pathlen = strlen(path);
70683321Speter    extlen = 0;
70783321Speter    if (extlist == NULL)
70883321Speter	extlist = emptyextlist;
70983321Speter    for (cpp = extlist; *cpp; cpp++) {
71083321Speter	len = strlen(*cpp);
71183321Speter	if (len > extlen)
71283321Speter	    extlen = len;
71383321Speter    }
71483321Speter    result = malloc(pathlen + namelen + extlen + 2);
71583321Speter    if (result == NULL)
71683321Speter	return (NULL);
71783321Speter    bcopy(path, result, pathlen);
71883321Speter    if (pathlen > 0 && result[pathlen - 1] != '/')
71983321Speter	result[pathlen++] = '/';
72083321Speter    cp = result + pathlen;
72183321Speter    bcopy(name, cp, namelen);
72283321Speter    cp += namelen;
72383321Speter    for (cpp = extlist; *cpp; cpp++) {
72483321Speter	strcpy(cp, *cpp);
72583321Speter	if (stat(result, &st) == 0 && S_ISREG(st.st_mode))
72683321Speter	    return result;
72783321Speter    }
72883321Speter    free(result);
72983321Speter    return NULL;
73083321Speter}
73183321Speter
73283321Speter/*
73383321Speter * Check if file name have any qualifiers
73483321Speter */
73583321Speterstatic int
73683321Speterfile_havepath(const char *name)
73783321Speter{
73883321Speter    const char		*cp;
73983321Speter
74083321Speter    archsw.arch_getdev(NULL, name, &cp);
74183321Speter    return (cp != name || strchr(name, '/') != NULL);
74283321Speter}
74383321Speter
74483321Speter/*
74539178Smsmith * Attempt to find the file (name) on the module searchpath.
74638764Smsmith * If (name) is qualified in any way, we simply check it and
74738764Smsmith * return it or NULL.  If it is not qualified, then we attempt
74838764Smsmith * to construct a path using entries in the environment variable
74938764Smsmith * module_path.
75038764Smsmith *
75138764Smsmith * The path we return a pointer to need never be freed, as we manage
75238764Smsmith * it internally.
75338764Smsmith */
75438764Smsmithstatic char *
75583321Speterfile_search(const char *name, char **extlist)
75638764Smsmith{
75783321Speter    struct moduledir	*mdp;
75883321Speter    struct stat		sb;
75944570Sdcs    char		*result;
76083321Speter    int			namelen;
76138764Smsmith
76238764Smsmith    /* Don't look for nothing */
76344210Sdcs    if (name == NULL)
76483321Speter	return(NULL);
76538764Smsmith
76644210Sdcs    if (*name == 0)
76744210Sdcs	return(strdup(name));
76844210Sdcs
76983321Speter    if (file_havepath(name)) {
77038764Smsmith	/* Qualified, so just see if it exists */
77138764Smsmith	if (stat(name, &sb) == 0)
77244210Sdcs	    return(strdup(name));
77338764Smsmith	return(NULL);
77438764Smsmith    }
77583321Speter    moduledir_rebuild();
77644570Sdcs    result = NULL;
77783321Speter    namelen = strlen(name);
77883321Speter    STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
77983321Speter	result = file_lookup(mdp->d_path, name, namelen, extlist);
78083321Speter	if (result)
78138764Smsmith	    break;
78238764Smsmith    }
78338764Smsmith    return(result);
78438764Smsmith}
78538764Smsmith
78683321Speter#define	INT_ALIGN(base, ptr)	ptr = \
787298433Spfg	(base) + roundup2((ptr) - (base), sizeof(int))
78883321Speter
78983321Speterstatic char *
79083321Spetermod_search_hints(struct moduledir *mdp, const char *modname,
79183321Speter	struct mod_depend *verinfo)
79283321Speter{
79383321Speter    u_char	*cp, *recptr, *bufend, *best;
79483321Speter    char	*result;
79583321Speter    int		*intp, bestver, blen, clen, found, ival, modnamelen, reclen;
79683321Speter
79783321Speter    moduledir_readhints(mdp);
79883321Speter    modnamelen = strlen(modname);
79983321Speter    found = 0;
80083321Speter    result = NULL;
80183321Speter    bestver = 0;
80283321Speter    if (mdp->d_hints == NULL)
80383321Speter	goto bad;
80483321Speter    recptr = mdp->d_hints;
80583321Speter    bufend = recptr + mdp->d_hintsz;
80683321Speter    clen = blen = 0;
80783321Speter    best = cp = NULL;
80883321Speter    while (recptr < bufend && !found) {
80983321Speter	intp = (int*)recptr;
81083321Speter	reclen = *intp++;
81183321Speter	ival = *intp++;
812298230Sallanjude	cp = (u_char*)intp;
81383321Speter	switch (ival) {
81483321Speter	case MDT_VERSION:
81583321Speter	    clen = *cp++;
81683321Speter	    if (clen != modnamelen || bcmp(cp, modname, clen) != 0)
81783321Speter		break;
81883321Speter	    cp += clen;
81983321Speter	    INT_ALIGN(mdp->d_hints, cp);
82083321Speter	    ival = *(int*)cp;
82183321Speter	    cp += sizeof(int);
82283321Speter	    clen = *cp++;
82383321Speter	    if (verinfo == NULL || ival == verinfo->md_ver_preferred) {
82483321Speter		found = 1;
82583321Speter		break;
82683321Speter	    }
827299499Spfg	    if (ival >= verinfo->md_ver_minimum &&
82883321Speter		ival <= verinfo->md_ver_maximum &&
82983321Speter		ival > bestver) {
83083321Speter		bestver = ival;
83183321Speter		best = cp;
83283321Speter		blen = clen;
83383321Speter	    }
83483321Speter	    break;
83583321Speter	default:
83683321Speter	    break;
83783321Speter	}
83883321Speter	recptr += reclen + sizeof(int);
83983321Speter    }
84083321Speter    /*
84183321Speter     * Finally check if KLD is in the place
84283321Speter     */
84383321Speter    if (found)
844298230Sallanjude	result = file_lookup(mdp->d_path, (const char *)cp, clen, NULL);
84583321Speter    else if (best)
846298230Sallanjude	result = file_lookup(mdp->d_path, (const char *)best, blen, NULL);
84783321Speterbad:
84883321Speter    /*
84983321Speter     * If nothing found or hints is absent - fallback to the old way
85083321Speter     * by using "kldname[.ko]" as module name.
85183321Speter     */
85283321Speter    if (!found && !bestver && result == NULL)
85383321Speter	result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list);
85483321Speter    return result;
85583321Speter}
85683321Speter
85738764Smsmith/*
85839178Smsmith * Attempt to locate the file containing the module (name)
85939178Smsmith */
86039178Smsmithstatic char *
86183321Spetermod_searchmodule(char *name, struct mod_depend *verinfo)
86239178Smsmith{
86383321Speter    struct	moduledir *mdp;
86483321Speter    char	*result;
86539178Smsmith
86683321Speter    moduledir_rebuild();
86783321Speter    /*
86883321Speter     * Now we ready to lookup module in the given directories
86983321Speter     */
87083321Speter    result = NULL;
87183321Speter    STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
87283321Speter	result = mod_search_hints(mdp, name, verinfo);
87383321Speter	if (result)
87483321Speter	    break;
87583321Speter    }
87683321Speter
87739178Smsmith    return(result);
87839178Smsmith}
87939178Smsmith
88059854Sbpint
88183321Speterfile_addmodule(struct preloaded_file *fp, char *modname, int version,
88259854Sbp	struct kernel_module **newmp)
88359854Sbp{
88459854Sbp    struct kernel_module *mp;
88583321Speter    struct mod_depend mdepend;
88639178Smsmith
88783321Speter    bzero(&mdepend, sizeof(mdepend));
88883321Speter    mdepend.md_ver_preferred = version;
88983321Speter    mp = file_findmodule(fp, modname, &mdepend);
89059854Sbp    if (mp)
89159854Sbp	return (EEXIST);
89259854Sbp    mp = malloc(sizeof(struct kernel_module));
89359854Sbp    if (mp == NULL)
89459854Sbp	return (ENOMEM);
89559854Sbp    bzero(mp, sizeof(struct kernel_module));
89659854Sbp    mp->m_name = strdup(modname);
89783321Speter    mp->m_version = version;
89859854Sbp    mp->m_fp = fp;
89959854Sbp    mp->m_next = fp->f_modules;
90059854Sbp    fp->f_modules = mp;
90159854Sbp    if (newmp)
90259854Sbp	*newmp = mp;
90359854Sbp    return (0);
90459854Sbp}
90559854Sbp
90639178Smsmith/*
90759854Sbp * Throw a file away
90838764Smsmith */
90938764Smsmithvoid
91059854Sbpfile_discard(struct preloaded_file *fp)
91138764Smsmith{
91259854Sbp    struct file_metadata	*md, *md1;
91359854Sbp    struct kernel_module	*mp, *mp1;
91459854Sbp    if (fp == NULL)
91559854Sbp	return;
91659854Sbp    md = fp->f_metadata;
91759854Sbp    while (md) {
91859854Sbp	md1 = md;
91959854Sbp	md = md->md_next;
92059854Sbp	free(md1);
92159854Sbp    }
92259854Sbp    mp = fp->f_modules;
92359854Sbp    while (mp) {
92459854Sbp	if (mp->m_name)
92539178Smsmith	    free(mp->m_name);
92659854Sbp	mp1 = mp;
92759854Sbp	mp = mp->m_next;
92859854Sbp	free(mp1);
929299499Spfg    }
93059854Sbp    if (fp->f_name != NULL)
93159854Sbp	free(fp->f_name);
93259854Sbp    if (fp->f_type != NULL)
93359854Sbp        free(fp->f_type);
93459854Sbp    if (fp->f_args != NULL)
93559854Sbp        free(fp->f_args);
93659854Sbp    free(fp);
93738764Smsmith}
93838764Smsmith
93938764Smsmith/*
94059854Sbp * Allocate a new file; must be used instead of malloc()
94139178Smsmith * to ensure safe initialisation.
94239178Smsmith */
94359854Sbpstruct preloaded_file *
94459854Sbpfile_alloc(void)
94539178Smsmith{
94659854Sbp    struct preloaded_file	*fp;
947299499Spfg
94859854Sbp    if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) {
94959854Sbp	bzero(fp, sizeof(struct preloaded_file));
95039178Smsmith    }
95159854Sbp    return (fp);
95239178Smsmith}
95339178Smsmith
95439178Smsmith/*
95538764Smsmith * Add a module to the chain
95638764Smsmith */
95738764Smsmithstatic void
95859854Sbpfile_insert_tail(struct preloaded_file *fp)
95938764Smsmith{
96059854Sbp    struct preloaded_file	*cm;
96138764Smsmith
96259854Sbp    /* Append to list of loaded file */
96359854Sbp    fp->f_next = NULL;
96459854Sbp    if (preloaded_files == NULL) {
96559854Sbp	preloaded_files = fp;
96638764Smsmith    } else {
96759854Sbp	for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next)
96838764Smsmith	    ;
96959854Sbp	cm->f_next = fp;
97038764Smsmith    }
97138764Smsmith}
97259854Sbp
97383321Speterstatic char *
97483321Spetermoduledir_fullpath(struct moduledir *mdp, const char *fname)
97583321Speter{
97683321Speter    char *cp;
97783321Speter
97883321Speter    cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2);
97983321Speter    if (cp == NULL)
98083321Speter	return NULL;
98183321Speter    strcpy(cp, mdp->d_path);
98283321Speter    strcat(cp, "/");
98383321Speter    strcat(cp, fname);
98483321Speter    return (cp);
98583321Speter}
98683321Speter
98783321Speter/*
98883321Speter * Read linker.hints file into memory performing some sanity checks.
98983321Speter */
99083321Speterstatic void
99183321Spetermoduledir_readhints(struct moduledir *mdp)
99283321Speter{
99383321Speter    struct stat	st;
99483321Speter    char	*path;
99583321Speter    int		fd, size, version;
99683321Speter
99783321Speter    if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS))
99883321Speter	return;
99983321Speter    path = moduledir_fullpath(mdp, "linker.hints");
1000154257Smarius    if (stat(path, &st) != 0 ||
1001154257Smarius	st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) ||
1002275261Simp	st.st_size > LINKER_HINTS_MAX || (fd = open(path, O_RDONLY)) < 0) {
100383321Speter	free(path);
100483321Speter	mdp->d_flags |= MDIR_NOHINTS;
100583321Speter	return;
100683321Speter    }
100783321Speter    free(path);
100883321Speter    size = read(fd, &version, sizeof(version));
100983321Speter    if (size != sizeof(version) || version != LINKER_HINTS_VERSION)
101083321Speter	goto bad;
101183321Speter    size = st.st_size - size;
101283321Speter    mdp->d_hints = malloc(size);
101383321Speter    if (mdp->d_hints == NULL)
101483321Speter	goto bad;
101583321Speter    if (read(fd, mdp->d_hints, size) != size)
101683321Speter	goto bad;
101783321Speter    mdp->d_hintsz = size;
101883321Speter    close(fd);
101983321Speter    return;
102083321Speterbad:
102183321Speter    close(fd);
102283321Speter    if (mdp->d_hints) {
102383321Speter	free(mdp->d_hints);
102483321Speter	mdp->d_hints = NULL;
102583321Speter    }
102683321Speter    mdp->d_flags |= MDIR_NOHINTS;
102783321Speter    return;
102883321Speter}
102983321Speter
103083321Speter/*
103183321Speter * Extract directories from the ';' separated list, remove duplicates.
103283321Speter */
103383321Speterstatic void
103483321Spetermoduledir_rebuild(void)
103583321Speter{
103683321Speter    struct	moduledir *mdp, *mtmp;
103783321Speter    const char	*path, *cp, *ep;
1038293724Ssmh    size_t	cplen;
103983321Speter
104083321Speter    path = getenv("module_path");
104183321Speter    if (path == NULL)
104283321Speter	path = default_searchpath;
104383321Speter    /*
104483321Speter     * Rebuild list of module directories if it changed
104583321Speter     */
104683321Speter    STAILQ_FOREACH(mdp, &moduledir_list, d_link)
104783321Speter	mdp->d_flags |= MDIR_REMOVED;
104883321Speter
104983321Speter    for (ep = path; *ep != 0;  ep++) {
105083321Speter	cp = ep;
105183321Speter	for (; *ep != 0 && *ep != ';'; ep++)
105283321Speter	    ;
105383321Speter	/*
105483321Speter	 * Ignore trailing slashes
105583321Speter	 */
105683321Speter	for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--)
105783321Speter	    ;
105883321Speter	STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
105983321Speter	    if (strlen(mdp->d_path) != cplen ||	bcmp(cp, mdp->d_path, cplen) != 0)
106083321Speter		continue;
106183321Speter	    mdp->d_flags &= ~MDIR_REMOVED;
106283321Speter	    break;
106383321Speter	}
106483321Speter	if (mdp == NULL) {
106583321Speter	    mdp = malloc(sizeof(*mdp) + cplen + 1);
106683321Speter	    if (mdp == NULL)
106783321Speter		return;
106883321Speter	    mdp->d_path = (char*)(mdp + 1);
106983321Speter	    bcopy(cp, mdp->d_path, cplen);
107083321Speter	    mdp->d_path[cplen] = 0;
107183321Speter	    mdp->d_hints = NULL;
107283321Speter	    mdp->d_flags = 0;
107383321Speter	    STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
107483321Speter	}
107594419Speter	if (*ep == 0)
107694419Speter	    break;
107783321Speter    }
107883321Speter    /*
107983321Speter     * Delete unused directories if any
108083321Speter     */
108183321Speter    mdp = STAILQ_FIRST(&moduledir_list);
108283321Speter    while (mdp) {
108383321Speter	if ((mdp->d_flags & MDIR_REMOVED) == 0) {
108483321Speter	    mdp = STAILQ_NEXT(mdp, d_link);
108583321Speter	} else {
108683321Speter	    if (mdp->d_hints)
108783321Speter		free(mdp->d_hints);
108883321Speter	    mtmp = mdp;
108983321Speter	    mdp = STAILQ_NEXT(mdp, d_link);
109083321Speter	    STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link);
109183321Speter	    free(mtmp);
109283321Speter	}
109383321Speter    }
109483321Speter    return;
109583321Speter}
1096