1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * autod_parse.c 23 * 24 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 27 */ 28 29 #include <stdio.h> 30 #include <ctype.h> 31 #include <string.h> 32 #include <syslog.h> 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <sys/param.h> 36 #include <errno.h> 37 #include <pwd.h> 38 #include <netinet/in.h> 39 #include <netdb.h> 40 #include <sys/tiuser.h> 41 #include <locale.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <thread.h> 45 #include <rpc/rpc.h> 46 #include <rpcsvc/mount.h> 47 #include <fcntl.h> 48 #include <limits.h> 49 #include "automount.h" 50 51 /* 52 * This structure is used to determine the hierarchical 53 * relationship between directories 54 */ 55 typedef struct _hiernode { 56 char dirname[MAXFILENAMELEN+1]; 57 struct _hiernode *subdir; 58 struct _hiernode *leveldir; 59 struct mapent *mapent; 60 } hiernode; 61 62 void free_mapent(struct mapent *); 63 64 static int mapline_to_mapent(struct mapent **, struct mapline *, char *, char *, 65 char *, char *, uint_t); 66 static int hierarchical_sort(struct mapent *, hiernode **, char *, char *); 67 static int push_options(hiernode *, char *, char *, int); 68 static int set_mapent_opts(struct mapent *, char *, char *, char *); 69 static void get_opts(char *, char *, char *, bool_t *); 70 static int fstype_opts(struct mapent *, char *, char *, char *); 71 static int modify_mapents(struct mapent **, char *, char *, char *, hiernode *, 72 char *, uint_t, bool_t); 73 static int set_and_fake_mapent_mntlevel(hiernode *, char *, char *, char *, 74 struct mapent **, uint_t, char *, bool_t); 75 static int mark_level1_root(hiernode *, char *); 76 static int mark_and_fake_level1_noroot(hiernode *, char *, char *, char *, 77 struct mapent **, uint_t i, char *); 78 static int convert_mapent_to_automount(struct mapent *, char *, char *); 79 static int automount_opts(char **, char *); 80 static int parse_fsinfo(char *, struct mapent *); 81 static int parse_nfs(char *, struct mapent *, char *, char *, char **, char **, 82 int); 83 static int parse_special(struct mapent *, char *, char *, char **, char **, 84 int); 85 static int get_dir_from_path(char *, char **, int); 86 static int alloc_hiernode(hiernode **, char *); 87 static void free_hiernode(hiernode *); 88 static void trace_mapents(char *, struct mapent *); 89 static void trace_hierarchy(hiernode *, int); 90 static struct mapent *do_mapent_hosts(char *, char *, uint_t); 91 static void freeex_ent(struct exportnode *); 92 static void freeex(struct exportnode *); 93 static void dump_mapent_err(struct mapent *, char *, char *); 94 95 #define PARSE_OK 0 96 #define PARSE_ERROR -1 97 #define MAX_FSLEN 32 98 99 /* 100 * mapentry error type defininitions 101 */ 102 #define MAPENT_NOERR 0 103 #define MAPENT_UATFS 1 104 105 /* 106 * parse_entry(char *key, char *mapname, char *mapopts, struct mapline *ml, 107 * char *subdir, uint_t isdirect, bool_t mount_access) 108 * Parses the data in ml to build a mapentry list containing the information 109 * for the mounts/lookups to be performed. Builds an intermediate mapentry list 110 * by processing ml, hierarchically sorts (builds a tree of) the list according 111 * to mountpoint. Then pushes options down the hierarchy, and fills in the mount 112 * file system. Finally, modifies the intermediate list depending on how far 113 * in the hierarchy the current request is (uses subdir). Deals with special 114 * case of /net map parsing. 115 * Returns a pointer to the head of the mapentry list. 116 */ 117 struct mapent * 118 parse_entry(char *key, char *mapname, char *mapopts, struct mapline *ml, 119 char *subdir, uint_t isdirect, bool_t mount_access) 120 { 121 char *p; 122 char defaultopts[AUTOFS_MAXOPTSLEN]; 123 124 struct mapent *mapents = NULL; 125 hiernode *rootnode = NULL; 126 char *lp = ml->linebuf; 127 128 if (trace > 1) 129 trace_prt(1, " mapline: %s\n", ml->linebuf); 130 131 /* 132 * Assure the key is only one token long. 133 * This prevents options from sneaking in through the 134 * command line or corruption of /etc/mnttab. 135 */ 136 for (p = key; *p != '\0'; p++) { 137 if (isspace(*p)) { 138 syslog(LOG_ERR, 139 "parse_entry: bad key in map %s: %s", mapname, key); 140 return ((struct mapent *)NULL); 141 } 142 } 143 144 /* 145 * select the appropriate parser, and build the mapentry list 146 */ 147 if (strcmp(lp, "-hosts") == 0) { 148 /* 149 * the /net parser - uses do_mapent_hosts to build mapents. 150 * The mapopts are considered default for every entry, so we 151 * don't push options down hierarchies. 152 */ 153 mapents = do_mapent_hosts(mapopts, key, isdirect); 154 if (mapents == NULL) /* nothing to free */ 155 return (mapents); 156 157 if (trace > 3) 158 trace_mapents("do_mapent_hosts:(return)", mapents); 159 160 if (hierarchical_sort(mapents, &rootnode, key, mapname) 161 != PARSE_OK) 162 goto parse_error; 163 } else { 164 /* 165 * all other parsing 166 */ 167 if (mapline_to_mapent(&mapents, ml, key, mapname, 168 mapopts, defaultopts, isdirect) != PARSE_OK) 169 goto parse_error; 170 171 if (mapents == NULL) 172 return (mapents); 173 174 if (hierarchical_sort(mapents, &rootnode, key, mapname) 175 != PARSE_OK) 176 goto parse_error; 177 178 if (push_options(rootnode, defaultopts, mapopts, 179 MAPENT_NOERR) != PARSE_OK) 180 goto parse_error; 181 182 if (trace > 3) { 183 trace_prt(1, "\n\tpush_options (return)\n"); 184 trace_prt(0, "\tdefault options=%s\n", defaultopts); 185 trace_hierarchy(rootnode, 0); 186 }; 187 188 if (parse_fsinfo(mapname, mapents) != PARSE_OK) 189 goto parse_error; 190 } 191 192 /* 193 * Modify the mapentry list. We *must* do this only after 194 * the mapentry list is completely built (since we need to 195 * have parse_fsinfo called first). 196 */ 197 if (modify_mapents(&mapents, mapname, mapopts, subdir, 198 rootnode, key, isdirect, mount_access) != PARSE_OK) 199 goto parse_error; 200 201 /* 202 * XXX: its dangerous to use rootnode after modify mapents as 203 * it may be pointing to mapents that have been freed 204 */ 205 if (rootnode != NULL) 206 free_hiernode(rootnode); 207 208 return (mapents); 209 210 parse_error: 211 syslog(LOG_ERR, "parse_entry: mapentry parse error: map=%s key=%s", 212 mapname, key); 213 free_mapent(mapents); 214 if (rootnode != NULL) 215 free_hiernode(rootnode); 216 return ((struct mapent *)NULL); 217 } 218 219 220 /* 221 * mapline_to_mapent(struct mapent **mapents, struct mapline *ml, 222 * char *key, char *mapname, char *mapopts, char *defaultopts, 223 * uint_t isdirect) 224 * Parses the mapline information in ml word by word to build an intermediate 225 * mapentry list, which is passed back to the caller. The mapentries may have 226 * holes (example no options), as they are completed only later. The logic is 227 * awkward, but needed to provide the supported flexibility in the map entries. 228 * (especially the first line). Note that the key is the full pathname of the 229 * directory to be mounted in a direct map, and ml is the mapentry beyond key. 230 * Returns PARSE_OK or an appropriate error value. 231 */ 232 static int 233 mapline_to_mapent(struct mapent **mapents, struct mapline *ml, char *key, 234 char *mapname, char *mapopts, char *defaultopts, 235 uint_t isdirect) 236 { 237 struct mapent *me = NULL; 238 struct mapent *mp; 239 char w[MAXPATHLEN]; 240 char wq[MAXPATHLEN]; 241 char w1[MAXPATHLEN]; 242 int implied; 243 244 char *lp = ml->linebuf; 245 char *lq = ml->lineqbuf; 246 247 /* do any macro expansions that are required to complete ml */ 248 if (macro_expand(key, lp, lq, LINESZ)) { 249 syslog(LOG_ERR, 250 "mapline_to_mapent: map %s: line too long (max %d chars)", 251 mapname, LINESZ - 1); 252 return (PARSE_ERROR); 253 } 254 if (trace > 3 && (strcmp(ml->linebuf, lp) != 0)) 255 trace_prt(1, 256 " mapline_to_mapent: (expanded) mapline (%s,%s)\n", 257 ml->linebuf, ml->lineqbuf); 258 259 /* init the head of mapentry list to null */ 260 *mapents = NULL; 261 262 /* 263 * Get the first word - its either a '-' if default options provided, 264 * a '/', if the mountroot is implicitly provided, or a mount filesystem 265 * if the mountroot is implicit. Note that if the first word begins with 266 * a '-' then the second must be read and it must be a mountpoint or a 267 * mount filesystem. Use mapopts if no default opts are provided. 268 */ 269 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 270 return (PARSE_ERROR); 271 if (*w == '-') { 272 strcpy(defaultopts, w); 273 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 274 return (PARSE_ERROR); 275 } else 276 strcpy(defaultopts, mapopts); 277 278 /* 279 * implied is true if there is no '/' 280 * We need the same code path if we have an smbfs mount. 281 */ 282 implied = (*w != '/') || (strstr(defaultopts, "fstype=smbfs") != NULL); 283 while (*w == '/' || implied) { 284 mp = me; 285 if ((me = (struct mapent *)malloc(sizeof (*me))) == NULL) 286 goto alloc_failed; 287 (void) memset((char *)me, 0, sizeof (*me)); 288 if (*mapents == NULL) /* special case of head */ 289 *mapents = me; 290 else 291 mp->map_next = me; 292 293 /* 294 * direct maps get an empty string as root - to be filled 295 * by the entire path later. Indirect maps get /key as the 296 * map root. Note that xfn maps don't care about the root 297 * - they override it in getmapent_fn(). 298 */ 299 if (isdirect) { 300 *w1 = '\0'; 301 } else { 302 strcpy(w1, "/"); 303 strcat(w1, key); 304 } 305 if ((me->map_root = strdup(w1)) == NULL) 306 goto alloc_failed; 307 308 /* mntpnt is empty for the mount root */ 309 if (strcmp(w, "/") == 0 || implied) 310 me->map_mntpnt = strdup(""); 311 else 312 me->map_mntpnt = strdup(w); 313 if (me->map_mntpnt == NULL) 314 goto alloc_failed; 315 316 /* 317 * If implied, the word must be a mount filesystem, 318 * and its already read in; also turn off implied - its 319 * not applicable except for the mount root. Else, 320 * read another (or two) words depending on if there's 321 * an option. 322 */ 323 if (implied) /* must be a mount filesystem */ 324 implied = 0; 325 else { 326 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 327 return (PARSE_ERROR); 328 if (w[0] == '-') { 329 /* mount options */ 330 if ((me->map_mntopts = strdup(w)) == NULL) 331 goto alloc_failed; 332 if (getword(w, wq, &lp, &lq, ' ', 333 sizeof (w)) == -1) 334 return (PARSE_ERROR); 335 } 336 } 337 338 /* 339 * must be a mount filesystem or a set of filesystems at 340 * this point. 341 */ 342 if (w[0] == '\0' || w[0] == '-') { 343 syslog(LOG_ERR, 344 "mapline_to_mapent: bad location=%s map=%s key=%s", 345 w, mapname, key); 346 return (PARSE_ERROR); 347 } 348 349 /* 350 * map_fsw and map_fswq hold information which will be 351 * used to determine filesystem information at a later 352 * point. This is required since we can only find out 353 * about the mount file system after the directories 354 * are hierarchically sorted and options have been pushed 355 * down the hierarchies. 356 */ 357 if (((me->map_fsw = strdup(w)) == NULL) || 358 ((me->map_fswq = strdup(wq)) == NULL)) 359 goto alloc_failed; 360 361 /* 362 * the next word, if any, is either another mount point or a 363 * mount filesystem if more than one server is listed. 364 */ 365 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 366 return (PARSE_ERROR); 367 while (*w && *w != '/') { /* more than 1 server listed */ 368 int len; 369 char *fsw, *fswq; 370 len = strlen(me->map_fsw) + strlen(w) + 4; 371 if ((fsw = (char *)malloc(len)) == NULL) 372 goto alloc_failed; 373 sprintf(fsw, "%s %s", me->map_fsw, w); 374 free(me->map_fsw); 375 me->map_fsw = fsw; 376 len = strlen(me->map_fswq) + strlen(wq) + 4; 377 if ((fswq = (char *)malloc(len)) == NULL) 378 goto alloc_failed; 379 sprintf(fswq, "%s %s", me->map_fswq, wq); 380 free(me->map_fswq); 381 me->map_fswq = fswq; 382 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 383 return (PARSE_ERROR); 384 } 385 386 /* initialize flags */ 387 me->map_mntlevel = -1; 388 me->map_modified = FALSE; 389 me->map_faked = FALSE; 390 me->map_err = MAPENT_NOERR; 391 392 me->map_next = NULL; 393 } 394 395 if (*mapents == NULL || w[0] != '\0') { /* sanity check */ 396 if (verbose) { 397 if (*mapents == NULL) 398 syslog(LOG_ERR, 399 "mapline_to_mapent: parsed with null mapents"); 400 else 401 syslog(LOG_ERR, 402 "mapline_to_mapent: parsed nononempty w=%s", w); 403 } 404 return (PARSE_ERROR); 405 } 406 407 if (trace > 3) 408 trace_mapents("mapline_to_mapent:", *mapents); 409 410 return (PARSE_OK); 411 412 alloc_failed: 413 syslog(LOG_ERR, "mapline_to_mapent: Memory allocation failed"); 414 return (ENOMEM); 415 } 416 417 /* 418 * hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key 419 * char *mapname) 420 * sorts the mntpnts in each mapent to build a hierarchy of nodes, with 421 * with the rootnode being the mount root. The hierarchy is setup as 422 * levels, and subdirs below each level. Provides a link from node to 423 * the relevant mapentry. 424 * Returns PARSE_OK or appropriate error value 425 */ 426 static int 427 hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key, 428 char *mapname) 429 { 430 hiernode *prevnode, *currnode, *newnode; 431 char *path; 432 char dirname[MAXFILENAMELEN]; 433 434 int rc = PARSE_OK; 435 struct mapent *me = mapents; 436 437 /* allocate the rootnode with a default path of "" */ 438 *rootnode = NULL; 439 if ((rc = alloc_hiernode(rootnode, "")) != PARSE_OK) 440 return (rc); 441 442 /* 443 * walk through mapents - for each mapent, locate the position 444 * within the hierarchy by walking across leveldirs, and 445 * subdirs of matched leveldirs. Starts one level below 446 * the root (assumes an implicit match with rootnode). 447 * XXX - this could probably be done more cleanly using recursion. 448 */ 449 while (me != NULL) { 450 451 path = me->map_mntpnt; 452 453 if ((rc = get_dir_from_path(dirname, &path, 454 sizeof (dirname))) != PARSE_OK) 455 return (rc); 456 457 prevnode = *rootnode; 458 currnode = (*rootnode)->subdir; 459 460 while (dirname[0] != '\0') { 461 if (currnode != NULL) { 462 if (strcmp(currnode->dirname, dirname) == 0) { 463 /* 464 * match found - mntpnt is a child of 465 * this node 466 */ 467 prevnode = currnode; 468 currnode = currnode->subdir; 469 } else { 470 prevnode = currnode; 471 currnode = currnode->leveldir; 472 473 if (currnode == NULL) { 474 /* 475 * No more leveldirs to match. 476 * Add a new one 477 */ 478 if ((rc = alloc_hiernode 479 (&newnode, dirname)) 480 != PARSE_OK) 481 return (rc); 482 prevnode->leveldir = newnode; 483 prevnode = newnode; 484 currnode = newnode->subdir; 485 } else { 486 /* try this leveldir */ 487 continue; 488 } 489 } 490 } else { 491 /* no more subdirs to match. Add a new one */ 492 if ((rc = alloc_hiernode(&newnode, 493 dirname)) != PARSE_OK) 494 return (rc); 495 prevnode->subdir = newnode; 496 prevnode = newnode; 497 currnode = newnode->subdir; 498 } 499 if ((rc = get_dir_from_path(dirname, &path, 500 sizeof (dirname))) != PARSE_OK) 501 return (rc); 502 } 503 504 if (prevnode->mapent != NULL) { 505 /* duplicate mntpoint found */ 506 syslog(LOG_ERR, 507 "hierarchical_sort: duplicate mntpnt map=%s key=%s", 508 mapname, key); 509 return (PARSE_ERROR); 510 } 511 512 /* provide a pointer from node to mapent */ 513 prevnode->mapent = me; 514 me = me->map_next; 515 } 516 517 if (trace > 3) { 518 trace_prt(1, "\n\thierarchical_sort:\n"); 519 trace_hierarchy(*rootnode, 0); /* 0 is rootnode's level */ 520 } 521 522 return (rc); 523 } 524 525 /* 526 * push_options(hiernode *node, char *opts, char *mapopts, int err) 527 * Pushes the options down a hierarchical structure. Works recursively from the 528 * root, which is passed in on the first call. Uses a replacement policy. 529 * If a node points to a mapentry, and it has an option, then thats the option 530 * for that mapentry. Else, the node's mapent inherits the option from the 531 * default (which may be the global option for the entry or mapopts). 532 * err is useful in flagging entries with errors in pushing options. 533 * returns PARSE_OK or appropriate error value. 534 */ 535 static int 536 push_options(hiernode *node, char *defaultopts, char *mapopts, int err) 537 { 538 int rc = PARSE_OK; 539 struct mapent *me = NULL; 540 541 /* ensure that all the dirs at a level are passed the default options */ 542 while (node != NULL) { 543 me = node->mapent; 544 if (me != NULL) { /* not all nodes point to a mapentry */ 545 me->map_err = err; 546 if ((rc = set_mapent_opts(me, me->map_mntopts, 547 defaultopts, mapopts)) != PARSE_OK) 548 return (rc); 549 } 550 551 /* push the options to subdirs */ 552 if (node->subdir != NULL) { 553 if (node->mapent && strcmp(node->mapent->map_fstype, 554 MNTTYPE_AUTOFS) == 0) 555 err = MAPENT_UATFS; 556 if ((rc = push_options(node->subdir, defaultopts, 557 mapopts, err)) != PARSE_OK) 558 return (rc); 559 } 560 node = node->leveldir; 561 } 562 return (rc); 563 } 564 565 #define FSTYPE "fstype" 566 #define FSTYPE_EQ "fstype=" 567 #define NO_OPTS "" 568 569 /* 570 * set_mapent_opts(struct mapent *me, char *opts, char *defaultopts, 571 * char *mapopts) 572 * sets the mapentry's options, fstype and mounter fields by separating 573 * out the fstype part from the opts. Use default options if opts is NULL. 574 * Note taht defaultopts may be the same as mapopts. 575 * Returns PARSE_OK or appropriate error value. 576 */ 577 static int 578 set_mapent_opts(struct mapent *me, char *opts, char *defaultopts, 579 char *mapopts) 580 { 581 char entryopts[AUTOFS_MAXOPTSLEN]; 582 char fstype[MAX_FSLEN], mounter[MAX_FSLEN]; 583 int rc = PARSE_OK; 584 bool_t fstype_opt = FALSE; 585 586 strcpy(fstype, MNTTYPE_NFS); /* default */ 587 588 /* set options to default options, if none exist for this entry */ 589 if (opts == NULL) { 590 opts = defaultopts; 591 if (defaultopts == NULL) { /* NULL opts for entry */ 592 strcpy(mounter, fstype); 593 goto done; 594 } 595 } 596 if (*opts == '-') 597 opts++; 598 599 /* separate opts into fstype and (other) entrypopts */ 600 get_opts(opts, entryopts, fstype, &fstype_opt); 601 602 /* replace any existing opts */ 603 if (me->map_mntopts != NULL) 604 free(me->map_mntopts); 605 if ((me->map_mntopts = strdup(entryopts)) == NULL) 606 return (ENOMEM); 607 strcpy(mounter, fstype); 608 609 /* 610 * child options are exactly fstype = somefs, we need to do some 611 * more option pushing work. 612 */ 613 if (fstype_opt == TRUE && 614 (strcmp(me->map_mntopts, NO_OPTS) == 0)) { 615 free(me->map_mntopts); 616 if ((rc = fstype_opts(me, opts, defaultopts, 617 mapopts)) != PARSE_OK) 618 return (rc); 619 } 620 621 done: 622 if (((me->map_fstype = strdup(fstype)) == NULL) || 623 ((me->map_mounter = strdup(mounter)) == NULL)) { 624 if (me->map_fstype != NULL) 625 free(me->map_fstype); 626 syslog(LOG_ERR, "set_mapent_opts: No memory"); 627 return (ENOMEM); 628 } 629 630 return (rc); 631 } 632 633 /* 634 * Check the option string for an "fstype" 635 * option. If found, return the fstype 636 * and the option string with the fstype 637 * option removed, e.g. 638 * 639 * input: "fstype=nfs,ro,nosuid" 640 * opts: "ro,nosuid" 641 * fstype: "nfs" 642 * 643 * Also indicates if the fstype option was present 644 * by setting a flag, if the pointer to the flag 645 * is not NULL. 646 */ 647 static void 648 get_opts(char *input, char *opts, char *fstype, bool_t *fstype_opt) 649 { 650 char *p, *pb; 651 char buf[MAXOPTSLEN]; 652 char *placeholder; 653 654 *opts = '\0'; 655 (void) strcpy(buf, input); 656 pb = buf; 657 while (p = (char *)strtok_r(pb, ",", &placeholder)) { 658 pb = NULL; 659 if (strncmp(p, FSTYPE_EQ, 7) == 0) { 660 if (fstype_opt != NULL) 661 *fstype_opt = TRUE; 662 (void) strcpy(fstype, p + 7); 663 } else { 664 if (*opts) 665 (void) strcat(opts, ","); 666 (void) strcat(opts, p); 667 } 668 } 669 } 670 671 /* 672 * fstype_opts(struct mapent *me, char *opts, char *defaultopts, 673 * char *mapopts) 674 * We need to push global options to the child entry if it is exactly 675 * fstype=somefs. 676 */ 677 static int 678 fstype_opts(struct mapent *me, char *opts, char *defaultopts, 679 char *mapopts) 680 { 681 char pushentryopts[AUTOFS_MAXOPTSLEN]; 682 char pushfstype[MAX_FSLEN]; 683 684 if (defaultopts && *defaultopts == '-') 685 defaultopts++; 686 687 /* 688 * the options to push are the global defaults for the entry, 689 * if they exist, or mapopts, if the global defaults for the 690 * entry does not exist. 691 */ 692 if (strcmp(defaultopts, opts) == 0) { 693 if (*mapopts == '-') 694 mapopts++; 695 get_opts(mapopts, pushentryopts, pushfstype, NULL); 696 } else { 697 get_opts(defaultopts, pushentryopts, pushfstype, NULL); 698 } 699 700 me->map_mntopts = strdup(pushentryopts); 701 702 if (!me->map_mntopts) { 703 syslog(LOG_ERR, "fstype_opts: No memory"); 704 return (ENOMEM); 705 } 706 707 return (PARSE_OK); 708 } 709 710 /* 711 * modify_mapents(struct mapent **mapents, char *mapname, 712 * char *mapopts, char *subdir, hiernode *rootnode, 713 * char *key, uint_t isdirect, bool_t mount_access) 714 * modifies the intermediate mapentry list into the final one, and passes 715 * back a pointer to it. The final list may contain faked mapentries for 716 * hiernodes that do not point to a mapentry, or converted mapentries, if 717 * hiernodes that point to a mapentry need to be converted from nfs to autofs. 718 * mounts. Entries that are not directly 1 level below the subdir are removed. 719 * Returns PARSE_OK or PARSE_ERROR 720 */ 721 static int 722 modify_mapents(struct mapent **mapents, char *mapname, 723 char *mapopts, char *subdir, hiernode *rootnode, 724 char *key, uint_t isdirect, bool_t mount_access) 725 { 726 struct mapent *mp = NULL; 727 char w[MAXPATHLEN]; 728 729 struct mapent *me; 730 int rc = PARSE_OK; 731 struct mapent *faked_mapents = NULL; 732 733 /* 734 * correct the mapentry mntlevel from default -1 to level depending on 735 * position in hierarchy, and build any faked mapentries, if required 736 * at one level below the rootnode given by subdir. 737 */ 738 if ((rc = set_and_fake_mapent_mntlevel(rootnode, subdir, key, mapname, 739 &faked_mapents, isdirect, mapopts, mount_access)) != PARSE_OK) 740 return (rc); 741 742 /* 743 * attaches faked mapents to real mapents list. Assumes mapents 744 * is not NULL. 745 */ 746 me = *mapents; 747 while (me->map_next != NULL) 748 me = me->map_next; 749 me->map_next = faked_mapents; 750 751 /* 752 * get rid of nodes marked at level -1 753 */ 754 me = *mapents; 755 while (me != NULL) { 756 if ((me->map_mntlevel == -1) || (me->map_err) || 757 (mount_access == FALSE && me->map_mntlevel == 0)) { 758 /* 759 * syslog any errors and free entry 760 */ 761 if (me->map_err) 762 dump_mapent_err(me, key, mapname); 763 764 if (me == (*mapents)) { 765 /* special case when head has to be freed */ 766 *mapents = me->map_next; 767 if ((*mapents) == NULL) { 768 /* something wierd happened */ 769 if (verbose) 770 syslog(LOG_ERR, 771 "modify_mapents: level error"); 772 return (PARSE_ERROR); 773 } 774 775 /* separate out the node */ 776 me->map_next = NULL; 777 free_mapent(me); 778 me = *mapents; 779 } else { 780 mp->map_next = me->map_next; 781 me->map_next = NULL; 782 free_mapent(me); 783 me = mp->map_next; 784 } 785 continue; 786 } 787 788 /* 789 * convert level 1 mapents that are not already autonodes 790 * to autonodes 791 */ 792 if (me->map_mntlevel == 1 && 793 (strcmp(me->map_fstype, MNTTYPE_AUTOFS) != 0) && 794 (me->map_faked != TRUE)) { 795 if ((rc = convert_mapent_to_automount(me, mapname, 796 mapopts)) != PARSE_OK) 797 return (rc); 798 } 799 strcpy(w, (me->map_mntpnt+strlen(subdir))); 800 strcpy(me->map_mntpnt, w); 801 mp = me; 802 me = me->map_next; 803 } 804 805 if (trace > 3) 806 trace_mapents("modify_mapents:", *mapents); 807 808 return (PARSE_OK); 809 } 810 811 /* 812 * set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key, 813 * char *mapname, struct mapent **faked_mapents, 814 * uint_t isdirect, char *mapopts, bool_t mount_access) 815 * sets the mapentry mount levels (depths) with respect to the subdir. 816 * Assigns a value of 0 to the new root. Finds the level1 directories by 817 * calling mark_*_level1_*(). Also cleans off extra /'s in level0 and 818 * level1 map_mntpnts. Note that one level below the new root is an existing 819 * mapentry if there's a mapentry (nfs mount) corresponding to the root, 820 * and the direct subdir set for the root, if there's no mapentry corresponding 821 * to the root (we install autodirs). Returns PARSE_OK or error value. 822 */ 823 static int 824 set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key, 825 char *mapname, struct mapent **faked_mapents, 826 uint_t isdirect, char *mapopts, bool_t mount_access) 827 { 828 char dirname[MAXFILENAMELEN]; 829 char traversed_path[MAXPATHLEN]; /* used in building fake mapentries */ 830 831 char *subdir_child = subdir; 832 hiernode *prevnode = rootnode; 833 hiernode *currnode = rootnode->subdir; 834 int rc = PARSE_OK; 835 traversed_path[0] = '\0'; 836 837 /* 838 * find and mark the root by tracing down subdir. Use traversed_path 839 * to keep track of how far we go, while guaranteeing that it 840 * contains no '/' at the end. Took some mucking to get that right. 841 */ 842 if ((rc = get_dir_from_path(dirname, &subdir_child, sizeof (dirname))) 843 != PARSE_OK) 844 return (rc); 845 846 if (dirname[0] != '\0') 847 sprintf(traversed_path, "%s/%s", traversed_path, dirname); 848 849 prevnode = rootnode; 850 currnode = rootnode->subdir; 851 while (dirname[0] != '\0' && currnode != NULL) { 852 if (strcmp(currnode->dirname, dirname) == 0) { 853 854 /* subdir is a child of currnode */ 855 prevnode = currnode; 856 currnode = currnode->subdir; 857 858 if ((rc = get_dir_from_path(dirname, &subdir_child, 859 sizeof (dirname))) != PARSE_OK) 860 return (rc); 861 if (dirname[0] != '\0') 862 sprintf(traversed_path, "%s/%s", 863 traversed_path, dirname); 864 865 } else { 866 /* try next leveldir */ 867 prevnode = currnode; 868 currnode = currnode->leveldir; 869 } 870 } 871 872 if (dirname[0] != '\0') { 873 if (verbose) 874 syslog(LOG_ERR, 875 "set_and_fake_mapent_mntlevel: subdir=%s error: map=%s", 876 subdir, mapname); 877 return (PARSE_ERROR); 878 } 879 880 /* 881 * see if level of root really points to a mapent and if 882 * have access to that filessystem - call appropriate 883 * routine to mark level 1 nodes, and build faked entries 884 */ 885 if (prevnode->mapent != NULL && mount_access == TRUE) { 886 if (trace > 3) 887 trace_prt(1, " node mountpoint %s\t travpath=%s\n", 888 prevnode->mapent->map_mntpnt, traversed_path); 889 890 /* 891 * Copy traversed path map_mntpnt to get rid of any extra 892 * '/' the map entry may contain. 893 */ 894 if (strlen(prevnode->mapent->map_mntpnt) < 895 strlen(traversed_path)) { /* sanity check */ 896 if (verbose) 897 syslog(LOG_ERR, 898 "set_and_fake_mapent_mntlevel: path=%s error", 899 traversed_path); 900 return (PARSE_ERROR); 901 } 902 if (strcmp(prevnode->mapent->map_mntpnt, traversed_path) != 0) 903 strcpy(prevnode->mapent->map_mntpnt, traversed_path); 904 905 prevnode->mapent->map_mntlevel = 0; /* root level is 0 */ 906 if (currnode != NULL) { 907 if ((rc = mark_level1_root(currnode, 908 traversed_path)) != PARSE_OK) 909 return (rc); 910 } 911 } else if (currnode != NULL) { 912 if (trace > 3) 913 trace_prt(1, " No rootnode, travpath=%s\n", 914 traversed_path); 915 if ((rc = mark_and_fake_level1_noroot(currnode, 916 traversed_path, key, mapname, faked_mapents, isdirect, 917 mapopts)) != PARSE_OK) 918 return (rc); 919 } 920 921 if (trace > 3) { 922 trace_prt(1, "\n\tset_and_fake_mapent_mntlevel\n"); 923 trace_hierarchy(rootnode, 0); 924 } 925 926 return (rc); 927 } 928 929 930 /* 931 * mark_level1_root(hiernode *node, char *traversed_path) 932 * marks nodes upto one level below the rootnode given by subdir 933 * recursively. Called if rootnode points to a mapent. 934 * In this routine, a level 1 node is considered to be the 1st existing 935 * mapentry below the root node, so there's no faking involved. 936 * Returns PARSE_OK or error value 937 */ 938 static int 939 mark_level1_root(hiernode *node, char *traversed_path) 940 { 941 /* ensure we touch all leveldirs */ 942 while (node) { 943 /* 944 * mark node level as 1, if one exists - else walk down 945 * subdirs until we find one. 946 */ 947 if (node->mapent == NULL) { 948 char w[MAXPATHLEN]; 949 950 if (node->subdir != NULL) { 951 sprintf(w, "%s/%s", traversed_path, 952 node->dirname); 953 if (mark_level1_root(node->subdir, w) 954 == PARSE_ERROR) 955 return (PARSE_ERROR); 956 } else { 957 if (verbose) { 958 syslog(LOG_ERR, 959 "mark_level1_root: hierarchy error"); 960 } 961 return (PARSE_ERROR); 962 } 963 } else { 964 char w[MAXPATHLEN]; 965 966 sprintf(w, "%s/%s", traversed_path, node->dirname); 967 if (trace > 3) 968 trace_prt(1, " node mntpnt %s\t travpath %s\n", 969 node->mapent->map_mntpnt, w); 970 971 /* replace mntpnt with travpath to clean extra '/' */ 972 if (strlen(node->mapent->map_mntpnt) < strlen(w)) { 973 if (verbose) { 974 syslog(LOG_ERR, 975 "mark_level1_root: path=%s error", 976 traversed_path); 977 } 978 return (PARSE_ERROR); 979 } 980 if (strcmp(node->mapent->map_mntpnt, w) != 0) 981 strcpy(node->mapent->map_mntpnt, w); 982 node->mapent->map_mntlevel = 1; 983 } 984 node = node->leveldir; 985 } 986 return (PARSE_OK); 987 } 988 989 /* 990 * mark_and_fake_level1_noroot(hiernode *node, char *traversed_path, 991 * char *key,char *mapname, struct mapent **faked_mapents, 992 * uint_t isdirect, char *mapopts) 993 * Called if the root of the hierarchy does not point to a mapent. marks nodes 994 * upto one physical level below the rootnode given by subdir. checks if 995 * there's a real mapentry. If not, it builds a faked one (autonode) at that 996 * point. The faked autonode is direct, with the map being the same as the 997 * original one from which the call originated. Options are same as that of 998 * the map and assigned in automount_opts(). Returns PARSE_OK or error value. 999 */ 1000 static int 1001 mark_and_fake_level1_noroot(hiernode *node, char *traversed_path, 1002 char *key, char *mapname, struct mapent **faked_mapents, 1003 uint_t isdirect, char *mapopts) 1004 { 1005 struct mapent *me; 1006 int rc = 0; 1007 char faked_map_mntpnt[MAXPATHLEN]; 1008 char w1[MAXPATHLEN]; 1009 char w[MAXPATHLEN]; 1010 1011 while (node != NULL) { 1012 if (node->mapent != NULL) { 1013 /* 1014 * existing mapentry at level 1 - copy travpath to 1015 * get rid of extra '/' in mntpnt 1016 */ 1017 sprintf(w, "%s/%s", traversed_path, node->dirname); 1018 if (trace > 3) 1019 trace_prt(1, " node mntpnt=%s\t travpath=%s\n", 1020 node->mapent->map_mntpnt, w); 1021 if (strlen(node->mapent->map_mntpnt) < strlen(w)) { 1022 /* sanity check */ 1023 if (verbose) 1024 syslog(LOG_ERR, 1025 "mark_fake_level1_noroot:path=%s error", 1026 traversed_path); 1027 return (PARSE_ERROR); 1028 } 1029 if (strcmp(node->mapent->map_mntpnt, w) != 0) 1030 strcpy(node->mapent->map_mntpnt, w); 1031 node->mapent->map_mntlevel = 1; 1032 } else { 1033 /* 1034 * build the faked autonode 1035 */ 1036 if ((me = (struct mapent *)malloc(sizeof (*me))) 1037 == NULL) { 1038 syslog(LOG_ERR, 1039 "mark_and_fake_level1_noroot: out of memory"); 1040 return (ENOMEM); 1041 } 1042 (void) memset((char *)me, 0, sizeof (*me)); 1043 1044 if ((me->map_fs = (struct mapfs *) 1045 malloc(sizeof (struct mapfs))) == NULL) 1046 return (ENOMEM); 1047 (void) memset(me->map_fs, 0, sizeof (struct mapfs)); 1048 1049 if (isdirect) { 1050 *w1 = '\0'; 1051 } else { 1052 strcpy(w1, "/"); 1053 strcat(w1, key); 1054 } 1055 me->map_root = strdup(w1); 1056 1057 sprintf(faked_map_mntpnt, "%s/%s", traversed_path, 1058 node->dirname); 1059 me->map_mntpnt = strdup(faked_map_mntpnt); 1060 me->map_fstype = strdup(MNTTYPE_AUTOFS); 1061 me->map_mounter = strdup(MNTTYPE_AUTOFS); 1062 1063 /* set options */ 1064 if ((rc = automount_opts(&me->map_mntopts, mapopts)) 1065 != PARSE_OK) 1066 return (rc); 1067 me->map_fs->mfs_dir = strdup(mapname); 1068 me->map_mntlevel = 1; 1069 me->map_modified = FALSE; 1070 me->map_faked = TRUE; /* mark as faked */ 1071 if (me->map_root == NULL || 1072 me->map_mntpnt == NULL || 1073 me->map_fstype == NULL || 1074 me->map_mounter == NULL || 1075 me->map_mntopts == NULL || 1076 me->map_fs->mfs_dir == NULL) { 1077 syslog(LOG_ERR, 1078 "mark_and_fake_level1_noroot: out of memory"); 1079 free_mapent(*faked_mapents); 1080 return (ENOMEM); 1081 } 1082 1083 if (*faked_mapents == NULL) 1084 *faked_mapents = me; 1085 else { /* attach to the head */ 1086 me->map_next = *faked_mapents; 1087 *faked_mapents = me; 1088 } 1089 node->mapent = me; 1090 } 1091 node = node->leveldir; 1092 } 1093 return (rc); 1094 } 1095 1096 /* 1097 * convert_mapent_to_automount(struct mapent *me, char *mapname, 1098 * char *mapopts) 1099 * change the mapentry me to an automount - free fields first and NULL them 1100 * to avoid freeing again, while freeing the mapentry at a later stage. 1101 * Could have avoided freeing entries here as we don't really look at them. 1102 * Give the converted mapent entry the options that came with the map using 1103 * automount_opts(). Returns PARSE_OK or appropriate error value. 1104 */ 1105 static int 1106 convert_mapent_to_automount(struct mapent *me, char *mapname, 1107 char *mapopts) 1108 { 1109 struct mapfs *mfs = me->map_fs; /* assumes it exists */ 1110 int rc = PARSE_OK; 1111 1112 /* free relevant entries */ 1113 if (mfs->mfs_host) { 1114 free(mfs->mfs_host); 1115 mfs->mfs_host = NULL; 1116 } 1117 while (me->map_fs->mfs_next != NULL) { 1118 mfs = me->map_fs->mfs_next; 1119 if (mfs->mfs_host) 1120 free(mfs->mfs_host); 1121 if (mfs->mfs_dir) 1122 free(mfs->mfs_dir); 1123 me->map_fs->mfs_next = mfs->mfs_next; /* nulls eventually */ 1124 free((void*)mfs); 1125 } 1126 1127 /* replace relevant entries */ 1128 if (me->map_fstype) 1129 free(me->map_fstype); 1130 if ((me->map_fstype = strdup(MNTTYPE_AUTOFS)) == NULL) 1131 goto alloc_failed; 1132 1133 if (me->map_mounter) 1134 free(me->map_mounter); 1135 if ((me->map_mounter = strdup(me->map_fstype)) == NULL) 1136 goto alloc_failed; 1137 1138 if (me->map_fs->mfs_dir) 1139 free(me->map_fs->mfs_dir); 1140 if ((me->map_fs->mfs_dir = strdup(mapname)) == NULL) 1141 goto alloc_failed; 1142 1143 /* set options */ 1144 if (me->map_mntopts) 1145 free(me->map_mntopts); 1146 if ((rc = automount_opts(&me->map_mntopts, mapopts)) != PARSE_OK) 1147 return (rc); 1148 1149 /* mucked with this entry, set the map_modified field to TRUE */ 1150 me->map_modified = TRUE; 1151 1152 return (rc); 1153 1154 alloc_failed: 1155 syslog(LOG_ERR, 1156 "convert_mapent_to_automount: Memory allocation failed"); 1157 return (ENOMEM); 1158 } 1159 1160 /* 1161 * automount_opts(char **map_mntopts, char *mapopts) 1162 * modifies automount opts - gets rid of all "indirect" and "direct" strings 1163 * if they exist, and then adds a direct string to force a direct automount. 1164 * Rest of the mapopts stay intact. Returns PARSE_OK or appropriate error. 1165 */ 1166 static int 1167 automount_opts(char **map_mntopts, char *mapopts) 1168 { 1169 char *opts; 1170 char *opt; 1171 int len; 1172 char *placeholder; 1173 char buf[AUTOFS_MAXOPTSLEN]; 1174 1175 char *addopt = "direct"; 1176 1177 len = strlen(mapopts)+ strlen(addopt)+2; /* +2 for ",", '\0' */ 1178 if (len > AUTOFS_MAXOPTSLEN) { 1179 syslog(LOG_ERR, 1180 "option string %s too long (max=%d)", mapopts, 1181 AUTOFS_MAXOPTSLEN-8); 1182 return (PARSE_ERROR); 1183 } 1184 1185 if (((*map_mntopts) = ((char *)malloc(len))) == NULL) { 1186 syslog(LOG_ERR, "automount_opts: Memory allocation failed"); 1187 return (ENOMEM); 1188 } 1189 memset(*map_mntopts, 0, len); 1190 1191 strcpy(buf, mapopts); 1192 opts = buf; 1193 while ((opt = strtok_r(opts, ",", &placeholder)) != NULL) { 1194 opts = NULL; 1195 1196 /* remove trailing and leading spaces */ 1197 while (isspace(*opt)) 1198 opt++; 1199 len = strlen(opt)-1; 1200 while (isspace(opt[len])) 1201 opt[len--] = '\0'; 1202 1203 /* 1204 * if direct or indirect found, get rid of it, else put it 1205 * back 1206 */ 1207 if ((strcmp(opt, "indirect") == 0) || 1208 (strcmp(opt, "direct") == 0)) 1209 continue; 1210 if (*map_mntopts[0] != '\0') 1211 strcat(*map_mntopts, ","); 1212 strcat(*map_mntopts, opt); 1213 } 1214 1215 /* add the direct string at the end */ 1216 if (*map_mntopts[0] != '\0') 1217 strcat(*map_mntopts, ","); 1218 strcat(*map_mntopts, addopt); 1219 1220 return (PARSE_OK); 1221 } 1222 1223 /* 1224 * parse_fsinfo(char *mapname, struct mapent *mapents) 1225 * parses the filesystem information stored in me->map_fsw and me->map_fswq 1226 * and calls appropriate filesystem parser. 1227 * Returns PARSE_OK or an appropriate error value. 1228 */ 1229 static int 1230 parse_fsinfo(char *mapname, struct mapent *mapents) 1231 { 1232 struct mapent *me = mapents; 1233 char *bufp; 1234 char *bufq; 1235 int wordsz = MAXPATHLEN; 1236 int err = 0; 1237 1238 while (me != NULL) { 1239 bufp = ""; 1240 bufq = ""; 1241 if (strcmp(me->map_fstype, MNTTYPE_NFS) == 0) { 1242 err = parse_nfs(mapname, me, me->map_fsw, 1243 me->map_fswq, &bufp, &bufq, wordsz); 1244 } else { 1245 err = parse_special(me, me->map_fsw, me->map_fswq, 1246 &bufp, &bufq, wordsz); 1247 } 1248 1249 if (err != PARSE_OK || *me->map_fsw != '\0' || 1250 *me->map_fswq != '\0') { 1251 /* sanity check */ 1252 if (verbose) 1253 syslog(LOG_ERR, 1254 "parse_fsinfo: mount location error %s", 1255 me->map_fsw); 1256 return (PARSE_ERROR); 1257 } 1258 1259 me = me->map_next; 1260 } 1261 1262 if (trace > 3) { 1263 trace_mapents("parse_fsinfo:", mapents); 1264 } 1265 1266 return (PARSE_OK); 1267 } 1268 1269 /* 1270 * This function parses the map entry for a nfs type file system 1271 * The input is the string lp (and lq) which can be one of the 1272 * following forms: 1273 * a) host[(penalty)][,host[(penalty)]]... :/directory 1274 * b) host[(penalty)]:/directory[ host[(penalty)]:/directory]... 1275 * This routine constructs a mapfs link-list for each of 1276 * the hosts and the corresponding file system. The list 1277 * is then attatched to the mapent struct passed in. 1278 */ 1279 int 1280 parse_nfs(char *mapname, struct mapent *me, char *fsw, char *fswq, 1281 char **lp, char **lq, int wsize) 1282 { 1283 struct mapfs *mfs, **mfsp; 1284 char *wlp, *wlq; 1285 char *hl, hostlist[1024], *hlq, hostlistq[1024]; 1286 char hostname_and_penalty[MXHOSTNAMELEN+5]; 1287 char *hn, *hnq, hostname[MXHOSTNAMELEN+1]; 1288 char dirname[MAXPATHLEN+1], subdir[MAXPATHLEN+1]; 1289 char qbuff[MAXPATHLEN+1], qbuff1[MAXPATHLEN+1]; 1290 char pbuff[10], pbuffq[10]; 1291 int penalty; 1292 char w[MAXPATHLEN]; 1293 char wq[MAXPATHLEN]; 1294 int host_cnt; 1295 1296 mfsp = &me->map_fs; 1297 *mfsp = NULL; 1298 1299 /* 1300 * there may be more than one entry in the map list. Get the 1301 * first one. Use temps to handle the word information and 1302 * copy back into fsw and fswq fields when done. 1303 */ 1304 *lp = fsw; 1305 *lq = fswq; 1306 if (getword(w, wq, lp, lq, ' ', wsize) == -1) 1307 return (PARSE_ERROR); 1308 while (*w && *w != '/') { 1309 bool_t maybe_url; 1310 1311 maybe_url = TRUE; 1312 1313 wlp = w; wlq = wq; 1314 if (getword(hostlist, hostlistq, &wlp, &wlq, ':', 1315 sizeof (hostlist)) == -1) 1316 return (PARSE_ERROR); 1317 if (!*hostlist) 1318 goto bad_entry; 1319 1320 if (strcmp(hostlist, "nfs") != 0) 1321 maybe_url = FALSE; 1322 1323 if (getword(dirname, qbuff, &wlp, &wlq, ':', 1324 sizeof (dirname)) == -1) 1325 return (PARSE_ERROR); 1326 if (*dirname == '\0') 1327 goto bad_entry; 1328 1329 if (maybe_url == TRUE && strncmp(dirname, "//", 2) != 0) 1330 maybe_url = FALSE; 1331 1332 /* 1333 * See the next block comment ("Once upon a time ...") to 1334 * understand this. It turns the deprecated concept 1335 * of "subdir mounts" produced some useful code for handling 1336 * the possibility of a ":port#" in the URL. 1337 */ 1338 if (maybe_url == FALSE) 1339 *subdir = '/'; 1340 else 1341 *subdir = ':'; 1342 1343 *qbuff = ' '; 1344 1345 /* 1346 * Once upon time, before autofs, there was support for 1347 * "subdir mounts". The idea was to "economize" the 1348 * number of mounts, so if you had a number of entries 1349 * all referring to a common subdirectory, e.g. 1350 * 1351 * carol seasons:/export/home11/carol 1352 * ted seasons:/export/home11/ted 1353 * alice seasons:/export/home11/alice 1354 * 1355 * then you could tell the automounter to mount a 1356 * common mountpoint which was delimited by the second 1357 * colon: 1358 * 1359 * carol seasons:/export/home11:carol 1360 * ted seasons:/export/home11:ted 1361 * alice seasons:/export/home11:alice 1362 * 1363 * The automounter would mount seasons:/export/home11 1364 * then for any other map entry that referenced the same 1365 * directory it would build a symbolic link that 1366 * appended the remainder of the path after the second 1367 * colon, i.e. once the common subdir was mounted, then 1368 * other directories could be accessed just by link 1369 * building - no further mounts required. 1370 * 1371 * In theory the "mount saving" idea sounded good. In 1372 * practice the saving didn't amount to much and the 1373 * symbolic links confused people because the common 1374 * mountpoint had to have a pseudonym. 1375 * 1376 * To remain backward compatible with the existing 1377 * maps, we interpret a second colon as a slash. 1378 */ 1379 if (getword(subdir+1, qbuff+1, &wlp, &wlq, ':', 1380 sizeof (subdir)) == -1) 1381 return (PARSE_ERROR); 1382 1383 if (*(subdir+1)) 1384 (void) strcat(dirname, subdir); 1385 1386 hl = hostlist; hlq = hostlistq; 1387 1388 host_cnt = 0; 1389 for (;;) { 1390 1391 if (getword(hostname_and_penalty, qbuff, &hl, &hlq, ',', 1392 sizeof (hostname_and_penalty)) == -1) 1393 return (PARSE_ERROR); 1394 if (!*hostname_and_penalty) 1395 break; 1396 1397 host_cnt++; 1398 if (host_cnt > 1) 1399 maybe_url = FALSE; 1400 1401 hn = hostname_and_penalty; 1402 hnq = qbuff; 1403 if (getword(hostname, qbuff1, &hn, &hnq, '(', 1404 sizeof (hostname)) == -1) 1405 return (PARSE_ERROR); 1406 if (hostname[0] == '\0') 1407 goto bad_entry; 1408 1409 if (strcmp(hostname, hostname_and_penalty) == 0) { 1410 penalty = 0; 1411 } else { 1412 maybe_url = FALSE; 1413 hn++; hnq++; 1414 if (getword(pbuff, pbuffq, &hn, &hnq, ')', 1415 sizeof (pbuff)) == -1) 1416 return (PARSE_ERROR); 1417 if (!*pbuff) 1418 penalty = 0; 1419 else 1420 penalty = atoi(pbuff); 1421 } 1422 mfs = (struct mapfs *)malloc(sizeof (*mfs)); 1423 if (mfs == NULL) { 1424 syslog(LOG_ERR, 1425 "parse_nfs: Memory allocation failed"); 1426 return (PARSE_ERROR); 1427 } 1428 (void) memset(mfs, 0, sizeof (*mfs)); 1429 *mfsp = mfs; 1430 mfsp = &mfs->mfs_next; 1431 1432 if (maybe_url == TRUE) { 1433 char *host; 1434 char *path; 1435 char *sport; 1436 1437 host = dirname+2; 1438 path = strchr(host, '/'); 1439 if (path == NULL) { 1440 syslog(LOG_ERR, "parse_nfs: illegal " 1441 "nfs url syntax: %s", host); 1442 1443 return (PARSE_ERROR); 1444 } 1445 *path = '\0'; 1446 sport = strchr(host, ':'); 1447 1448 if (sport != NULL && sport < path) { 1449 *sport = '\0'; 1450 mfs->mfs_port = atoi(sport+1); 1451 1452 if (mfs->mfs_port > USHRT_MAX) { 1453 syslog(LOG_ERR, 1454 "parse_nfs: invalid " 1455 "port number (%d) in " 1456 "NFS URL", 1457 mfs->mfs_port); 1458 1459 return (PARSE_ERROR); 1460 } 1461 1462 } 1463 1464 path++; 1465 if (*path == '\0') 1466 path = "."; 1467 1468 mfs->mfs_flags |= MFS_URL; 1469 1470 mfs->mfs_host = strdup(host); 1471 mfs->mfs_dir = strdup(path); 1472 } else { 1473 mfs->mfs_host = strdup(hostname); 1474 mfs->mfs_dir = strdup(dirname); 1475 } 1476 1477 mfs->mfs_penalty = penalty; 1478 if (mfs->mfs_host == NULL || mfs->mfs_dir == NULL) { 1479 syslog(LOG_ERR, 1480 "parse_nfs: Memory allocation failed"); 1481 return (PARSE_ERROR); 1482 } 1483 } 1484 /* 1485 * We check host_cnt to make sure we haven't parsed an entry 1486 * with no host information. 1487 */ 1488 if (host_cnt == 0) { 1489 syslog(LOG_ERR, 1490 "parse_nfs: invalid host specified - bad entry " 1491 "in map %s \"%s\"", 1492 mapname, w); 1493 return (PARSE_ERROR); 1494 } 1495 if (getword(w, wq, lp, lq, ' ', wsize) == -1) 1496 return (PARSE_ERROR); 1497 } 1498 1499 strcpy(fsw, w); 1500 strcpy(fswq, wq); 1501 1502 return (PARSE_OK); 1503 1504 bad_entry: 1505 syslog(LOG_ERR, "parse_nfs: bad entry in map %s \"%s\"", mapname, w); 1506 return (PARSE_ERROR); 1507 } 1508 1509 static int 1510 parse_special(struct mapent *me, char *w, char *wq, char **lp, char **lq, 1511 int wsize) 1512 { 1513 char devname[MAXPATHLEN + 1], qbuf[MAXPATHLEN + 1]; 1514 char *wlp, *wlq; 1515 struct mapfs *mfs; 1516 1517 wlp = w; 1518 wlq = wq; 1519 if (getword(devname, qbuf, &wlp, &wlq, ' ', sizeof (devname)) == -1) 1520 return (PARSE_ERROR); 1521 if (devname[0] == '\0') 1522 return (PARSE_ERROR); 1523 1524 mfs = malloc(sizeof (struct mapfs)); 1525 if (mfs == NULL) 1526 return (PARSE_ERROR); 1527 (void) memset(mfs, 0, sizeof (*mfs)); 1528 1529 /* 1530 * A device name that begins with a slash could 1531 * be confused with a mountpoint path, hence use 1532 * a colon to escape a device string that begins 1533 * with a slash, e.g. 1534 * 1535 * foo -ro /bar foo:/bar 1536 * and 1537 * foo -ro /dev/sr0 1538 * 1539 * would confuse the parser. The second instance 1540 * must use a colon: 1541 * 1542 * foo -ro :/dev/sr0 1543 */ 1544 mfs->mfs_dir = strdup(&devname[devname[0] == ':']); 1545 if (mfs->mfs_dir == NULL) 1546 return (PARSE_ERROR); 1547 me->map_fs = mfs; 1548 if (getword(w, wq, lp, lq, ' ', wsize) == -1) 1549 return (PARSE_ERROR); 1550 return (0); 1551 } 1552 1553 /* 1554 * get_dir_from_path(char *dir, char **path, int dirsz) 1555 * gets the directory name dir from path for max string of length dirsz. 1556 * A modification of the getword routine. Assumes the delimiter is '/' 1557 * and that excess /'s are redundant. 1558 * Returns PARSE_OK or PARSE_ERROR 1559 */ 1560 static int 1561 get_dir_from_path(char *dir, char **path, int dirsz) 1562 { 1563 char *tmp = dir; 1564 int count = dirsz; 1565 1566 if (dirsz <= 0) { 1567 if (verbose) 1568 syslog(LOG_ERR, 1569 "get_dir_from_path: invalid directory size %d", dirsz); 1570 return (PARSE_ERROR); 1571 } 1572 1573 /* get rid of leading /'s in path */ 1574 while (**path == '/') 1575 (*path)++; 1576 1577 /* now at a word or at the end of path */ 1578 while ((**path) && ((**path) != '/')) { 1579 if (--count <= 0) { 1580 *tmp = '\0'; 1581 syslog(LOG_ERR, 1582 "get_dir_from_path: max pathlength exceeded %d", dirsz); 1583 return (PARSE_ERROR); 1584 } 1585 *dir++ = *(*path)++; 1586 } 1587 1588 *dir = '\0'; 1589 1590 /* get rid of trailing /'s in path */ 1591 while (**path == '/') 1592 (*path)++; 1593 1594 return (PARSE_OK); 1595 } 1596 1597 /* 1598 * alloc_hiernode(hiernode **newnode, char *dirname) 1599 * allocates a new hiernode corresponding to a new directory entry 1600 * in the hierarchical structure, and passes a pointer to it back 1601 * to the calling program. 1602 * Returns PARSE_OK or appropriate error value. 1603 */ 1604 static int 1605 alloc_hiernode(hiernode **newnode, char *dirname) 1606 { 1607 if ((*newnode = (hiernode *)malloc(sizeof (hiernode))) == NULL) { 1608 syslog(LOG_ERR, "alloc_hiernode: Memory allocation failed"); 1609 return (ENOMEM); 1610 } 1611 1612 memset(((char *)*newnode), 0, sizeof (hiernode)); 1613 strcpy(((*newnode)->dirname), dirname); 1614 return (PARSE_OK); 1615 } 1616 1617 /* 1618 * free_hiernode(hiernode *node) 1619 * frees the allocated hiernode given the head of the structure 1620 * recursively calls itself until it frees entire structure. 1621 * Returns nothing. 1622 */ 1623 static void 1624 free_hiernode(hiernode *node) 1625 { 1626 hiernode *currnode = node; 1627 hiernode *prevnode = NULL; 1628 1629 while (currnode != NULL) { 1630 if (currnode->subdir != NULL) 1631 free_hiernode(currnode->subdir); 1632 prevnode = currnode; 1633 currnode = currnode->leveldir; 1634 free((void*)prevnode); 1635 } 1636 } 1637 1638 /* 1639 * free_mapent(struct mapent *) 1640 * free the mapentry and its fields 1641 */ 1642 void 1643 free_mapent(struct mapent *me) 1644 { 1645 struct mapfs *mfs; 1646 struct mapent *m; 1647 1648 while (me) { 1649 while (me->map_fs) { 1650 mfs = me->map_fs; 1651 if (mfs->mfs_host) 1652 free(mfs->mfs_host); 1653 if (mfs->mfs_dir) 1654 free(mfs->mfs_dir); 1655 if (mfs->mfs_args) 1656 free(mfs->mfs_args); 1657 if (mfs->mfs_nconf) 1658 freenetconfigent(mfs->mfs_nconf); 1659 me->map_fs = mfs->mfs_next; 1660 free(mfs); 1661 } 1662 1663 if (me->map_root) 1664 free(me->map_root); 1665 if (me->map_mntpnt) 1666 free(me->map_mntpnt); 1667 if (me->map_mntopts) 1668 free(me->map_mntopts); 1669 if (me->map_fstype) 1670 free(me->map_fstype); 1671 if (me->map_mounter) 1672 free(me->map_mounter); 1673 if (me->map_fsw) 1674 free(me->map_fsw); 1675 if (me->map_fswq) 1676 free(me->map_fswq); 1677 1678 m = me; 1679 me = me->map_next; 1680 free(m); 1681 } 1682 } 1683 1684 /* 1685 * trace_mapents(struct mapent *mapents) 1686 * traces through the mapentry structure and prints it element by element 1687 * returns nothing 1688 */ 1689 static void 1690 trace_mapents(char *s, struct mapent *mapents) 1691 { 1692 struct mapfs *mfs; 1693 struct mapent *me; 1694 1695 trace_prt(1, "\n\t%s\n", s); 1696 for (me = mapents; me; me = me->map_next) { 1697 trace_prt(1, " (%s,%s)\t %s%s -%s\n", 1698 me->map_fstype ? me->map_fstype : "", 1699 me->map_mounter ? me->map_mounter : "", 1700 me->map_root ? me->map_root : "", 1701 me->map_mntpnt ? me->map_mntpnt : "", 1702 me->map_mntopts ? me->map_mntopts : ""); 1703 for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next) 1704 trace_prt(0, "\t\t%s:%s\n", 1705 mfs->mfs_host ? mfs->mfs_host: "", 1706 mfs->mfs_dir ? mfs->mfs_dir : ""); 1707 1708 trace_prt(1, "\tme->map_fsw=%s\n", 1709 me->map_fsw ? me->map_fsw:"", 1710 me->map_fswq ? me->map_fsw:""); 1711 trace_prt(1, "\t mntlevel=%d\t%s\t%s err=%d\n", 1712 me->map_mntlevel, 1713 me->map_modified ? "modify=TRUE":"modify=FALSE", 1714 me->map_faked ? "faked=TRUE":"faked=FALSE", 1715 me->map_err); 1716 } 1717 } 1718 1719 /* 1720 * trace_hierarchy(hiernode *node) 1721 * traces the allocated hiernode given the head of the structure 1722 * recursively calls itself until it traces entire structure. 1723 * the first call made at the root is made with a zero level. 1724 * nodelevel is simply used to print tab and make the tracing clean. 1725 * Returns nothing. 1726 */ 1727 static void 1728 trace_hierarchy(hiernode *node, int nodelevel) 1729 { 1730 hiernode *currnode = node; 1731 int i; 1732 1733 while (currnode != NULL) { 1734 if (currnode->subdir != NULL) { 1735 for (i = 0; i < nodelevel; i++) 1736 trace_prt(0, "\t"); 1737 trace_prt(0, "\t(%s, ", currnode->dirname); 1738 if (currnode->mapent) { 1739 trace_prt(0, "%d, %s)\n", 1740 currnode->mapent->map_mntlevel, 1741 currnode->mapent->map_mntopts ? 1742 currnode->mapent->map_mntopts:""); 1743 } 1744 else 1745 trace_prt(0, " ,)\n"); 1746 nodelevel++; 1747 trace_hierarchy(currnode->subdir, nodelevel); 1748 } else { 1749 for (i = 0; i < nodelevel; i++) 1750 trace_prt(0, "\t"); 1751 trace_prt(0, "\t(%s, ", currnode->dirname); 1752 if (currnode->mapent) { 1753 trace_prt(0, "%d, %s)\n", 1754 currnode->mapent->map_mntlevel, 1755 currnode->mapent->map_mntopts ? 1756 currnode->mapent->map_mntopts:""); 1757 } 1758 else 1759 trace_prt(0, ", )\n"); 1760 } 1761 currnode = currnode->leveldir; 1762 } 1763 } 1764 1765 struct mapent * 1766 do_mapent_hosts(char *mapopts, char *host, uint_t isdirect) 1767 { 1768 CLIENT *cl; 1769 struct mapent *me, *ms, *mp; 1770 struct mapfs *mfs; 1771 struct exportnode *ex = NULL; 1772 struct exportnode *exlist, *texlist, **texp, *exnext; 1773 struct timeval timeout; 1774 enum clnt_stat clnt_stat; 1775 char name[MAXPATHLEN]; 1776 char entryopts[MAXOPTSLEN]; 1777 char fstype[32], mounter[32]; 1778 int exlen, duplicate; 1779 struct mnttab mb; /* needed for hasmntopt() to get nfs version */ 1780 rpcvers_t nfsvers; /* version in map options, 0 if not there */ 1781 rpcvers_t vers, versmin; /* used to negotiate nfs vers in pingnfs() */ 1782 int retries, delay; 1783 int foundvers; 1784 1785 if (trace > 1) 1786 trace_prt(1, " do_mapent_hosts: host %s\n", host); 1787 1788 /* check for special case: host is me */ 1789 1790 if (self_check(host)) { 1791 ms = (struct mapent *)malloc(sizeof (*ms)); 1792 if (ms == NULL) 1793 goto alloc_failed; 1794 (void) memset((char *)ms, 0, sizeof (*ms)); 1795 (void) strcpy(fstype, MNTTYPE_NFS); 1796 get_opts(mapopts, entryopts, fstype, NULL); 1797 ms->map_mntopts = strdup(entryopts); 1798 if (ms->map_mntopts == NULL) 1799 goto alloc_failed; 1800 ms->map_mounter = strdup(fstype); 1801 if (ms->map_mounter == NULL) 1802 goto alloc_failed; 1803 ms->map_fstype = strdup(MNTTYPE_NFS); 1804 if (ms->map_fstype == NULL) 1805 goto alloc_failed; 1806 1807 if (isdirect) 1808 name[0] = '\0'; 1809 else { 1810 (void) strcpy(name, "/"); 1811 (void) strcat(name, host); 1812 } 1813 ms->map_root = strdup(name); 1814 if (ms->map_root == NULL) 1815 goto alloc_failed; 1816 ms->map_mntpnt = strdup(""); 1817 if (ms->map_mntpnt == NULL) 1818 goto alloc_failed; 1819 mfs = (struct mapfs *)malloc(sizeof (*mfs)); 1820 if (mfs == NULL) 1821 goto alloc_failed; 1822 (void) memset((char *)mfs, 0, sizeof (*mfs)); 1823 ms->map_fs = mfs; 1824 mfs->mfs_host = strdup(host); 1825 if (mfs->mfs_host == NULL) 1826 goto alloc_failed; 1827 mfs->mfs_dir = strdup("/"); 1828 if (mfs->mfs_dir == NULL) 1829 goto alloc_failed; 1830 1831 /* initialize mntlevel and modify */ 1832 ms->map_mntlevel = -1; 1833 ms->map_modified = FALSE; 1834 ms->map_faked = FALSE; 1835 1836 if (trace > 1) 1837 trace_prt(1, 1838 " do_mapent_hosts: self-host %s OK\n", host); 1839 1840 return (ms); 1841 } 1842 1843 /* 1844 * Call pingnfs. Note that we can't have replicated hosts in /net. 1845 * XXX - we would like to avoid duplicating the across the wire calls 1846 * made here in nfsmount(). The pingnfs cache should help avoid it. 1847 */ 1848 mb.mnt_mntopts = mapopts; 1849 foundvers = nopt(&mb, MNTOPT_VERS, (int *)&nfsvers); 1850 if (!foundvers) 1851 nfsvers = 0; 1852 if (set_versrange(nfsvers, &vers, &versmin) != 0) { 1853 syslog(LOG_ERR, "Incorrect NFS version specified for %s", host); 1854 return (NULL); 1855 } 1856 if (pingnfs(host, get_retry(mapopts) + 1, &vers, versmin, 0, FALSE, 1857 NULL, NULL) != RPC_SUCCESS) 1858 return (NULL); 1859 1860 retries = get_retry(mapopts); 1861 delay = INITDELAY; 1862 retry: 1863 /* get export list of host */ 1864 cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "circuit_v"); 1865 if (cl == NULL) { 1866 cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "datagram_v"); 1867 if (cl == NULL) { 1868 syslog(LOG_ERR, 1869 "do_mapent_hosts: %s %s", host, clnt_spcreateerror("")); 1870 return (NULL); 1871 } 1872 1873 } 1874 #ifdef MALLOC_DEBUG 1875 add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__); 1876 add_alloc("AUTH_HANDLE", cl->cl_auth, 0, __FILE__, __LINE__); 1877 #endif 1878 1879 timeout.tv_usec = 0; 1880 timeout.tv_sec = 25; 1881 if (clnt_stat = clnt_call(cl, MOUNTPROC_EXPORT, xdr_void, 0, 1882 xdr_exports, (caddr_t)&ex, timeout)) { 1883 1884 if (retries-- > 0) { 1885 clnt_destroy(cl); 1886 DELAY(delay); 1887 goto retry; 1888 } 1889 1890 syslog(LOG_ERR, 1891 "do_mapent_hosts: %s: export list: %s", 1892 host, clnt_sperrno(clnt_stat)); 1893 #ifdef MALLOC_DEBUG 1894 drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__); 1895 drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__); 1896 #endif 1897 clnt_destroy(cl); 1898 return ((struct mapent *)NULL); 1899 } 1900 1901 #ifdef MALLOC_DEBUG 1902 drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__); 1903 drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__); 1904 #endif 1905 clnt_destroy(cl); 1906 1907 if (ex == NULL) { 1908 if (trace > 1) 1909 trace_prt(1, 1910 gettext(" getmapent_hosts: null export list\n")); 1911 return ((struct mapent *)NULL); 1912 } 1913 1914 /* now sort by length of names - to get mount order right */ 1915 exlist = ex; 1916 texlist = NULL; 1917 for (; ex; ex = exnext) { 1918 exnext = ex->ex_next; 1919 exlen = strlen(ex->ex_dir); 1920 duplicate = 0; 1921 for (texp = &texlist; *texp; texp = &((*texp)->ex_next)) { 1922 if (exlen < (int)strlen((*texp)->ex_dir)) 1923 break; 1924 duplicate = (strcmp(ex->ex_dir, (*texp)->ex_dir) == 0); 1925 if (duplicate) { 1926 /* disregard duplicate entry */ 1927 freeex_ent(ex); 1928 break; 1929 } 1930 } 1931 if (!duplicate) { 1932 ex->ex_next = *texp; 1933 *texp = ex; 1934 } 1935 } 1936 exlist = texlist; 1937 1938 (void) strcpy(fstype, MNTTYPE_NFS); 1939 get_opts(mapopts, entryopts, fstype, NULL); 1940 (void) strcpy(mounter, fstype); 1941 1942 /* Now create a mapent from the export list */ 1943 ms = NULL; 1944 me = NULL; 1945 1946 for (ex = exlist; ex; ex = ex->ex_next) { 1947 mp = me; 1948 me = (struct mapent *)malloc(sizeof (*me)); 1949 if (me == NULL) 1950 goto alloc_failed; 1951 (void) memset((char *)me, 0, sizeof (*me)); 1952 1953 if (ms == NULL) 1954 ms = me; 1955 else 1956 mp->map_next = me; 1957 1958 if (isdirect) 1959 name[0] = '\0'; 1960 else { 1961 (void) strcpy(name, "/"); 1962 (void) strcat(name, host); 1963 } 1964 me->map_root = strdup(name); 1965 if (me->map_root == NULL) 1966 goto alloc_failed; 1967 1968 *name = '\0'; 1969 if (strcmp(ex->ex_dir, "/") != 0) { 1970 if (*(ex->ex_dir) != '/') 1971 (void) strcpy(name, "/"); 1972 (void) strcat(name, ex->ex_dir); 1973 } 1974 me->map_mntpnt = strdup(name); 1975 if (me->map_mntpnt == NULL) 1976 goto alloc_failed; 1977 1978 me->map_fstype = strdup(fstype); 1979 if (me->map_fstype == NULL) 1980 goto alloc_failed; 1981 me->map_mounter = strdup(mounter); 1982 if (me->map_mounter == NULL) 1983 goto alloc_failed; 1984 me->map_mntopts = strdup(entryopts); 1985 if (me->map_mntopts == NULL) 1986 goto alloc_failed; 1987 1988 mfs = (struct mapfs *)malloc(sizeof (*mfs)); 1989 if (mfs == NULL) 1990 goto alloc_failed; 1991 (void) memset((char *)mfs, 0, sizeof (*mfs)); 1992 me->map_fs = mfs; 1993 mfs->mfs_host = strdup(host); 1994 if (mfs->mfs_host == NULL) 1995 goto alloc_failed; 1996 mfs->mfs_dir = strdup(ex->ex_dir); 1997 if (mfs->mfs_dir == NULL) 1998 goto alloc_failed; 1999 2000 /* initialize mntlevel and modify values */ 2001 me->map_mntlevel = -1; 2002 me->map_modified = FALSE; 2003 me->map_faked = FALSE; 2004 } 2005 freeex(exlist); 2006 2007 if (trace > 1) 2008 trace_prt(1, " do_mapent_hosts: host %s OK\n", host); 2009 2010 return (ms); 2011 2012 alloc_failed: 2013 syslog(LOG_ERR, "do_mapent_hosts: Memory allocation failed"); 2014 free_mapent(ms); 2015 freeex(exlist); 2016 return (NULL); 2017 } 2018 2019 2020 static void 2021 freeex_ent(struct exportnode *ex) 2022 { 2023 struct groupnode *groups, *tmpgroups; 2024 2025 free(ex->ex_dir); 2026 groups = ex->ex_groups; 2027 while (groups) { 2028 free(groups->gr_name); 2029 tmpgroups = groups->gr_next; 2030 free((char *)groups); 2031 groups = tmpgroups; 2032 } 2033 free(ex); 2034 } 2035 2036 static void 2037 freeex(struct exportnode *ex) 2038 { 2039 struct exportnode *tmpex; 2040 2041 while (ex) { 2042 tmpex = ex->ex_next; 2043 freeex_ent(ex); 2044 ex = tmpex; 2045 } 2046 } 2047 2048 static const char uatfs_err[] = "submount under fstype=autofs not supported"; 2049 /* 2050 * dump_mapent_err(struct mapent *me, char *key, char *mapname) 2051 * syslog appropriate error in mapentries. 2052 */ 2053 static void dump_mapent_err(struct mapent *me, char *key, char *mapname) 2054 { 2055 switch (me->map_err) { 2056 case MAPENT_NOERR: 2057 if (verbose) 2058 syslog(LOG_ERR, 2059 "map=%s key=%s mntpnt=%s: no error"); 2060 break; 2061 case MAPENT_UATFS: 2062 syslog(LOG_ERR, 2063 "mountpoint %s in map %s key %s not mounted: %s", 2064 me->map_mntpnt, mapname, key, uatfs_err); 2065 break; 2066 default: 2067 if (verbose) 2068 syslog(LOG_ERR, 2069 "map=%s key=%s mntpnt=%s: unknown mapentry error"); 2070 } 2071 } 2072