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 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <stdio.h> 31 #include <ctype.h> 32 #include <string.h> 33 #include <syslog.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <sys/param.h> 37 #include <errno.h> 38 #include <pwd.h> 39 #include <netinet/in.h> 40 #include <netdb.h> 41 #include <sys/tiuser.h> 42 #include <locale.h> 43 #include <stdlib.h> 44 #include <unistd.h> 45 #include <thread.h> 46 #include <rpc/rpc.h> 47 #include <rpcsvc/mount.h> 48 #include <fcntl.h> 49 #include <limits.h> 50 #include "automount.h" 51 52 /* 53 * This structure is used to determine the hierarchical 54 * relationship between directories 55 */ 56 typedef struct _hiernode { 57 char dirname[MAXFILENAMELEN+1]; 58 struct _hiernode *subdir; 59 struct _hiernode *leveldir; 60 struct mapent *mapent; 61 } hiernode; 62 63 void free_mapent(struct mapent *); 64 65 static int mapline_to_mapent(struct mapent **, struct mapline *, char *, char *, 66 char *, char *, uint_t); 67 static int hierarchical_sort(struct mapent *, hiernode **, char *, char *); 68 static int push_options(hiernode *, char *, char *, int); 69 static int set_mapent_opts(struct mapent *, char *, char *, char *); 70 static void get_opts(char *, char *, char *, bool_t *); 71 static int fstype_opts(struct mapent *, char *, char *, char *); 72 static int modify_mapents(struct mapent **, char *, char *, char *, hiernode *, 73 char *, uint_t, bool_t); 74 static int set_and_fake_mapent_mntlevel(hiernode *, char *, char *, char *, 75 struct mapent **, uint_t, char *, bool_t); 76 static int mark_level1_root(hiernode *, char *); 77 static int mark_and_fake_level1_noroot(hiernode *, char *, char *, char *, 78 struct mapent **, uint_t i, char *); 79 static int convert_mapent_to_automount(struct mapent *, char *, char *); 80 static int automount_opts(char **, char *); 81 static int parse_fsinfo(char *, struct mapent *); 82 static int parse_nfs(char *, struct mapent *, char *, char *, char **, char **, 83 int); 84 static int parse_special(struct mapent *, char *, char *, char **, char **, 85 int); 86 static int get_dir_from_path(char *, char **, int); 87 static int alloc_hiernode(hiernode **, char *); 88 static void free_hiernode(hiernode *); 89 static void trace_mapents(char *, struct mapent *); 90 static void trace_hierarchy(hiernode *, int); 91 static struct mapent *do_mapent_hosts(char *, char *, uint_t); 92 static void freeex_ent(struct exportnode *); 93 static void freeex(struct exportnode *); 94 static void dump_mapent_err(struct mapent *, char *, char *); 95 96 #define PARSE_OK 0 97 #define PARSE_ERROR -1 98 #define MAX_FSLEN 32 99 100 /* 101 * mapentry error type defininitions 102 */ 103 #define MAPENT_NOERR 0 104 #define MAPENT_UATFS 1 105 106 /* 107 * parse_entry(char *key, char *mapname, char *mapopts, struct mapline *ml, 108 * char *subdir, uint_t isdirect, bool_t mount_access) 109 * Parses the data in ml to build a mapentry list containing the information 110 * for the mounts/lookups to be performed. Builds an intermediate mapentry list 111 * by processing ml, hierarchically sorts (builds a tree of) the list according 112 * to mountpoint. Then pushes options down the hierarchy, and fills in the mount 113 * file system. Finally, modifies the intermediate list depending on how far 114 * in the hierarchy the current request is (uses subdir). Deals with special 115 * case of /net map parsing. 116 * Returns a pointer to the head of the mapentry list. 117 */ 118 struct mapent * 119 parse_entry(char *key, char *mapname, char *mapopts, struct mapline *ml, 120 char *subdir, uint_t isdirect, bool_t mount_access) 121 { 122 char *p; 123 char defaultopts[AUTOFS_MAXOPTSLEN]; 124 125 struct mapent *mapents = NULL; 126 hiernode *rootnode = NULL; 127 char *lp = ml->linebuf; 128 129 if (trace > 1) 130 trace_prt(1, " mapline: %s\n", ml->linebuf); 131 132 /* 133 * Assure the key is only one token long. 134 * This prevents options from sneaking in through the 135 * command line or corruption of /etc/mnttab. 136 */ 137 for (p = key; *p != '\0'; p++) { 138 if (isspace(*p)) { 139 syslog(LOG_ERR, 140 "parse_entry: bad key in map %s: %s", mapname, key); 141 return ((struct mapent *)NULL); 142 } 143 } 144 145 /* 146 * select the appropriate parser, and build the mapentry list 147 */ 148 if (strcmp(lp, "-hosts") == 0) { 149 /* 150 * the /net parser - uses do_mapent_hosts to build mapents. 151 * The mapopts are considered default for every entry, so we 152 * don't push options down hierarchies. 153 */ 154 mapents = do_mapent_hosts(mapopts, key, isdirect); 155 if (mapents == NULL) /* nothing to free */ 156 return (mapents); 157 158 if (trace > 3) 159 trace_mapents("do_mapent_hosts:(return)", mapents); 160 161 if (hierarchical_sort(mapents, &rootnode, key, mapname) 162 != PARSE_OK) 163 goto parse_error; 164 } else { 165 /* 166 * all other parsing 167 */ 168 if (mapline_to_mapent(&mapents, ml, key, mapname, 169 mapopts, defaultopts, isdirect) != PARSE_OK) 170 goto parse_error; 171 172 if (mapents == NULL) 173 return (mapents); 174 175 if (hierarchical_sort(mapents, &rootnode, key, mapname) 176 != PARSE_OK) 177 goto parse_error; 178 179 if (push_options(rootnode, defaultopts, mapopts, 180 MAPENT_NOERR) != PARSE_OK) 181 goto parse_error; 182 183 if (trace > 3) { 184 trace_prt(1, "\n\tpush_options (return)\n"); 185 trace_prt(0, "\tdefault options=%s\n", defaultopts); 186 trace_hierarchy(rootnode, 0); 187 }; 188 189 if (parse_fsinfo(mapname, mapents) != PARSE_OK) 190 goto parse_error; 191 } 192 193 /* 194 * Modify the mapentry list. We *must* do this only after 195 * the mapentry list is completely built (since we need to 196 * have parse_fsinfo called first). 197 */ 198 if (modify_mapents(&mapents, mapname, mapopts, subdir, 199 rootnode, key, isdirect, mount_access) != PARSE_OK) 200 goto parse_error; 201 202 /* 203 * XXX: its dangerous to use rootnode after modify mapents as 204 * it may be pointing to mapents that have been freed 205 */ 206 if (rootnode != NULL) 207 free_hiernode(rootnode); 208 209 return (mapents); 210 211 parse_error: 212 syslog(LOG_ERR, "parse_entry: mapentry parse error: map=%s key=%s", 213 mapname, key); 214 free_mapent(mapents); 215 if (rootnode != NULL) 216 free_hiernode(rootnode); 217 return ((struct mapent *)NULL); 218 } 219 220 221 /* 222 * mapline_to_mapent(struct mapent **mapents, struct mapline *ml, 223 * char *key, char *mapname, char *mapopts, char *defaultopts, 224 * uint_t isdirect) 225 * Parses the mapline information in ml word by word to build an intermediate 226 * mapentry list, which is passed back to the caller. The mapentries may have 227 * holes (example no options), as they are completed only later. The logic is 228 * awkward, but needed to provide the supported flexibility in the map entries. 229 * (especially the first line). Note that the key is the full pathname of the 230 * directory to be mounted in a direct map, and ml is the mapentry beyond key. 231 * Returns PARSE_OK or an appropriate error value. 232 */ 233 static int 234 mapline_to_mapent(struct mapent **mapents, struct mapline *ml, char *key, 235 char *mapname, char *mapopts, char *defaultopts, 236 uint_t isdirect) 237 { 238 struct mapent *me = NULL; 239 struct mapent *mp; 240 char w[MAXPATHLEN]; 241 char wq[MAXPATHLEN]; 242 char w1[MAXPATHLEN]; 243 int implied; 244 245 char *lp = ml->linebuf; 246 char *lq = ml->lineqbuf; 247 248 /* do any macro expansions that are required to complete ml */ 249 if (macro_expand(key, lp, lq, LINESZ)) { 250 syslog(LOG_ERR, 251 "mapline_to_mapent: map %s: line too long (max %d chars)", 252 mapname, LINESZ - 1); 253 return (PARSE_ERROR); 254 } 255 if (trace > 3 && (strcmp(ml->linebuf, lp) != 0)) 256 trace_prt(1, 257 " mapline_to_mapent: (expanded) mapline (%s,%s)\n", 258 ml->linebuf, ml->lineqbuf); 259 260 /* init the head of mapentry list to null */ 261 *mapents = NULL; 262 263 /* 264 * Get the first word - its either a '-' if default options provided, 265 * a '/', if the mountroot is implicitly provided, or a mount filesystem 266 * if the mountroot is implicit. Note that if the first word begins with 267 * a '-' then the second must be read and it must be a mountpoint or a 268 * mount filesystem. Use mapopts if no default opts are provided. 269 */ 270 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 271 return (PARSE_ERROR); 272 if (*w == '-') { 273 strcpy(defaultopts, w); 274 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 275 return (PARSE_ERROR); 276 } else 277 strcpy(defaultopts, mapopts); 278 279 /* 280 * implied is true if there is no '/' (the usual NFS case) 281 * or if there are two slashes (the smbfs case) 282 */ 283 implied = ((*w != '/') || (*(w+1) == '/')); 284 while (*w == '/' || implied) { 285 mp = me; 286 if ((me = (struct mapent *)malloc(sizeof (*me))) == NULL) 287 goto alloc_failed; 288 (void) memset((char *)me, 0, sizeof (*me)); 289 if (*mapents == NULL) /* special case of head */ 290 *mapents = me; 291 else 292 mp->map_next = me; 293 294 /* 295 * direct maps get an empty string as root - to be filled 296 * by the entire path later. Indirect maps get /key as the 297 * map root. Note that xfn maps don't care about the root 298 * - they override it in getmapent_fn(). 299 */ 300 if (isdirect) { 301 *w1 = '\0'; 302 } else { 303 strcpy(w1, "/"); 304 strcat(w1, key); 305 } 306 if ((me->map_root = strdup(w1)) == NULL) 307 goto alloc_failed; 308 309 /* mntpnt is empty for the mount root */ 310 if (strcmp(w, "/") == 0 || implied) 311 me->map_mntpnt = strdup(""); 312 else 313 me->map_mntpnt = strdup(w); 314 if (me->map_mntpnt == NULL) 315 goto alloc_failed; 316 317 /* 318 * If implied, the word must be a mount filesystem, 319 * and its already read in; also turn off implied - its 320 * not applicable except for the mount root. Else, 321 * read another (or two) words depending on if there's 322 * an option. 323 */ 324 if (implied) /* must be a mount filesystem */ 325 implied = 0; 326 else { 327 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 328 return (PARSE_ERROR); 329 if (w[0] == '-') { 330 /* mount options */ 331 if ((me->map_mntopts = strdup(w)) == NULL) 332 goto alloc_failed; 333 if (getword(w, wq, &lp, &lq, ' ', 334 sizeof (w)) == -1) 335 return (PARSE_ERROR); 336 } 337 } 338 339 /* 340 * must be a mount filesystem or a set of filesystems at 341 * this point. 342 */ 343 if (w[0] == '\0' || w[0] == '-') { 344 syslog(LOG_ERR, 345 "mapline_to_mapent: bad location=%s map=%s key=%s", 346 w, mapname, key); 347 return (PARSE_ERROR); 348 } 349 350 /* 351 * map_fsw and map_fswq hold information which will be 352 * used to determine filesystem information at a later 353 * point. This is required since we can only find out 354 * about the mount file system after the directories 355 * are hierarchically sorted and options have been pushed 356 * down the hierarchies. 357 */ 358 if (((me->map_fsw = strdup(w)) == NULL) || 359 ((me->map_fswq = strdup(wq)) == NULL)) 360 goto alloc_failed; 361 362 /* 363 * the next word, if any, is either another mount point or a 364 * mount filesystem if more than one server is listed. 365 */ 366 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 367 return (PARSE_ERROR); 368 while (*w && *w != '/') { /* more than 1 server listed */ 369 int len; 370 char *fsw, *fswq; 371 len = strlen(me->map_fsw) + strlen(w) + 4; 372 if ((fsw = (char *)malloc(len)) == NULL) 373 goto alloc_failed; 374 sprintf(fsw, "%s %s", me->map_fsw, w); 375 free(me->map_fsw); 376 me->map_fsw = fsw; 377 len = strlen(me->map_fswq) + strlen(wq) + 4; 378 if ((fswq = (char *)malloc(len)) == NULL) 379 goto alloc_failed; 380 sprintf(fswq, "%s %s", me->map_fswq, wq); 381 free(me->map_fswq); 382 me->map_fswq = fswq; 383 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 384 return (PARSE_ERROR); 385 } 386 387 /* initialize flags */ 388 me->map_mntlevel = -1; 389 me->map_modified = FALSE; 390 me->map_faked = FALSE; 391 me->map_err = MAPENT_NOERR; 392 393 me->map_next = NULL; 394 } 395 396 if (*mapents == NULL || w[0] != '\0') { /* sanity check */ 397 if (verbose) { 398 if (*mapents == NULL) 399 syslog(LOG_ERR, 400 "mapline_to_mapent: parsed with null mapents"); 401 else 402 syslog(LOG_ERR, 403 "mapline_to_mapent: parsed nononempty w=%s", w); 404 } 405 return (PARSE_ERROR); 406 } 407 408 if (trace > 3) 409 trace_mapents("mapline_to_mapent:", *mapents); 410 411 return (PARSE_OK); 412 413 alloc_failed: 414 syslog(LOG_ERR, "mapline_to_mapent: Memory allocation failed"); 415 return (ENOMEM); 416 } 417 418 /* 419 * hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key 420 * char *mapname) 421 * sorts the mntpnts in each mapent to build a hierarchy of nodes, with 422 * with the rootnode being the mount root. The hierarchy is setup as 423 * levels, and subdirs below each level. Provides a link from node to 424 * the relevant mapentry. 425 * Returns PARSE_OK or appropriate error value 426 */ 427 static int 428 hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key, 429 char *mapname) 430 { 431 hiernode *prevnode, *currnode, *newnode; 432 char *path; 433 char dirname[MAXFILENAMELEN]; 434 435 int rc = PARSE_OK; 436 struct mapent *me = mapents; 437 438 /* allocate the rootnode with a default path of "" */ 439 *rootnode = NULL; 440 if ((rc = alloc_hiernode(rootnode, "")) != PARSE_OK) 441 return (rc); 442 443 /* 444 * walk through mapents - for each mapent, locate the position 445 * within the hierarchy by walking across leveldirs, and 446 * subdirs of matched leveldirs. Starts one level below 447 * the root (assumes an implicit match with rootnode). 448 * XXX - this could probably be done more cleanly using recursion. 449 */ 450 while (me != NULL) { 451 452 path = me->map_mntpnt; 453 454 if ((rc = get_dir_from_path(dirname, &path, 455 sizeof (dirname))) != PARSE_OK) 456 return (rc); 457 458 prevnode = *rootnode; 459 currnode = (*rootnode)->subdir; 460 461 while (dirname[0] != '\0') { 462 if (currnode != NULL) { 463 if (strcmp(currnode->dirname, dirname) == 0) { 464 /* 465 * match found - mntpnt is a child of 466 * this node 467 */ 468 prevnode = currnode; 469 currnode = currnode->subdir; 470 } else { 471 prevnode = currnode; 472 currnode = currnode->leveldir; 473 474 if (currnode == NULL) { 475 /* 476 * No more leveldirs to match. 477 * Add a new one 478 */ 479 if ((rc = alloc_hiernode 480 (&newnode, dirname)) 481 != PARSE_OK) 482 return (rc); 483 prevnode->leveldir = newnode; 484 prevnode = newnode; 485 currnode = newnode->subdir; 486 } else { 487 /* try this leveldir */ 488 continue; 489 } 490 } 491 } else { 492 /* no more subdirs to match. Add a new one */ 493 if ((rc = alloc_hiernode(&newnode, 494 dirname)) != PARSE_OK) 495 return (rc); 496 prevnode->subdir = newnode; 497 prevnode = newnode; 498 currnode = newnode->subdir; 499 } 500 if ((rc = get_dir_from_path(dirname, &path, 501 sizeof (dirname))) != PARSE_OK) 502 return (rc); 503 } 504 505 if (prevnode->mapent != NULL) { 506 /* duplicate mntpoint found */ 507 syslog(LOG_ERR, 508 "hierarchical_sort: duplicate mntpnt map=%s key=%s", 509 mapname, key); 510 return (PARSE_ERROR); 511 } 512 513 /* provide a pointer from node to mapent */ 514 prevnode->mapent = me; 515 me = me->map_next; 516 } 517 518 if (trace > 3) { 519 trace_prt(1, "\n\thierarchical_sort:\n"); 520 trace_hierarchy(*rootnode, 0); /* 0 is rootnode's level */ 521 } 522 523 return (rc); 524 } 525 526 /* 527 * push_options(hiernode *node, char *opts, char *mapopts, int err) 528 * Pushes the options down a hierarchical structure. Works recursively from the 529 * root, which is passed in on the first call. Uses a replacement policy. 530 * If a node points to a mapentry, and it has an option, then thats the option 531 * for that mapentry. Else, the node's mapent inherits the option from the 532 * default (which may be the global option for the entry or mapopts). 533 * err is useful in flagging entries with errors in pushing options. 534 * returns PARSE_OK or appropriate error value. 535 */ 536 static int 537 push_options(hiernode *node, char *defaultopts, char *mapopts, int err) 538 { 539 int rc = PARSE_OK; 540 struct mapent *me = NULL; 541 542 /* ensure that all the dirs at a level are passed the default options */ 543 while (node != NULL) { 544 me = node->mapent; 545 if (me != NULL) { /* not all nodes point to a mapentry */ 546 me->map_err = err; 547 if ((rc = set_mapent_opts(me, me->map_mntopts, 548 defaultopts, mapopts)) != PARSE_OK) 549 return (rc); 550 } 551 552 /* push the options to subdirs */ 553 if (node->subdir != NULL) { 554 if (node->mapent && strcmp(node->mapent->map_fstype, 555 MNTTYPE_AUTOFS) == 0) 556 err = MAPENT_UATFS; 557 if ((rc = push_options(node->subdir, defaultopts, 558 mapopts, err)) != PARSE_OK) 559 return (rc); 560 } 561 node = node->leveldir; 562 } 563 return (rc); 564 } 565 566 #define BACKFSTYPE "backfstype" /* used in cachefs options */ 567 #define BACKFSTYPE_EQ "backfstype=" 568 #define FSTYPE "fstype" 569 #define FSTYPE_EQ "fstype=" 570 #define NO_OPTS "" 571 572 /* 573 * set_mapent_opts(struct mapent *me, char *opts, char *defaultopts, 574 * char *mapopts) 575 * sets the mapentry's options, fstype and mounter fields by separating 576 * out the fstype part from the opts. Use default options if opts is NULL. 577 * Note taht defaultopts may be the same as mapopts. 578 * Returns PARSE_OK or appropriate error value. 579 */ 580 static int 581 set_mapent_opts(struct mapent *me, char *opts, char *defaultopts, 582 char *mapopts) 583 { 584 char entryopts[AUTOFS_MAXOPTSLEN]; 585 char fstype[MAX_FSLEN], mounter[MAX_FSLEN]; 586 int rc = PARSE_OK; 587 bool_t fstype_opt = FALSE; 588 589 strcpy(fstype, MNTTYPE_NFS); /* default */ 590 591 /* set options to default options, if none exist for this entry */ 592 if (opts == NULL) { 593 opts = defaultopts; 594 if (defaultopts == NULL) { /* NULL opts for entry */ 595 strcpy(mounter, fstype); 596 goto done; 597 } 598 } 599 if (*opts == '-') 600 opts++; 601 602 /* separate opts into fstype and (other) entrypopts */ 603 get_opts(opts, entryopts, fstype, &fstype_opt); 604 605 /* replace any existing opts */ 606 if (me->map_mntopts != NULL) 607 free(me->map_mntopts); 608 if ((me->map_mntopts = strdup(entryopts)) == NULL) 609 return (ENOMEM); 610 strcpy(mounter, fstype); 611 612 /* 613 * The following ugly chunk of code crept in as a result of 614 * cachefs. If it's a cachefs mount of an nfs filesystem, then 615 * it's important to parse the nfs special field. Otherwise, 616 * just hand the special field to the fs-specific mount 617 */ 618 if (strcmp(fstype, MNTTYPE_CACHEFS) == 0) { 619 struct mnttab m; 620 char *p; 621 622 m.mnt_mntopts = entryopts; 623 if ((p = hasmntopt(&m, BACKFSTYPE)) != NULL) { 624 int len = strlen(MNTTYPE_NFS); 625 626 p += strlen(BACKFSTYPE_EQ); 627 628 if (strncmp(p, MNTTYPE_NFS, len) == 0 && 629 (p[len] == '\0' || p[len] == ',')) { 630 /* 631 * Cached nfs mount 632 */ 633 (void) strcpy(fstype, MNTTYPE_NFS); 634 (void) strcpy(mounter, MNTTYPE_CACHEFS); 635 } 636 } 637 } 638 639 /* 640 * child options are exactly fstype = somefs, we need to do some 641 * more option pushing work. 642 */ 643 if (fstype_opt == TRUE && 644 (strcmp(me->map_mntopts, NO_OPTS) == 0)) { 645 free(me->map_mntopts); 646 if ((rc = fstype_opts(me, opts, defaultopts, 647 mapopts)) != PARSE_OK) 648 return (rc); 649 } 650 651 done: 652 if (((me->map_fstype = strdup(fstype)) == NULL) || 653 ((me->map_mounter = strdup(mounter)) == NULL)) { 654 if (me->map_fstype != NULL) 655 free(me->map_fstype); 656 syslog(LOG_ERR, "set_mapent_opts: No memory"); 657 return (ENOMEM); 658 } 659 660 return (rc); 661 } 662 663 /* 664 * Check the option string for an "fstype" 665 * option. If found, return the fstype 666 * and the option string with the fstype 667 * option removed, e.g. 668 * 669 * input: "fstype=cachefs,ro,nosuid" 670 * opts: "ro,nosuid" 671 * fstype: "cachefs" 672 * 673 * Also indicates if the fstype option was present 674 * by setting a flag, if the pointer to the flag 675 * is not NULL. 676 */ 677 static void 678 get_opts(input, opts, fstype, fstype_opt) 679 char *input; 680 char *opts; /* output */ 681 char *fstype; /* output */ 682 bool_t *fstype_opt; 683 { 684 char *p, *pb; 685 char buf[MAXOPTSLEN]; 686 char *placeholder; 687 688 *opts = '\0'; 689 (void) strcpy(buf, input); 690 pb = buf; 691 while (p = (char *)strtok_r(pb, ",", &placeholder)) { 692 pb = NULL; 693 if (strncmp(p, FSTYPE_EQ, 7) == 0) { 694 if (fstype_opt != NULL) 695 *fstype_opt = TRUE; 696 (void) strcpy(fstype, p + 7); 697 } else { 698 if (*opts) 699 (void) strcat(opts, ","); 700 (void) strcat(opts, p); 701 } 702 } 703 } 704 705 /* 706 * fstype_opts(struct mapent *me, char *opts, char *defaultopts, 707 * char *mapopts) 708 * We need to push global options to the child entry if it is exactly 709 * fstype=somefs. 710 */ 711 static int 712 fstype_opts(struct mapent *me, char *opts, char *defaultopts, 713 char *mapopts) 714 { 715 char pushopts[AUTOFS_MAXOPTSLEN]; 716 char pushentryopts[AUTOFS_MAXOPTSLEN]; 717 char pushfstype[MAX_FSLEN]; 718 719 if (defaultopts && *defaultopts == '-') 720 defaultopts++; 721 722 /* 723 * the options to push are the global defaults for the entry, 724 * if they exist, or mapopts, if the global defaults for the 725 * entry does not exist. 726 */ 727 if (strcmp(defaultopts, opts) == 0) { 728 if (*mapopts == '-') 729 mapopts++; 730 get_opts(mapopts, pushentryopts, pushfstype, NULL); 731 strcpy(pushopts, mapopts); 732 } else { 733 get_opts(defaultopts, pushentryopts, pushfstype, NULL); 734 strcpy(pushopts, defaultopts); 735 } 736 737 if (strcmp(pushfstype, MNTTYPE_CACHEFS) == 0) 738 me->map_mntopts = strdup(pushopts); 739 else 740 me->map_mntopts = strdup(pushentryopts); 741 742 if (!me->map_mntopts) { 743 syslog(LOG_ERR, "fstype_opts: No memory"); 744 return (ENOMEM); 745 } 746 747 return (PARSE_OK); 748 } 749 750 /* 751 * modify_mapents(struct mapent **mapents, char *mapname, 752 * char *mapopts, char *subdir, hiernode *rootnode, 753 * char *key, uint_t isdirect, bool_t mount_access) 754 * modifies the intermediate mapentry list into the final one, and passes 755 * back a pointer to it. The final list may contain faked mapentries for 756 * hiernodes that do not point to a mapentry, or converted mapentries, if 757 * hiernodes that point to a mapentry need to be converted from nfs to autofs. 758 * mounts. Entries that are not directly 1 level below the subdir are removed. 759 * Returns PARSE_OK or PARSE_ERROR 760 */ 761 static int 762 modify_mapents(struct mapent **mapents, char *mapname, 763 char *mapopts, char *subdir, hiernode *rootnode, 764 char *key, uint_t isdirect, bool_t mount_access) 765 { 766 struct mapent *mp = NULL; 767 char w[MAXPATHLEN]; 768 769 struct mapent *me; 770 int rc = PARSE_OK; 771 struct mapent *faked_mapents = NULL; 772 773 /* 774 * correct the mapentry mntlevel from default -1 to level depending on 775 * position in hierarchy, and build any faked mapentries, if required 776 * at one level below the rootnode given by subdir. 777 */ 778 if ((rc = set_and_fake_mapent_mntlevel(rootnode, subdir, key, mapname, 779 &faked_mapents, isdirect, mapopts, mount_access)) != PARSE_OK) 780 return (rc); 781 782 /* 783 * attaches faked mapents to real mapents list. Assumes mapents 784 * is not NULL. 785 */ 786 me = *mapents; 787 while (me->map_next != NULL) 788 me = me->map_next; 789 me->map_next = faked_mapents; 790 791 /* 792 * get rid of nodes marked at level -1 793 */ 794 me = *mapents; 795 while (me != NULL) { 796 if ((me->map_mntlevel == -1) || (me->map_err) || 797 (mount_access == FALSE && me->map_mntlevel == 0)) { 798 /* 799 * syslog any errors and free entry 800 */ 801 if (me->map_err) 802 dump_mapent_err(me, key, mapname); 803 804 if (me == (*mapents)) { 805 /* special case when head has to be freed */ 806 *mapents = me->map_next; 807 if ((*mapents) == NULL) { 808 /* something wierd happened */ 809 if (verbose) 810 syslog(LOG_ERR, 811 "modify_mapents: level error"); 812 return (PARSE_ERROR); 813 } 814 815 /* separate out the node */ 816 me->map_next = NULL; 817 free_mapent(me); 818 me = *mapents; 819 } else { 820 mp->map_next = me->map_next; 821 me->map_next = NULL; 822 free_mapent(me); 823 me = mp->map_next; 824 } 825 continue; 826 } 827 828 /* 829 * convert level 1 mapents that are not already autonodes 830 * to autonodes 831 */ 832 if (me->map_mntlevel == 1 && 833 (strcmp(me->map_fstype, MNTTYPE_AUTOFS) != 0) && 834 (me->map_faked != TRUE)) { 835 if ((rc = convert_mapent_to_automount(me, mapname, 836 mapopts)) != PARSE_OK) 837 return (rc); 838 } 839 strcpy(w, (me->map_mntpnt+strlen(subdir))); 840 strcpy(me->map_mntpnt, w); 841 mp = me; 842 me = me->map_next; 843 } 844 845 if (trace > 3) 846 trace_mapents("modify_mapents:", *mapents); 847 848 return (PARSE_OK); 849 } 850 851 /* 852 * set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key, 853 * char *mapname, struct mapent **faked_mapents, 854 * uint_t isdirect, char *mapopts, bool_t mount_access) 855 * sets the mapentry mount levels (depths) with respect to the subdir. 856 * Assigns a value of 0 to the new root. Finds the level1 directories by 857 * calling mark_*_level1_*(). Also cleans off extra /'s in level0 and 858 * level1 map_mntpnts. Note that one level below the new root is an existing 859 * mapentry if there's a mapentry (nfs mount) corresponding to the root, 860 * and the direct subdir set for the root, if there's no mapentry corresponding 861 * to the root (we install autodirs). Returns PARSE_OK or error value. 862 */ 863 static int 864 set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key, 865 char *mapname, struct mapent **faked_mapents, 866 uint_t isdirect, char *mapopts, bool_t mount_access) 867 { 868 char dirname[MAXFILENAMELEN]; 869 char traversed_path[MAXPATHLEN]; /* used in building fake mapentries */ 870 871 char *subdir_child = subdir; 872 hiernode *prevnode = rootnode; 873 hiernode *currnode = rootnode->subdir; 874 int rc = PARSE_OK; 875 traversed_path[0] = '\0'; 876 877 /* 878 * find and mark the root by tracing down subdir. Use traversed_path 879 * to keep track of how far we go, while guaranteeing that it 880 * contains no '/' at the end. Took some mucking to get that right. 881 */ 882 if ((rc = get_dir_from_path(dirname, &subdir_child, sizeof (dirname))) 883 != PARSE_OK) 884 return (rc); 885 886 if (dirname[0] != '\0') 887 sprintf(traversed_path, "%s/%s", traversed_path, dirname); 888 889 prevnode = rootnode; 890 currnode = rootnode->subdir; 891 while (dirname[0] != '\0' && currnode != NULL) { 892 if (strcmp(currnode->dirname, dirname) == 0) { 893 894 /* subdir is a child of currnode */ 895 prevnode = currnode; 896 currnode = currnode->subdir; 897 898 if ((rc = get_dir_from_path(dirname, &subdir_child, 899 sizeof (dirname))) != PARSE_OK) 900 return (rc); 901 if (dirname[0] != '\0') 902 sprintf(traversed_path, "%s/%s", 903 traversed_path, dirname); 904 905 } else { 906 /* try next leveldir */ 907 prevnode = currnode; 908 currnode = currnode->leveldir; 909 } 910 } 911 912 if (dirname[0] != '\0') { 913 if (verbose) 914 syslog(LOG_ERR, 915 "set_and_fake_mapent_mntlevel: subdir=%s error: map=%s", 916 subdir, mapname); 917 return (PARSE_ERROR); 918 } 919 920 /* 921 * see if level of root really points to a mapent and if 922 * have access to that filessystem - call appropriate 923 * routine to mark level 1 nodes, and build faked entries 924 */ 925 if (prevnode->mapent != NULL && mount_access == TRUE) { 926 if (trace > 3) 927 trace_prt(1, " node mountpoint %s\t travpath=%s\n", 928 prevnode->mapent->map_mntpnt, traversed_path); 929 930 /* 931 * Copy traversed path map_mntpnt to get rid of any extra 932 * '/' the map entry may contain. 933 */ 934 if (strlen(prevnode->mapent->map_mntpnt) < 935 strlen(traversed_path)) { /* sanity check */ 936 if (verbose) 937 syslog(LOG_ERR, 938 "set_and_fake_mapent_mntlevel: path=%s error", 939 traversed_path); 940 return (PARSE_ERROR); 941 } 942 if (strcmp(prevnode->mapent->map_mntpnt, traversed_path) != 0) 943 strcpy(prevnode->mapent->map_mntpnt, traversed_path); 944 945 prevnode->mapent->map_mntlevel = 0; /* root level is 0 */ 946 if (currnode != NULL) { 947 if ((rc = mark_level1_root(currnode, 948 traversed_path)) != PARSE_OK) 949 return (rc); 950 } 951 } else if (currnode != NULL) { 952 if (trace > 3) 953 trace_prt(1, " No rootnode, travpath=%s\n", 954 traversed_path); 955 if ((rc = mark_and_fake_level1_noroot(currnode, 956 traversed_path, key, mapname, faked_mapents, isdirect, 957 mapopts)) != PARSE_OK) 958 return (rc); 959 } 960 961 if (trace > 3) { 962 trace_prt(1, "\n\tset_and_fake_mapent_mntlevel\n"); 963 trace_hierarchy(rootnode, 0); 964 } 965 966 return (rc); 967 } 968 969 970 /* 971 * mark_level1_root(hiernode *node, char *traversed_path) 972 * marks nodes upto one level below the rootnode given by subdir 973 * recursively. Called if rootnode points to a mapent. 974 * In this routine, a level 1 node is considered to be the 1st existing 975 * mapentry below the root node, so there's no faking involved. 976 * Returns PARSE_OK or error value 977 */ 978 static int 979 mark_level1_root(hiernode *node, char *traversed_path) 980 { 981 /* ensure we touch all leveldirs */ 982 while (node) { 983 /* 984 * mark node level as 1, if one exists - else walk down 985 * subdirs until we find one. 986 */ 987 if (node->mapent == NULL) { 988 char w[MAXPATHLEN]; 989 990 if (node->subdir != NULL) { 991 sprintf(w, "%s/%s", traversed_path, 992 node->dirname); 993 if (mark_level1_root(node->subdir, w) 994 == PARSE_ERROR) 995 return (PARSE_ERROR); 996 } else { 997 if (verbose) { 998 syslog(LOG_ERR, 999 "mark_level1_root: hierarchy error"); 1000 } 1001 return (PARSE_ERROR); 1002 } 1003 } else { 1004 char w[MAXPATHLEN]; 1005 1006 sprintf(w, "%s/%s", traversed_path, node->dirname); 1007 if (trace > 3) 1008 trace_prt(1, " node mntpnt %s\t travpath %s\n", 1009 node->mapent->map_mntpnt, w); 1010 1011 /* replace mntpnt with travpath to clean extra '/' */ 1012 if (strlen(node->mapent->map_mntpnt) < strlen(w)) { 1013 if (verbose) { 1014 syslog(LOG_ERR, 1015 "mark_level1_root: path=%s error", 1016 traversed_path); 1017 } 1018 return (PARSE_ERROR); 1019 } 1020 if (strcmp(node->mapent->map_mntpnt, w) != 0) 1021 strcpy(node->mapent->map_mntpnt, w); 1022 node->mapent->map_mntlevel = 1; 1023 } 1024 node = node->leveldir; 1025 } 1026 return (PARSE_OK); 1027 } 1028 1029 /* 1030 * mark_and_fake_level1_noroot(hiernode *node, char *traversed_path, 1031 * char *key,char *mapname, struct mapent **faked_mapents, 1032 * uint_t isdirect, char *mapopts) 1033 * Called if the root of the hierarchy does not point to a mapent. marks nodes 1034 * upto one physical level below the rootnode given by subdir. checks if 1035 * there's a real mapentry. If not, it builds a faked one (autonode) at that 1036 * point. The faked autonode is direct, with the map being the same as the 1037 * original one from which the call originated. Options are same as that of 1038 * the map and assigned in automount_opts(). Returns PARSE_OK or error value. 1039 */ 1040 static int 1041 mark_and_fake_level1_noroot(hiernode *node, char *traversed_path, 1042 char *key, char *mapname, struct mapent **faked_mapents, 1043 uint_t isdirect, char *mapopts) 1044 { 1045 struct mapent *me; 1046 int rc = 0; 1047 char faked_map_mntpnt[MAXPATHLEN]; 1048 char w1[MAXPATHLEN]; 1049 char w[MAXPATHLEN]; 1050 1051 while (node != NULL) { 1052 if (node->mapent != NULL) { 1053 /* 1054 * existing mapentry at level 1 - copy travpath to 1055 * get rid of extra '/' in mntpnt 1056 */ 1057 sprintf(w, "%s/%s", traversed_path, node->dirname); 1058 if (trace > 3) 1059 trace_prt(1, " node mntpnt=%s\t travpath=%s\n", 1060 node->mapent->map_mntpnt, w); 1061 if (strlen(node->mapent->map_mntpnt) < strlen(w)) { 1062 /* sanity check */ 1063 if (verbose) 1064 syslog(LOG_ERR, 1065 "mark_fake_level1_noroot:path=%s error", 1066 traversed_path); 1067 return (PARSE_ERROR); 1068 } 1069 if (strcmp(node->mapent->map_mntpnt, w) != 0) 1070 strcpy(node->mapent->map_mntpnt, w); 1071 node->mapent->map_mntlevel = 1; 1072 } else { 1073 /* 1074 * build the faked autonode 1075 */ 1076 if ((me = (struct mapent *)malloc(sizeof (*me))) 1077 == NULL) { 1078 syslog(LOG_ERR, 1079 "mark_and_fake_level1_noroot: out of memory"); 1080 return (ENOMEM); 1081 } 1082 (void) memset((char *)me, 0, sizeof (*me)); 1083 1084 if ((me->map_fs = (struct mapfs *) 1085 malloc(sizeof (struct mapfs))) == NULL) 1086 return (ENOMEM); 1087 (void) memset(me->map_fs, 0, sizeof (struct mapfs)); 1088 1089 if (isdirect) { 1090 *w1 = '\0'; 1091 } else { 1092 strcpy(w1, "/"); 1093 strcat(w1, key); 1094 } 1095 me->map_root = strdup(w1); 1096 1097 sprintf(faked_map_mntpnt, "%s/%s", traversed_path, 1098 node->dirname); 1099 me->