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