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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22/* 23 * autod_parse.c 24 * 25 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 29/* 30 * Portions Copyright 2007-2012 Apple Inc. 31 */ 32 33#pragma ident "@(#)autod_parse.c 1.31 05/06/08 SMI" 34 35#include <stdio.h> 36#include <ctype.h> 37#include <string.h> 38#include <syslog.h> 39#include <sys/types.h> 40#include <sys/stat.h> 41#include <sys/param.h> 42#include <errno.h> 43#include <pwd.h> 44#include <mntopts.h> 45#include <netinet/in.h> 46#include <netdb.h> 47#include <fstab.h> 48#include <locale.h> 49#include <stdlib.h> 50#include <unistd.h> 51#include <oncrpc/rpc.h> 52#include <rpcsvc/mount.h> 53#include <fcntl.h> 54#include <limits.h> 55 56#include "automount.h" 57#include "auto_mntopts.h" 58 59/* 60 * This structure is used to determine the hierarchical 61 * relationship between directories 62 */ 63struct _hiernode 64{ 65 char dirname[MAXFILENAMELEN+1]; 66 struct _hiernode *subdir; 67 struct _hiernode *leveldir; 68 struct mapent *mapent; 69}; 70typedef struct _hiernode hiernode; 71 72void free_mapent(struct mapent *); 73 74static int mapline_to_mapent(struct mapent **, struct mapline *, const char *, 75 const char *, const char *, char *, uint_t); 76static int hierarchical_sort(struct mapent *, hiernode **, const char *, 77 const char *); 78static int push_options(hiernode *, char *, const char *, int); 79static int set_mapent_opts(struct mapent *, char *, const char *); 80static int get_opts(const char *, char *, char *, size_t, bool_t *); 81static int fstype_opts(struct mapent *, const char *, const char *, 82 const char *); 83static int modify_mapents(struct mapent **, const char *, const char *, 84 const char *, hiernode *, const char *, uint_t, bool_t); 85static int set_and_fake_mapent_mntlevel(hiernode *, const char *, const char *, 86 const char *, struct mapent **, uint_t, 87 const char *, bool_t); 88static int mark_level1_root(hiernode *, char *); 89static int mark_and_fake_level1_noroot(hiernode *, const char *, const char *, 90 const char *, struct mapent **, uint_t i, 91 const char *); 92static int convert_mapent_to_automount(struct mapent *, const char *, 93 const char *); 94static int automount_opts(char **, const char *); 95static int parse_fsinfo(const char *, struct mapent *); 96static int is_nonnfs_url(char *, char *); 97static int parse_nfs(const char *, struct mapent *, char *, char *, char **, 98 char **); 99static int parse_special(struct mapent *, char *, char *, char **, char **, 100 int); 101static int get_dir_from_path(char *, const char **, int); 102static int alloc_hiernode(hiernode **, char *); 103static void free_hiernode(hiernode *); 104static void trace_mapents(char *, struct mapent *); 105static void trace_hierarchy(hiernode *, int); 106static struct mapent *do_mapent_hosts(const char *, const char *, uint_t, 107 int *); 108static void freeex_ent(struct exportnode *); 109static void freeex(struct exportnode *); 110static struct mapent *do_mapent_fstab(const char *, const char *, uint_t, 111 int *, int *); 112static struct mapent *do_mapent_static(const char *, uint_t, int *); 113static void dump_mapent_err(struct mapent *, const char *, const char *); 114 115#define PARSE_OK 0 116#define PARSE_ERROR -1 117#define MAX_FSLEN 32 118 119/* 120 * mapentry error type defininitions 121 */ 122#define MAPENT_NOERR 0 123#define MAPENT_UATFS 1 124 125/* 126 * Make a copy of a string, if it's not too long, and save a pointer to 127 * it in the pointed-to location. The maximum length includes the 128 * null terminator (e.g., a value of 2 means the longest string can 129 * have only 1 character). 130 * 131 * Return 0 on success, ENAMETOOLONG if the string is too long, and 132 * ENOMEM if we can't allocate memory for the copy. 133 */ 134static int 135strldup(char **dstp, const char *str, size_t maxlen) 136{ 137 size_t len; 138 char *dst; 139 140 len = strlen(str) + 1; 141 if (len > maxlen) 142 return (ENAMETOOLONG); 143 dst = malloc(len); 144 if (dst == NULL) 145 return (ENOMEM); 146 memcpy(dst, str, len); 147 *dstp = dst; 148 return (0); 149} 150 151/* 152 * Make a copy of a string, with a "/" preceding the string. 153 */ 154static char * 155strprefix_slash(const char *str) 156{ 157 size_t outstrsize; 158 char *outstr; 159 160 outstrsize = strlen(str) + 2; 161 outstr = malloc(outstrsize); 162 if (outstr == NULL) 163 return (NULL); 164 *outstr = '/'; 165 memcpy(outstr + 1, str, outstrsize - 1); 166 return (outstr); 167} 168 169/* 170 * parse_entry(const char *key, const char *mapname, const char *mapopts, 171 * const char *subdir, uint_t isdirect, int *node_type, 172 * bool_t isrestricted, bool_t mount_access, int *err) 173 * Looks up the specified key in the specified map, and then parses the 174 * result to build a mapentry list containing the information for the 175 * mounts/lookups to be performed. Builds an intermediate mapentry list by 176 * processing the map entry, hierarchically sorts (builds a tree of) the 177 * list according to mountpoint. Then pushes options down the hierarchy, 178 * and fills in the mount file system. Finally, modifies the intermediate 179 * list depending on how far in the hierarchy the current request is (uses 180 * subdir). Deals with special cases of -hosts, -fstab, and -static map 181 * parsing. 182 * 183 * mapopts must be at most AUTOFS_MAXOPTSLEN bytes long (including the null 184 * terminator). 185 * 186 * Returns a pointer to the head of the mapentry list. 187 */ 188struct mapent * 189parse_entry(const char *key, const char *mapname, const char *mapopts, 190 const char *subdir, uint_t isdirect, int *node_type, 191 bool_t isrestricted, bool_t mount_access, int *err) 192{ 193 struct dir_entry *dirp; 194 char *stack[STACKSIZ]; 195 char **stkptr = stack; 196 struct mapline ml; 197 bool_t iswildcard; 198 char defaultopts[AUTOFS_MAXOPTSLEN]; 199 200 struct mapent *mapents = NULL; 201 struct mapent *me; 202 hiernode *rootnode = NULL; 203 204 /* 205 * For now, assume the map entry is not a self-link in -fstab, 206 * is not a wildcard match, and is not a trigger. 207 */ 208 if (node_type != NULL) 209 *node_type = 0; 210 211 /* 212 * Check whether this is a special or regular map and, based 213 * on that, do the appropriate lookup, select the appropriate 214 * parser, and build the mapentry list. 215 */ 216 if (strcmp(mapname, "-hosts") == 0) { 217 /* 218 * the /net parser - uses do_mapent_hosts to build mapents. 219 * The mapopts are considered default for every entry, so we 220 * don't push options down hierarchies. 221 */ 222 mapents = do_mapent_hosts(mapopts, key, isdirect, err); 223 if (mapents == NULL) /* nothing to free */ 224 return (mapents); 225 226 if (trace > 3) 227 trace_mapents("do_mapent_hosts:(return)", mapents); 228 229 *err = hierarchical_sort(mapents, &rootnode, key, mapname); 230 if (*err != 0) 231 goto parse_error_quiet; 232 } else if (strcmp(mapname, "-fstab") == 0) { 233 /* 234 * the /Network/Servers parser - uses do_mapent_fstab to 235 * build mapents. 236 * 237 * All entries in fstab have mount options ("ro" and 238 * "rw", if nothing else), so the default mapopts are 239 * ignored. 240 */ 241 mapents = do_mapent_fstab(mapopts, key, isdirect, node_type, 242 err); 243 if (mapents == NULL) /* nothing to free */ 244 return (mapents); 245 246 if (trace > 3) 247 trace_mapents("do_mapent_fstab:(return)", mapents); 248 249 *err = hierarchical_sort(mapents, &rootnode, key, mapname); 250 if (*err != 0) 251 goto parse_error_quiet; 252 } else if (strcmp(mapname, "-static") == 0) { 253 /* 254 * the static fstab parser - looks up the fstab entry 255 * and builds mapents. 256 * (We don't want mapline_to_mapent to process it, 257 * as we don't want macro expansion to be done on 258 * it.) 259 * 260 * All entries in fstab have mount options ("ro" and 261 * "rw", if nothing else), so the default mapopts are 262 * ignored. 263 */ 264 mapents = do_mapent_static(key, isdirect, err); 265 if (mapents == NULL) /* nothing to free */ 266 return (mapents); 267 268 if (trace > 3) 269 trace_mapents("do_mapent_static:(return)", mapents); 270 271 *err = hierarchical_sort(mapents, &rootnode, key, mapname); 272 if (*err != 0) 273 goto parse_error_quiet; 274 } else { 275 /* 276 * All other maps. 277 */ 278 279 /* Is there an entry for that map/key in the readdir cache? */ 280 dirp = rddir_entry_lookup(mapname, key); 281 if (dirp == NULL || dirp->line == NULL) { 282 /* 283 * Either there's no entry, or the entry has a 284 * name but no map line information. We'll need 285 * to look up the entry in the map. 286 */ 287 288 /* initialize the stack of open files for this thread */ 289 stack_op(INIT, NULL, stack, &stkptr); 290 291 /* 292 * Look up the entry in the map. 293 */ 294 switch (getmapent(key, mapname, &ml, stack, &stkptr, 295 &iswildcard, isrestricted)) { 296 297 case __NSW_SUCCESS: 298 /* 299 * XXX - we failed to find this entry 300 * in the readdir cache, but we did find 301 * it in the map. That means the readdir 302 * cache is out of date for this map, 303 * and we should flush the entry for 304 * this map. 305 */ 306 break; 307 case __NSW_UNAVAIL: 308 if (verbose) { 309 syslog(LOG_ERR, "parse_entry: getmapent for map %s, key %s failed", 310 mapname, key); 311 } 312 case __NSW_NOTFOUND: 313 *err = ENOENT; /* no such map entry */ 314 return ((struct mapent *)NULL); /* we failed to find it */ 315 } 316 } else { 317 /* 318 * Yes, and it has map line information. 319 * Use it, rather than looking it up again 320 * in the map. 321 * 322 * It's not a wildcard entry, as it corresponds 323 * to an entry in the cache; wildcard entries 324 * are not cached, as they're not returned by 325 * readdir. 326 */ 327 if (CHECK_STRCPY(ml.linebuf, dirp->line, LINESZ)) { 328 goto parse_error; 329 } 330 if (CHECK_STRCPY(ml.lineqbuf, dirp->lineq, LINESZ)){ 331 goto parse_error; 332 } 333 iswildcard = FALSE; 334 } 335 336 /* 337 * Regular maps have no symlinks, but they *can* have 338 * wildcard entries. If this is a wildcard entry, 339 * all references to it must trigger a mount, as that's 340 * the only way to check whether it exists or not. 341 */ 342 if (node_type != NULL) { 343 if (iswildcard) 344 *node_type |= NT_FORCEMOUNT; 345 } 346 347 if (trace > 1) 348 trace_prt(1, " mapline: %s\n", ml.linebuf); 349 350 *err = mapline_to_mapent(&mapents, &ml, key, mapname, 351 mapopts, defaultopts, isdirect); 352 if (*err != 0) 353 goto parse_error; 354 355 if (mapents == NULL) { 356 *err = 0; 357 return (mapents); 358 } 359 360 *err = hierarchical_sort(mapents, &rootnode, key, mapname); 361 if (*err != 0) 362 goto parse_error_quiet; 363 364 *err = push_options(rootnode, defaultopts, mapopts, 365 MAPENT_NOERR); 366 if (*err != 0) 367 goto parse_error; 368 369 if (trace > 3) { 370 trace_prt(1, "\tpush_options (return)\tdefault options=%s\n", 371 defaultopts); 372 trace_hierarchy(rootnode, 0); 373 }; 374 375 *err = parse_fsinfo(mapname, mapents); 376 if (*err != 0) 377 goto parse_error; 378 } 379 380 /* 381 * Modify the mapentry list. We *must* do this only after 382 * the mapentry list is completely built (since we need to 383 * have parse_fsinfo called first). 384 */ 385 *err = modify_mapents(&mapents, mapname, mapopts, subdir, 386 rootnode, key, isdirect, mount_access); 387 if (*err != 0) { 388 /* 389 * ENOENT means that something in subdir wasn't found 390 * in the map entry list; that's just a user error. 391 */ 392 if (*err == ENOENT) 393 goto parse_error_quiet; 394 else 395 goto parse_error; 396 } 397 398 /* 399 * XXX: its dangerous to use rootnode after modify mapents as 400 * it may be pointing to mapents that have been freed 401 */ 402 if (rootnode != NULL) 403 free_hiernode(rootnode); 404 405 /* 406 * Is this a directory that has any non-faked, non-modified 407 * entries at level 0? 408 * 409 * If so, this is a trigger, as something should be mounted 410 * on it; otherwise it isn't, as nothing should be mounted 411 * directly on it (or it's a symlink). 412 */ 413 if (node_type != NULL && !(*node_type & NT_SYMLINK)) { 414 for (me = mapents; me; me = me->map_next) { 415 if (me->map_mntlevel == 0 && 416 !me->map_faked && !me->map_modified) { 417 *node_type |= NT_TRIGGER; 418 break; 419 } 420 } 421 } 422 423 return (mapents); 424 425parse_error: 426 syslog(LOG_ERR, "parse_entry: mapentry parse error: map=%s key=%s", 427 mapname, key); 428parse_error_quiet: 429 if (mapents != NULL) { 430 free_mapent(mapents); 431 } 432 if (rootnode != NULL) 433 free_hiernode(rootnode); 434 return ((struct mapent *)NULL); 435} 436 437 438/* 439 * mapline_to_mapent(struct mapent **mapents, struct mapline *ml, 440 * const char *key, const char *mapname, const char *mapopts, 441 * char *defaultopts, uint_t isdirect) 442 * Parses the mapline information in ml word by word to build an intermediate 443 * mapentry list, which is passed back to the caller. The mapentries may have 444 * holes (example no options), as they are completed only later. The logic is 445 * awkward, but needed to provide the supported flexibility in the map entries. 446 * (especially the first line). Note that the key is the full pathname of the 447 * directory to be mounted in a direct map, and ml is the mapentry beyond key. 448 * 449 * mapopts must be at most MAXOPTSLEN bytes long (including the null 450 * terminator). 451 * 452 * defaultopts must be in a buffer at least MAXOPTSLEN bytes in size; we 453 * guarantee not to copy to it a string with more bytes than that (again, 454 * including the null terminator). 455 * 456 * Returns 0 or an appropriate error value. 457 */ 458static int 459mapline_to_mapent(struct mapent **mapents, struct mapline *ml, const char *key, 460 const char *mapname, const char *mapopts, 461 char *defaultopts, uint_t isdirect) 462{ 463 struct mapent *me = NULL; 464 struct mapent *mp; 465 char w[MAXPATHLEN]; 466 char wq[MAXPATHLEN]; 467 int implied; 468 int err; 469 470 char *lp = ml->linebuf; 471 char *lq = ml->lineqbuf; 472 473 /* do any macro expansions that are required to complete ml */ 474 switch (macro_expand(key, lp, lq, LINESZ)) { 475 476 case MEXPAND_OK: 477 break; 478 479 case MEXPAND_LINE_TOO_LONG: 480 syslog(LOG_ERR, 481 "mapline_to_mapent: map %s: line too long (max %d chars)", 482 mapname, LINESZ - 1); 483 return (EIO); 484 485 case MEXPAND_VARNAME_TOO_LONG: 486 syslog(LOG_ERR, 487 "mapline_to_mapent: map %s: variable name too long", 488 mapname); 489 return (EIO); 490 } 491 if (trace > 3 && (strcmp(ml->linebuf, lp) != 0)) 492 trace_prt(1, " mapline_to_mapent: (expanded) mapline (%s,%s)\n", 493 ml->linebuf, ml->lineqbuf); 494 495 /* init the head of mapentry list to null */ 496 *mapents = NULL; 497 498 /* 499 * Get the first word - its either a '-' if default options provided, 500 * a '/', if the mountroot is explicitly provided, or a mount filesystem 501 * if the mountroot is implicit. Note that if the first word begins with 502 * a '-' then the second must be read and it must be a mountpoint or a 503 * mount filesystem. Use mapopts if no default opts are provided. 504 */ 505 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 506 return (EIO); 507 if (*w == '-') { 508 if (strlcpy(defaultopts, w, MAXOPTSLEN) >= MAXOPTSLEN) { 509 syslog(LOG_ERR, "default options are too long"); 510 return (EIO); 511 } 512 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 513 return (EIO); 514 } else 515 if (CHECK_STRCPY(defaultopts, mapopts, MAXOPTSLEN)) { 516 return (EIO); 517 } 518 519 implied = *w != '/'; /* implied is 1 only if '/' is implicit */ 520 while (*w == '/' || implied) { 521 mp = me; 522 if ((me = (struct mapent *)malloc(sizeof (*me))) == NULL) 523 goto alloc_failed; 524 (void) memset((char *)me, 0, sizeof (*me)); 525 if (*mapents == NULL) /* special case of head */ 526 *mapents = me; 527 else 528 mp->map_next = me; 529 530 /* 531 * direct maps get an empty string as root - to be filled 532 * by the entire path later. Indirect maps get /key as the 533 * map root. 534 */ 535 if (isdirect) 536 me->map_root = strdup(""); 537 else 538 me->map_root = strprefix_slash(key); 539 if (me->map_root == NULL) 540 goto alloc_failed; 541 542 /* mntpnt is empty for the mount root */ 543 if (strcmp(w, "/") == 0 || implied) 544 me->map_mntpnt = strdup(""); 545 else 546 me->map_mntpnt = strdup(w); 547 if (me->map_mntpnt == NULL) 548 goto alloc_failed; 549 550 /* 551 * If implied, the word must be a mount filesystem, 552 * and its already read in; also turn off implied - its 553 * not applicable except for the mount root. Else, 554 * read another (or two) words depending on if there's 555 * an option. 556 */ 557 if (implied) /* must be a mount filesystem */ 558 implied = 0; 559 else { 560 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 561 return (EIO); 562 if (w[0] == '-') { 563 /* mount options */ 564 err = strldup(&me->map_mntopts, w, MAXOPTSLEN); 565 if (err == ENAMETOOLONG) { 566 syslog(LOG_ERR, 567 "mount options for are too long"); 568 return (EIO); 569 } 570 if (err == ENOMEM) 571 goto alloc_failed; 572 if (getword(w, wq, &lp, &lq, ' ', 573 sizeof (w)) == -1) 574 return (EIO); 575 } 576 } 577 578 /* 579 * must be a mount filesystem or a set of filesystems at 580 * this point. 581 */ 582 if (w[0] == '\0' || w[0] == '-') { 583 syslog(LOG_ERR, 584 "mapline_to_mapent: bad location=%s map=%s key=%s", 585 w, mapname, key); 586 return (EIO); 587 } 588 589 /* 590 * map_fsw and map_fswq hold information which will be 591 * used to determine filesystem information at a later 592 * point. This is required since we can only find out 593 * about the mount file system after the directories 594 * are hierarchically sorted and options have been pushed 595 * down the hierarchies. 596 */ 597 if (((me->map_fsw = strdup(w)) == NULL) || 598 ((me->map_fswq = strdup(wq)) == NULL)) 599 goto alloc_failed; 600 601 /* 602 * the next word, if any, is either another mount point or a 603 * mount filesystem if more than one server is listed. 604 */ 605 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 606 return (EIO); 607 while (*w && *w != '/') { /* more than 1 server listed */ 608 char *fsw, *fswq; 609 if (asprintf(&fsw, "%s %s", me->map_fsw, w) == -1) 610 goto alloc_failed; 611 free(me->map_fsw); 612 me->map_fsw = fsw; 613 if (asprintf(&fswq, "%s %s", me->map_fswq, wq) == -1) 614 goto alloc_failed; 615 free(me->map_fswq); 616 me->map_fswq = fswq; 617 if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) 618 return (EIO); 619 } 620 621 /* initialize flags */ 622 me->map_mntlevel = -1; 623 me->map_modified = FALSE; 624 me->map_faked = FALSE; 625 me->map_err = MAPENT_NOERR; 626 627 me->map_next = NULL; 628 } 629 630 if (*mapents == NULL || w[0] != '\0') { /* sanity check */ 631 if (verbose) { 632 if (*mapents == NULL) 633 syslog(LOG_ERR, 634 "mapline_to_mapent: parsed with null mapents"); 635 else 636 syslog(LOG_ERR, 637 "mapline_to_mapent: parsed nononempty w=%s", w); 638 } 639 return (EIO); 640 } 641 642 if (trace > 3) 643 trace_mapents("mapline_to_mapent:", *mapents); 644 645 return (0); 646 647alloc_failed: 648 syslog(LOG_ERR, "mapline_to_mapent: Memory allocation failed"); 649 return (ENOMEM); 650} 651 652/* 653 * hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key 654 * char *mapname) 655 * sorts the mntpnts in each mapent to build a hierarchy of nodes, with 656 * with the rootnode being the mount root. The hierarchy is setup as 657 * levels, and subdirs below each level. Provides a link from node to 658 * the relevant mapentry. 659 * Returns 0 or appropriate error value; logs a message on error. 660 */ 661static int 662hierarchical_sort(struct mapent *mapents, hiernode **rootnode, const char *key, 663 const char *mapname) 664{ 665 hiernode *prevnode, *currnode, *newnode; 666 const char *path; 667 char dirname[MAXFILENAMELEN]; 668 669 int rc = 0; 670 struct mapent *me = mapents; 671 672 /* allocate the rootnode with a default path of "" */ 673 *rootnode = NULL; 674 if ((rc = alloc_hiernode(rootnode, "")) != 0) 675 return (rc); 676 677 /* 678 * walk through mapents - for each mapent, locate the position 679 * within the hierarchy by walking across leveldirs, and 680 * subdirs of matched leveldirs. Starts one level below 681 * the root (assumes an implicit match with rootnode). 682 * XXX - this could probably be done more cleanly using recursion. 683 */ 684 while (me != NULL) { 685 686 path = me->map_mntpnt; 687 688 if ((rc = get_dir_from_path(dirname, &path, 689 sizeof (dirname))) != 0) 690 return (rc); 691 692 prevnode = *rootnode; 693 currnode = (*rootnode)->subdir; 694 695 while (dirname[0] != '\0') { 696 if (currnode != NULL) { 697 if (strcmp(currnode->dirname, dirname) == 0) { 698 /* 699 * match found - mntpnt is a child of 700 * this node 701 */ 702 prevnode = currnode; 703 currnode = currnode->subdir; 704 } else { 705 prevnode = currnode; 706 currnode = currnode->leveldir; 707 708 if (currnode == NULL) { 709 /* 710 * No more leveldirs to match. 711 * Add a new one 712 */ 713 if ((rc = alloc_hiernode 714 (&newnode, dirname)) 715 != 0) 716 return (rc); 717 prevnode->leveldir = newnode; 718 prevnode = newnode; 719 currnode = newnode->subdir; 720 } else { 721 /* try this leveldir */ 722 continue; 723 } 724 } 725 } else { 726 /* no more subdirs to match. Add a new one */ 727 if ((rc = alloc_hiernode(&newnode, 728 dirname)) != 0) 729 return (rc); 730 prevnode->subdir = newnode; 731 prevnode = newnode; 732 currnode = newnode->subdir; 733 } 734 if ((rc = get_dir_from_path(dirname, &path, 735 sizeof (dirname))) != 0) 736 return (rc); 737 } 738 739 if (prevnode->mapent != NULL) { 740 /* duplicate mntpoint found */ 741 char *root; 742 743 if (strcmp(mapname, "-fstab") == 0) { 744 syslog(LOG_ERR, 745 "Duplicate mounts for %s:%s in fstab", 746 key, me->map_mntpnt); 747 } else { 748 root = me->map_root; 749 while (*root == '/') 750 root++; 751 syslog(LOG_ERR, 752 "Duplicate submounts for %s%s in map %s, key %s", 753 root, me->map_mntpnt, mapname, key); 754 } 755 return (EIO); 756 } 757 758 /* provide a pointer from node to mapent */ 759 prevnode->mapent = me; 760 me = me->map_next; 761 } 762 763 if (trace > 3) { 764 trace_prt(1, "\thierarchical_sort:\n"); 765 trace_hierarchy(*rootnode, 0); /* 0 is rootnode's level */ 766 } 767 768 return (rc); 769} 770 771/* 772 * push_options(hiernode *node, const char *defaultopts, const char *mapopts, 773 * int err) 774 * Pushes the options down a hierarchical structure. Works recursively from the 775 * root, which is passed in on the first call. Uses a replacement policy. 776 * If a node points to a mapentry, and it has an option, then thats the option 777 * for that mapentry. Else, the node's mapent inherits the option from the 778 * default (which may be the global option for the entry or mapopts). 779 * err is useful in flagging entries with errors in pushing options. 780 * returns 0 or appropriate error value. 781 * 782 * defaultopts and mapopts must each be at most MAXOPTSLEN bytes long 783 * (including the null terminator). 784 */ 785static int 786push_options(hiernode *node, char *defaultopts, const char *mapopts, int err) 787{ 788 int rc = 0; 789 struct mapent *me = NULL; 790 791 /* ensure that all the dirs at a level are passed the default options */ 792 while (node != NULL) { 793 me = node->mapent; 794 if (me != NULL) { /* not all nodes point to a mapentry */ 795 me->map_err = err; 796 if ((rc = set_mapent_opts(me, defaultopts, mapopts)) != 0) 797 return (rc); 798 } 799 800 /* push the options to subdirs */ 801 if (node->subdir != NULL) { 802 if (node->mapent && node->mapent->map_fstype && 803 strcmp(node->mapent->map_fstype, MNTTYPE_AUTOFS) == 0) 804 err = MAPENT_UATFS; 805 if ((rc = push_options(node->subdir, defaultopts, 806 mapopts, err)) != 0) 807 return (rc); 808 } 809 node = node->leveldir; 810 } 811 return (rc); 812} 813 814#define BACKFSTYPE "backfstype" /* used in cachefs options */ 815#define BACKFSTYPE_EQ "backfstype=" 816#define FSTYPE "fstype" 817#define FSTYPE_EQ "fstype=" 818#define NO_OPTS "" 819 820/* 821 * set_mapent_opts(struct mapent *me, char *defaultopts, const char *mapopts) 822 * sets the mapentry's options, fstype and mounter fields by separating 823 * out the fstype part from the opts. Use default options if me->map_mntopts 824 * is NULL. Note that defaultopts may be the same as mapopts. 825 * 826 * me->map_mntopts, defaultopts, and mapopts must each be at most 827 * AUTOFS_MAXOPTSLEN bytes long (including the null terminator). 828 * 829 * Returns 0 or appropriate error value. 830 */ 831static int 832set_mapent_opts(struct mapent *me, char *defaultopts, const char *mapopts) 833{ 834 char optsbuf[AUTOFS_MAXOPTSLEN]; 835 char *opts; 836 char entryopts[AUTOFS_MAXOPTSLEN]; 837 char fstype[MAX_FSLEN], mounter[MAX_FSLEN]; 838 int rc = 0; 839 bool_t fstype_opt = FALSE; 840 841 /* set options to default options, if none exist for this entry */ 842 if (me->map_mntopts == NULL) { 843 opts = defaultopts; 844 if (defaultopts == NULL) { /* NULL opts for entry */ 845 /* 846 * File system type not explicitly specified, as 847 * no options were specified; it will be determined 848 * later. 849 */ 850 me->map_fstype = NULL; 851 me->map_mounter = NULL; 852 return (0); 853 } 854 } else { 855 /* 856 * Make a copy of map_mntopts, as we'll free it below. 857 */ 858 if (CHECK_STRCPY(optsbuf, me->map_mntopts, AUTOFS_MAXOPTSLEN)) { 859 return (EIO); 860 } 861 opts = optsbuf; 862 } 863 864 if (*opts == '-') 865 opts++; 866 867 /* separate opts into fstype and (other) entrypopts */ 868 if (!get_opts(opts, entryopts, fstype, sizeof (fstype), &fstype_opt)) { 869 syslog(LOG_ERR, "file system type is too long"); 870 return (EIO); 871 } 872 873 /* replace any existing opts */ 874 if (me->map_mntopts != NULL) 875 free(me->map_mntopts); 876 if ((me->map_mntopts = strdup(entryopts)) == NULL) 877 return (ENOMEM); 878 879 if (fstype_opt == TRUE) { 880 strlcpy(mounter, fstype, sizeof(mounter)); 881 882#ifdef MNTTYPE_CACHEFS 883 /* 884 * The following ugly chunk of code crept in as a result of 885 * cachefs. If it's a cachefs mount of an nfs filesystem, then 886 * it's important to parse the nfs special field. Otherwise, 887 * just hand the special field to the fs-specific mount 888 */ 889 if (strcmp(fstype, MNTTYPE_CACHEFS) == 0) { 890 struct mnttab m; 891 char *p; 892 893 m.mnt_mntopts = entryopts; 894 if ((p = hasmntopt(&m, BACKFSTYPE)) != NULL) { 895 int len = strlen(MNTTYPE_NFS); 896 897 p += strlen(BACKFSTYPE_EQ); 898 899 if (strncmp(p, MNTTYPE_NFS, len) == 0 && 900 (p[len] == '\0' || p[len] == ',')) { 901 /* 902 * Cached nfs mount 903 */ 904 if (CHECK_STRCPY(fstype, MNTTYPE_NFS, sizeof (fstype))) { 905 return EIO; 906 } 907 if (CHECK_STRCPY(mounter, MNTTYPE_CACHEFS, sizeof (mounter))) { 908 return EIO; 909 } 910 } 911 } 912 } 913#endif 914 915 /* 916 * If the child options are exactly fstype = somefs, i.e. 917 * if no other options are specified, we need to do some 918 * more option pushing work. 919 */ 920 if (strcmp(me->map_mntopts, NO_OPTS) == 0) { 921 free(me->map_mntopts); 922 me->map_mntopts = NULL; 923 if ((rc = fstype_opts(me, opts, defaultopts, 924 mapopts)) != 0) 925 return (rc); 926 } 927 928 /* 929 * File system type explicitly specified; set it. 930 */ 931 if (((me->map_fstype = strdup(fstype)) == NULL) || 932 ((me->map_mounter = strdup(mounter)) == NULL)) { 933 if (me->map_fstype != NULL) 934 free(me->map_fstype); 935 syslog(LOG_ERR, "set_mapent_opts: No memory"); 936 return (ENOMEM); 937 } 938 } else { 939 /* 940 * File system type not explicitly specified; it 941 * will be determined later. 942 */ 943 me->map_fstype = NULL; 944 me->map_mounter = NULL; 945 } 946 947 return (rc); 948} 949 950/* 951 * Check the option string for an "fstype" 952 * option. If found, return the fstype 953 * and the option string with the fstype 954 * option removed, e.g. 955 * 956 * input: "fstype=cachefs,ro,nosuid" 957 * opts: "ro,nosuid" 958 * fstype: "cachefs" 959 * 960 * Also indicates if the fstype option was present 961 * by setting a flag, if the pointer to the flag 962 * is not NULL. 963 * 964 * input must be at most MAXOPTSLEN bytes long (including the null terminator). 965 * 966 * opts must point to a buffer at least MAXOPTSLEN bytes in size; we guarantee 967 * not to copy to it a string with more bytes than that (again, including the 968 * null terminator). 969 * 970 * 0 is returned if fstype would be more than fstype_size bytes long, 971 * otherwise 1 is returned. 972 */ 973static int 974get_opts(input, opts, fstype, fstype_size, fstype_opt) 975 const char *input; 976 char *opts; /* output */ 977 char *fstype; /* output */ 978 size_t fstype_size; 979 bool_t *fstype_opt; 980{ 981 char *p, *pb; 982 char buf[MAXOPTSLEN]; 983 char *placeholder; 984 985 *opts = '\0'; 986 if (CHECK_STRCPY(buf, input, sizeof (buf))) { 987 return 0; 988 } 989 pb = buf; 990 while ((p = strtok_r(pb, ",", &placeholder)) != NULL) { 991 pb = NULL; 992 if (strncmp(p, FSTYPE_EQ, 7) == 0) { 993 if (fstype_opt != NULL) 994 *fstype_opt = TRUE; 995 if (strlcpy(fstype, p + 7, fstype_size) >= fstype_size) 996 return (0); 997 } else { 998 if (*opts) { 999 if (CHECK_STRCAT(opts, ",", MAXOPTSLEN)) { 1000 return 0; 1001 } 1002 } 1003 if (CHECK_STRCAT(opts, p, MAXOPTSLEN)) { 1004 return 0; 1005 } 1006 } 1007 } 1008 return (1); 1009} 1010 1011/* 1012 * fstype_opts(struct mapent *me, const char *opts, const char *defaultopts, 1013 * const char *mapopts) 1014 * We need to push global options to the child entry if it is exactly 1015 * fstype=somefs. 1016 * 1017 * opts, defaultopts, and mapopts must each be at most AUTOFS_MAXOPTSLEN 1018 * bytes long (including the null terminator). 1019 */ 1020static int 1021fstype_opts(struct mapent *me, const char *opts, const char *defaultopts, 1022 const char *mapopts) 1023{ 1024 const char *optstopush; 1025 char pushopts[AUTOFS_MAXOPTSLEN]; 1026 char pushentryopts[AUTOFS_MAXOPTSLEN]; 1027 char pushfstype[MAX_FSLEN]; 1028 int err; 1029 1030 if (defaultopts && *defaultopts == '-') 1031 defaultopts++; 1032 1033 /* 1034 * the options to push are the global defaults for the entry, 1035 * if they exist, or mapopts, if the global defaults for the 1036 * entry does not exist. 1037 */ 1038 if (strcmp(defaultopts, opts) == 0) { 1039 if (*mapopts == '-') 1040 mapopts++; 1041 optstopush = mapopts; 1042 } else 1043 optstopush = defaultopts; 1044 if (!get_opts(optstopush, pushentryopts, pushfstype, 1045 sizeof (pushfstype), NULL)) { 1046 syslog(LOG_ERR, "file system type is too long"); 1047 return (EIO); 1048 } 1049 if (CHECK_STRCPY(pushopts, optstopush, sizeof (pushopts))) { 1050 return EIO; 1051 } 1052 1053#ifdef MNTTYPE_CACHEFS 1054 if (strcmp(pushfstype, MNTTYPE_CACHEFS) == 0) 1055 err = strldup(&me->map_mntopts, pushopts, MAXOPTSLEN); 1056 else 1057#endif 1058 err = strldup(&me->map_mntopts, pushentryopts, MAXOPTSLEN); 1059 1060 if (err == ENAMETOOLONG) { 1061 syslog(LOG_ERR, "mount options are too long"); 1062 return (EIO); 1063 } 1064 if (err == ENOMEM) { 1065 syslog(LOG_ERR, "fstype_opts: No memory"); 1066 return (ENOMEM); 1067 } 1068 1069 return (0); 1070} 1071 1072/* 1073 * modify_mapents(struct mapent **mapents, const char *mapname, 1074 * const char *mapopts, const char *subdir, 1075 * hiernode *rootnode, const char *key, 1076 * uint_t isdirect, bool_t mount_access) 1077 * modifies the intermediate mapentry list into the final one, and passes 1078 * back a pointer to it. The final list may contain faked mapentries for 1079 * hiernodes that do not point to a mapentry, or converted mapentries, if 1080 * hiernodes that point to a mapentry need to be converted from nfs to autofs. 1081 * mounts. Entries that are not directly 1 level below the subdir are removed. 1082 * Returns 0, ENOENT, or EIO 1083 */ 1084static int 1085modify_mapents(struct mapent **mapents, const char *mapname, 1086 const char *mapopts, const char *subdir, 1087 hiernode *rootnode, const char *key, 1088 uint_t isdirect, bool_t mount_access) 1089{ 1090 struct mapent *mp = NULL; 1091 char w[MAXPATHLEN]; 1092 1093 struct mapent *me; 1094 int rc = 0; 1095 struct mapent *faked_mapents = NULL; 1096 1097 /* 1098 * correct the mapentry mntlevel from default -1 to level depending on 1099 * position in hierarchy, and build any faked mapentries, if required 1100 * at one level below the rootnode given by subdir. 1101 */ 1102 if ((rc = set_and_fake_mapent_mntlevel(rootnode, subdir, key, mapname, 1103 &faked_mapents, isdirect, mapopts, mount_access)) != 0) 1104 return (rc); 1105 1106 /* 1107 * attaches faked mapents to real mapents list. Assumes mapents 1108 * is not NULL. 1109 */ 1110 me = *mapents; 1111 while (me->map_next != NULL) 1112 me = me->map_next; 1113 me->map_next = faked_mapents; 1114 1115 /* 1116 * get rid of nodes marked at level -1 1117 */ 1118 me = *mapents; 1119 while (me != NULL) { 1120 if ((me->map_mntlevel == -1) || (me->map_err) || 1121 (mount_access == FALSE && me->map_mntlevel == 0)) { 1122 /* 1123 * syslog any errors and free entry 1124 */ 1125 if (me->map_err) 1126 dump_mapent_err(me, key, mapname); 1127 1128 if (me == (*mapents)) { 1129 /* special case when head has to be freed */ 1130 *mapents = me->map_next; 1131 if ((*mapents) == NULL) { 1132 /* something wierd happened */ 1133 if (verbose) 1134 syslog(LOG_ERR, 1135 "modify_mapents: level error"); 1136 return (EIO); 1137 } 1138 1139 /* separate out the node */ 1140 me->map_next = NULL; 1141 free_mapent(me); 1142 me = *mapents; 1143 } else { 1144 mp->map_next = me->map_next; 1145 me->map_next = NULL; 1146 free_mapent(me); 1147 me = mp->map_next; 1148 } 1149 continue; 1150 } 1151 1152 /* 1153 * convert level 1 mapents that are not already autonodes 1154 * to autonodes 1155 */ 1156 if (me->map_mntlevel == 1 && me->map_fstype != NULL && 1157 (strcmp(me->map_fstype, MNTTYPE_AUTOFS) != 0) && 1158 (me->map_faked != TRUE)) { 1159 if ((rc = convert_mapent_to_automount(me, mapname, 1160 mapopts)) != 0) 1161 return (rc); 1162 } 1163 if (CHECK_STRCPY(w, (me->map_mntpnt+strlen(subdir)), sizeof (w))) { 1164 return EIO; 1165 } 1166 /* w is shorter than me->map_mntpnt, so this strcpy is safe */ 1167 strcpy(me->map_mntpnt, w); 1168 mp = me; 1169 me = me->map_next; 1170 } 1171 1172 if (trace > 3) 1173 trace_mapents("modify_mapents:", *mapents); 1174 1175 return (0); 1176} 1177 1178/* 1179 * set_and_fake_mapent_mntlevel(hiernode *rootnode, const char *subdir, 1180 * const char *key, const char *mapname, 1181 * struct mapent **faked_mapents, uint_t isdirect, 1182 * const char *mapopts, bool_t mount_access) 1183 * sets the mapentry mount levels (depths) with respect to the subdir. 1184 * Assigns a value of 0 to the new root. Finds the level1 directories by 1185 * calling mark_*_level1_*(). Also cleans off extra /'s in level0 and 1186 * level1 map_mntpnts. Note that one level below the new root is an existing 1187 * mapentry if there's a mapentry (nfs mount) corresponding to the root, 1188 * and the direct subdir set for the root, if there's no mapentry corresponding 1189 * to the root (we install autodirs). Returns 0 or error value. 1190 */ 1191static int 1192set_and_fake_mapent_mntlevel(hiernode *rootnode, const char *subdir, 1193 const char *key, const char *mapname, 1194 struct mapent **faked_mapents, uint_t isdirect, 1195 const char *mapopts, bool_t mount_access) 1196{ 1197 char dirname[MAXFILENAMELEN]; 1198 char traversed_path[MAXPATHLEN]; /* used in building fake mapentries */ 1199 1200 const char *subdir_child = subdir; 1201 hiernode *prevnode = rootnode; 1202 hiernode *currnode = rootnode->subdir; 1203 int rc = 0; 1204 traversed_path[0] = '\0'; 1205 1206 /* 1207 * find and mark the root by tracing down subdir. Use traversed_path 1208 * to keep track of how far we go, while guaranteeing that it 1209 * contains no '/' at the end. Took some mucking to get that right. 1210 */ 1211 if ((rc = get_dir_from_path(dirname, &subdir_child, sizeof (dirname))) 1212 != 0) 1213 return (rc); 1214 1215 if (dirname[0] != '\0') { 1216 if (strlcat(traversed_path, "/", sizeof (traversed_path)) >= 1217 sizeof (traversed_path) || 1218 strlcat(traversed_path, dirname, sizeof (traversed_path)) >= 1219 sizeof (traversed_path)) { 1220 syslog(LOG_ERR, "set_and_fake_mapent_mntlevel: maximum path name length exceeded"); 1221 return (EIO); 1222 } 1223 } 1224 1225 prevnode = rootnode; 1226 currnode = rootnode->subdir; 1227 while (dirname[0] != '\0' && currnode != NULL) { 1228 if (strcmp(currnode->dirname, dirname) == 0) { 1229 1230 /* subdir is a child of currnode */ 1231 prevnode = currnode; 1232 currnode = currnode->subdir; 1233 1234 if ((rc = get_dir_from_path(dirname, &subdir_child, 1235 sizeof (dirname))) != 0) 1236 return (rc); 1237 if (dirname[0] != '\0') { 1238 if (strlcat(traversed_path, "/", sizeof (traversed_path)) >= 1239 sizeof (traversed_path) || 1240 strlcat(traversed_path, dirname, sizeof (traversed_path)) >= 1241 sizeof (traversed_path)) { 1242 syslog(LOG_ERR, 1243 "set_and_fake_mapent_mntlevel: maximum path name length exceeded"); 1244 return (EIO); 1245 } 1246 } 1247 } else { 1248 /* try next leveldir */ 1249 prevnode = currnode; 1250 currnode = currnode->leveldir; 1251 } 1252 } 1253 1254 if (dirname[0] != '\0') { 1255 /* 1256 * We didn't find dirname in the map entry. 1257 */ 1258 if (verbose) 1259 syslog(LOG_ERR, 1260 "set_and_fake_mapent_mntlevel: subdir=%s error: %s not found in map=%s", 1261 subdir, dirname, mapname); 1262 return (ENOENT); 1263 } 1264 1265 /* 1266 * see if level of root really points to a mapent and if 1267 * have access to that filessystem - call appropriate 1268 * routine to mark level 1 nodes, and build faked entries 1269 */ 1270 if (prevnode->mapent != NULL && mount_access == TRUE) { 1271 if (trace > 3) 1272 trace_prt(1, " node mountpoint %s\t travpath=%s\n", 1273 prevnode->mapent->map_mntpnt, traversed_path); 1274 1275 /* 1276 * Copy traversed path map_mntpnt to get rid of any extra 1277 * '/' the map entry may contain. 1278 */ 1279 if (strlen(prevnode->mapent->map_mntpnt) < 1280 strlen(traversed_path)) { /* sanity check */ 1281 /* 1282 * prevnode->mapent->map_mntpnt is too small to hold 1283 * traversed_path. 1284 */ 1285 if (verbose) 1286 syslog(LOG_ERR, 1287 "set_and_fake_mapent_mntlevel: path=%s error", 1288 traversed_path); 1289 return (EIO); 1290 } 1291 /* 1292 * traversed_path is shorter than prevnode->mapent->map_mntpnt, 1293 * so we can safely copy traversed_path to 1294 * prevnode->mapent->map_mntpnt. 1295 */ 1296 if (strcmp(prevnode->mapent->map_mntpnt, traversed_path) != 0) 1297 strcpy(prevnode->mapent->map_mntpnt, traversed_path); 1298 1299 prevnode->mapent->map_mntlevel = 0; /* root level is 0 */ 1300 if (currnode != NULL) { 1301 if ((rc = mark_level1_root(currnode, 1302 traversed_path)) != 0) 1303 return (rc); 1304 } 1305 } else if (currnode != NULL) { 1306 if (trace > 3) 1307 trace_prt(1, " No rootnode, travpath=%s\n", traversed_path); 1308 if ((rc = mark_and_fake_level1_noroot(currnode, 1309 traversed_path, key, mapname, faked_mapents, isdirect, 1310 mapopts)) != 0) 1311 return (rc); 1312 } 1313 1314 if (trace > 3) { 1315 trace_prt(1, "\tset_and_fake_mapent_mntlevel\n"); 1316 trace_hierarchy(rootnode, 0); 1317 } 1318 1319 return (rc); 1320} 1321 1322 1323/* 1324 * mark_level1_root(hiernode *node, char *traversed_path) 1325 * marks nodes upto one level below the rootnode given by subdir 1326 * recursively. Called if rootnode points to a mapent. 1327 * In this routine, a level 1 node is considered to be the 1st existing 1328 * mapentry below the root node, so there's no faking involved. 1329 * Returns 0 or error value 1330 */ 1331static int 1332mark_level1_root(hiernode *node, char *traversed_path) 1333{ 1334 /* ensure we touch all leveldirs */ 1335 while (node) { 1336 /* 1337 * mark node level as 1, if one exists - else walk down 1338 * subdirs until we find one. 1339 */ 1340 if (node->mapent == NULL) { 1341 char w[MAXPATHLEN]; 1342 1343 if (node->subdir != NULL) { 1344 if (snprintf(w, sizeof (w), "%s/%s", 1345 traversed_path, node->dirname) >= 1346 (int)sizeof (w)) { 1347 syslog(LOG_ERR, "mark_level1_root: maximum path name length exceeded"); 1348 return (EIO); 1349 } 1350 if (mark_level1_root(node->subdir, w) == EIO) 1351 return (EIO); 1352 } else { 1353 if (verbose) { 1354 syslog(LOG_ERR, 1355 "mark_level1_root: hierarchy error"); 1356 } 1357 return (EIO); 1358 } 1359 } else { 1360 char w[MAXPATHLEN]; 1361 1362 if (snprintf(w, sizeof (w), "%s/%s", traversed_path, 1363 node->dirname) >= (int)sizeof (w)) { 1364 syslog(LOG_ERR, "mark_level1_root: maximum path name length exceeded"); 1365 return (EIO); 1366 } 1367 if (trace > 3) 1368 trace_prt(1, " node mntpnt %s\t travpath %s\n", 1369 node->mapent->map_mntpnt, w); 1370 1371 /* replace mntpnt with travpath to clean extra '/' */ 1372 if (strlen(node->mapent->map_mntpnt) < strlen(w)) { 1373 if (verbose) { 1374 /* 1375 * node->mapent->map_mntpnt is too 1376 * small to hold w. 1377 */ 1378 syslog(LOG_ERR, 1379 "mark_level1_root: path=%s error", 1380 traversed_path); 1381 } 1382 return (EIO); 1383 } 1384 /* 1385 * w is shorter than node->mapent->map_mntpnt, 1386 * so we can safely copy w to node->mapent->map_mntpnt. 1387 */ 1388 if (strcmp(node->mapent->map_mntpnt, w) != 0) 1389 strcpy(node->mapent->map_mntpnt, w); 1390 node->mapent->map_mntlevel = 1; 1391 } 1392 node = node->leveldir; 1393 } 1394 return (0); 1395} 1396 1397/* 1398 * mark_and_fake_level1_noroot(hiernode *node, char *traversed_path, 1399 * char *key,char *mapname, struct mapent **faked_mapents, 1400 * uint_t isdirect, char *mapopts) 1401 * Called if the root of the hierarchy does not point to a mapent. marks nodes 1402 * upto one physical level below the rootnode given by subdir. checks if 1403 * there's a real mapentry. If not, it builds a faked one (autonode) at that 1404 * point. The faked autonode is direct, with the map being the same as the 1405 * original one from which the call originated. Options are same as that of 1406 * the map and assigned in automount_opts(). Returns 0 or error value. 1407 */ 1408static int 1409mark_and_fake_level1_noroot(hiernode *node, const char *traversed_path, 1410 const char *key, const char *mapname, 1411 struct mapent **faked_mapents, uint_t isdirect, 1412 const char *mapopts) 1413{ 1414 struct mapent *me; 1415 int rc = 0; 1416 char w[MAXPATHLEN]; 1417 1418 while (node != NULL) { 1419 me = NULL; 1420 if (node->mapent != NULL) { 1421 /* 1422 * existing mapentry at level 1 - copy travpath to 1423 * get rid of extra '/' in mntpnt 1424 */ 1425 if (snprintf(w, sizeof (w), "%s/%s", traversed_path, 1426 node->dirname) >= (int)sizeof (w)) { 1427 syslog(LOG_ERR, 1428 "mark_fake_level1_noroot: maximum path name length exceeded"); 1429 return (EIO); 1430 } 1431 if (trace > 3) 1432 trace_prt(1, " node mntpnt=%s\t travpath=%s\n", 1433 node->mapent->map_mntpnt, w); 1434 if (strlen(node->mapent->map_mntpnt) < strlen(w)) { 1435 /* sanity check */ 1436 if (verbose) { 1437 /* 1438 * node->mapent->map_mntpnt is too 1439 * small to hold w. 1440 */ 1441 syslog(LOG_ERR, 1442 "mark_fake_level1_noroot:path=%s error", 1443 traversed_path); 1444 } 1445 return (EIO); 1446 } 1447 /* 1448 * w is shorter than node->mapent->map_mntpnt, 1449 * so we can safely copy w to node->mapent->map_mntpnt. 1450 */ 1451 if (strcmp(node->mapent->map_mntpnt, w) != 0) 1452 strcpy(node->mapent->map_mntpnt, w); 1453 node->mapent->map_mntlevel = 1; 1454 } else { 1455 /* 1456 * build the faked autonode 1457 */ 1458 if ((me = (struct mapent *)malloc(sizeof (*me))) 1459 == NULL) 1460 goto alloc_failed; 1461 (void) memset((char *)me, 0, sizeof (*me)); 1462 1463 if ((me->map_fs = (struct mapfs *) 1464 malloc(sizeof (struct mapfs))) == NULL) 1465 goto alloc_failed; 1466 (void) memset(me->map_fs, 0, sizeof (struct mapfs)); 1467 1468 if (isdirect) 1469 me->map_root = strdup(""); 1470 else 1471 me->map_root = strprefix_slash(key); 1472 if (me->map_root == NULL) 1473 goto alloc_failed; 1474 1475 if (asprintf(&me->map_mntpnt, "%s/%s", traversed_path, 1476 node->dirname) == -1) 1477 goto alloc_failed; 1478 me->map_fstype = strdup(MNTTYPE_AUTOFS); 1479 me->map_mounter = strdup(MNTTYPE_AUTOFS); 1480 1481 /* set options */ 1482 if ((rc = automount_opts(&me->map_mntopts, mapopts)) 1483 != 0) { 1484 free_mapent(me); 1485 return (rc); 1486 } 1487 me->map_fs->mfs_dir = strdup(mapname); 1488 me->map_mntlevel = 1; 1489 me->map_modified = FALSE; 1490 me->map_faked = TRUE; /* mark as faked */ 1491 if (me->map_root == NULL || 1492 me->map_mntpnt == NULL || 1493 me->map_fstype == NULL || 1494 me->map_mounter == NULL || 1495 me->map_mntopts == NULL || 1496 me->map_fs->mfs_dir == NULL) 1497 goto alloc_failed; 1498 1499 if (*faked_mapents == NULL) 1500 *faked_mapents = me; 1501 else { /* attach to the head */ 1502 me->map_next = *faked_mapents; 1503 *faked_mapents = me; 1504 } 1505 node->mapent = me; 1506 } 1507 node = node->leveldir; 1508 } 1509 return (rc); 1510 1511alloc_failed: 1512 syslog(LOG_ERR, "mark_and_fake_level1_noroot: out of memory"); 1513 if (me != NULL) { 1514 if (me->map_mounter != NULL) 1515 free(me->map_mounter); 1516 if (me->map_fstype != NULL) 1517 free(me->map_fstype); 1518 if (me->map_mntpnt != NULL) 1519 free(me->map_mntpnt); 1520 if (me->map_root != NULL) 1521 free(me->map_root); 1522 if (me->map_fs != NULL) { 1523 if (me->map_fs->mfs_dir != NULL) 1524 free(me->map_fs->mfs_dir); 1525 free(me->map_fs); 1526 } 1527 free(me); 1528 } 1529 free_mapent(*faked_mapents); 1530 return (ENOMEM); 1531} 1532 1533/* 1534 * convert_mapent_to_automount(struct mapent *me, const char *mapname, 1535 * const char *mapopts) 1536 * change the mapentry me to an automount - free fields first and NULL them 1537 * to avoid freeing again, while freeing the mapentry at a later stage. 1538 * Could have avoided freeing entries here as we don't really look at them. 1539 * Give the converted mapent entry the options that came with the map using 1540 * automount_opts(). Returns 0 or appropriate error value. 1541 */ 1542static int 1543convert_mapent_to_automount(struct mapent *me, const char *mapname, 1544 const char *mapopts) 1545{ 1546 struct mapfs *mfs = me->map_fs; /* assumes it exists */ 1547 int rc = 0; 1548 1549 /* free relevant entries */ 1550 if (mfs->mfs_host) { 1551 free(mfs->mfs_host); 1552 mfs->mfs_host = NULL; 1553 } 1554 while (me->map_fs->mfs_next != NULL) { 1555 mfs = me->map_fs->mfs_next; 1556 if (mfs->mfs_host) 1557 free(mfs->mfs_host); 1558 if (mfs->mfs_dir) 1559 free(mfs->mfs_dir); 1560 me->map_fs->mfs_next = mfs->mfs_next; /* nulls eventually */ 1561 free((void*)mfs); 1562 } 1563 1564 /* replace relevant entries */ 1565 if (me->map_fstype) 1566 free(me->map_fstype); 1567 if ((me->map_fstype = strdup(MNTTYPE_AUTOFS)) == NULL) 1568 goto alloc_failed; 1569 1570 if (me->map_mounter) 1571 free(me->map_mounter); 1572 if ((me->map_mounter = strdup(me->map_fstype)) == NULL) 1573 goto alloc_failed; 1574 1575 if (me->map_fs->mfs_dir) 1576 free(me->map_fs->mfs_dir); 1577 if ((me->map_fs->mfs_dir = strdup(mapname)) == NULL) 1578 goto alloc_failed; 1579 1580 /* set options */ 1581 if (me->map_mntopts) { 1582 free(me->map_mntopts); 1583 me->map_mntopts = NULL; 1584 } 1585 if ((rc = automount_opts(&me->map_mntopts, mapopts)) != 0) 1586 return (rc); 1587 1588 /* mucked with this entry, set the map_modified field to TRUE */ 1589 me->map_modified = TRUE; 1590 1591 return (rc); 1592 1593alloc_failed: 1594 syslog(LOG_ERR, 1595 "convert_mapent_to_automount: Memory allocation failed"); 1596 return (ENOMEM); 1597} 1598 1599/* 1600 * automount_opts(char **map_mntopts, const char *mapopts) 1601 * modifies automount opts - gets rid of all "indirect" and "direct" strings 1602 * if they exist, and then adds a direct string to force a direct automount. 1603 * Rest of the mapopts stay intact. Returns 0 or appropriate error. 1604 */ 1605static int 1606automount_opts(char **map_mntopts, const char *mapopts) 1607{ 1608 char *opts; 1609 char *opt; 1610 size_t len; 1611 char *placeholder; 1612 char buf[AUTOFS_MAXOPTSLEN]; 1613 1614 char *addopt = "direct"; 1615 1616 len = strlen(mapopts)+ strlen(addopt)+2; /* +2 for ",", '\0' */ 1617 if (len > AUTOFS_MAXOPTSLEN) { 1618 syslog(LOG_ERR, 1619 "option string %s too long (max=%d)", mapopts, 1620 AUTOFS_MAXOPTSLEN-8); 1621 return (EIO); 1622 } 1623 1624 if (((*map_mntopts) = ((char *)malloc(len))) == NULL) { 1625 syslog(LOG_ERR, "automount_opts: Memory allocation failed"); 1626 return (ENOMEM); 1627 } 1628 memset(*map_mntopts, 0, len); 1629 1630 strlcpy(buf, mapopts, sizeof(buf)); 1631 opts = buf; 1632 while ((opt = strtok_r(opts, ",", &placeholder)) != NULL) { 1633 opts = NULL; 1634 1635 /* remove trailing and leading spaces */ 1636 while (isspace(*opt)) 1637 opt++; 1638 len = strlen(opt)-1; 1639 while (isspace(opt[len])) 1640 opt[len--] = '\0'; 1641 1642 /* 1643 * if direct or indirect found, get rid of it, else put it 1644 * back 1645 */ 1646 if ((strcmp(opt, "indirect") == 0) || 1647 (strcmp(opt, "direct") == 0)) 1648 continue; 1649 /* 1650 * *map_mntopts is large enough to hold a copy of mapopts, 1651 * so these strcat()s are safe. 1652 */ 1653 if (*map_mntopts[0] != '\0') 1654 strcat(*map_mntopts, ","); 1655 strcat(*map_mntopts, opt); 1656 } 1657 1658 /* 1659 * Add the direct string at the end. 1660 * *map_mntopts is large enough to hold a copy of mapopts, plus 1661 * a comma, plus addopt, so these strcat()s are safe. 1662 */ 1663 if (*map_mntopts[0] != '\0') 1664 strcat(*map_mntopts, ","); 1665 strcat(*map_mntopts, addopt); 1666 1667 return (0); 1668} 1669 1670/* 1671 * parse_fsinfo(char *mapname, struct mapent *mapents) 1672 * parses the filesystem information stored in me->map_fsw and me->map_fswq 1673 * and calls appropriate filesystem parser. 1674 * Returns 0 or an appropriate error value. 1675 */ 1676static int 1677parse_fsinfo(const char *mapname, struct mapent *mapents) 1678{ 1679 struct mapent *me = mapents; 1680 char *bufp; 1681 char *bufq; 1682 int err = 0; 1683 1684 while (me != NULL) { 1685 bufp = ""; 1686 bufq = ""; 1687 if (me->map_fstype == NULL) { 1688 /* 1689 * No file system type explicitly specified. 1690 * If the thing to be mounted looks like 1691 * a non-NFS URL, make the type "url", otherwise 1692 * make it "nfs". 1693 */ 1694 if (is_nonnfs_url(me->map_fsw, me->map_fswq)) 1695 me->map_fstype = strdup("url"); 1696 else 1697 me->map_fstype = strdup("nfs"); 1698 } 1699 if (strcmp(me->map_fstype, "nfs") == 0) { 1700 err = parse_nfs(mapname, me, me->map_fsw, 1701 me->map_fswq, &bufp, &bufq); 1702 } else { 1703 err = parse_special(me, me->map_fsw, me->map_fswq, 1704 &bufp, &bufq, MAXPATHLEN); 1705 } 1706 1707 if (err != 0 || *me->map_fsw != '\0' || 1708 *me->map_fswq != '\0') { 1709 /* sanity check */ 1710 if (verbose) 1711 syslog(LOG_ERR, 1712 "parse_fsinfo: mount location error %s", 1713 me->map_fsw); 1714 return (EIO); 1715 } 1716 1717 me = me->map_next; 1718 } 1719 1720 if (trace > 3) { 1721 trace_mapents("parse_fsinfo:", mapents); 1722 } 1723 1724 return (0); 1725} 1726 1727/* 1728 * Check whether the first token we see looks like a non-NFS URL or not. 1729 */ 1730static int 1731is_nonnfs_url(fsw, fswq) 1732 char *fsw, *fswq; 1733{ 1734 char tok1[MAXPATHLEN + 1], tok1q[MAXPATHLEN + 1]; 1735 char *tok1p, *tok1qp; 1736 char c; 1737 1738 if (getword(tok1, tok1q, &fsw, &fswq, ' ', sizeof (tok1)) == -1) 1739 return (0); /* error, doesn't look like anything */ 1740 1741 tok1p = tok1; 1742 tok1qp = tok1q; 1743 1744 /* 1745 * Scan until we find a character that doesn't belong in a URL 1746 * scheme. According to RFC 3986, "Scheme names consist of a 1747 * sequence of characters beginning with a letter and followed 1748 * by any combination of letters, digits, plus ("+"), period ("."), 1749 * or hyphen ("-")." 1750 * 1751 * It's irrelevant whether the characters are quoted or not; 1752 * quoting doesn't mean that the character is allowed to be part 1753 * of a URL. 1754 */ 1755 while ((c = *tok1p) != '\0' && isascii(c) && 1756 (isalnum(c) || c == '+' || c == '.' || c == '-')) { 1757 tok1p++; 1758 tok1qp++; 1759 } 1760 1761 /* 1762 * The character in question had better be a colon (quoted or not), 1763 * otherwise this doesn't look like a URL. 1764 */ 1765 if (*tok1p != ':') 1766 return (0); /* not a URL */ 1767 1768 /* 1769 * However, if it's a colon, that doesn't prove it's a URL; we 1770 * could have server:/path. Check whether the colon is followed 1771 * by two slashes (quoted or not). 1772 */ 1773 if (*(tok1p + 1) != '/' || *(tok1p + 2) != '/') 1774 return (0); /* not a URL */ 1775 1776 /* 1777 * scheme://... 1778 * Probably a URL; is the scheme "nfs"? 1779 */ 1780 if (tok1p - tok1 != 3) { 1781 /* 1782 * It's not 3 characters long, so it's not "nfs", so 1783 * we have something that looks like a non-NFS URL. 1784 */ 1785 return (1); 1786 } 1787 1788 /* 1789 * It's 3 characters long; is it "nfs" (case-insensitive)? 1790 */ 1791 if (strncasecmp(tok1, "nfs", 3) != 0) 1792 return (1); /* no, so probably a non-NFS URL */ 1793 return (0); /* yes, so not a non-NFS URL */ 1794} 1795 1796/* 1797 * This function parses the map entry for a nfs type file system 1798 * The input is the string lp (and lq) which can be one of the 1799 * following forms: 1800 * a) host[(penalty)][,host[(penalty)]]... :/directory 1801 * b) host[(penalty)]:/directory[ host[(penalty)]:/directory]... 1802 * This routine constructs a mapfs link-list for each of 1803 * the hosts and the corresponding file system. The list 1804 * is then attatched to the mapent struct passed in. 1805 */ 1806static int 1807parse_nfs(mapname, me, fsw, fswq, lp, lq) 1808 struct mapent *me; 1809 const char *mapname; 1810 char *fsw, *fswq, **lp, **lq; 1811{ 1812 struct mapfs *mfs, **mfsp; 1813 char *wlp, *wlq; 1814 char *hl, hostlist[1024], *hlq, hostlistq[1024]; 1815 char hostname_and_penalty[MXHOSTNAMELEN+5]; 1816 char *hn, *hnq, hostname[MXHOSTNAMELEN+1]; 1817 char dirname[MAXPATHLEN+1], subdir[MAXPATHLEN+1]; 1818 char qbuff[MAXPATHLEN+1], qbuff1[MAXPATHLEN+1]; 1819 char pbuff[10], pbuffq[10]; 1820 int penalty; 1821 char w[MAXPATHLEN]; 1822 char wq[MAXPATHLEN]; 1823 int host_cnt; 1824 1825 mfsp = &me->map_fs; 1826 *mfsp = NULL; 1827 1828 /* 1829 * there may be more than one entry in the map list. Get the 1830 * first one. Use temps to handle the word information and 1831 * copy back into fsw and fswq fields when done. 1832 */ 1833 *lp = fsw; 1834 *lq = fswq; 1835 if (getword(w, wq, lp, lq, ' ', MAXPATHLEN) == -1) 1836 return (EIO); 1837 while (*w && *w != '/') { 1838 bool_t maybe_url; 1839 1840 maybe_url = TRUE; 1841 1842 wlp = w; wlq = wq; 1843 if (getword(hostlist, hostlistq, &wlp, &wlq, ':', 1844 sizeof (hostlist)) == -1) 1845 return (EIO); 1846 if (!*hostlist) { 1847 syslog(LOG_ERR, 1848 "parse_nfs: host list empty in map %s \"%s\"", 1849 mapname, w); 1850 goto bad_entry; 1851 } 1852 1853 if (strcmp(hostlist, "nfs") != 0) 1854 maybe_url = FALSE; 1855 1856 if (getword(dirname, qbuff, &wlp, &wlq, ':', 1857 sizeof (dirname)) == -1) 1858 return (EIO); 1859 if (*dirname == '\0') { 1860 syslog(LOG_ERR, 1861 "parse_nfs: directory name empty in map %s \"%s\"", 1862 mapname, w); 1863 goto bad_entry; 1864 } 1865 1866 if (maybe_url == TRUE && strncmp(dirname, "//", 2) != 0) 1867 maybe_url = FALSE; 1868 1869 /* 1870 * See the next block comment ("Once upon a time ...") to 1871 * understand this. It turns the deprecated concept 1872 * of "subdir mounts" produced some useful code for handling 1873 * the possibility of a ":port#" in the URL. 1874 */ 1875 if (maybe_url == FALSE) 1876 *subdir = '/'; 1877 else 1878 *subdir = ':'; 1879 1880 *qbuff = ' '; 1881 1882 /* 1883 * Once upon time, before autofs, there was support for 1884 * "subdir mounts". The idea was to "economize" the 1885 * number of mounts, so if you had a number of entries 1886 * all referring to a common subdirectory, e.g. 1887 * 1888 * carol seasons:/export/home11/carol 1889 * ted seasons:/export/home11/ted 1890 * alice seasons:/export/home11/alice 1891 * 1892 * then you could tell the automounter to mount a 1893 * common mountpoint which was delimited by the second 1894 * colon: 1895 * 1896 * carol seasons:/export/home11:carol 1897 * ted seasons:/export/home11:ted 1898 * alice seasons:/export/home11:alice 1899 * 1900 * The automounter would mount seasons:/export/home11 1901 * then for any other map entry that referenced the same 1902 * directory it would build a symbolic link that 1903 * appended the remainder of the path after the second 1904 * colon, i.e. once the common subdir was mounted, then 1905 * other directories could be accessed just by link 1906 * building - no further mounts required. 1907 * 1908 * In theory the "mount saving" idea sounded good. In 1909 * practice the saving didn't amount to much and the 1910 * symbolic links confused people because the common 1911 * mountpoint had to have a pseudonym. 1912 * 1913 * To remain backward compatible with the existing 1914 * maps, we interpret a second colon as a slash. 1915 */ 1916 if (getword(subdir+1, qbuff+1, &wlp, &wlq, ':', 1917 sizeof (subdir)) == -1) 1918 return (EIO); 1919 1920 if (*(subdir+1)) { 1921 if (strlcat(dirname, subdir, sizeof (dirname)) >= 1922 sizeof (dirname)) { 1923 syslog(LOG_ERR, "directory+subdirectory is longer than MAXPATHLEN"); 1924 return (EIO); 1925 } 1926 } 1927 1928 hl = hostlist; hlq = hostlistq; 1929 1930 host_cnt = 0; 1931 for (;;) { 1932 1933 if (getword(hostname_and_penalty, qbuff, &hl, &hlq, ',', 1934 sizeof (hostname_and_penalty)) == -1) 1935 return (EIO); 1936 if (!*hostname_and_penalty) 1937 break; 1938 1939 host_cnt++; 1940 if (host_cnt > 1) 1941 maybe_url = FALSE; 1942 1943 hn = hostname_and_penalty; 1944 hnq = qbuff; 1945 if (getword(hostname, qbuff1, &hn, &hnq, '(', 1946 sizeof (hostname)) == -1) 1947 return (EIO); 1948 if (hostname[0] == '\0') { 1949 syslog(LOG_ERR, 1950 "parse_nfs: host name empty in host list in map %s \"%s\"", 1951 mapname, w); 1952 goto bad_entry; 1953 } 1954 1955 if (strcmp(hostname, hostname_and_penalty) == 0) { 1956 penalty = 0; 1957 } else { 1958 maybe_url = FALSE; 1959 hn++; hnq++; 1960 if (getword(pbuff, pbuffq, &hn, &hnq, ')', 1961 sizeof (pbuff)) == -1) 1962 return (EIO); 1963 if (!*pbuff) 1964 penalty = 0; 1965 else 1966 penalty = atoi(pbuff); 1967 } 1968 mfs = (struct mapfs *)malloc(sizeof (*mfs)); 1969 if (mfs == NULL) { 1970 syslog(LOG_ERR, 1971 "parse_nfs: Memory allocation failed"); 1972 return (EIO); 1973 } 1974 (void) memset(mfs, 0, sizeof (*mfs)); 1975 *mfsp = mfs; 1976 mfsp = &mfs->mfs_next; 1977 1978 if (maybe_url == TRUE) { 1979 char *host; 1980 char *path; 1981 char *sport; 1982 1983 host = dirname+2; 1984 path = strchr(host, '/'); 1985 if (path == NULL) { 1986 syslog(LOG_ERR, 1987 "parse_nfs: illegal nfs url syntax: %s", 1988 host); 1989 1990 return (EIO); 1991 } 1992 *path = '\0'; 1993 sport = strchr(host, ':'); 1994 1995 if (sport != NULL && sport < path) { 1996 *sport = '\0'; 1997 mfs->mfs_port = atoi(sport+1); 1998 1999 if (mfs->mfs_port > USHRT_MAX) { 2000 syslog(LOG_ERR, 2001 "parse_nfs: invalid " 2002 "port number (%d) in " 2003 "NFS URL", 2004 mfs->mfs_port); 2005 2006 return (EIO); 2007 } 2008 2009 } 2010 2011 path++; 2012 if (*path == '\0') 2013 path = "."; 2014 2015 mfs->mfs_flags |= MFS_URL; 2016 2017 mfs->mfs_host = strdup(host); 2018 mfs->mfs_dir = strdup(path); 2019 } else { 2020 mfs->mfs_host = strdup(hostname); 2021 mfs->mfs_dir = strdup(dirname); 2022 } 2023 2024 mfs->mfs_penalty = penalty; 2025 if (mfs->mfs_host == NULL || mfs->mfs_dir == NULL) { 2026 syslog(LOG_ERR, 2027 "parse_nfs: Memory allocation failed"); 2028 return (EIO); 2029 } 2030 } 2031 /* 2032 * We check host_cnt to make sure we haven't parsed an entry 2033 * with no host information. 2034 */ 2035 if (host_cnt == 0) { 2036 syslog(LOG_ERR, 2037 "parse_nfs: invalid host specified - bad entry " 2038 "in map %s \"%s\"", 2039 mapname, w); 2040 return (EIO); 2041 } 2042 if (getword(w, wq, lp, lq, ' ', MAXPATHLEN) == -1) 2043 return (EIO); 2044 } 2045 2046 /* 2047 * w and wq are constructed from the input fsw and fswq, 2048 * respectively, by extracting items, so they're guaranteed 2049 * to be shorter than fsw and fswq, so these copies are safe. 2050 */ 2051 strcpy(fsw, w); 2052 strcpy(fswq, wq); 2053 2054 return (0); 2055 2056bad_entry: 2057 return (EIO); 2058} 2059 2060static int 2061parse_special(me, w, wq, lp, lq, wsize) 2062 struct mapent *me; 2063 char *w, *wq, **lp, **lq; 2064 int wsize; 2065{ 2066 char mountname[MAXPATHLEN + 1], qbuf[MAXPATHLEN + 1]; 2067 char *wlp, *wlq; 2068 struct mapfs *mfs; 2069 2070 wlp = w; 2071 wlq = wq; 2072 if (getword(mountname, qbuf, &wlp, &wlq, ' ', sizeof (mountname)) == -1) 2073 return (EIO); 2074 if (mountname[0] == '\0') 2075 return (EIO); 2076 2077 mfs = (struct mapfs *)malloc(sizeof (struct mapfs)); 2078 if (mfs == NULL) { 2079 syslog(LOG_ERR, "parse_special: Memory allocation failed"); 2080 return (EIO); 2081 } 2082 (void) memset(mfs, 0, sizeof (*mfs)); 2083 2084 /* 2085 * A device name that begins with a slash could 2086 * be confused with a mountpoint path, hence use 2087 * a colon to escape a device string that begins 2088 * with a slash, e.g. 2089 * 2090 * foo -ro /bar foo:/bar 2091 * and 2092 * foo -ro /dev/sr0 2093 * 2094 * would confuse the parser. The second instance 2095 * must use a colon: 2096 * 2097 * foo -ro :/dev/sr0 2098 */ 2099 mfs->mfs_dir = strdup(&mountname[mountname[0] == ':']); 2100 if (mfs->mfs_dir == NULL) { 2101 syslog(LOG_ERR, "parse_special: Memory allocation failed"); 2102 return (EIO); 2103 } 2104 me->map_fs = mfs; 2105 if (getword(w, wq, lp, lq, ' ', wsize) == -1) 2106 return (EIO); 2107 return (0); 2108} 2109 2110/* 2111 * get_dir_from_path(char *dir, char **path, int dirsz) 2112 * gets the directory name dir from path for max string of length dirsz. 2113 * A modification of the getword routine. Assumes the delimiter is '/' 2114 * and that excess /'s are redundant. 2115 * Returns 0 or EIO 2116 */ 2117static int 2118get_dir_from_path(char *dir, const char **path, int dirsz) 2119{ 2120 char *tmp = dir; 2121 int count = dirsz; 2122 2123 if (dirsz <= 0) { 2124 if (verbose) 2125 syslog(LOG_ERR, 2126 "get_dir_from_path: invalid directory size %d", dirsz); 2127 return (EIO); 2128 } 2129 2130 /* get rid of leading /'s in path */ 2131 while (**path == '/') 2132 (*path)++; 2133 2134 /* now at a word or at the end of path */ 2135 while ((**path) && ((**path) != '/')) { 2136 if (--count <= 0) { 2137 *tmp = '\0'; 2138 syslog(LOG_ERR, 2139 "get_dir_from_path: max pathlength exceeded %d", dirsz); 2140 return (EIO); 2141 } 2142 *dir++ = *(*path)++; 2143 } 2144 2145 *dir = '\0'; 2146 2147 /* get rid of trailing /'s in path */ 2148 while (**path == '/') 2149 (*path)++; 2150 2151 return (0); 2152} 2153 2154/* 2155 * alloc_hiernode(hiernode **newnode, char *dirname) 2156 * allocates a new hiernode corresponding to a new directory entry 2157 * in the hierarchical structure, and passes a pointer to it back 2158 * to the calling program. 2159 * Returns 0 or appropriate error value. 2160 */ 2161static int 2162alloc_hiernode(hiernode **newnode, char *dirname) 2163{ 2164 if ((*newnode = (hiernode *)malloc(sizeof (hiernode))) == NULL) { 2165 syslog(LOG_ERR, "alloc_hiernode: Memory allocation failed"); 2166 return (ENOMEM); 2167 } 2168 2169 memset(((char *)*newnode), 0, sizeof (hiernode)); 2170 if (CHECK_STRCPY((*newnode)->dirname, dirname, sizeof ((*newnode)->dirname))) { 2171 return EIO; 2172 } 2173 return (0); 2174} 2175 2176/* 2177 * free_hiernode(hiernode *node) 2178 * frees the allocated hiernode given the head of the structure 2179 * recursively calls itself until it frees entire structure. 2180 * Returns nothing. 2181 */ 2182static void 2183free_hiernode(hiernode *node) 2184{ 2185 hiernode *currnode = node; 2186 hiernode *prevnode = NULL; 2187 2188 while (currnode != NULL) { 2189 if (currnode->subdir != NULL) 2190 free_hiernode(currnode->subdir); 2191 prevnode = currnode; 2192 currnode = currnode->leveldir; 2193 free((void*)prevnode); 2194 } 2195} 2196 2197/* 2198 * free_mapent(struct mapent *) 2199 * free the mapentry and its fields 2200 */ 2201void 2202free_mapent(me) 2203 struct mapent *me; 2204{ 2205 struct mapfs *mfs; 2206 struct mapent *m; 2207 2208 while (me) { 2209 while (me->map_fs) { 2210 mfs = me->map_fs; 2211 if (mfs->mfs_host) 2212 free(mfs->mfs_host); 2213 if (mfs->mfs_dir) 2214 free(mfs->mfs_dir); 2215 if (mfs->mfs_args) 2216 free(mfs->mfs_args); 2217 me->map_fs = mfs->mfs_next; 2218 free((char *)mfs); 2219 } 2220 2221 if (me->map_root) 2222 free(me->map_root); 2223 if (me->map_mntpnt) 2224 free(me->map_mntpnt); 2225 if (me->map_mntopts) 2226 free(me->map_mntopts); 2227 if (me->map_fstype) 2228 free(me->map_fstype); 2229 if (me->map_mounter) 2230 free(me->map_mounter); 2231 if (me->map_fsw) 2232 free(me->map_fsw); 2233 if (me->map_fswq) 2234 free(me->map_fswq); 2235 2236 m = me; 2237 me = me->map_next; 2238 free((char *)m); 2239 } 2240} 2241 2242/* 2243 * trace_mapents(struct mapent *mapents) 2244 * traces through the mapentry structure and prints it element by element 2245 * returns nothing 2246 */ 2247static void 2248trace_mapents(char *s, struct mapent *mapents) 2249{ 2250 struct mapfs *mfs; 2251 struct mapent *me; 2252 2253 trace_prt(1, "\t%s\n", s); 2254 for (me = mapents; me; me = me->map_next) { 2255 trace_prt(1, "\t (%s,%s)\t %s%s -%s\n", 2256 me->map_fstype ? me->map_fstype : "", 2257 me->map_mounter ? me->map_mounter : "", 2258 me->map_root ? me->map_root : "", 2259 me->map_mntpnt ? me->map_mntpnt : "", 2260 me->map_mntopts ? me->map_mntopts : ""); 2261 for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next) 2262 trace_prt(0, "\t\t%s:%s\n", 2263 mfs->mfs_host ? mfs->mfs_host: "", 2264 mfs->mfs_dir ? mfs->mfs_dir : ""); 2265 2266 trace_prt(1, "\t\tme->map_fsw=%s me->map_fswq=%s mntlevel=%d %s %s err=%d\n", 2267 me->map_fsw ? me->map_fsw:"", 2268 me->map_fswq ? me->map_fswq:"", 2269 me->map_mntlevel, 2270 me->map_modified ? "modify=TRUE":"modify=FALSE", 2271 me->map_faked ? "faked=TRUE":"faked=FALSE", 2272 me->map_err); 2273 } 2274} 2275 2276/* 2277 * trace_hierarchy(hiernode *node) 2278 * traces the allocated hiernode given the head of the structure 2279 * recursively calls itself until it traces entire structure. 2280 * the first call made at the root is made with a zero level. 2281 * nodelevel is simply used to print tab and make the tracing clean. 2282 * Returns nothing. 2283 */ 2284static void 2285trace_hierarchy(hiernode *node, int nodelevel) 2286{ 2287 char tabs[16]; 2288 int i; 2289 2290 tabs[0] = '\0'; 2291 for (i = 0; i < nodelevel; i++) 2292 strlcat(tabs, "\t", 16); 2293 2294 while (node != NULL) { 2295 if (node->mapent) { 2296 trace_prt(1, "%s(\"%s\", %d, %s)\n", tabs, 2297 node->dirname ? node->dirname :"-null-", 2298 node->mapent->map_mntlevel, 2299 node->mapent->map_mntopts ? node->mapent->map_mntopts:""); 2300 } else 2301 trace_prt(1, "%s(\"%s\")\n", tabs, node->dirname ? node->dirname :"-null-"); 2302 2303 if (node->subdir) 2304 trace_hierarchy(node->subdir, nodelevel + 1); 2305 2306 node = node->leveldir; 2307 } 2308} 2309 2310static const struct mntopt mopts_vers[] = { 2311 MOPT_VERS, 2312 { NULL, 0, 0, 0 } 2313}; 2314 2315static int 2316clnt_stat_to_errno(enum clnt_stat clnt_stat) 2317{ 2318 switch (clnt_stat) { 2319 2320 case RPC_UNKNOWNHOST: 2321 return (ENOENT); /* no such host = no such directory */ 2322 2323 case RPC_TIMEDOUT: 2324 return (ETIMEDOUT); 2325 2326 default: 2327 return (EIO); 2328 } 2329} 2330 2331/* 2332 * mapopts must be at most MAXOPTSLEN bytes long (including the null 2333 * terminator). 2334 */ 2335struct mapent * 2336do_mapent_hosts(mapopts, host, isdirect, err) 2337 const char *mapopts, *host; 2338 uint_t isdirect; 2339 int *err; 2340{ 2341 CLIENT *cl; 2342 struct mapent *me, *ms = NULL, *mp; 2343 struct mapfs *mfs; 2344 struct exportnode *ex = NULL; 2345 struct exportnode *exlist = NULL, *texlist, **texp, *exnext; 2346 struct timeval timeout = {10, 0}; // sec,usec 2347 enum clnt_stat clnt_stat; 2348 char entryopts[MAXOPTSLEN]; 2349 char fstype[MAX_FSLEN], mounter[MAX_FSLEN]; 2350 size_t exlen; 2351 int duplicate; 2352 mntoptparse_t mop; 2353 int flags; 2354 int altflags; 2355 long optval; 2356 rpcvers_t nfsvers; /* version in map options, 0 if not there */ 2357 rpcvers_t vers, versmin; /* used to negotiate nfs vers in pingnfs() */ 2358 int retries, delay; 2359 2360 if (trace > 1) 2361 trace_prt(1, " do_mapent_hosts: host %s\n", host); 2362 2363 /* 2364 * Check whether the host has a name that begins with "."; 2365 * if so, it's not a valid host name, and we return ENOENT. 2366 * That way, we avoid doing host name lookups for various 2367 * dot-file names, e.g. ".localized" and ".DS_Store". 2368 */ 2369 if (host[0] == '.') { 2370 *err = ENOENT; 2371 return ((struct mapent *)NULL); 2372 } 2373 2374 /* 2375 * XXX - this appears to assume that you can get *all* the 2376 * file systems - or, at least, all the local file systems - 2377 * from this machine simply by mounting / with the loopback 2378 * file system. We don't yet have a loopback file system, 2379 * so we have to mount individual NFS exports. 2380 */ 2381#ifdef HAVE_LOFS 2382 /* check for special case: host is me */ 2383 2384 if (self_check(host)) { 2385 ms = (struct mapent *)malloc(sizeof (*ms)); 2386 if (ms == NULL) 2387 goto alloc_failed; 2388 (void) memset((char *)ms, 0, sizeof (*ms)); 2389 if (CHECK_STRCPY(fstype, MNTTYPE_NFS, sizeof (fstype))) { 2390 *err = EIO; 2391 return ((struct mapent *)NULL); 2392 } 2393 2394 if (!get_opts(mapopts, entryopts, fstype, sizeof (fstype), NULL)) 2395 goto fstype_too_long; 2396 ms->map_mntopts = strdup(entryopts); 2397 if (ms->map_mntopts == NULL) 2398 goto alloc_failed; 2399 ms->map_mounter = strdup(fstype); 2400 if (ms->map_mounter == NULL) 2401 goto alloc_failed; 2402 ms->map_fstype = strdup(MNTTYPE_NFS); 2403 if (ms->map_fstype == NULL) 2404 goto alloc_failed; 2405 2406 if (isdirect) 2407 ms->map_root = strdup(""); 2408 else 2409 ms->map_root = strprefix_slash(host); 2410 if (ms->map_root == NULL) 2411 goto alloc_failed; 2412 ms->map_mntpnt = strdup(""); 2413 if (ms->map_mntpnt == NULL) 2414 goto alloc_failed; 2415 mfs = (struct mapfs *)malloc(sizeof (*mfs)); 2416 if (mfs == NULL) 2417 goto alloc_failed; 2418 (void) memset((char *)mfs, 0, sizeof (*mfs)); 2419 ms->map_fs = mfs; 2420 mfs->mfs_host = strdup(host); 2421 if (mfs->mfs_host == NULL) 2422 goto alloc_failed; 2423 mfs->mfs_dir = strdup("/"); 2424 if (mfs->mfs_dir == NULL) 2425 goto alloc_failed; 2426 2427 /* initialize mntlevel and modify */ 2428 ms->map_mntlevel = -1; 2429 ms->map_modified = FALSE; 2430 ms->map_faked = FALSE; 2431 2432 if (trace > 1) 2433 trace_prt(1, " do_mapent_hosts: self-host %s OK\n", host); 2434 2435 *err = 0; 2436 return (ms); 2437 } 2438#endif 2439 2440 /* 2441 * Call pingnfs. Note that we can't have replicated hosts in /net. 2442 * XXX - we would like to avoid duplicating the across the wire calls 2443 * made here in nfsmount(). The pingnfs cache should help avoid it. 2444 */ 2445 flags = altflags = 0; 2446 getmnt_silent = 1; 2447 mop = getmntopts(mapopts, mopts_vers, &flags, &altflags); 2448 if (mop == NULL) { 2449 syslog(LOG_ERR, "Couldn't parse mount options \"%s\" for %s: %m", 2450 mapopts, host); 2451 *err = EIO; 2452 return ((struct mapent *)NULL); 2453 } 2454 if (altflags & (NFS_MNT_VERS|NFS_MNT_NFSVERS)) { 2455 optval = get_nfs_vers(mop, altflags); 2456 if (optval == 0) { 2457 syslog(LOG_ERR, "Invalid NFS version number for %s", host); 2458 freemntopts(mop); 2459 *err = EIO; 2460 return ((struct mapent *)NULL); 2461 } 2462 nfsvers = (rpcvers_t)optval; 2463 } else 2464 nfsvers = 0; 2465 freemntopts(mop); 2466 if (set_versrange(nfsvers, &vers, &versmin) != 0) { 2467 syslog(LOG_ERR, "Incorrect NFS version specified for %s", host); 2468 *err = EIO; 2469 return ((struct mapent *)NULL); 2470 } 2471 clnt_stat = pingnfs(host, &vers, versmin, 0, NULL, NULL); 2472 if (clnt_stat != RPC_SUCCESS) { 2473 *err = clnt_stat_to_errno(clnt_stat); 2474 return ((struct mapent *)NULL); 2475 } 2476 2477 retries = get_retry(mapopts); 2478 delay = INITDELAY; 2479retry: 2480 /* get export list of host */ 2481 cl = clnt_create_timeout((char *)host, MOUNTPROG, MOUNTVERS, "tcp", &timeout); 2482 if (cl == NULL) { 2483 cl = clnt_create_timeout((char *)host, MOUNTPROG, MOUNTVERS, "udp", &timeout); 2484 if (cl == NULL) { 2485 syslog(LOG_ERR, 2486 "do_mapent_hosts: %s %s", host, clnt_spcreateerror("")); 2487 *err = clnt_stat_to_errno(rpc_createerr.cf_stat); 2488 return ((struct mapent *)NULL); 2489 } 2490 2491 } 2492#ifdef MALLOC_DEBUG 2493 add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__); 2494 add_alloc("AUTH_HANDLE", cl->cl_auth, 0, 2495 __FILE__, __LINE__); 2496#endif 2497 2498 if ((clnt_stat = clnt_call(cl, MOUNTPROC_EXPORT, (xdrproc_t)xdr_void, 0, 2499 (xdrproc_t)xdr_exports, (caddr_t)&ex, timeout)) != RPC_SUCCESS) { 2500 2501 if (retries-- > 0) { 2502 clnt_destroy(cl); 2503 DO_DELAY(delay); 2504 goto retry; 2505 } 2506 2507 syslog(LOG_ERR, 2508 "do_mapent_hosts: %s: export list: %s", 2509 host, clnt_sperrno(clnt_stat)); 2510#ifdef MALLOC_DEBUG 2511 drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__); 2512 drop_alloc("AUTH_HANDLE", cl->cl_auth, 2513 __FILE__, __LINE__); 2514#endif 2515 clnt_destroy(cl); 2516 *err = clnt_stat_to_errno(clnt_stat); 2517 return ((struct mapent *)NULL); 2518 } 2519 2520#ifdef MALLOC_DEBUG 2521 drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__); 2522 drop_alloc("AUTH_HANDLE", cl->cl_auth, 2523 __FILE__, __LINE__); 2524#endif 2525 clnt_destroy(cl); 2526 2527 if (ex == NULL) { 2528 if (trace > 1) 2529 trace_prt(1, " getmapent_hosts: null export list\n"); 2530 *err = 0; 2531 return ((struct mapent *)NULL); 2532 } 2533 2534 /* now sort by length of names - to get mount order right */ 2535 exlist = ex; 2536 texlist = NULL; 2537#ifdef lint 2538 exnext = NULL; 2539#endif 2540 for (; ex; ex = exnext) { 2541 exnext = ex->ex_next; 2542 exlen = strlen(ex->ex_dir); 2543 duplicate = 0; 2544 for (texp = &texlist; *texp; texp = &((*texp)->ex_next)) { 2545 if (exlen < strlen((*texp)->ex_dir)) 2546 break; 2547 duplicate = (strcmp(ex->ex_dir, (*texp)->ex_dir) == 0); 2548 if (duplicate) { 2549 /* disregard duplicate entry */ 2550 freeex_ent(ex); 2551 break; 2552 } 2553 } 2554 if (!duplicate) { 2555 ex->ex_next = *texp; 2556 *texp = ex; 2557 } 2558 } 2559 exlist = texlist; 2560 2561 /* 2562 * The following ugly chunk of code crept in as 2563 * a result of cachefs. If it's a cachefs mount 2564 * of an nfs filesystem, then have it handled as 2565 * an nfs mount but have cachefs do the mount. 2566 */ 2567 if (CHECK_STRCPY(fstype, MNTTYPE_NFS, sizeof (fstype))) { 2568 goto fstype_too_long; 2569 } 2570 2571 if (!get_opts(mapopts, entryopts, fstype, sizeof (fstype), NULL)) 2572 goto fstype_too_long; 2573 (void) strlcpy(mounter, fstype, sizeof(mounter)); 2574#ifdef MNTTYPE_CACHEFS 2575 if (strcmp(fstype, MNTTYPE_CACHEFS) == 0) { 2576 struct mnttab m; 2577 char *p; 2578 2579 m.mnt_mntopts = entryopts; 2580 if ((p = hasmntopt(&m, "backfstype")) != NULL) { 2581 int len = strlen(MNTTYPE_NFS); 2582 2583 p += 11; 2584 if (strncmp(p, MNTTYPE_NFS, len) == 0 && 2585 (p[len] == '\0' || p[len] == ',')) { 2586 /* 2587 * Cached nfs mount 2588 */ 2589 if (CHECK_STRCPY(fstype, MNTTYPE_NFS, sizeof (fstype))) { 2590 goto fstype_too_long; 2591 } 2592 if (CHECK_STRCPY(mounter, MNTTYPE_CACHEFS, sizeof (mounter))){ 2593 goto fstype_too_long; 2594 } 2595 } 2596 } 2597 } 2598#endif 2599 2600 /* Now create a mapent from the export list */ 2601 ms = NULL; 2602 me = NULL; 2603 2604 for (ex = exlist; ex; ex = ex->ex_next) { 2605 mp = me; 2606 me = (struct mapent *)malloc(sizeof (*me)); 2607 if (me == NULL) 2608 goto alloc_failed; 2609 (void) memset((char *)me, 0, sizeof (*me)); 2610 2611 if (ms == NULL) 2612 ms = me; 2613 else 2614 mp->map_next = me; 2615 2616 if (isdirect) 2617 me->map_root = strdup(""); 2618 else 2619 me->map_root = strprefix_slash(host); 2620 if (me->map_root == NULL) 2621 goto alloc_failed; 2622 2623 if (strcmp(ex->ex_dir, "/") != 0) { 2624 if (*(ex->ex_dir) != '/') 2625 me->map_mntpnt = strprefix_slash(ex->ex_dir); 2626 else 2627 me->map_mntpnt = strdup(ex->ex_dir); 2628 } else 2629 me->map_mntpnt = strdup(""); 2630 if (me->map_mntpnt == NULL) 2631 goto alloc_failed; 2632 2633 me->map_fstype = strdup(fstype); 2634 if (me->map_fstype == NULL) 2635 goto alloc_failed; 2636 me->map_mounter = strdup(mounter); 2637 if (me->map_mounter == NULL) 2638 goto alloc_failed; 2639 me->map_mntopts = strdup(entryopts); 2640 if (me->map_mntopts == NULL) 2641 goto alloc_failed; 2642 2643 mfs = (struct mapfs *)malloc(sizeof (*mfs)); 2644 if (mfs == NULL) 2645 goto alloc_failed; 2646 (void) memset((char *)mfs, 0, sizeof (*mfs)); 2647 me->map_fs = mfs; 2648 mfs->mfs_host = strdup(host); 2649 if (mfs->mfs_host == NULL) 2650 goto alloc_failed; 2651 mfs->mfs_dir = strdup(ex->ex_dir); 2652 if (mfs->mfs_dir == NULL) 2653 goto alloc_failed; 2654 2655 /* initialize mntlevel and modify values */ 2656 me->map_mntlevel = -1; 2657 me->map_modified = FALSE; 2658 me->map_faked = FALSE; 2659 } 2660 freeex(exlist); 2661 2662 if (trace > 1) 2663 trace_prt(1, " do_mapent_hosts: host %s OK\n", host); 2664 2665 *err = 0; 2666 return (ms); 2667 2668alloc_failed: 2669 syslog(LOG_ERR, "do_mapent_hosts: Memory allocation failed"); 2670 free_mapent(ms); 2671 freeex(exlist); 2672 *err = ENOMEM; 2673 return ((struct mapent *)NULL); 2674 2675fstype_too_long: 2676 syslog(LOG_ERR, "do_mapent_hosts: File system type is too long"); 2677 free_mapent(ms); 2678 freeex(exlist); 2679 *err = EIO; 2680 return ((struct mapent *)NULL); 2681} 2682 2683 2684static void 2685freeex_ent(ex) 2686 struct exportnode *ex; 2687{ 2688 struct groupnode *group, *tmpgroups; 2689 2690 free(ex->ex_dir); 2691 group = ex->ex_groups; 2692 while (group) { 2693 free(group->gr_name); 2694 tmpgroups = group->gr_next; 2695 free((char *)group); 2696 group = tmpgroups; 2697 } 2698 free((char *)ex); 2699} 2700 2701static void 2702freeex(ex) 2703 struct exportnode *ex; 2704{ 2705 struct exportnode *tmpex; 2706 2707 while (ex) { 2708 tmpex = ex->ex_next; 2709 freeex_ent(ex); 2710 ex = tmpex; 2711 } 2712} 2713 2714struct create_mapent_args { 2715 uint_t isdirect; 2716 const char *host; 2717 struct mapent *ms; 2718 struct mapent *me; 2719}; 2720 2721static int 2722create_mapent(struct fstabnode *fst, void *arg) 2723{ 2724 struct create_mapent_args *args = arg; 2725 struct mapent *me, *mp; 2726 struct mapfs *mfs; 2727 2728 mp = args->me; 2729 me = (struct mapent *)malloc(sizeof (*me)); 2730 if (me == NULL) 2731 goto alloc_failed; 2732 (void) memset((char *)me, 0, sizeof (*me)); 2733 args->me = me; 2734 2735 if (args->isdirect) 2736 me->map_root = strdup(""); 2737 else 2738 me->map_root = strprefix_slash(args->host); 2739 if (me->map_root == NULL) 2740 goto alloc_failed; 2741 2742 if (strcmp(fst->fst_dir, "/") == 0) 2743 me->map_mntpnt = strdup(""); 2744 else { 2745 if (*(fst->fst_dir) != '/') 2746 me->map_mntpnt = strprefix_slash(fst->fst_dir); 2747 else 2748 me->map_mntpnt = strdup(fst->fst_dir); 2749 } 2750 if (me->map_mntpnt == NULL) 2751 goto alloc_failed; 2752 2753 me->map_fstype = strdup(fst->fst_vfstype); 2754 if (me->map_fstype == NULL) 2755 goto alloc_failed; 2756 me->map_mounter = strdup(fst->fst_vfstype); 2757 if (me->map_mounter == NULL) 2758 goto alloc_failed; 2759 me->map_mntopts = strdup(fst->fst_mntops); 2760 if (me->map_mntopts == NULL) 2761 goto alloc_failed; 2762 2763 mfs = (struct mapfs *)malloc(sizeof (*mfs)); 2764 if (mfs == NULL) 2765 goto alloc_failed; 2766 (void) memset((char *)mfs, 0, sizeof (*mfs)); 2767 me->map_fs = mfs; 2768 mfs->mfs_host = strdup(args->host); 2769 if (mfs->mfs_host == NULL) 2770 goto alloc_failed; 2771 if (strcmp(fst->fst_vfstype, "url") == 0) { 2772 /* Use the URL. */ 2773 mfs->mfs_dir = strdup(fst->fst_url); 2774 } else 2775 mfs->mfs_dir = strdup(fst->fst_dir); 2776 if (mfs->mfs_dir == NULL) 2777 goto alloc_failed; 2778 2779 /* initialize mntlevel and modify values */ 2780 me->map_mntlevel = -1; 2781 me->map_modified = FALSE; 2782 me->map_faked = FALSE; 2783 2784 if (args->ms == NULL) 2785 args->ms = me; 2786 else 2787 mp->map_next = me; 2788 2789 return (0); 2790 2791alloc_failed: 2792 free_mapent(me); 2793 return (ENOMEM); 2794} 2795 2796/* 2797 * mapopts must be at most MAXOPTSLEN bytes long (including the null 2798 * terminator). 2799 */ 2800struct mapent * 2801do_mapent_fstab(mapopts, host, isdirect, node_type, err) 2802 const char *mapopts, *host; 2803 uint_t isdirect; 2804 int *node_type; 2805 int *err; 2806{ 2807 struct mapent *ms = NULL; 2808 struct mapfs *mfs = NULL; 2809 char entryopts[MAXOPTSLEN]; 2810 char fstype[MAX_FSLEN]; 2811 struct create_mapent_args args; 2812 2813 if (trace > 1) 2814 trace_prt(1, " do_mapent_fstab: host %s\n", host); 2815 2816 /* 2817 * Check for special case: host is me. 2818 * We check based on the name, as was done for the selflink 2819 * in the old automounter; we don't check based on the 2820 * IP address, as that's expensive (it requires that we 2821 * resolve the IP address of the server, which is really 2822 * expensive the first time it's done, as it's likely not 2823 * to be in the DNS resolver's cache). The host_is_us() 2824 * check handles multi-homed hosts (it checks against the 2825 * names corresponding to *all* this host's IP addresses) 2826 * and handles local names (it checks against this host's 2827 * .local name, if it has one). 2828 */ 2829 2830 if (host_is_us(host, strlen(host))) { 2831 ms = (struct mapent *)malloc(sizeof (*ms)); 2832 if (ms == NULL) 2833 goto alloc_failed; 2834 (void) memset((char *)ms, 0, sizeof (*ms)); 2835 if (CHECK_STRCPY(fstype, MNTTYPE_NFS, sizeof (fstype))) { 2836 goto fstype_too_long; 2837 } 2838 if (!get_opts(mapopts, entryopts, fstype, sizeof (fstype), NULL)) 2839 goto fstype_too_long; 2840 ms->map_mntopts = strdup(entryopts); 2841 if (ms->map_mntopts == NULL) 2842 goto alloc_failed; 2843 ms->map_mounter = strdup(fstype); 2844 if (ms->map_mounter == NULL) 2845 goto alloc_failed; 2846 ms->map_fstype = strdup(MNTTYPE_NFS); 2847 if (ms->map_fstype == NULL) 2848 goto alloc_failed; 2849 2850 if (isdirect) 2851 ms->map_root = strdup(""); 2852 else 2853 ms->map_root = strprefix_slash(host); 2854 if (ms->map_root == NULL) 2855 goto alloc_failed; 2856 ms->map_mntpnt = strdup(""); 2857 if (ms->map_mntpnt == NULL) 2858 goto alloc_failed; 2859 mfs = (struct mapfs *)malloc(sizeof (*mfs)); 2860 if (mfs == NULL) 2861 goto alloc_failed; 2862 (void) memset((char *)mfs, 0, sizeof (*mfs)); 2863 ms->map_fs = mfs; 2864 mfs->mfs_host = strdup(host); 2865 if (mfs->mfs_host == NULL) 2866 goto alloc_failed; 2867 mfs->mfs_dir = strdup("/"); 2868 if (mfs->mfs_dir == NULL) 2869 goto alloc_failed; 2870 2871 /* initialize mntlevel and modify */ 2872 ms->map_mntlevel = -1; 2873 ms->map_modified = FALSE; 2874 ms->map_faked = FALSE; 2875 2876 if (trace > 1) 2877 trace_prt(1, " do_mapent_fstab: self-host %s OK\n", host); 2878 2879 /* 2880 * This is a symlink to /. 2881 */ 2882 if (node_type != NULL) 2883 *node_type = NT_SYMLINK; 2884 2885 *err = 0; 2886 return (ms); 2887 } 2888 2889 /* 2890 * Create a mapent from the list of fstab map entries 2891 * that refer to host. 2892 */ 2893 args.isdirect = isdirect; 2894 args.host = host; 2895 args.ms = NULL; 2896 args.me = NULL; 2897 *err = fstab_process_host(host, create_mapent, &args); 2898 if (*err != 0) { 2899 /* 2900 * -1 means "there's no entry for that host". 2901 */ 2902 if (*err == -1) 2903 *err = ENOENT; 2904 else 2905 syslog(LOG_ERR, "do_mapent_fstab: Memory allocation failed"); 2906 return ((struct mapent *)NULL); 2907 } 2908 2909 if (trace > 1) 2910 trace_prt(1, " do_mapent_fstab: host %s OK\n", host); 2911 2912 return (args.ms); 2913 2914alloc_failed: 2915 syslog(LOG_ERR, "do_mapent_fstab: Memory allocation failed"); 2916 if (mfs) { 2917 free(mfs); 2918 } 2919 if (ms) { 2920 free_mapent(ms); 2921 } 2922 *err = ENOMEM; 2923 return ((struct mapent *)NULL); 2924 2925fstype_too_long: 2926 syslog(LOG_ERR, "do_mapent_hosts: File system type is too long"); 2927 if (ms) { 2928 free_mapent(ms); 2929 } 2930 *err = EIO; 2931 return ((struct mapent *)NULL); 2932} 2933 2934 2935struct mapent * 2936do_mapent_static(key, isdirect, err) 2937 const char *key; 2938 uint_t isdirect; 2939 int *err; 2940{ 2941 struct staticmap *static_ent; 2942 struct mapent *me; 2943 struct mapfs *mfs; 2944 2945 /* 2946 * Look up the fstab entry with the specified key 2947 * as its mount point. 2948 */ 2949 static_ent = get_staticmap_entry(key); 2950 if (static_ent == NULL) { 2951 *err = ENOENT; 2952 return (NULL); 2953 } 2954 2955 if ((me = (struct mapent *)malloc(sizeof (*me))) == NULL) 2956 goto alloc_failed; 2957 memset((char *)me, 0, sizeof (*me)); 2958 /* 2959 * direct maps get an empty string as root - to be filled 2960 * by the entire path later. Indirect maps get /key as the 2961 * map root. 2962 */ 2963 if (isdirect) 2964 me->map_root = strdup(""); 2965 else 2966 me->map_root = strprefix_slash(key); 2967 if (me->map_root == NULL) 2968 goto alloc_failed; 2969 2970 me->map_mntpnt = strdup(""); 2971 if (me->map_mntpnt == NULL) 2972 goto alloc_failed; 2973 me->map_fstype = strdup(static_ent->vfstype); 2974 if (me->map_fstype == NULL) 2975 goto alloc_failed; 2976 me->map_mounter = strdup(static_ent->vfstype); 2977 if (me->map_mounter == NULL) 2978 goto alloc_failed; 2979 me->map_mntopts = strdup(static_ent->mntops); 2980 if (me->map_mntopts == NULL) 2981 goto alloc_failed; 2982 me->map_fsw = strdup(""); 2983 if (me->map_fsw == NULL) 2984 goto alloc_failed; 2985 me->map_fswq = strdup(""); 2986 if (me->map_fswq == NULL) 2987 goto alloc_failed; 2988 2989 /* initialize flags */ 2990 me->map_mntlevel = -1; 2991 me->map_modified = FALSE; 2992 me->map_faked = FALSE; 2993 me->map_err = MAPENT_NOERR; 2994 2995 /* Add the one and only mapfs entry. */ 2996 mfs = (struct mapfs *)malloc(sizeof (*mfs)); 2997 if (mfs == NULL) 2998 goto alloc_failed; 2999 (void) memset((char *)mfs, 0, sizeof (*mfs)); 3000 me->map_fs = mfs; 3001 mfs->mfs_host = strdup(static_ent->host); 3002 if (mfs->mfs_host == NULL) 3003 goto alloc_failed; 3004 mfs->mfs_dir = strdup(static_ent->spec); 3005 if (mfs->mfs_dir == NULL) 3006 goto alloc_failed; 3007 3008 me->map_next = NULL; 3009 3010 release_staticmap_entry(static_ent); 3011 *err = 0; 3012 return (me); 3013 3014alloc_failed: 3015 release_staticmap_entry(static_ent); 3016 syslog(LOG_ERR, "do_mapent_static: Memory allocation failed"); 3017 free_mapent(me); 3018 *err = ENOMEM; 3019 return (NULL); 3020} 3021 3022static const char uatfs_err[] = "submount under fstype=autofs not supported"; 3023/* 3024 * dump_mapent_err(struct mapent *me, const char *key, const char *mapname) 3025 * syslog appropriate error in mapentries. 3026 */ 3027static void dump_mapent_err(struct mapent *me, const char *key, 3028 const char *mapname) 3029{ 3030 switch (me->map_err) { 3031 case MAPENT_NOERR: 3032 if (verbose) 3033 syslog(LOG_ERR, 3034 "map=%s key=%s mntpnt=%s: no error", 3035 mapname, key, me->map_mntpnt); 3036 break; 3037 case MAPENT_UATFS: 3038 syslog(LOG_ERR, 3039 "mountpoint %s in map %s key %s not mounted: %s", 3040 me->map_mntpnt, mapname, key, uatfs_err); 3041 break; 3042 default: 3043 if (verbose) 3044 syslog(LOG_ERR, 3045 "map=%s key=%s mntpnt=%s: unknown mapentry error", 3046 mapname, key, me->map_mntpnt); 3047 } 3048} 3049