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(input, opts, fstype, fstype_opt) 649 char *input; 650 char *opts; /* output */ 651 char *fstype; /* output */ 652 bool_t *fstype_opt; 653 { 654 char *p, *pb; 655 char buf[MAXOPTSLEN]; 656 char *placeholder; 657 658 *opts = '\0'; 659 (void) strcpy(buf, input); 660 pb = buf; 661 while (p = (char *)strtok_r(pb, ",", &placeholder)) { 662 pb = NULL; 663 if (strncmp(p, FSTYPE_EQ, 7) == 0) { 664 if (fstype_opt != NULL) 665 *fstype_opt = TRUE; 666 (void) strcpy(fstype, p + 7); 667 } else { 668 if (*opts) 669 (void) strcat(opts, ","); 670 (void) strcat(opts, p); 671 } 672 } 673 } 674 675 /* 676 * fstype_opts(struct mapent *me, char *opts, char *defaultopts, 677 * char *mapopts) 678 * We need to push global options to the child entry if it is exactly 679 * fstype=somefs. 680 */ 681 static int 682 fstype_opts(struct mapent *me, char *opts, char *defaultopts, 683 char *mapopts) 684 { 685 char pushentryopts[AUTOFS_MAXOPTSLEN]; 686 char pushfstype[MAX_FSLEN]; 687 688 if (defaultopts && *defaultopts == '-') 689 defaultopts++; 690 691 /* 692 * the options to push are the global defaults for the entry, 693 * if they exist, or mapopts, if the global defaults for the 694 * entry does not exist. 695 */ 696 if (strcmp(defaultopts, opts) == 0) { 697 if (*mapopts == '-') 698 mapopts++; 699 get_opts(mapopts, pushentryopts, pushfstype, NULL); 700 } else { 701 get_opts(defaultopts, pushentryopts, pushfstype, NULL); 702 } 703 704 me->map_mntopts = strdup(pushentryopts); 705 706 if (!me->map_mntopts) { 707 syslog(LOG_ERR, "fstype_opts: No memory"); 708 return (ENOMEM); 709 } 710 711 return (PARSE_OK); 712 } 713 714 /* 715 * modify_mapents(struct mapent **mapents, char *mapname, 716 * char *mapopts, char *subdir, hiernode *rootnode, 717 * char *key, uint_t isdirect, bool_t mount_access) 718 * modifies the intermediate mapentry list into the final one, and passes 719 * back a pointer to it. The final list may contain faked mapentries for 720 * hiernodes that do not point to a mapentry, or converted mapentries, if 721 * hiernodes that point to a mapentry need to be converted from nfs to autofs. 722 * mounts. Entries that are not directly 1 level below the subdir are removed. 723 * Returns PARSE_OK or PARSE_ERROR 724 */ 725 static int 726 modify_mapents(struct mapent **mapents, char *mapname, 727 char *mapopts, char *subdir, hiernode *rootnode, 728 char *key, uint_t isdirect, bool_t mount_access) 729 { 730 struct mapent *mp = NULL; 731 char w[MAXPATHLEN]; 732 733 struct mapent *me; 734 int rc = PARSE_OK; 735 struct mapent *faked_mapents = NULL; 736 737 /* 738 * correct the mapentry mntlevel from default -1 to level depending on 739 * position in hierarchy, and build any faked mapentries, if required 740 * at one level below the rootnode given by subdir. 741 */ 742 if ((rc = set_and_fake_mapent_mntlevel(rootnode, subdir, key, mapname, 743 &faked_mapents, isdirect, mapopts, mount_access)) != PARSE_OK) 744 return (rc); 745 746 /* 747 * attaches faked mapents to real mapents list. Assumes mapents 748 * is not NULL. 749 */ 750 me = *mapents; 751 while (me->map_next != NULL) 752 me = me->map_next; 753 me->map_next = faked_mapents; 754 755 /* 756 * get rid of nodes marked at level -1 757 */ 758 me = *mapents; 759 while (me != NULL) { 760 if ((me->map_mntlevel == -1) || (me->map_err) || 761 (mount_access == FALSE && me->map_mntlevel == 0)) { 762 /* 763 * syslog any errors and free entry 764 */ 765 if (me->map_err) 766 dump_mapent_err(me, key, mapname); 767 768 if (me == (*mapents)) { 769 /* special case when head has to be freed */ 770 *mapents = me->map_next; 771 if ((*mapents) == NULL) { 772 /* something wierd happened */ 773 if (verbose) 774 syslog(LOG_ERR, 775 "modify_mapents: level error"); 776 return (PARSE_ERROR); 777 } 778 779 /* separate out the node */ 780 me->map_next = NULL; 781 free_mapent(me); 782 me = *mapents; 783 } else { 784 mp->map_next = me->map_next; 785 me->map_next = NULL; 786 free_mapent(me); 787 me = mp->map_next; 788 } 789 continue; 790 } 791 792 /* 793 * convert level 1 mapents that are not already autonodes 794 * to autonodes 795 */ 796 if (me->map_mntlevel == 1 && 797 (strcmp(me->map_fstype, MNTTYPE_AUTOFS) != 0) && 798 (me->map_faked != TRUE)) { 799 if ((rc = convert_mapent_to_automount(me, mapname, 800 mapopts)) != PARSE_OK) 801 return (rc); 802 } 803 strcpy(w, (me->map_mntpnt+strlen(subdir))); 804 strcpy(me->map_mntpnt, w); 805 mp = me; 806 me = me->map_next; 807 } 808 809 if (trace > 3) 810 trace_mapents("modify_mapents:", *mapents); 811 812 return (PARSE_OK); 813 } 814 815 /* 816 * set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key, 817 * char *mapname, struct mapent **faked_mapents, 818 * uint_t isdirect, char *mapopts, bool_t mount_access) 819 * sets the mapentry mount levels (depths) with respect to the subdir. 820 * Assigns a value of 0 to the new root. Finds the level1 directories by 821 * calling mark_*_level1_*(). Also cleans off extra /'s in level0 and 822 * level1 map_mntpnts. Note that one level below the new root is an existing 823 * mapentry if there's a mapentry (nfs mount) corresponding to the root, 824 * and the direct subdir set for the root, if there's no mapentry corresponding 825 * to the root (we install autodirs). Returns PARSE_OK or error value. 826 */ 827 static int 828 set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key, 829 char *mapname, struct mapent **faked_mapents, 830 uint_t isdirect, char *mapopts, bool_t mount_access) 831 { 832 char dirname[MAXFILENAMELEN]; 833 char traversed_path[MAXPATHLEN]; /* used in building fake mapentries */ 834 835 char *subdir_child = subdir; 836 hiernode *prevnode = rootnode; 837 hiernode *currnode = rootnode->subdir; 838 int rc = PARSE_OK; 839 traversed_path[0] = '\0'; 840 841 /* 842 * find and mark the root by tracing down subdir. Use traversed_path 843 * to keep track of how far we go, while guaranteeing that it 844 * contains no '/' at the end. Took some mucking to get that right. 845 */ 846 if ((rc = get_dir_from_path(dirname, &subdir_child, sizeof (dirname))) 847 != PARSE_OK) 848 return (rc); 849 850 if (dirname[0] != '\0') 851 sprintf(traversed_path, "%s/%s", traversed_path, dirname); 852 853 prevnode = rootnode; 854 currnode = rootnode->subdir; 855 while (dirname[0] != '\0' && currnode != NULL) { 856 if (strcmp(currnode->dirname, dirname) == 0) { 857 858 /* subdir is a child of currnode */ 859 prevnode = currnode; 860 currnode = currnode->subdir; 861 862 if ((rc = get_dir_from_path(dirname, &subdir_child, 863 sizeof (dirname))) != PARSE_OK) 864 return (rc); 865 if (dirname[0] != '\0') 866 sprintf(traversed_path, "%s/%s", 867 traversed_path, dirname); 868 869 } else { 870 /* try next leveldir */ 871 prevnode = currnode; 872 currnode = currnode->leveldir; 873 } 874 } 875 876 if (dirname[0] != '\0') { 877 if (verbose) 878 syslog(LOG_ERR, 879 "set_and_fake_mapent_mntlevel: subdir=%s error: map=%s", 880 subdir, mapname); 881 return (PARSE_ERROR); 882 } 883 884 /* 885 * see if level of root really points to a mapent and if 886 * have access to that filessystem - call appropriate 887 * routine to mark level 1 nodes, and build faked entries 888 */ 889 if (prevnode->mapent != NULL && mount_access == TRUE) { 890 if (trace > 3) 891 trace_prt(1, " node mountpoint %s\t travpath=%s\n", 892 prevnode->mapent->map_mntpnt, traversed_path); 893 894 /* 895 * Copy traversed path map_mntpnt to get rid of any extra 896 * '/' the map entry may contain. 897 */ 898 if (strlen(prevnode->mapent->map_mntpnt) < 899 strlen(traversed_path)) { /* sanity check */ 900 if (verbose) 901 syslog(LOG_ERR, 902 "set_and_fake_mapent_mntlevel: path=%s error", 903 traversed_path); 904 return (PARSE_ERROR); 905 } 906 if (strcmp(prevnode->mapent->map_mntpnt, traversed_path) != 0) 907 strcpy(prevnode->mapent->map_mntpnt, traversed_path); 908 909 prevnode->mapent->map_mntlevel = 0; /* root level is 0 */ 910 if (currnode != NULL) { 911 if ((rc = mark_level1_root(currnode, 912 traversed_path)) != PARSE_OK) 913 return (rc); 914 } 915 } else if (currnode != NULL) { 916 if (trace > 3) 917 trace_prt(1, " No rootnode, travpath=%s\n", 918 traversed_path); 919 if ((rc = mark_and_fake_level1_noroot(currnode, 920 traversed_path, key, mapname, faked_mapents, isdirect, 921 mapopts)) != PARSE_OK) 922 return (rc); 923 } 924 925 if (trace > 3) { 926 trace_prt(1, "\n\tset_and_fake_mapent_mntlevel\n"); 927 trace_hierarchy(rootnode, 0); 928 } 929 930 return (rc); 931 } 932 933 934 /* 935 * mark_level1_root(hiernode *node, char *traversed_path) 936 * marks nodes upto one level below the rootnode given by subdir 937 * recursively. Called if rootnode points to a mapent. 938 * In this routine, a level 1 node is considered to be the 1st existing 939 * mapentry below the root node, so there's no faking involved. 940 * Returns PARSE_OK or error value 941 */ 942 static int 943 mark_level1_root(hiernode *node, char *traversed_path) 944 { 945 /* ensure we touch all leveldirs */ 946 while (node) { 947 /* 948 * mark node level as 1, if one exists - else walk down 949 * subdirs until we find one. 950 */ 951 if (node->mapent == NULL) { 952 char w[MAXPATHLEN]; 953 954 if (node->subdir != NULL) { 955 sprintf(w, "%s/%s", traversed_path, 956 node->dirname); 957 if (mark_level1_root(node->subdir, w) 958 == PARSE_ERROR) 959 return (PARSE_ERROR); 960 } else { 961 if (verbose) { 962 syslog(LOG_ERR, 963 "mark_level1_root: hierarchy error"); 964 } 965 return (PARSE_ERROR); 966 } 967 } else { 968 char w[MAXPATHLEN]; 969 970 sprintf(w, "%s/%s", traversed_path, node->dirname); 971 if (trace > 3) 972 trace_prt(1, " node mntpnt %s\t travpath %s\n", 973 node->mapent->map_mntpnt, w); 974 975 /* replace mntpnt with travpath to clean extra '/' */ 976 if (strlen(node->mapent->map_mntpnt) < strlen(w)) { 977 if (verbose) { 978 syslog(LOG_ERR, 979 "mark_level1_root: path=%s error", 980 traversed_path); 981 } 982 return (PARSE_ERROR); 983 } 984 if (strcmp(node->mapent->map_mntpnt, w) != 0) 985 strcpy(node->mapent->map_mntpnt, w); 986 node->mapent->map_mntlevel = 1; 987 } 988 node = node->leveldir; 989 } 990 return (PARSE_OK); 991 } 992 993 /* 994 * mark_and_fake_level1_noroot(hiernode *node, char *traversed_path, 995 * char *key,char *mapname, struct mapent **faked_mapents, 996 * uint_t isdirect, char *mapopts) 997 * Called if the root of the hierarchy does not point to a mapent. marks nodes 998 * upto one physical level below the rootnode given by subdir. checks if 999 * there's a real mapentry. If not, it builds a faked one (autonode) at that 1000 * point. The faked autonode is direct, with the map being the same as the 1001 * original one from which the call originated. Options are same as that of 1002 * the map and assigned in automount_opts(). Returns PARSE_OK or error value. 1003 */ 1004 static int 1005 mark_and_fake_level1_noroot(hiernode *node, char *traversed_path, 1006 char *key, char *mapname, struct mapent **faked_mapents, 1007 uint_t isdirect, char *mapopts) 1008 { 1009 struct mapent *me; 1010 int rc = 0; 1011 char faked_map_mntpnt[MAXPATHLEN]; 1012 char w1[MAXPATHLEN]; 1013 char w[MAXPATHLEN]; 1014 1015 while (node != NULL) { 1016 if (node->mapent != NULL) { 1017 /* 1018 * existing mapentry at level 1 - copy travpath to 1019 * get rid of extra '/' in mntpnt 1020 */ 1021 sprintf(w, "%s/%s", traversed_path, node->dirname); 1022 if (trace > 3) 1023 trace_prt(1, " node mntpnt=%s\t travpath=%s\n", 1024 node->mapent->map_mntpnt, w); 1025 if (strlen(node->mapent->map_mntpnt) < strlen(w)) { 1026 /* sanity check */ 1027 if (verbose) 1028 syslog(LOG_ERR, 1029 "mark_fake_level1_noroot:path=%s error", 1030 traversed_path); 1031 return (PARSE_ERROR); 1032 } 1033 if (strcmp(node->mapent->map_mntpnt, w) != 0) 1034 strcpy(node->mapent->map_mntpnt, w); 1035 node->mapent->map_mntlevel = 1; 1036 } else { 1037 /* 1038 * build the faked autonode 1039 */ 1040 if ((me = (struct mapent *)malloc(sizeof (*me))) 1041 == NULL) { 1042 syslog(LOG_ERR, 1043 "mark_and_fake_level1_noroot: out of memory"); 1044 return (ENOMEM); 1045 } 1046 (void) memset((char *)me, 0, sizeof (*me)); 1047 1048 if ((me->map_fs = (struct mapfs *) 1049 malloc(sizeof (struct mapfs))) == NULL) 1050 return (ENOMEM); 1051 (void) memset(me->map_fs, 0, sizeof (struct mapfs)); 1052 1053 if (isdirect) { 1054 *w1 = '\0'; 1055 } else { 1056 strcpy(w1, "/"); 1057 strcat(w1, key); 1058 } 1059 me->map_root = strdup(w1); 1060 1061 sprintf(faked_map_mntpnt, "%s/%s", traversed_path, 1062 node->dirname); 1063 me->map_mntpnt = strdup(faked_map_mntpnt); 1064 me->map_fstype = strdup(MNTTYPE_AUTOFS); 1065 me->map_mounter = strdup(MNTTYPE_AUTOFS); 1066 1067 /* set options */ 1068 if ((rc = automount_opts(&me->map_mntopts, mapopts)) 1069 != PARSE_OK) 1070 return (rc); 1071 me->map_fs->mfs_dir = strdup(mapname); 1072 me->map_mntlevel = 1; 1073 me->map_modified = FALSE; 1074 me->map_faked = TRUE; /* mark as faked */ 1075 if (me->map_root == NULL || 1076 me->map_mntpnt == NULL || 1077 me->map_fstype == NULL || 1078 me->map_mounter == NULL || 1079 me->map_mntopts == NULL || 1080 me->map_fs->mfs_dir == NULL) { 1081 syslog(LOG_ERR, 1082 "mark_and_fake_level1_noroot: out of memory"); 1083 free_mapent(*faked_mapents); 1084 return (ENOMEM); 1085 } 1086 1087 if (*faked_mapents == NULL) 1088 *faked_mapents = me; 1089 else { /* attach to the head */ 1090 me->map_next = *faked_mapents; 1091 *faked_mapents = me; 1092 } 1093 node->mapent = me; 1094 } 1095 node = node->leveldir; 1096 } 1097 return (rc); 1098 } 1099 1100 /* 1101 * convert_mapent_to_automount(struct mapent *me, char *mapname, 1102 * char *mapopts) 1103 * change the mapentry me to an automount - free fields first and NULL them 1104 * to avoid freeing again, while freeing the mapentry at a later stage. 1105 * Could have avoided freeing entries here as we don't really look at them. 1106 * Give the converted mapent entry the options that came with the map using 1107 * automount_opts(). Returns PARSE_OK or appropriate error value. 1108 */ 1109 static int 1110 convert_mapent_to_automount(struct mapent *me, char *mapname, 1111 char *mapopts) 1112 { 1113 struct mapfs *mfs = me->map_fs; /* assumes it exists */ 1114 int rc = PARSE_OK; 1115 1116 /* free relevant entries */ 1117 if (mfs->mfs_host) { 1118 free(mfs->mfs_host); 1119 mfs->mfs_host = NULL; 1120 } 1121 while (me->map_fs->mfs_next != NULL) { 1122 mfs = me->map_fs->mfs_next; 1123 if (mfs->mfs_host) 1124 free(mfs->mfs_host); 1125 if (mfs->mfs_dir) 1126 free(mfs->mfs_dir); 1127 me->map_fs->mfs_next = mfs->mfs_next; /* nulls eventually */ 1128 free((void*)mfs); 1129 } 1130 1131 /* replace relevant entries */ 1132 if (me->map_fstype) 1133 free(me->map_fstype); 1134 if ((me->map_fstype = strdup(MNTTYPE_AUTOFS)) == NULL) 1135 goto alloc_failed; 1136 1137 if (me->map_mounter) 1138 free(me->map_mounter); 1139 if ((me->map_mounter = strdup(me->map_fstype)) == NULL) 1140 goto alloc_failed; 1141 1142 if (me->map_fs->mfs_dir) 1143 free(me->map_fs->mfs_dir); 1144 if ((me->map_fs->mfs_dir = strdup(mapname)) == NULL) 1145 goto alloc_failed; 1146 1147 /* set options */ 1148 if (me->map_mntopts) 1149 free(me->map_mntopts); 1150 if ((rc = automount_opts(&me->map_mntopts, mapopts)) != PARSE_OK) 1151 return (rc); 1152 1153 /* mucked with this entry, set the map_modified field to TRUE */ 1154 me->map_modified = TRUE; 1155 1156 return (rc); 1157 1158 alloc_failed: 1159 syslog(LOG_ERR, 1160 "convert_mapent_to_automount: Memory allocation failed"); 1161 return (ENOMEM); 1162 } 1163 1164 /* 1165 * automount_opts(char **map_mntopts, char *mapopts) 1166 * modifies automount opts - gets rid of all "indirect" and "direct" strings 1167 * if they exist, and then adds a direct string to force a direct automount. 1168 * Rest of the mapopts stay intact. Returns PARSE_OK or appropriate error. 1169 */ 1170 static int 1171 automount_opts(char **map_mntopts, char *mapopts) 1172 { 1173 char *opts; 1174 char *opt; 1175 int len; 1176 char *placeholder; 1177 char buf[AUTOFS_MAXOPTSLEN]; 1178 1179 char *addopt = "direct"; 1180 1181 len = strlen(mapopts)+ strlen(addopt)+2; /* +2 for ",", '\0' */ 1182 if (len > AUTOFS_MAXOPTSLEN) { 1183 syslog(LOG_ERR, 1184 "option string %s too long (max=%d)", mapopts, 1185 AUTOFS_MAXOPTSLEN-8); 1186 return (PARSE_ERROR); 1187 } 1188 1189 if (((*map_mntopts) = ((char *)malloc(len))) == NULL) { 1190 syslog(LOG_ERR, "automount_opts: Memory allocation failed"); 1191 return (ENOMEM); 1192 } 1193 memset(*map_mntopts, 0, len); 1194 1195 strcpy(buf, mapopts); 1196 opts = buf; 1197 while ((opt = strtok_r(opts, ",", &placeholder)) != NULL) { 1198 opts = NULL; 1199 1200 /* remove trailing and leading spaces */ 1201 while (isspace(*opt)) 1202 opt++; 1203 len = strlen(opt)-1; 1204 while (isspace(opt[len])) 1205 opt[len--] = '\0'; 1206 1207 /* 1208 * if direct or indirect found, get rid of it, else put it 1209 * back 1210 */ 1211 if ((strcmp(opt, "indirect") == 0) || 1212 (strcmp(opt, "direct") == 0)) 1213 continue; 1214 if (*map_mntopts[0] != '\0') 1215 strcat(*map_mntopts, ","); 1216 strcat(*map_mntopts, opt); 1217 } 1218 1219 /* add the direct string at the end */ 1220 if (*map_mntopts[0] != '\0') 1221 strcat(*map_mntopts, ","); 1222 strcat(*map_mntopts, addopt); 1223 1224 return (PARSE_OK); 1225 } 1226 1227 /* 1228 * parse_fsinfo(char *mapname, struct mapent *mapents) 1229 * parses the filesystem information stored in me->map_fsw and me->map_fswq 1230 * and calls appropriate filesystem parser. 1231 * Returns PARSE_OK or an appropriate error value. 1232 */ 1233 static int 1234 parse_fsinfo(char *mapname, struct mapent *mapents) 1235 { 1236 struct mapent *me = mapents; 1237 char *bufp; 1238 char *bufq; 1239 int wordsz = MAXPATHLEN; 1240 int err = 0; 1241 1242 while (me != NULL) { 1243 bufp = ""; 1244 bufq = ""; 1245 if (strcmp(me->map_fstype, MNTTYPE_NFS) == 0) { 1246 err = parse_nfs(mapname, me, me->map_fsw, 1247 me->map_fswq, &bufp, &bufq, wordsz); 1248 } else { 1249 err = parse_special(me, me->map_fsw, me->map_fswq, 1250 &bufp, &bufq, wordsz); 1251 } 1252 1253 if (err != PARSE_OK || *me->map_fsw != '\0' || 1254 *me->map_fswq != '\0') { 1255 /* sanity check */ 1256 if (verbose) 1257 syslog(LOG_ERR, 1258 "parse_fsinfo: mount location error %s", 1259 me->map_fsw); 1260 return (PARSE_ERROR); 1261 } 1262 1263 me = me->map_next; 1264 } 1265 1266 if (trace > 3) { 1267 trace_mapents("parse_fsinfo:", mapents); 1268 } 1269 1270 return (PARSE_OK); 1271 } 1272 1273 /* 1274 * This function parses the map entry for a nfs type file system 1275 * The input is the string lp (and lq) which can be one of the 1276 * following forms: 1277 * a) host[(penalty)][,host[(penalty)]]... :/directory 1278 * b) host[(penalty)]:/directory[ host[(penalty)]:/directory]... 1279 * This routine constructs a mapfs link-list for each of 1280 * the hosts and the corresponding file system. The list 1281 * is then attatched to the mapent struct passed in. 1282 */ 1283 int 1284 parse_nfs(mapname, me, fsw, fswq, lp, lq, wsize) 1285 struct mapent *me; 1286 char *mapname, *fsw, *fswq, **lp, **lq; 1287 int wsize; 1288 { 1289 struct mapfs *mfs, **mfsp; 1290 char *wlp, *wlq; 1291 char *hl, hostlist[1024], *hlq, hostlistq[1024]; 1292 char hostname_and_penalty[MXHOSTNAMELEN+5]; 1293 char *hn, *hnq, hostname[MXHOSTNAMELEN+1]; 1294 char dirname[MAXPATHLEN+1], subdir[MAXPATHLEN+1]; 1295 char qbuff[MAXPATHLEN+1], qbuff1[MAXPATHLEN+1]; 1296 char pbuff[10], pbuffq[10]; 1297 int penalty; 1298 char w[MAXPATHLEN]; 1299 char wq[MAXPATHLEN]; 1300 int host_cnt; 1301 1302 mfsp = &me->map_fs; 1303 *mfsp = NULL; 1304 1305 /* 1306 * there may be more than one entry in the map list. Get the 1307 * first one. Use temps to handle the word information and 1308 * copy back into fsw and fswq fields when done. 1309 */ 1310 *lp = fsw; 1311 *lq = fswq; 1312 if (getword(w, wq, lp, lq, ' ', wsize) == -1) 1313 return (PARSE_ERROR); 1314 while (*w && *w != '/') { 1315 bool_t maybe_url; 1316 1317 maybe_url = TRUE; 1318 1319 wlp = w; wlq = wq; 1320 if (getword(hostlist, hostlistq, &wlp, &wlq, ':', 1321 sizeof (hostlist)) == -1) 1322 return (PARSE_ERROR); 1323 if (!*hostlist) 1324 goto bad_entry; 1325 1326 if (strcmp(hostlist, "nfs") != 0) 1327 maybe_url = FALSE; 1328 1329 if (getword(dirname, qbuff, &wlp, &wlq, ':', 1330 sizeof (dirname)) == -1) 1331 return (PARSE_ERROR); 1332 if (*dirname == '\0') 1333 goto bad_entry; 1334 1335 if (maybe_url == TRUE && strncmp(dirname, "//", 2) != 0) 1336 maybe_url = FALSE; 1337 1338 /* 1339 * See the next block comment ("Once upon a time ...") to 1340 * understand this. It turns the deprecated concept 1341 * of "subdir mounts" produced some useful code for handling 1342 * the possibility of a ":port#" in the URL. 1343 */ 1344 if (maybe_url == FALSE) 1345 *subdir = '/'; 1346 else 1347 *subdir = ':'; 1348 1349 *qbuff = ' '; 1350 1351 /* 1352 * Once upon time, before autofs, there was support for 1353 * "subdir mounts". The idea was to "economize" the 1354 * number of mounts, so if you had a number of entries 1355 * all referring to a common subdirectory, e.g. 1356 * 1357 * carol seasons:/export/home11/carol 1358 * ted seasons:/export/home11/ted 1359 * alice seasons:/export/home11/alice 1360 * 1361 * then you could tell the automounter to mount a 1362 * common mountpoint which was delimited by the second 1363 * colon: 1364 * 1365 * carol seasons:/export/home11:carol 1366 * ted seasons:/export/home11:ted 1367 * alice seasons:/export/home11:alice 1368 * 1369 * The automounter would mount seasons:/export/home11 1370 * then for any other map entry that referenced the same 1371 * directory it would build a symbolic link that 1372 * appended the remainder of the path after the second 1373 * colon, i.e. once the common subdir was mounted, then 1374 * other directories could be accessed just by link 1375 * building - no further mounts required. 1376 * 1377 * In theory the "mount saving" idea sounded good. In 1378 * practice the saving didn't amount to much and the 1379 * symbolic links confused people because the common 1380 * mountpoint had to have a pseudonym. 1381 * 1382 * To remain backward compatible with the existing 1383 * maps, we interpret a second colon as a slash. 1384 */ 1385 if (getword(subdir+1, qbuff+1, &wlp, &wlq, ':', 1386 sizeof (subdir)) == -1) 1387 return (PARSE_ERROR); 1388 1389 if (*(subdir+1)) 1390 (void) strcat(dirname, subdir); 1391 1392 hl = hostlist; hlq = hostlistq; 1393 1394 host_cnt = 0; 1395 for (;;) { 1396 1397 if (getword(hostname_and_penalty, qbuff, &hl, &hlq, ',', 1398 sizeof (hostname_and_penalty)) == -1) 1399 return (PARSE_ERROR); 1400 if (!*hostname_and_penalty) 1401 break; 1402 1403 host_cnt++; 1404 if (host_cnt > 1) 1405 maybe_url = FALSE; 1406 1407 hn = hostname_and_penalty; 1408 hnq = qbuff; 1409 if (getword(hostname, qbuff1, &hn, &hnq, '(', 1410 sizeof (hostname)) == -1) 1411 return (PARSE_ERROR); 1412 if (hostname[0] == '\0') 1413 goto bad_entry; 1414 1415 if (strcmp(hostname, hostname_and_penalty) == 0) { 1416 penalty = 0; 1417 } else { 1418 maybe_url = FALSE; 1419 hn++; hnq++; 1420 if (getword(pbuff, pbuffq, &hn, &hnq, ')', 1421 sizeof (pbuff)) == -1) 1422 return (PARSE_ERROR); 1423 if (!*pbuff) 1424 penalty = 0; 1425 else 1426 penalty = atoi(pbuff); 1427 } 1428 mfs = (struct mapfs *)malloc(sizeof (*mfs)); 1429 if (mfs == NULL) { 1430 syslog(LOG_ERR, 1431 "parse_nfs: Memory allocation failed"); 1432 return (PARSE_ERROR); 1433 } 1434 (void) memset(mfs, 0, sizeof (*mfs)); 1435 *mfsp = mfs; 1436 mfsp = &mfs->mfs_next; 1437 1438 if (maybe_url == TRUE) { 1439 char *host; 1440 char *path; 1441 char *sport; 1442 1443 host = dirname+2; 1444 path = strchr(host, '/'); 1445 if (path == NULL) { 1446 syslog(LOG_ERR, 1447 "parse_nfs: illegal nfs url syntax: %s", 1448 host); 1449 1450 return (PARSE_ERROR); 1451 } 1452 *path = '\0'; 1453 sport = strchr(host, ':'); 1454 1455 if (sport != NULL && sport < path) { 1456 *sport = '\0'; 1457 mfs->mfs_port = atoi(sport+1); 1458 1459 if (mfs->mfs_port > USHRT_MAX) { 1460 syslog(LOG_ERR, 1461 "parse_nfs: invalid " 1462 "port number (%d) in " 1463 "NFS URL", 1464 mfs->mfs_port); 1465 1466 return (PARSE_ERROR); 1467 } 1468 1469 } 1470 1471 path++; 1472 if (*path == '\0') 1473 path = "."; 1474 1475 mfs->mfs_flags |= MFS_URL; 1476 1477 mfs->mfs_host = strdup(host); 1478 mfs->mfs_dir = strdup(path); 1479 } else { 1480 mfs->mfs_host = strdup(hostname); 1481 mfs->mfs_dir = strdup(dirname); 1482 } 1483 1484 mfs->mfs_penalty = penalty; 1485 if (mfs->mfs_host == NULL || mfs->mfs_dir == NULL) { 1486 syslog(LOG_ERR, 1487 "parse_nfs: Memory allocation failed"); 1488 return (PARSE_ERROR); 1489 } 1490 } 1491 /* 1492 * We check host_cnt to make sure we haven't parsed an entry 1493 * with no host information. 1494 */ 1495 if (host_cnt == 0) { 1496 syslog(LOG_ERR, 1497 "parse_nfs: invalid host specified - bad entry " 1498 "in map %s \"%s\"", 1499 mapname, w); 1500 return (PARSE_ERROR); 1501 } 1502 if (getword(w, wq, lp, lq, ' ', wsize) == -1) 1503 return (PARSE_ERROR); 1504 } 1505 1506 strcpy(fsw, w); 1507 strcpy(fswq, wq); 1508 1509 return (PARSE_OK); 1510 1511 bad_entry: 1512 syslog(LOG_ERR, "parse_nfs: bad entry in map %s \"%s\"", mapname, w); 1513 return (PARSE_ERROR); 1514 } 1515 1516 static int 1517 parse_special(me, w, wq, lp, lq, wsize) 1518 struct mapent *me; 1519 char *w, *wq, **lp, **lq; 1520 int wsize; 1521 { 1522 char devname[MAXPATHLEN + 1], qbuf[MAXPATHLEN + 1]; 1523 char *wlp, *wlq; 1524 struct mapfs *mfs; 1525 1526 wlp = w; 1527 wlq = wq; 1528 if (getword(devname, qbuf, &wlp, &wlq, ' ', sizeof (devname)) == -1) 1529 return (PARSE_ERROR); 1530 if (devname[0] == '\0') 1531 return (PARSE_ERROR); 1532 1533 mfs = (struct mapfs *)malloc(sizeof (struct mapfs)); 1534 if (mfs == NULL) 1535 return (PARSE_ERROR); 1536 (void) memset(mfs, 0, sizeof (*mfs)); 1537 1538 /* 1539 * A device name that begins with a slash could 1540 * be confused with a mountpoint path, hence use 1541 * a colon to escape a device string that begins 1542 * with a slash, e.g. 1543 * 1544 * foo -ro /bar foo:/bar 1545 * and 1546 * foo -ro /dev/sr0 1547 * 1548 * would confuse the parser. The second instance 1549 * must use a colon: 1550 * 1551 * foo -ro :/dev/sr0 1552 */ 1553 mfs->mfs_dir = strdup(&devname[devname[0] == ':']); 1554 if (mfs->mfs_dir == NULL) 1555 return (PARSE_ERROR); 1556 me->map_fs = mfs; 1557 if (getword(w, wq, lp, lq, ' ', wsize) == -1) 1558 return (PARSE_ERROR); 1559 return (0); 1560 } 1561 1562 /* 1563 * get_dir_from_path(char *dir, char **path, int dirsz) 1564 * gets the directory name dir from path for max string of length dirsz. 1565 * A modification of the getword routine. Assumes the delimiter is '/' 1566 * and that excess /'s are redundant. 1567 * Returns PARSE_OK or PARSE_ERROR 1568 */ 1569 static int 1570 get_dir_from_path(char *dir, char **path, int dirsz) 1571 { 1572 char *tmp = dir; 1573 int count = dirsz; 1574 1575 if (dirsz <= 0) { 1576 if (verbose) 1577 syslog(LOG_ERR, 1578 "get_dir_from_path: invalid directory size %d", dirsz); 1579 return (PARSE_ERROR); 1580 } 1581 1582 /* get rid of leading /'s in path */ 1583 while (**path == '/') 1584 (*path)++; 1585 1586 /* now at a word or at the end of path */ 1587 while ((**path) && ((**path) != '/')) { 1588 if (--count <= 0) { 1589 *tmp = '\0'; 1590 syslog(LOG_ERR, 1591 "get_dir_from_path: max pathlength exceeded %d", dirsz); 1592 return (PARSE_ERROR); 1593 } 1594 *dir++ = *(*path)++; 1595 } 1596 1597 *dir = '\0'; 1598 1599 /* get rid of trailing /'s in path */ 1600 while (**path == '/') 1601 (*path)++; 1602 1603 return (PARSE_OK); 1604 } 1605 1606 /* 1607 * alloc_hiernode(hiernode **newnode, char *dirname) 1608 * allocates a new hiernode corresponding to a new directory entry 1609 * in the hierarchical structure, and passes a pointer to it back 1610 * to the calling program. 1611 * Returns PARSE_OK or appropriate error value. 1612 */ 1613 static int 1614 alloc_hiernode(hiernode **newnode, char *dirname) 1615 { 1616 if ((*newnode = (hiernode *)malloc(sizeof (hiernode))) == NULL) { 1617 syslog(LOG_ERR, "alloc_hiernode: Memory allocation failed"); 1618 return (ENOMEM); 1619 } 1620 1621 memset(((char *)*newnode), 0, sizeof (hiernode)); 1622 strcpy(((*newnode)->dirname), dirname); 1623 return (PARSE_OK); 1624 } 1625 1626 /* 1627 * free_hiernode(hiernode *node) 1628 * frees the allocated hiernode given the head of the structure 1629 * recursively calls itself until it frees entire structure. 1630 * Returns nothing. 1631 */ 1632 static void 1633 free_hiernode(hiernode *node) 1634 { 1635 hiernode *currnode = node; 1636 hiernode *prevnode = NULL; 1637 1638 while (currnode != NULL) { 1639 if (currnode->subdir != NULL) 1640 free_hiernode(currnode->subdir); 1641 prevnode = currnode; 1642 currnode = currnode->leveldir; 1643 free((void*)prevnode); 1644 } 1645 } 1646 1647 /* 1648 * free_mapent(struct mapent *) 1649 * free the mapentry and its fields 1650 */ 1651 void 1652 free_mapent(me) 1653 struct mapent *me; 1654 { 1655 struct mapfs *mfs; 1656 struct mapent *m; 1657 1658 while (me) { 1659 while (me->map_fs) { 1660 mfs = me->map_fs; 1661 if (mfs->mfs_host) 1662 free(mfs->mfs_host); 1663 if (mfs->mfs_dir) 1664 free(mfs->mfs_dir); 1665 if (mfs->mfs_args) 1666 free(mfs->mfs_args); 1667 if (mfs->mfs_nconf) 1668 freenetconfigent(mfs->mfs_nconf); 1669 me->map_fs = mfs->mfs_next; 1670 free((char *)mfs); 1671 } 1672 1673 if (me->map_root) 1674 free(me->map_root); 1675 if (me->map_mntpnt) 1676 free(me->map_mntpnt); 1677 if (me->map_mntopts) 1678 free(me->map_mntopts); 1679 if (me->map_fstype) 1680 free(me->map_fstype); 1681 if (me->map_mounter) 1682 free(me->map_mounter); 1683 if (me->map_fsw) 1684 free(me->map_fsw); 1685 if (me->map_fswq) 1686 free(me->map_fswq); 1687 1688 m = me; 1689 me = me->map_next; 1690 free((char *)m); 1691 } 1692 } 1693 1694 /* 1695 * trace_mapents(struct mapent *mapents) 1696 * traces through the mapentry structure and prints it element by element 1697 * returns nothing 1698 */ 1699 static void 1700 trace_mapents(char *s, struct mapent *mapents) 1701 { 1702 struct mapfs *mfs; 1703 struct mapent *me; 1704 1705 trace_prt(1, "\n\t%s\n", s); 1706 for (me = mapents; me; me = me->map_next) { 1707 trace_prt(1, " (%s,%s)\t %s%s -%s\n", 1708 me->map_fstype ? me->map_fstype : "", 1709 me->map_mounter ? me->map_mounter : "", 1710 me->map_root ? me->map_root : "", 1711 me->map_mntpnt ? me->map_mntpnt : "", 1712 me->map_mntopts ? me->map_mntopts : ""); 1713 for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next) 1714 trace_prt(0, "\t\t%s:%s\n", 1715 mfs->mfs_host ? mfs->mfs_host: "", 1716 mfs->mfs_dir ? mfs->mfs_dir : ""); 1717 1718 trace_prt(1, "\tme->map_fsw=%s\n", 1719 me->map_fsw ? me->map_fsw:"", 1720 me->map_fswq ? me->map_fsw:""); 1721 trace_prt(1, "\t mntlevel=%d\t%s\t%s err=%d\n", 1722 me->map_mntlevel, 1723 me->map_modified ? "modify=TRUE":"modify=FALSE", 1724 me->map_faked ? "faked=TRUE":"faked=FALSE", 1725 me->map_err); 1726 } 1727 } 1728 1729 /* 1730 * trace_hierarchy(hiernode *node) 1731 * traces the allocated hiernode given the head of the structure 1732 * recursively calls itself until it traces entire structure. 1733 * the first call made at the root is made with a zero level. 1734 * nodelevel is simply used to print tab and make the tracing clean. 1735 * Returns nothing. 1736 */ 1737 static void 1738 trace_hierarchy(hiernode *node, int nodelevel) 1739 { 1740 hiernode *currnode = node; 1741 int i; 1742 1743 while (currnode != NULL) { 1744 if (currnode->subdir != NULL) { 1745 for (i = 0; i < nodelevel; i++) 1746 trace_prt(0, "\t"); 1747 trace_prt(0, "\t(%s, ", 1748 currnode->dirname ? currnode->dirname :""); 1749 if (currnode->mapent) { 1750 trace_prt(0, "%d, %s)\n", 1751 currnode->mapent->map_mntlevel, 1752 currnode->mapent->map_mntopts ? 1753 currnode->mapent->map_mntopts:""); 1754 } 1755 else 1756 trace_prt(0, " ,)\n"); 1757 nodelevel++; 1758 trace_hierarchy(currnode->subdir, nodelevel); 1759 } else { 1760 for (i = 0; i < nodelevel; i++) 1761 trace_prt(0, "\t"); 1762 trace_prt(0, "\t(%s, ", 1763 currnode->dirname ? currnode->dirname :""); 1764 if (currnode->mapent) { 1765 trace_prt(0, "%d, %s)\n", 1766 currnode->mapent->map_mntlevel, 1767 currnode->mapent->map_mntopts ? 1768 currnode->mapent->map_mntopts:""); 1769 } 1770 else 1771 trace_prt(0, ", )\n"); 1772 } 1773 currnode = currnode->leveldir; 1774 } 1775 } 1776 1777 struct mapent * 1778 do_mapent_hosts(mapopts, host, isdirect) 1779 char *mapopts, *host; 1780 uint_t isdirect; 1781 { 1782 CLIENT *cl; 1783 struct mapent *me, *ms, *mp; 1784 struct mapfs *mfs; 1785 struct exportnode *ex = NULL; 1786 struct exportnode *exlist, *texlist, **texp, *exnext; 1787 struct timeval timeout; 1788 enum clnt_stat clnt_stat; 1789 char name[MAXPATHLEN]; 1790 char entryopts[MAXOPTSLEN]; 1791 char fstype[32], mounter[32]; 1792 int exlen, duplicate; 1793 struct mnttab mb; /* needed for hasmntopt() to get nfs version */ 1794 rpcvers_t nfsvers; /* version in map options, 0 if not there */ 1795 rpcvers_t vers, versmin; /* used to negotiate nfs vers in pingnfs() */ 1796 int retries, delay; 1797 int foundvers; 1798 1799 if (trace > 1) 1800 trace_prt(1, " do_mapent_hosts: host %s\n", host); 1801 1802 /* check for special case: host is me */ 1803 1804 if (self_check(host)) { 1805 ms = (struct mapent *)malloc(sizeof (*ms)); 1806 if (ms == NULL) 1807 goto alloc_failed; 1808 (void) memset((char *)ms, 0, sizeof (*ms)); 1809 (void) strcpy(fstype, MNTTYPE_NFS); 1810 get_opts(mapopts, entryopts, fstype, NULL); 1811 ms->map_mntopts = strdup(entryopts); 1812 if (ms->map_mntopts == NULL) 1813 goto alloc_failed; 1814 ms->map_mounter = strdup(fstype); 1815 if (ms->map_mounter == NULL) 1816 goto alloc_failed; 1817 ms->map_fstype = strdup(MNTTYPE_NFS); 1818 if (ms->map_fstype == NULL) 1819 goto alloc_failed; 1820 1821 if (isdirect) 1822 name[0] = '\0'; 1823 else { 1824 (void) strcpy(name, "/"); 1825 (void) strcat(name, host); 1826 } 1827 ms->map_root = strdup(name); 1828 if (ms->map_root == NULL) 1829 goto alloc_failed; 1830 ms->map_mntpnt = strdup(""); 1831 if (ms->map_mntpnt == NULL) 1832 goto alloc_failed; 1833 mfs = (struct mapfs *)malloc(sizeof (*mfs)); 1834 if (mfs == NULL) 1835 goto alloc_failed; 1836 (void) memset((char *)mfs, 0, sizeof (*mfs)); 1837 ms->map_fs = mfs; 1838 mfs->mfs_host = strdup(host); 1839 if (mfs->mfs_host == NULL) 1840 goto alloc_failed; 1841 mfs->mfs_dir = strdup("/"); 1842 if (mfs->mfs_dir == NULL) 1843 goto alloc_failed; 1844 1845 /* initialize mntlevel and modify */ 1846 ms->map_mntlevel = -1; 1847 ms->map_modified = FALSE; 1848 ms->map_faked = FALSE; 1849 1850 if (trace > 1) 1851 trace_prt(1, 1852 " do_mapent_hosts: self-host %s OK\n", host); 1853 1854 return (ms); 1855 } 1856 1857 /* 1858 * Call pingnfs. Note that we can't have replicated hosts in /net. 1859 * XXX - we would like to avoid duplicating the across the wire calls 1860 * made here in nfsmount(). The pingnfs cache should help avoid it. 1861 */ 1862 mb.mnt_mntopts = mapopts; 1863 foundvers = nopt(&mb, MNTOPT_VERS, (int *)&nfsvers); 1864 if (!foundvers) 1865 nfsvers = 0; 1866 if (set_versrange(nfsvers, &vers, &versmin) != 0) { 1867 syslog(LOG_ERR, "Incorrect NFS version specified for %s", host); 1868 return ((struct mapent *)NULL); 1869 } 1870 if (pingnfs(host, get_retry(mapopts) + 1, &vers, versmin, 0, FALSE, 1871 NULL, NULL) != RPC_SUCCESS) 1872 return ((struct mapent *)NULL); 1873 1874 retries = get_retry(mapopts); 1875 delay = INITDELAY; 1876 retry: 1877 /* get export list of host */ 1878 cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "circuit_v"); 1879 if (cl == NULL) { 1880 cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "datagram_v"); 1881 if (cl == NULL) { 1882 syslog(LOG_ERR, 1883 "do_mapent_hosts: %s %s", host, clnt_spcreateerror("")); 1884 return ((struct mapent *)NULL); 1885 } 1886 1887 } 1888 #ifdef MALLOC_DEBUG 1889 add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__); 1890 add_alloc("AUTH_HANDLE", cl->cl_auth, 0, 1891 __FILE__, __LINE__); 1892 #endif 1893 1894 timeout.tv_usec = 0; 1895 timeout.tv_sec = 25; 1896 if (clnt_stat = clnt_call(cl, MOUNTPROC_EXPORT, xdr_void, 0, 1897 xdr_exports, (caddr_t)&ex, timeout)) { 1898 1899 if (retries-- > 0) { 1900 clnt_destroy(cl); 1901 DELAY(delay); 1902 goto retry; 1903 } 1904 1905 syslog(LOG_ERR, 1906 "do_mapent_hosts: %s: export list: %s", 1907 host, clnt_sperrno(clnt_stat)); 1908 #ifdef MALLOC_DEBUG 1909 drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__); 1910 drop_alloc("AUTH_HANDLE", cl->cl_auth, 1911 __FILE__, __LINE__); 1912 #endif 1913 clnt_destroy(cl); 1914 return ((struct mapent *)NULL); 1915 } 1916 1917 #ifdef MALLOC_DEBUG 1918 drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__); 1919 drop_alloc("AUTH_HANDLE", cl->cl_auth, 1920 __FILE__, __LINE__); 1921 #endif 1922 clnt_destroy(cl); 1923 1924 if (ex == NULL) { 1925 if (trace > 1) 1926 trace_prt(1, 1927 gettext(" getmapent_hosts: null export list\n")); 1928 return ((struct mapent *)NULL); 1929 } 1930 1931 /* now sort by length of names - to get mount order right */ 1932 exlist = ex; 1933 texlist = NULL; 1934 #ifdef lint 1935 exnext = NULL; 1936 #endif 1937 for (; ex; ex = exnext) { 1938 exnext = ex->ex_next; 1939 exlen = strlen(ex->ex_dir); 1940 duplicate = 0; 1941 for (texp = &texlist; *texp; texp = &((*texp)->ex_next)) { 1942 if (exlen < (int)strlen((*texp)->ex_dir)) 1943 break; 1944 duplicate = (strcmp(ex->ex_dir, (*texp)->ex_dir) == 0); 1945 if (duplicate) { 1946 /* disregard duplicate entry */ 1947 freeex_ent(ex); 1948 break; 1949 } 1950 } 1951 if (!duplicate) { 1952 ex->ex_next = *texp; 1953 *texp = ex; 1954 } 1955 } 1956 exlist = texlist; 1957 1958 (void) strcpy(fstype, MNTTYPE_NFS); 1959 get_opts(mapopts, entryopts, fstype, NULL); 1960 (void) strcpy(mounter, fstype); 1961 1962 /* Now create a mapent from the export list */ 1963 ms = NULL; 1964 me = NULL; 1965 1966 for (ex = exlist; ex; ex = ex->ex_next) { 1967 mp = me; 1968 me = (struct mapent *)malloc(sizeof (*me)); 1969 if (me == NULL) 1970 goto alloc_failed; 1971 (void) memset((char *)me, 0, sizeof (*me)); 1972 1973 if (ms == NULL) 1974 ms = me; 1975 else 1976 mp->map_next = me; 1977 1978 if (isdirect) 1979 name[0] = '\0'; 1980 else { 1981 (void) strcpy(name, "/"); 1982 (void) strcat(name, host); 1983 } 1984 me->map_root = strdup(name); 1985 if (me->map_root == NULL) 1986 goto alloc_failed; 1987 1988 *name = '\0'; 1989 if (strcmp(ex->ex_dir, "/") != 0) { 1990 if (*(ex->ex_dir) != '/') 1991 (void) strcpy(name, "/"); 1992 (void) strcat(name, ex->ex_dir); 1993 } 1994 me->map_mntpnt = strdup(name); 1995 if (me->map_mntpnt == NULL) 1996 goto alloc_failed; 1997 1998 me->map_fstype = strdup(fstype); 1999 if (me->map_fstype == NULL) 2000 goto alloc_failed; 2001 me->map_mounter = strdup(mounter); 2002 if (me->map_mounter == NULL) 2003 goto alloc_failed; 2004 me->map_mntopts = strdup(entryopts); 2005 if (me->map_mntopts == NULL) 2006 goto alloc_failed; 2007 2008 mfs = (struct mapfs *)malloc(sizeof (*mfs)); 2009 if (mfs == NULL) 2010 goto alloc_failed; 2011 (void) memset((char *)mfs, 0, sizeof (*mfs)); 2012 me->map_fs = mfs; 2013 mfs->mfs_host = strdup(host); 2014 if (mfs->mfs_host == NULL) 2015 goto alloc_failed; 2016 mfs->mfs_dir = strdup(ex->ex_dir); 2017 if (mfs->mfs_dir == NULL) 2018 goto alloc_failed; 2019 2020 /* initialize mntlevel and modify values */ 2021 me->map_mntlevel = -1; 2022 me->map_modified = FALSE; 2023 me->map_faked = FALSE; 2024 } 2025 freeex(exlist); 2026 2027 if (trace > 1) 2028 trace_prt(1, " do_mapent_hosts: host %s OK\n", host); 2029 2030 return (ms); 2031 2032 alloc_failed: 2033 syslog(LOG_ERR, "do_mapent_hosts: Memory allocation failed"); 2034 free_mapent(ms); 2035 freeex(exlist); 2036 return ((struct mapent *)NULL); 2037 } 2038 2039 2040 static void 2041 freeex_ent(ex) 2042 struct exportnode *ex; 2043 { 2044 struct groupnode *groups, *tmpgroups; 2045 2046 free(ex->ex_dir); 2047 groups = ex->ex_groups; 2048 while (groups) { 2049 free(groups->gr_name); 2050 tmpgroups = groups->gr_next; 2051 free((char *)groups); 2052 groups = tmpgroups; 2053 } 2054 free((char *)ex); 2055 } 2056 2057 static void 2058 freeex(ex) 2059 struct exportnode *ex; 2060 { 2061 struct exportnode *tmpex; 2062 2063 while (ex) { 2064 tmpex = ex->ex_next; 2065 freeex_ent(ex); 2066 ex = tmpex; 2067 } 2068 } 2069 2070 static const char uatfs_err[] = "submount under fstype=autofs not supported"; 2071 /* 2072 * dump_mapent_err(struct mapent *me, char *key, char *mapname) 2073 * syslog appropriate error in mapentries. 2074 */ 2075 static void dump_mapent_err(struct mapent *me, char *key, char *mapname) 2076 { 2077 switch (me->map_err) { 2078 case MAPENT_NOERR: 2079 if (verbose) 2080 syslog(LOG_ERR, 2081 "map=%s key=%s mntpnt=%s: no error"); 2082 break; 2083 case MAPENT_UATFS: 2084 syslog(LOG_ERR, 2085 "mountpoint %s in map %s key %s not mounted: %s", 2086 me->map_mntpnt, mapname, key, uatfs_err); 2087 break; 2088 default: 2089 if (verbose) 2090 syslog(LOG_ERR, 2091 "map=%s key=%s mntpnt=%s: unknown mapentry error"); 2092 } 2093 }