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