xref: /openbsd/usr.sbin/amd/amd/mapc.c (revision bf0193d8)
1 /*	$OpenBSD: mapc.c,v 1.24 2021/10/21 10:55:56 deraadt Exp $	*/
2 
3 /*-
4  * Copyright (c) 1989 Jan-Simon Pendry
5  * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
6  * Copyright (c) 1989, 1993
7  *	The Regents of the University of California.  All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Jan-Simon Pendry at Imperial College, London.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 /*
38  * Mount map cache
39  */
40 
41 #include "am.h"
42 #include <regex.h>
43 
44 /*
45  * Hash table size
46  */
47 #define	NKVHASH	(1 << 2)		/* Power of two */
48 
49 /*
50  * Wildcard key
51  */
52 static char wildcard[] = "*";
53 
54 /*
55  * Map cache types
56  * default, none, incremental, all, regexp
57  * MAPC_RE implies MAPC_ALL and must be numerically
58  * greater.
59  */
60 #define	MAPC_DFLT	0x000
61 #define	MAPC_NONE	0x001
62 #define	MAPC_INC	0x002
63 #define	MAPC_ROOT	0x004
64 #define	MAPC_ALL	0x010
65 #define MAPC_RE		0x020
66 #define	MAPC_ISRE(m) ((m)->alloc == MAPC_RE)
67 #define	MAPC_CACHE_MASK	0x0ff
68 #define	MAPC_SYNC	0x100
69 
70 static struct opt_tab mapc_opt[] = {
71 	{ "all", MAPC_ALL },
72 	{ "default", MAPC_DFLT },
73 	{ "inc", MAPC_INC },
74 	{ "mapdefault", MAPC_DFLT },
75 	{ "none", MAPC_NONE },
76 	{ "re", MAPC_RE },
77 	{ "regexp", MAPC_RE },
78 	{ "sync", MAPC_SYNC },
79 	{ 0, 0 }
80 };
81 
82 /*
83  * Lookup recursion
84  */
85 #define	MREC_FULL	2
86 #define	MREC_PART	1
87 #define	MREC_NONE	0
88 
89 /*
90  * Cache map operations
91  */
92 typedef void	add_fn(mnt_map *, char *, char *);
93 typedef int	init_fn(char *, time_t *);
94 typedef int	search_fn(mnt_map *, char *, char *, char **, time_t *);
95 typedef int	reload_fn(mnt_map *, char *, add_fn *);
96 typedef int	mtime_fn(char *, time_t *);
97 
98 static void	mapc_sync(mnt_map *);
99 
100 /*
101  * Map type
102  */
103 typedef struct map_type map_type;
104 struct map_type {
105 	char *name;			/* Name of this map type */
106 	init_fn *init;			/* Initialisation */
107 	reload_fn *reload;		/* Reload or fill */
108 	search_fn *search;		/* Search for new entry */
109 	mtime_fn *mtime;		/* Find modify time */
110 	int def_alloc;			/* Default allocation mode */
111 };
112 
113 /*
114  * Key-value pair
115  */
116 typedef struct kv kv;
117 struct kv {
118 	kv *next;
119 	char *key;
120 	char *val;
121 };
122 
123 struct mnt_map {
124 	qelem hdr;
125 	int refc;			/* Reference count */
126 	short flags;			/* Allocation flags */
127 	short alloc;			/* Allocation mode */
128 	time_t modify;			/* Modify time of map */
129 	char *map_name;			/* Name of this map */
130 	char *wildcard;			/* Wildcard value */
131 	reload_fn *reload;		/* Function to be used for reloads */
132 	search_fn *search;		/* Function to be used for searching */
133 	mtime_fn *mtime;		/* Modify time function */
134 	kv *kvhash[NKVHASH];		/* Cached data */
135 };
136 
137 /*
138  * Map for root node
139  */
140 static mnt_map *root_map;
141 
142 /*
143  * List of known maps
144  */
145 extern qelem map_list_head;
146 qelem map_list_head = { &map_list_head, &map_list_head };
147 
148 /*
149  * Configuration
150  */
151 
152 /* ROOT MAP */
153 static int	root_init(char *, time_t *);
154 
155 /* FILE MAPS */
156 extern int	file_init(char *, time_t *);
157 extern int	file_reload(mnt_map *, char *, add_fn *);
158 extern int	file_search(mnt_map *, char *, char *, char **, time_t *);
159 extern int	file_mtime(char *, time_t *);
160 
161 /* Network Information Service (NIS) MAPS */
162 extern int	nis_init(char *, time_t *);
163 extern int	nis_reload(mnt_map *, char *, add_fn *);
164 extern int	nis_search(mnt_map *, char *, char *, char **, time_t *);
165 #define nis_mtime nis_init
166 
167 /* NDBM MAPS */
168 #ifdef HAS_NDBM_MAPS
169 extern int	ndbm_init(char *, time_t *);
170 extern int	ndbm_search(mnt_map *, char *, charo *, char **, time_t *);
171 #define ndbm_mtime ndbm_init
172 #endif /* HAS_NDBM_MAPS */
173 
174 /* PASSWD MAPS */
175 extern int	passwd_init(char *, time_t *);
176 extern int	passwd_search(mnt_map *, char *, char *, char **, time_t *);
177 
178 /* UNION MAPS */
179 extern int	union_init(char *, time_t *);
180 extern int	union_search(mnt_map *, char *, char *, char **, time_t *);
181 extern int	union_reload(mnt_map *, char *, add_fn *);
182 
183 /* ERROR MAP */
184 static int	error_init(char *, time_t *);
185 static int	error_reload(mnt_map *, char *, add_fn *);
186 static int	error_search(mnt_map *, char *, char *, char **, time_t *);
187 static int	error_mtime(char *, time_t *);
188 
189 static map_type maptypes[] = {
190 	{ "root", root_init, error_reload, error_search, error_mtime, MAPC_ROOT },
191 
192 	{ "passwd", passwd_init, error_reload, passwd_search, error_mtime, MAPC_INC },
193 
194 	{ "union", union_init, union_reload, union_search, error_mtime, MAPC_ALL },
195 
196 	{ "nis", nis_init, nis_reload, nis_search, nis_mtime, MAPC_INC },
197 
198 #ifdef HAS_NDBM_MAPS
199 	{ "ndbm", ndbm_init, error_reload, ndbm_search, ndbm_mtime, MAPC_INC },
200 #endif
201 
202 	{ "file", file_init, file_reload, file_search, file_mtime, MAPC_ALL },
203 
204 	{ "error", error_init, error_reload, error_search, error_mtime, MAPC_NONE },
205 };
206 
207 /*
208  * Hash function
209  */
210 static unsigned int
kvhash_of(char * key)211 kvhash_of(char *key)
212 {
213 	unsigned int i, j;
214 
215 	for (i = 0; (j = *key++); i += j)
216 		;
217 
218 	return i % NKVHASH;
219 }
220 
221 void
mapc_showtypes(FILE * fp)222 mapc_showtypes(FILE *fp)
223 {
224 	map_type *mt;
225 	char *sep = "";
226 
227 	for (mt = maptypes; mt < maptypes+sizeof(maptypes)/sizeof(maptypes[0]); mt++) {
228 		fprintf(fp, "%s%s", sep, mt->name);
229 		sep = ", ";
230 	}
231 }
232 
233 /*
234  * Add key and val to the map m.
235  * key and val are assumed to be safe copies
236  */
237 void
mapc_add_kv(mnt_map * m,char * key,char * val)238 mapc_add_kv(mnt_map *m, char *key, char *val)
239 {
240 	kv **h;
241 	kv *n;
242 	int hash = kvhash_of(key);
243 
244 #ifdef DEBUG
245 	dlog("add_kv: %s -> %s", key, val);
246 #endif
247 
248 	if (MAPC_ISRE(m)) {
249 		char keyb[PATH_MAX];
250 		regex_t *re;
251 		int err;
252 
253 		/*
254 		 * Make sure the string is bound to the start and end
255 		 */
256 		snprintf(keyb, sizeof(keyb), "^%s$", key);
257 		re = malloc(sizeof(*re));
258 		if (re == NULL) {
259 			plog(XLOG_USER, "error allocating RE \"%s\"", keyb);
260 			return;
261 		}
262 		err = regcomp(re, keyb, 0);
263 		if (err) {
264 			char errbuf[100];
265 
266 			regerror(err, re, errbuf, sizeof errbuf);
267 			free(re);
268 			plog(XLOG_USER, "error compiling RE \"%s\": %s",
269 			    keyb, errbuf);
270 			return;
271 		}
272 
273 		free(key);
274 		key = (char *)re;
275 	}
276 
277 	h = &m->kvhash[hash];
278 	n = ALLOC(kv);
279 	n->key = key;
280 	n->val = val;
281 	n->next = *h;
282 	*h = n;
283 }
284 
285 static void
mapc_repl_kv(mnt_map * m,char * key,char * val)286 mapc_repl_kv(mnt_map *m, char *key, char *val)
287 {
288 	kv *k;
289 
290 	/*
291 	 * Compute the hash table offset
292 	 */
293 	k = m->kvhash[kvhash_of(key)];
294 
295 	/*
296 	 * Scan the linked list for the key
297 	 */
298 	while (k && !FSTREQ(k->key, key))
299 		k = k->next;
300 
301 	if (k) {
302 		free(k->val);
303 		k->val = val;
304 	} else {
305 		mapc_add_kv(m, key, val);
306 	}
307 
308 }
309 
310 /*
311  * Search a map for a key.
312  * Calls map specific search routine.
313  * While map is out of date, keep re-syncing.
314  */
search_map(mnt_map * m,char * key,char ** valp)315 static int search_map(mnt_map *m, char *key, char **valp)
316 {
317 	int rc;
318 
319 	do {
320 		rc = (*m->search)(m, m->map_name, key, valp, &m->modify);
321 		if (rc < 0) {
322 			plog(XLOG_MAP, "Re-synchronizing cache for map %s", m->map_name);
323 			mapc_sync(m);
324 		}
325 	} while (rc < 0);
326 
327 	return rc;
328 }
329 
330 /*
331  * Do a wildcard lookup in the map and
332  * save the result.
333  */
334 static void
mapc_find_wildcard(mnt_map * m)335 mapc_find_wildcard(mnt_map *m)
336 {
337 	/*
338 	 * Attempt to find the wildcard entry
339 	 */
340 	int rc = search_map(m, wildcard, &m->wildcard);
341 
342 	if (rc != 0)
343 		m->wildcard = 0;
344 }
345 
346 /*
347  * Make a duplicate reference to an existing map
348  */
349 #define mapc_dup(m) ((m)->refc++, (m))
350 
351 /*
352  * Do a map reload
353  */
354 static int
mapc_reload_map(mnt_map * m)355 mapc_reload_map(mnt_map *m)
356 {
357 	int error;
358 #ifdef DEBUG
359 	dlog("calling map reload on %s", m->map_name);
360 #endif
361 	error = (*m->reload)(m, m->map_name, mapc_add_kv);
362 	if (error)
363 		return error;
364 	m->wildcard = 0;
365 #ifdef DEBUG
366 	dlog("calling mapc_search for wildcard");
367 #endif
368 	error = mapc_search(m, wildcard, &m->wildcard);
369 	if (error)
370 		m->wildcard = 0;
371 	return 0;
372 }
373 
374 /*
375  * Create a new map
376  */
377 static mnt_map *
mapc_create(char * map,char * opt)378 mapc_create(char *map, char *opt)
379 {
380 	mnt_map *m = ALLOC(mnt_map);
381 	map_type *mt;
382 	time_t modify;
383 	int alloc = 0;
384 
385 	(void) cmdoption(opt, mapc_opt, &alloc);
386 
387 	for (mt = maptypes; mt < maptypes+sizeof(maptypes)/sizeof(maptypes[0]); mt++)
388 		if ((*mt->init)(map, &modify) == 0)
389 			break;
390 	/* assert: mt in maptypes */
391 
392 	m->flags = alloc & ~MAPC_CACHE_MASK;
393 	alloc &= MAPC_CACHE_MASK;
394 
395 	if (alloc == MAPC_DFLT)
396 		alloc = mt->def_alloc;
397 	switch (alloc) {
398 	default:
399 		plog(XLOG_USER, "Ambiguous map cache type \"%s\"; using \"inc\"", opt);
400 		alloc = MAPC_INC;
401 		/* fallthrough... */
402 	case MAPC_NONE:
403 	case MAPC_INC:
404 	case MAPC_ROOT:
405 		break;
406 	case MAPC_ALL:
407 		/*
408 		 * If there is no support for reload and it was requested
409 		 * then back off to incremental instead.
410 		 */
411 		if (mt->reload == error_reload) {
412 			plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"all\"; using \"inc\"", mt->name);
413 			alloc = MAPC_INC;
414 		}
415 		break;
416 	case MAPC_RE:
417 		if (mt->reload == error_reload) {
418 			plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"re\"", mt->name);
419 			mt = &maptypes[sizeof(maptypes)/sizeof(maptypes[0]) - 1];
420 			/* assert: mt->name == "error" */
421 		}
422 		break;
423 	}
424 
425 #ifdef DEBUG
426 	dlog("Map for %s coming from maptype %s", map, mt->name);
427 #endif
428 
429 	m->alloc = alloc;
430 	m->reload = mt->reload;
431 	m->modify = modify;
432 	m->search = alloc >= MAPC_ALL ? error_search : mt->search;
433 	m->mtime = mt->mtime;
434 	bzero(m->kvhash, sizeof(m->kvhash));
435 	m->map_name = strdup(map);
436 	m->refc = 1;
437 	m->wildcard = 0;
438 
439 	/*
440 	 * synchronize cache with reality
441 	 */
442 	mapc_sync(m);
443 
444 	return m;
445 }
446 
447 /*
448  * Free the cached data in a map
449  */
450 static void
mapc_clear(mnt_map * m)451 mapc_clear(mnt_map *m)
452 {
453 	int i;
454 
455 	/*
456 	 * For each of the hash slots, chain
457 	 * along free'ing the data.
458 	 */
459 	for (i = 0; i < NKVHASH; i++) {
460 		kv *k = m->kvhash[i];
461 		while (k) {
462 			kv *n = k->next;
463 			free(k->key);
464 			free(k->val);
465 			free(k);
466 			k = n;
467 		}
468 	}
469 	/*
470 	 * Zero the hash slots
471 	 */
472 	bzero(m->kvhash, sizeof(m->kvhash));
473 	/*
474 	 * Free the wildcard if it exists
475 	 */
476 	if (m->wildcard) {
477 		free(m->wildcard);
478 		m->wildcard = 0;
479 	}
480 }
481 
482 /*
483  * Find a map, or create one if it does not exist
484  */
485 mnt_map *
mapc_find(char * map,char * opt)486 mapc_find(char *map, char *opt)
487 {
488 	mnt_map *m;
489 
490 	/*
491 	 * Search the list of known maps to see if
492 	 * it has already been loaded.  If it is found
493 	 * then return a duplicate reference to it.
494 	 * Otherwise make a new map as required and
495 	 * add it to the list of maps
496 	 */
497 	ITER(m, mnt_map, &map_list_head)
498 		if (STREQ(m->map_name, map))
499 			return mapc_dup(m);
500 
501 	m = mapc_create(map, opt);
502 	ins_que(&m->hdr, &map_list_head);
503 	return m;
504 }
505 
506 /*
507  * Free a map.
508  */
509 void
mapc_free(void * arg)510 mapc_free(void *arg)
511 {
512 	mnt_map *m = arg;
513 	/*
514 	 * Decrement the reference count.
515 	 * If the reference count hits zero
516 	 * then throw the map away.
517 	 */
518 	if (m && --m->refc == 0) {
519 		mapc_clear(m);
520 		free(m->map_name);
521 		rem_que(&m->hdr);
522 		free(m);
523 	}
524 }
525 
526 /*
527  * Search the map for the key.
528  * Put a safe copy in *pval or return
529  * an error code
530  */
531 int
mapc_meta_search(mnt_map * m,char * key,char ** pval,int recurse)532 mapc_meta_search(mnt_map *m, char *key, char **pval, int recurse)
533 {
534 	int error = 0;
535 	kv *k = 0;
536 
537 	/*
538 	 * Firewall
539 	 */
540 	if (!m) {
541 		plog(XLOG_ERROR, "Null map request for %s", key);
542 		return ENOENT;
543 	}
544 
545 	if (m->flags & MAPC_SYNC) {
546 		/*
547 		 * Get modify time...
548 		 */
549 		time_t t;
550 		error = (*m->mtime)(m->map_name, &t);
551 		if (error || t > m->modify) {
552 			m->modify = t;
553 			plog(XLOG_INFO, "Map %s is out of date", m->map_name);
554 			mapc_sync(m);
555 		}
556 	}
557 
558 	if (!MAPC_ISRE(m)) {
559 		/*
560 		 * Compute the hash table offset
561 		 */
562 		k = m->kvhash[kvhash_of(key)];
563 
564 		/*
565 		 * Scan the linked list for the key
566 		 */
567 		while (k && !FSTREQ(k->key, key)) k = k->next;
568 
569 	}
570 	else if (recurse == MREC_FULL) {
571 		/*
572 		 * Try for an RE match against the entire map.
573 		 * Note that this will be done in a "random"
574 		 * order.
575 		 */
576 
577 		int i;
578 
579 		for (i = 0; i < NKVHASH; i++) {
580 			k = m->kvhash[i];
581 			while (k) {
582 				if (regexec((regex_t *)k->key, key,
583 				    0, NULL, 0) == 0)
584 					break;
585 				k = k->next;
586 			}
587 			if (k)
588 				break;
589 		}
590 	}
591 
592 	/*
593 	 * If found then take a copy
594 	 */
595 	if (k) {
596 		if (k->val)
597 			*pval = strdup(k->val);
598 		else
599 			error = ENOENT;
600 	} else if (m->alloc >= MAPC_ALL) {
601 		/*
602 		 * If the entire map is cached then this
603 		 * key does not exist.
604 		 */
605 		error = ENOENT;
606 	} else {
607 		/*
608 		 * Otherwise search the map.  If we are
609 		 * in incremental mode then add the key
610 		 * to the cache.
611 		 */
612 		error = search_map(m, key, pval);
613 		if (!error && m->alloc == MAPC_INC)
614 			mapc_add_kv(m, strdup(key), strdup(*pval));
615 	}
616 
617 	/*
618 	 * If an error, and a wildcard exists,
619 	 * and the key is not internal then
620 	 * return a copy of the wildcard.
621 	 */
622 	if (error > 0) {
623 		if (recurse == MREC_FULL && !MAPC_ISRE(m)) {
624 			char wildname[PATH_MAX];
625 			char *subp;
626 			if (*key == '/')
627 				return error;
628 			/*
629 			 * Keep chopping sub-directories from the RHS
630 			 * and replacing with "/ *" and repeat the lookup.
631 			 * For example:
632 			 * "src/gnu/gcc" -> "src / gnu / *" -> "src / *"
633 			 */
634 			strlcpy(wildname, key, sizeof wildname);
635 			while (error && (subp = strrchr(wildname, '/'))) {
636 				strlcpy(subp, "/*", 3);
637 #ifdef DEBUG
638 				dlog("mapc recurses on %s", wildname);
639 #endif
640 				error = mapc_meta_search(m, wildname, pval, MREC_PART);
641 				if (error)
642 					*subp = 0;
643 			}
644 			if (error > 0 && m->wildcard) {
645 				*pval = strdup(m->wildcard);
646 				error = 0;
647 			}
648 		}
649 	}
650 
651 	return error;
652 }
653 
654 int
mapc_search(mnt_map * m,char * key,char ** pval)655 mapc_search(mnt_map *m, char *key, char **pval)
656 {
657 	return mapc_meta_search(m, key, pval, MREC_FULL);
658 }
659 
660 /*
661  * Get map cache in sync with physical representation
662  */
663 static void
mapc_sync(mnt_map * m)664 mapc_sync(mnt_map *m)
665 {
666 	if (m->alloc != MAPC_ROOT) {
667 		mapc_clear(m);
668 
669 		if (m->alloc >= MAPC_ALL)
670 			if (mapc_reload_map(m))
671 				m->alloc = MAPC_INC;
672 		/*
673 		 * Attempt to find the wildcard entry
674 		 */
675 		if (m->alloc < MAPC_ALL)
676 			mapc_find_wildcard(m);
677 	}
678 }
679 
680 /*
681  * Reload all the maps
682  * Called when Amd gets hit by a SIGHUP.
683  */
mapc_reload(void)684 void mapc_reload(void)
685 {
686 	mnt_map *m;
687 
688 	/*
689 	 * For all the maps,
690 	 * Throw away the existing information.
691 	 * Do a reload
692 	 * Find the wildcard
693 	 */
694 	ITER(m, mnt_map, &map_list_head)
695 		mapc_sync(m);
696 }
697 
698 /*
699  * Root map.
700  * The root map is used to bootstrap amd.
701  * All the require top-level mounts are added
702  * into the root map and then the map is iterated
703  * and a lookup is done on all the mount points.
704  * This causes the top level mounts to be automounted.
705  */
706 
707 static int
root_init(char * map,time_t * tp)708 root_init(char *map, time_t *tp)
709 {
710 	*tp = clocktime();
711 	return strcmp(map, ROOT_MAP) == 0 ? 0 : ENOENT;
712 }
713 
714 /*
715  * Add a new entry to the root map
716  *
717  * dir - directory (key)
718  * opts - mount options
719  * map - map name
720  */
721 void
root_newmap(char * dir,char * opts,char * map)722 root_newmap(char *dir, char *opts, char *map)
723 {
724 	char str[PATH_MAX];
725 
726 	/*
727 	 * First make sure we have a root map to talk about...
728 	 */
729 	if (!root_map)
730 		root_map = mapc_find(ROOT_MAP, "mapdefault");
731 
732 	/*
733 	 * Then add the entry...
734 	 */
735 	dir = strdup(dir);
736 	if (map)
737 		snprintf(str, sizeof(str), "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s",
738 			map, opts ? opts : "");
739 	else
740 		strlcpy(str, opts, sizeof str);
741 	mapc_repl_kv(root_map, dir, strdup(str));
742 }
743 
744 int
mapc_keyiter(mnt_map * m,void (* fn)(char *,void *),void * arg)745 mapc_keyiter(mnt_map *m, void (*fn)(char *,void *), void *arg)
746 {
747 	int i;
748 	int c = 0;
749 
750 	for (i = 0; i < NKVHASH; i++) {
751 		kv *k = m->kvhash[i];
752 		while (k) {
753 			(*fn)(k->key, arg);
754 			k = k->next;
755 			c++;
756 		}
757 	}
758 
759 	return c;
760 }
761 
762 /*
763  * Iterate over the root map
764  * and call (*fn)() on the key
765  * of all the nodes.
766  * Finally throw away the root map.
767  */
768 int
root_keyiter(void (* fn)(char *,void *),void * arg)769 root_keyiter(void (*fn)(char *,void *), void *arg)
770 {
771 	if (root_map) {
772 		int c = mapc_keyiter(root_map, fn, arg);
773 #ifdef notdef
774 		mapc_free(root_map);
775 		root_map = 0;
776 #endif
777 		return c;
778 	}
779 	return 0;
780 }
781 
782 /*
783  * Error map
784  */
785 static int
error_init(char * map,time_t * tp)786 error_init(char *map, time_t *tp)
787 {
788 	plog(XLOG_USER, "No source data for map %s", map);
789 	*tp = 0;
790 	return 0;
791 }
792 
793 static int
error_search(mnt_map * m,char * map,char * key,char ** pval,time_t * tp)794 error_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
795 {
796 	return ENOENT;
797 }
798 
799 static int
error_reload(mnt_map * m,char * map,add_fn * fn)800 error_reload(mnt_map *m, char *map, add_fn *fn)
801 {
802 	return ENOENT;
803 }
804 
805 static int
error_mtime(char * map,time_t * tp)806 error_mtime(char *map, time_t *tp)
807 {
808 	*tp = 0;
809 	return 0;
810 }
811