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