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 * ns_fnmount.c 24 * 25 * Copyright (c) 1996 Sun Microsystems Inc 26 * All Rights Reserved. 27 */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <ctype.h> 35 #include <syslog.h> 36 #include <rpc/rpc.h> 37 #include <rpcsvc/nis.h> 38 #include <xfn/xfn.h> 39 #include "automount.h" 40 #include "ns_fnutils.h" 41 42 43 /* 44 * The maximum sizes of map names, key names, composite names, and status 45 * descriptions, including the trailing '\0'. 46 */ 47 #define MAPNAMESZ (size_t)(AUTOFS_MAXCOMPONENTLEN + 1) 48 #define KEYNAMESZ (size_t)(AUTOFS_MAXCOMPONENTLEN + 1) 49 #define COMPNAMESZ (size_t)(MAPNAMESZ - FNPREFIXLEN + KEYNAMESZ - 2) 50 #define DESCSZ (size_t)512 51 52 53 /* 54 * Number of the home directory field in NIS+ password tables. 55 */ 56 #define NIS_HOME 5 57 58 59 typedef struct mapent mapent; 60 typedef struct mapline mapline; 61 62 63 /* 64 * The name of an attribute. 65 */ 66 static const FN_identifier_t attr_exported = {FN_ID_STRING, 8, "exported"}; 67 68 69 /* 70 * Given a request by a particular user to mount the name "key" under 71 * map/context "map", and a set of default mount options, return (in 72 * "res") either a list of mapents giving the mounts that need to be 73 * performed, or a symbolic link to be created for a user-relative 74 * context. If "shallow" is true return, in place of the list of 75 * mapents, a single mapent representing an indirect mount point. 76 * 77 * void 78 * getmapent_fn(char *key, char *map, char *opts, uid_t uid, 79 * bool_t shallow, getmapent_fn_res *res); 80 */ 81 82 /* 83 * Given a reference, its composite name, default mount options, and a 84 * mapent root, return a list of mapents to mount. If "shallow" is 85 * true return, in place of the list of mapents, a single mapent 86 * representing an indirect mount point. The map and key strings are 87 * pieces of the composite name such that: 88 * "FNPREFIX/cname" == "map/key". 89 */ 90 static mapent * 91 process_ref(const FN_ref_t *ref, const char *cname, char *map, char *key, 92 char *opts, char *root, bool_t shallow, FN_status_t *status); 93 94 /* 95 * Traverse the namespace to find a frontier below ref along which 96 * future mounts may need to be triggered. Add to mapents the 97 * corresponding direct autofs mount points. 98 * map: map name for ref 99 * maplen: strlen(map) 100 * mntpnt: suffix of map where the current mount request begins 101 * (starts off as "", and grows as we traverse the namespace) 102 * opts: default mount options 103 * status: passed from above to avoid having to allocate one on each call 104 * Works by calling frontier_aux() on each name bound under ref. 105 * Return the new mapents, or free mapents and return NULL on failure. 106 */ 107 static mapent * 108 frontier(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen, 109 char *mntpnt, char *opts, FN_status_t *status); 110 111 /* 112 * Called by frontier(), once for each "name" that it finds. map is 113 * passed unchanged from frontier(). ref is the reference named by 114 * "map/name". If ref is found to be along the frontier, add the 115 * corresponding direct autofs mount point to mapents. Otherwise 116 * continue traversing the namespace to find the frontier. Other 117 * arguments and the return value are as for frontier(). 118 */ 119 static mapent * 120 frontier_aux(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen, 121 char *mntpnt, const char *name, char *opts, FN_status_t *status); 122 123 /* 124 * Given a reference with an address type of ADDR_HOST and its 125 * composite name, check the attr_exported attribute to determine if 126 * the corresponding directory is exported. Return FALSE on error. 127 */ 128 static bool_t 129 exported(const FN_ref_t *ref, const char *cname, FN_status_t *status); 130 131 /* 132 * Find a reference's address type and, if "data" is not NULL, its 133 * data string. If there is no address of a known type, set *typep to 134 * NUM_ADDRTYPES; if there are several, stop after finding the first. 135 * Return 0 on success. 136 */ 137 static int 138 addr_from_ref(const FN_ref_t *ref, const char *cname, addrtype_t *typep, 139 char *data, size_t datasz); 140 141 /* 142 * Decode an address's data into a string. Return 0 on success. 143 */ 144 static int 145 str_from_addr(const char *cname, const FN_ref_addr_t *addr, char str[], 146 size_t strsz); 147 148 /* 149 * Perform a NIS+ query to find a home directory. The result is a 150 * newly-allocated string, or NULL on error. 151 */ 152 static char * 153 nisplus_homedir(const char *nisname); 154 155 /* 156 * Given a map name and its current length, append "/name". Return 157 * the new length. On error, syslog a warning and return 0. 158 */ 159 static size_t 160 append_mapname(char *map, size_t maplen, const char *name); 161 162 /* 163 * Concatenate two strings using the given separator. The result is a 164 * newly-allocated string, or NULL on error. 165 */ 166 static char * 167 concat(const char *s1, char sep, const char *s2); 168 169 /* 170 * Add the "nosuid" option to a mapent. Also check for a sneaky 171 * hacker trying to override this option by manually inserting a 172 * multiple mount entry into the XFN namespace. Return FALSE on error. 173 */ 174 static bool_t 175 safe_mapent(mapent *me); 176 177 /* 178 * Append "nosuid" to a list of options. The result is a 179 * newly-allocated string, or NULL on error. 180 */ 181 static char * 182 safe_opts(const char *opts); 183 184 /* 185 * Trim comments and trailing whitespace from ml->linebuf, then 186 * unquote it and leave the result in ml. Return 0 on success. 187 */ 188 static int 189 trim_line(mapline *ml); 190 191 /* 192 * Determine whether ml contains an option string (such as "-ro") and 193 * nothing else. 194 */ 195 static bool_t 196 opts_only(const mapline *ml); 197 198 /* 199 * Allocate a new mapent structure. The arguments must have been 200 * malloc'ed, and are owned by the mapent; they are freed if 201 * new_mapent() fails. If any argument is NULL, the call fails and a 202 * memory allocation failure is logged. A root argument of 'noroot' 203 * indicates that the map_root field does not need to be set (it's 204 * only needed in the first of a list of mapents). 205 */ 206 static char *noroot = "[no root]"; 207 static mapent * 208 new_mapent(char *root, char *mntpnt, char *fstype, char *mntopts, char *host, 209 char *dir); 210 211 /* 212 * Determine whether cname is a user-relative binding -- such as "myself" -- 213 * in the initial context. 214 */ 215 static bool_t 216 is_user_relative(const char *cname); 217 218 /* 219 * Given the name of a user-relative binding, return an equivalent 220 * name that is not user-relative. 221 */ 222 static char * 223 equiv_name(FN_ctx_t *, const char *cname, FN_status_t *); 224 225 void 226 getmapent_fn(char *key, char *map, char *opts, uid_t uid, bool_t shallow, 227 getmapent_fn_res *res) 228 { 229 size_t maplen; 230 FN_status_t *status; 231 FN_ctx_t *init_ctx = NULL; 232 int statcode; 233 char cname[COMPNAMESZ]; 234 FN_composite_name_t *compname; 235 FN_ref_t *ref; 236 char mapname[MAPNAMESZ]; 237 char *root; 238 239 res->type = FN_NONE; 240 res->m_or_l.mapents = NULL; 241 242 if (init_fn() != 0) { 243 return; 244 } 245 246 /* 247 * For direct mounts, the key is the entire path, and the map 248 * name already has the final key component appended. Split 249 * apart the map name and key. The "root" of the mapent is 250 * "/key" for indirect mounts, and "" for direct mounts. 251 */ 252 strcpy(mapname, map); 253 if (key[0] == '/') { 254 key = strrchr(key, '/') + 1; 255 *strrchr(mapname, '/') = '\0'; 256 root = strdup(""); 257 } else { 258 root = concat("", '/', key); 259 } 260 map = mapname; 261 maplen = strlen(map); 262 263 if ((maplen - FNPREFIXLEN + strlen(key)) >= COMPNAMESZ) { 264 if (verbose) { 265 syslog(LOG_ERR, "name %s/%s too long", map, key); 266 } 267 return; 268 } 269 if (maplen == FNPREFIXLEN) { 270 strcpy(cname, key); 271 } else { 272 sprintf(cname, "%s/%s", map + FNPREFIXLEN + 1, key); 273 } 274 275 status = fn_status_create(); 276 if (status == NULL) { 277 if (verbose) { 278 syslog(LOG_ERR, "Could not create FNS status object"); 279 } 280 return; 281 } 282 init_ctx = _fn_ctx_handle_from_initial_with_uid(uid, 0, status); 283 if (init_ctx == NULL) { 284 logstat(status, "", "No initial context"); 285 goto done; 286 } 287 288 #ifndef XFN1ENV 289 if (is_user_relative(cname)) { 290 res->type = FN_SYMLINK; 291 res->m_or_l.symlink = equiv_name(init_ctx, cname, status); 292 goto done; 293 } 294 #endif 295 296 if ((compname = new_cname(cname)) == NULL) { 297 goto done; 298 } 299 ref = fn_ctx_lookup(init_ctx, compname, status); 300 statcode = fn_status_code(status); 301 fn_composite_name_destroy(compname); 302 303 if (trace > 1 && !shallow) { 304 trace_prt(1, " FNS traversal: %s\n", cname); 305 } 306 307 if (ref == NULL) { 308 if ((statcode != FN_E_NAME_NOT_FOUND) && 309 (statcode != FN_E_NOT_A_CONTEXT)) { 310 logstat(status, "lookup failed on", cname); 311 } 312 goto done; 313 } 314 315 res->type = FN_MAPENTS; 316 res->m_or_l.mapents = 317 process_ref(ref, cname, map, key, opts, root, shallow, status); 318 fn_ref_destroy(ref); 319 done: 320 fn_ctx_handle_destroy(init_ctx); 321 fn_status_destroy(status); 322 } 323 324 325 static mapent * 326 process_ref(const FN_ref_t *ref, const char *cname, char *map, char *key, 327 char *opts, char *root, bool_t shallow, FN_status_t *status) 328 { 329 addrtype_t addrtype; 330 mapline ml; 331 char *addrdata = ml.linebuf; 332 mapent *mapents; 333 bool_t self; 334 char *homedir; 335 size_t maplen; 336 char *colon; 337 char *nfshost; 338 char *nfsdir; 339 340 if ((reftype(ref) < NUM_REFTYPES) && 341 (addr_from_ref(ref, cname, &addrtype, addrdata, LINESZ) == 0)) { 342 343 switch (addrtype) { 344 case ADDR_MOUNT: 345 if (trim_line(&ml) != 0) { 346 return (NULL); 347 } 348 if (opts_only(&ml)) { 349 /* parse_entry() can't handle such lines */ 350 if (macro_expand("&", ml.linebuf, 351 ml.lineqbuf, LINESZ)) { 352 syslog(LOG_ERR, 353 "%s/%s: opts too long (max %d chars)", 354 FNPREFIX, cname, LINESZ - 1); 355 return (NULL); 356 } 357 opts = ml.linebuf + 1; /* skip '-' */ 358 goto indirect; 359 } 360 mapents = parse_entry(key, map, opts, &ml, NULL, 0, 361 TRUE); 362 if (mapents == NULL || !safe_mapent(mapents)) { 363 free_mapent(mapents); 364 return (NULL); 365 } 366 free(mapents->map_root); 367 mapents->map_root = root; 368 break; 369 370 case ADDR_HOST: 371 /* 372 * Address is of the form "host:dir". 373 * If "dir" is not supplied, it defaults to "/". 374 */ 375 colon = strchr(addrdata, ':'); 376 if (colon == NULL || colon[1] == '\0') { 377 nfsdir = strdup("/"); 378 } else { 379 *colon = '\0'; 380 nfsdir = strdup(colon + 1); 381 } 382 nfshost = strdup(addrdata); 383 /* 384 * If nfshost is the local host, the NFS mount 385 * request will be converted to a loopback 386 * mount. Otherwise check that the file system 387 * is exported. 388 */ 389 if (nfshost != NULL) { 390 self = self_check(nfshost); 391 if (!self && !exported(ref, cname, status)) { 392 if (transient(status)) { 393 return (NULL); 394 } else { 395 goto indirect; 396 } 397 } 398 } 399 mapents = new_mapent(root, strdup(""), strdup("nfs"), 400 safe_opts(opts), nfshost, nfsdir); 401 if (self && !shallow) { 402 return (mapents); 403 } 404 break; 405 406 case ADDR_USER: 407 homedir = strdup(addrdata); 408 homedir[strcspn(homedir, " \t\r\n")] = '\0'; 409 mapents = new_mapent(root, strdup(""), strdup("lofs"), 410 strdup(opts), strdup(""), homedir); 411 break; 412 case ADDR_USER_NISPLUS: 413 homedir = nisplus_homedir(addrdata); 414 mapents = (homedir == NULL) 415 ? NULL 416 : new_mapent(root, strdup(""), strdup("lofs"), 417 strdup(opts), strdup(""), homedir); 418 break; 419 } 420 421 if (mapents == NULL) { 422 return (NULL); 423 } 424 if (shallow) { 425 mapents->map_root = NULL; /* don't free "root" */ 426 free_mapent(mapents); 427 goto indirect; 428 } 429 430 /* "map" => "map/key" */ 431 if ((maplen = append_mapname(map, strlen(map), key)) == 0) { 432 return (mapents); 433 } 434 return (frontier(mapents, ref, map, maplen, map + maplen, 435 opts, status)); 436 } 437 438 /* Ref type wasn't recognized. */ 439 440 indirect: 441 /* Install an indirect autofs mount point. */ 442 return (new_mapent(root, strdup(""), strdup("autofs"), strdup(opts), 443 strdup(""), concat(map, '/', key))); 444 } 445 446 447 /* 448 * All that this function really does is call frontier_aux() on every 449 * name bound under ref. The rest is error checking(!) 450 * 451 * The error handling strategy is to reject the entire mount request 452 * (by freeing mapents) if any (potentially) transient error occurs, 453 * and to treat nontransient errors as holes in the affected portions 454 * of the namespace. 455 */ 456 static mapent * 457 frontier(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen, 458 char *mntpnt, char *opts, FN_status_t *status) 459 { 460 FN_ctx_t *ctx; 461 FN_bindinglist_t *bindings = NULL; 462 FN_ref_t *child_ref; 463 FN_string_t *child_s; 464 const char *child; 465 unsigned int statcode; 466 467 ctx = fn_ctx_handle_from_ref(ref, XFN2(0) status); 468 if (ctx == NULL) { 469 if (fn_status_code(status) != FN_E_NO_SUPPORTED_ADDRESS) { 470 logstat(status, "from_ref failed for", map); 471 } 472 goto checkerr_return; 473 } 474 475 bindings = fn_ctx_list_bindings(ctx, empty_cname, status); 476 fn_ctx_handle_destroy(ctx); 477 if (bindings == NULL) { 478 logstat(status, "list_bindings failed for", map); 479 goto checkerr_return; 480 } 481 482 while ((child_s = fn_bindinglist_next(bindings, &child_ref, status)) 483 != NULL) { 484 child = (const char *)fn_string_str(child_s, &statcode); 485 if (child == NULL) { 486 if (verbose) { 487 syslog(LOG_ERR, 488 "FNS string error listing %s", map); 489 } 490 fn_string_destroy(child_s); 491 goto err_return; 492 } 493 mapents = frontier_aux(mapents, child_ref, map, maplen, 494 mntpnt, child, opts, status); 495 fn_string_destroy(child_s); 496 fn_ref_destroy(child_ref); 497 if (mapents == NULL) { 498 goto noerr_return; 499 } 500 } 501 if (fn_status_is_success(status)) { 502 goto noerr_return; 503 } else { 504 logstat(status, "error while listing", map); 505 /* Fall through to checkerr_return. */ 506 } 507 508 checkerr_return: 509 if (!transient(status)) { 510 goto noerr_return; 511 } 512 err_return: 513 free_mapent(mapents); 514 mapents = NULL; 515 noerr_return: 516 fn_bindinglist_destroy(bindings XFN1(status)); 517 return (mapents); 518 } 519 520 521 static mapent * 522 frontier_aux(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen, 523 char *mntpnt, const char *name, char *opts, FN_status_t *status) 524 { 525 addrtype_t addrtype; 526 bool_t at_frontier; 527 mapent *me; 528 size_t maplen_save = maplen; 529 char *cname = map + FNPREFIXLEN + 1; /* for error msgs */ 530 531 if (reftype(ref) >= NUM_REFTYPES) { 532 /* 533 * We could instead install an indirect autofs mount point 534 * here. That would allow, for example, a user to be bound 535 * beneath a file system. 536 */ 537 return (mapents); 538 } 539 540 /* "map" => "map/name" */ 541 if ((maplen = append_mapname(map, maplen, name)) == 0) { 542 return (mapents); 543 } 544 if (trace > 1) { 545 trace_prt(1, " FNS traversal: %s/\n", cname); 546 } 547 548 /* 549 * If this is an address type that we know how to mount, then 550 * we have reached the frontier. 551 */ 552 at_frontier = (addr_from_ref(ref, cname, &addrtype, NULL, 0) == 0); 553 /* 554 * For an ADDR_HOST address, treat a non-exported directory as 555 * if the address type were not known: continue searching for 556 * exported subdirectories. 557 */ 558 if (at_frontier && (addrtype == ADDR_HOST)) { 559 if (!exported(ref, cname, status)) { 560 if (transient(status)) { 561 free_mapent(mapents); 562 return (NULL); 563 } else { 564 at_frontier = FALSE; 565 } 566 } 567 } 568 /* 569 * If we have reached the frontier, install a direct autofs 570 * mount point (which will trigger the actual mount if the 571 * user steps on it later). Otherwise, continue traversing 572 * the namespace looking for known address types. 573 */ 574 if (at_frontier) { 575 opts = (opts[0] != '\0') 576 ? concat(opts, ',', "direct") 577 : strdup("direct"); 578 me = new_mapent(noroot, strdup(mntpnt), strdup("autofs"), opts, 579 strdup(""), strdup(map)); 580 if (me != NULL) { 581 /* Link new mapent into list (not at the head). */ 582 me->map_next = mapents->map_next; 583 mapents->map_next = me; 584 } else { 585 free_mapent(mapents); 586 mapents = NULL; 587 } 588 } else { 589 mapents = 590 frontier(mapents, ref, map, maplen, mntpnt, opts, status); 591 } 592 map[maplen_save] = '\0'; /* "map/name" => "map" */ 593 return (mapents); 594 } 595 596 597 static bool_t 598 exported(const FN_ref_t *ref, const char *cname, FN_status_t *status) 599 { 600 FN_ctx_t *ctx; 601 FN_attribute_t *attr; 602 603 ctx = fn_ctx_handle_from_ref(ref, XFN2(0) status); 604 if (ctx == NULL) { 605 logstat(status, "from_ref failed for", cname); 606 return (FALSE); 607 } 608 attr = fn_attr_get(ctx, empty_cname, &attr_exported, XFN2(1) status); 609 fn_ctx_handle_destroy(ctx); 610 611 switch (fn_status_code(status)) { 612 case FN_SUCCESS: 613 fn_attribute_destroy(attr); 614 break; 615 case FN_E_NO_SUCH_ATTRIBUTE: 616 break; 617 default: 618 logstat(status, "could not get attributes for", cname); 619 } 620 return (attr != NULL); 621 } 622 623 624 static int 625 addr_from_ref(const FN_ref_t *ref, const char *cname, addrtype_t *typep, 626 char *data, size_t datasz) 627 { 628 const FN_ref_addr_t *addr; 629 void *iter_pos; 630 631 addr = fn_ref_first(ref, &iter_pos); 632 if (addr == NULL) { 633 if (verbose) { 634 syslog(LOG_ERR, "FNS ref with no address: %s", cname); 635 } 636 return (-1); 637 } 638 while (addr != NULL) { 639 *typep = addrtype(addr); 640 if (*typep < NUM_ADDRTYPES) { 641 return ((data != NULL) 642 ? str_from_addr(cname, addr, data, datasz) 643 : 0); 644 } 645 addr = fn_ref_next(ref, &iter_pos); 646 } 647 return (-1); 648 } 649 650 651 static int 652 str_from_addr(const char *cname, const FN_ref_addr_t *addr, char str[], 653 size_t strsz) 654 { 655 XDR xdr; 656 int res; 657 658 xdrmem_create(&xdr, (caddr_t)fn_ref_addr_data(addr), 659 fn_ref_addr_length(addr), XDR_DECODE); 660 if (!xdr_string(&xdr, &str, strsz)) { 661 if (verbose) { 662 syslog(LOG_ERR, 663 "Could not decode FNS address for %s", cname); 664 } 665 res = -1; 666 } else { 667 res = 0; 668 } 669 xdr_destroy(&xdr); 670 return (res); 671 } 672 673 674 static char * 675 nisplus_homedir(const char *nisname) 676 { 677 nis_result *res; 678 void *val; 679 size_t len; 680 char *homedir = NULL; 681 682 /* NIS+ query to find passwd table entry */ 683 res = nis_list((nis_name)nisname, 684 FOLLOW_LINKS | FOLLOW_PATH | USE_DGRAM, NULL, NULL); 685 if (res == NULL) { 686 if (verbose) { 687 syslog(LOG_ERR, 688 "FNS home dir query failed: %s", nisname); 689 } 690 return (NULL); 691 } 692 if (res->status != NIS_SUCCESS && res->status != NIS_S_SUCCESS) { 693 if ((res->status != NIS_NOTFOUND) && verbose) { 694 syslog(LOG_ERR, 695 "FNS home dir query failed: %s", nisname); 696 } 697 goto done; 698 } 699 val = ENTRY_VAL(NIS_RES_OBJECT(res), NIS_HOME); 700 len = ENTRY_LEN(NIS_RES_OBJECT(res), NIS_HOME); 701 702 if (len < 1) { 703 if (verbose) { 704 syslog(LOG_ERR, "FNS home dir not found: %s", nisname); 705 } 706 goto done; 707 } 708 homedir = malloc(len + 1); 709 if (homedir == NULL) { 710 log_mem_failure(); 711 goto done; 712 } 713 714 strncpy(homedir, (char *)val, len); 715 homedir[len] = '\0'; 716 done: 717 nis_freeresult(res); 718 return (homedir); 719 } 720 721 722 static size_t 723 append_mapname(char *map, size_t maplen, const char *name) 724 { 725 size_t namelen = strlen(name); 726 727 if (maplen + 1 + namelen >= MAPNAMESZ) { 728 if (verbose) { 729 syslog(LOG_ERR, "FNS name %s/%s too long", 730 map + FNPREFIXLEN + 1, name); 731 } 732 return (0); 733 } 734 sprintf(map + maplen, "/%s", name); 735 return (maplen + 1 + namelen); 736 } 737 738 739 static char * 740 concat(const char *s1, char sep, const char *s2) 741 { 742 char *s = malloc(strlen(s1) + 1 + strlen(s2) + 1); 743 744 if (s != NULL) { 745 sprintf(s, "%s%c%s", s1, sep, s2); 746 } 747 return (s); 748 } 749 750 751 static bool_t 752 safe_mapent(mapent *me) 753 { 754 char *opts; 755 756 if (me->map_next != NULL) { 757 /* Multiple mounts don't belong in XFN namespace. */ 758 return (NULL); 759 } 760 opts = me->map_mntopts; 761 me->map_mntopts = safe_opts(opts); 762 free(opts); 763 return (me->map_mntopts != NULL); 764 } 765 766 767 static char * 768 safe_opts(const char *opts) 769 { 770 char *start; 771 size_t len; 772 773 if (opts[0] == '\0') { 774 return (strdup(MNTOPT_NOSUID)); 775 } 776 777 /* A quick-and-dirty check to see if "nosuid" is already there. */ 778 start = strstr(opts, MNTOPT_NOSUID); 779 len = sizeof (MNTOPT_NOSUID) - 1; /* "-1" for trailing '\0' */ 780 if (start != NULL) { 781 while (start > opts && isspace(*(start - 1))) { 782 start--; 783 } 784 if ((start == opts || *(start - 1) == ',') && 785 opts[len] == ',' || opts[len] == '\0') { 786 return (strdup(opts)); 787 } 788 } 789 return (concat(opts, ',', MNTOPT_NOSUID)); 790 } 791 792 793 static int 794 trim_line(mapline *ml) 795 { 796 char *end; /* pointer to '\0' at end of linebuf */ 797 798 end = ml->linebuf + strcspn(ml->linebuf, "#"); 799 while ((end > ml->linebuf) && isspace(end[-1])) { 800 end--; 801 } 802 if (end <= ml->linebuf) { 803 return (-1); 804 } 805 *end = '\0'; 806 unquote(ml->linebuf, ml->lineqbuf); 807 return (0); 808 } 809 810 811 static bool_t 812 opts_only(const mapline *ml) 813 { 814 const char *s = ml->linebuf; 815 const char *q = ml->lineqbuf; 816 817 if (*s != '-') { 818 return (FALSE); 819 } 820 for (; *s != '\0'; s++, q++) { 821 if (isspace(*s) && (*q == ' ')) { 822 return (FALSE); 823 } 824 } 825 return (TRUE); 826 } 827 828 829 static mapent * 830 new_mapent(char *root, char *mntpnt, char *fstype, char *mntopts, char *host, 831 char *dir) 832 { 833 mapent *me; 834 struct mapfs *mfs; 835 char *mounter = NULL; 836 837 me = calloc(1, sizeof (*me)); 838 mfs = calloc(1, sizeof (*mfs)); 839 if (fstype != NULL) { 840 mounter = strdup(fstype); 841 } 842 if ((mntpnt == NULL) || (fstype == NULL) || (mntopts == NULL) || 843 (host == NULL) || (dir == NULL) || (me == NULL) || (mfs == NULL) || 844 (mounter == NULL) || (root == NULL)) { 845 log_mem_failure(); 846 free(me); 847 free(mfs); 848 free(mounter); 849 free(root); 850 free(mntpnt); 851 free(fstype); 852 free(mntopts); 853 free(host); 854 free(dir); 855 return (NULL); 856 } 857 me->map_root = (root != noroot) ? root : NULL; 858 me->map_fstype = fstype; 859 me->map_mounter = mounter; 860 me->map_mntpnt = mntpnt; 861 me->map_mntopts = mntopts; 862 me->map_fsw = NULL; 863 me->map_fswq = NULL; 864 me->map_fs = mfs; 865 mfs->mfs_host = host; 866 mfs->mfs_dir = dir; 867 me->map_mntlevel = -1; 868 me->map_modified = FALSE; 869 me->map_faked = FALSE; 870 me->map_err = 0; /* MAPENT_NOERR */ 871 return (me); 872 } 873 874 875 #ifndef XFN1ENV 876 877 /* 878 * User-relative bindings in the initial context, and the leading components 879 * of their non-user-relative equivalents. Leading components are listed in 880 * the order in which they should be tried. Each list is NULL-terminated 881 * (the compiler generously does this for us). 882 * For "myorgunit", for example, we first check if it is equivalent to 883 * "thisorgunit". If not, we translate it into "org/<something>". 884 */ 885 #define MAX_LEADS 3 886 887 static struct { 888 const char *binding; 889 const char *leads[MAX_LEADS + 1]; 890 } user_rel[] = { 891 {"thisuser", {"user", "thisorgunit", "org"}}, 892 {"myself", {"user", "thisorgunit", "org"}}, 893 {"_myself", {"_user", "_thisorgunit", "_orgunit"}}, 894 {"myorgunit", {"thisorgunit", "org"}}, 895 {"_myorgunit", {"_thisorgunit", "_orgunit"}}, 896 {"myens", {"thisens"}}, 897 {"_myens", {"_thisens"}} 898 }; 899 900 901 static bool_t 902 is_user_relative(const char *cname) 903 { 904 int i; 905 906 for (i = 0; i < sizeof (user_rel) / sizeof (user_rel[0]); i++) { 907 if (strcmp(cname, user_rel[i].binding) == 0) { 908 return (TRUE); 909 } 910 } 911 return (FALSE); 912 } 913 914 915 static char * 916 equiv_name(FN_ctx_t *ctx, const char *cname, FN_status_t *status) 917 { 918 FN_composite_name_t *name; 919 FN_string_t *leading_name; 920 FN_composite_name_t *equiv; 921 FN_string_t *equiv_string; 922 const char *equiv_str; 923 char *equiv_str_dup; 924 const char **leads; 925 unsigned int stat; 926 int i; 927 928 for (i = 0; i < sizeof (user_rel) / sizeof (user_rel[0]); i++) { 929 if (strcmp(cname, user_rel[i].binding) == 0) { 930 break; 931 } 932 } 933 if ((name = new_cname(cname)) == NULL) { 934 return (NULL); 935 } 936 leads = user_rel[i].leads; /* array of leading names to try */ 937 do { 938 leading_name = fn_string_from_str((unsigned char *)*leads); 939 if (leading_name == NULL) { 940 log_mem_failure(); 941 fn_composite_name_destroy(name); 942 return (NULL); 943 } 944 equiv = prelim_fn_ctx_equivalent_name(ctx, name, leading_name, 945 status); 946 fn_string_destroy(leading_name); 947 } while (equiv == NULL && *++leads != NULL); 948 949 fn_composite_name_destroy(name); 950 951 if (equiv == NULL) { 952 if (transient(status)) { 953 logstat(status, "could not find equivalent of", cname); 954 } 955 return (NULL); 956 } 957 equiv_string = fn_string_from_composite_name(equiv, &stat); 958 fn_composite_name_destroy(equiv); 959 if (equiv_string == NULL) { 960 log_mem_failure(); 961 return (NULL); 962 } 963 equiv_str = (const char *)fn_string_str(equiv_string, &stat); 964 if (equiv_str == NULL || 965 (equiv_str_dup = strdup(equiv_str)) == NULL) { 966 log_mem_failure(); 967 fn_string_destroy(equiv_string); 968 return (NULL); 969 } 970 fn_string_destroy(equiv_string); 971 return (equiv_str_dup); 972 } 973 974 #endif /* XFN1ENV */ 975