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