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