1 /* 2 * Copyright (c) 1989 Jan-Simon Pendry 3 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 4 * Copyright (c) 1989 The Regents of the University of California. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Jan-Simon Pendry at Imperial College, London. 9 * 10 * %sccs.include.redist.c% 11 * 12 * @(#)opts.c 5.3 (Berkeley) 05/12/91 13 * 14 * $Id: opts.c,v 5.2.1.7 91/05/07 22:18:24 jsp Alpha $ 15 * 16 */ 17 18 #include "am.h" 19 20 extern char *getenv P((const char *)); 21 22 /* 23 * static copy of the options with 24 * which to play 25 */ 26 static struct am_opts fs_static; 27 28 static char *opt_host = hostname; 29 static char *opt_hostd = hostd; 30 static char nullstr[] = ""; 31 static char *opt_key = nullstr; 32 static char *opt_map = nullstr; 33 static char *opt_path = nullstr; 34 35 static char *vars[8]; 36 37 /* 38 * Length of longest option name 39 */ 40 #define NLEN 16 /* conservative */ 41 #define S(x) (x) , (sizeof(x)-1) 42 static struct opt { 43 char *name; /* Name of the option */ 44 int nlen; /* Length of option name */ 45 char **optp; /* Pointer to option value string */ 46 char **sel_p; /* Pointer to selector value string */ 47 } opt_fields[] = { 48 /* Options in something corresponding to frequency of use */ 49 { S("opts"), &fs_static.opt_opts, 0 }, 50 { S("host"), 0, &opt_host }, 51 { S("hostd"), 0, &opt_hostd }, 52 { S("type"), &fs_static.opt_type, 0 }, 53 { S("rhost"), &fs_static.opt_rhost, 0 }, 54 { S("rfs"), &fs_static.opt_rfs, 0 }, 55 { S("fs"), &fs_static.opt_fs, 0 }, 56 { S("key"), 0, &opt_key }, 57 { S("map"), 0, &opt_map }, 58 { S("sublink"), &fs_static.opt_sublink, 0 }, 59 { S("arch"), 0, &arch }, 60 { S("dev"), &fs_static.opt_dev, 0 }, 61 { S("pref"), &fs_static.opt_pref, 0 }, 62 { S("path"), 0, &opt_path }, 63 { S("autodir"), 0, &auto_dir }, 64 { S("delay"), &fs_static.opt_delay, 0 }, 65 { S("domain"), 0, &hostdomain }, 66 { S("karch"), 0, &karch }, 67 { S("cluster"), 0, &cluster }, 68 { S("wire"), 0, &wire }, 69 { S("byte"), 0, &endian }, 70 { S("os"), 0, &op_sys }, 71 { S("mount"), &fs_static.opt_mount, 0 }, 72 { S("unmount"), &fs_static.opt_unmount, 0 }, 73 { S("cache"), &fs_static.opt_cache, 0 }, 74 { S("user"), &fs_static.opt_user, 0 }, 75 { S("group"), &fs_static.opt_group, 0 }, 76 { S("var0"), &vars[0], 0 }, 77 { S("var1"), &vars[1], 0 }, 78 { S("var2"), &vars[2], 0 }, 79 { S("var3"), &vars[3], 0 }, 80 { S("var4"), &vars[4], 0 }, 81 { S("var5"), &vars[5], 0 }, 82 { S("var6"), &vars[6], 0 }, 83 { S("var7"), &vars[7], 0 }, 84 { 0, 0, 0, 0 }, 85 }; 86 87 typedef struct opt_apply opt_apply; 88 struct opt_apply { 89 char **opt; 90 char *val; 91 }; 92 93 /* 94 * Specially expand the remote host name first 95 */ 96 static opt_apply rhost_expansion[] = { 97 { &fs_static.opt_rhost, "${host}" }, 98 { 0, 0 }, 99 }; 100 /* 101 * List of options which need to be expanded 102 * Note that this the order here _may_ be important. 103 */ 104 static opt_apply expansions[] = { 105 /* { &fs_static.opt_dir, 0 }, */ 106 { &fs_static.opt_sublink, 0 }, 107 { &fs_static.opt_rfs, "${path}" }, 108 { &fs_static.opt_fs, "${autodir}/${rhost}${rfs}" }, 109 { &fs_static.opt_opts, "rw" }, 110 { &fs_static.opt_mount, 0 }, 111 { &fs_static.opt_unmount, 0 }, 112 { 0, 0 }, 113 }; 114 115 /* 116 * List of options which need to be free'ed before re-use 117 */ 118 static opt_apply to_free[] = { 119 { &fs_static.fs_glob, 0 }, 120 { &fs_static.fs_local, 0 }, 121 { &fs_static.fs_mtab, 0 }, 122 /* { &fs_static.opt_dir, 0 }, */ 123 { &fs_static.opt_sublink, 0 }, 124 { &fs_static.opt_rfs, 0 }, 125 { &fs_static.opt_fs, 0 }, 126 { &fs_static.opt_rhost, 0 }, 127 { &fs_static.opt_opts, 0 }, 128 { &fs_static.opt_mount, 0 }, 129 { &fs_static.opt_unmount, 0 }, 130 { &vars[0], 0 }, 131 { &vars[1], 0 }, 132 { &vars[2], 0 }, 133 { &vars[3], 0 }, 134 { &vars[4], 0 }, 135 { &vars[5], 0 }, 136 { &vars[6], 0 }, 137 { &vars[7], 0 }, 138 { 0, 0 }, 139 }; 140 141 /* 142 * Skip to next option in the string 143 */ 144 static char *opt P((char**)); 145 static char *opt(p) 146 char **p; 147 { 148 char *cp = *p; 149 char *dp = cp; 150 char *s = cp; 151 152 top: 153 while (*cp && *cp != ';') { 154 if (*cp == '\"') { 155 /* 156 * Skip past string 157 */ 158 cp++; 159 while (*cp && *cp != '\"') 160 *dp++ = *cp++; 161 if (*cp) 162 cp++; 163 } else { 164 *dp++ = *cp++; 165 } 166 } 167 168 /* 169 * Skip past any remaining ';'s 170 */ 171 while (*cp == ';') 172 cp++; 173 174 /* 175 * If we have a zero length string 176 * and there are more fields, then 177 * parse the next one. This allows 178 * sequences of empty fields. 179 */ 180 if (*cp && dp == s) 181 goto top; 182 183 *dp = '\0'; 184 185 *p = cp; 186 return s; 187 } 188 189 static int eval_opts P((char*, char*)); 190 static int eval_opts(opts, mapkey) 191 char *opts; 192 char *mapkey; 193 { 194 /* 195 * Fill in the global structure fs_static by 196 * cracking the string opts. opts may be 197 * scribbled on at will. 198 */ 199 char *o = opts; 200 char *f; 201 202 /* 203 * For each user-specified option 204 */ 205 while (*(f = opt(&o))) { 206 struct opt *op; 207 enum vs_opt { OldSyn, SelEQ, SelNE, VarAss } vs_opt; 208 char *eq = strchr(f, '='); 209 char *opt; 210 if (!eq || eq[1] == '\0' || eq == f) { 211 /* 212 * No value, just continue 213 */ 214 plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f); 215 continue; 216 } 217 218 /* 219 * Check what type of operation is happening 220 * !=, =! is SelNE 221 * == is SelEQ 222 * := is VarAss 223 * = is OldSyn (either SelEQ or VarAss) 224 */ 225 if (eq[-1] == '!') { /* != */ 226 vs_opt = SelNE; 227 eq[-1] = '\0'; 228 opt = eq + 1; 229 } else if (eq[-1] == ':') { /* := */ 230 vs_opt = VarAss; 231 eq[-1] = '\0'; 232 opt = eq + 1; 233 } else if (eq[1] == '=') { /* == */ 234 vs_opt = SelEQ; 235 eq[0] = '\0'; 236 opt = eq + 2; 237 } else if (eq[1] == '!') { /* =! */ 238 vs_opt = SelNE; 239 eq[0] = '\0'; 240 opt = eq + 2; 241 } else { /* = */ 242 vs_opt = OldSyn; 243 eq[0] = '\0'; 244 opt = eq + 1; 245 } 246 247 /* 248 * For each recognised option 249 */ 250 for (op = opt_fields; op->name; op++) { 251 /* 252 * Check whether they match 253 */ 254 if (FSTREQ(op->name, f)) { 255 switch (vs_opt) { 256 #if AMD_COMPAT <= 5000108 257 case OldSyn: 258 plog(XLOG_WARNING, "key %s: Old syntax selector found: %s=%s", mapkey, f, opt); 259 if (!op->sel_p) { 260 *op->optp = opt; 261 break; 262 } 263 /* fall through ... */ 264 #endif /* 5000108 */ 265 case SelEQ: 266 case SelNE: 267 if (op->sel_p && (STREQ(*op->sel_p, opt) == (vs_opt == SelNE))) { 268 plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s", 269 mapkey, 270 op->name, 271 *op->sel_p, 272 vs_opt == SelNE ? "not " : "", 273 opt); 274 return 0; 275 } 276 break; 277 278 case VarAss: 279 if (op->sel_p) { 280 plog(XLOG_USER, "key %s: Can't assign to a selector (%s)", mapkey, op->name); 281 return 0; 282 } 283 *op->optp = opt; 284 break; 285 } 286 break; 287 } 288 } 289 290 if (!op->name) 291 plog(XLOG_USER, "key %s: Unrecognised key/option \"%s\"", mapkey, f); 292 } 293 294 return 1; 295 } 296 297 /* 298 * Free an option 299 */ 300 static void free_op P((opt_apply*, int)); 301 /*ARGSUSED*/ 302 static void free_op(p, b) 303 opt_apply *p; 304 int b; 305 { 306 if (*p->opt) { 307 free(*p->opt); 308 *p->opt = 0; 309 } 310 } 311 312 /* 313 * Normalize slashes in the string. 314 */ 315 void normalize_slash P((char *p)); 316 void normalize_slash(p) 317 char *p; 318 { 319 char *f = strchr(p, '/'); 320 if (f) { 321 char *t = f; 322 do { 323 /* assert(*f == '/'); */ 324 /* copy a single / across */ 325 *t++ = *f++; 326 327 /* assert(f[-1] == '/'); */ 328 /* skip past more /'s */ 329 while (*f == '/') 330 f++; 331 332 /* assert(*f != '/'); */ 333 /* keep copying up to next / */ 334 do { 335 *t++ = *f++; 336 } while (*f && *f != '/'); 337 338 /* assert(*f == 0 || *f == '/'); */ 339 340 } while (*f); 341 *t = 0; /* derived from fix by Steven Glassman */ 342 } 343 } 344 345 /* 346 * Macro-expand an option. Note that this does not 347 * handle recursive expansions. They will go badly wrong. 348 * If sel is true then old expand selectors, otherwise 349 * don't expand selectors. 350 */ 351 static void expand_op P((opt_apply*, int)); 352 static void expand_op(p, sel_p) 353 opt_apply *p; 354 int sel_p; 355 { 356 /* 357 * The BUFSPACE macros checks that there is enough space 358 * left in the expansion buffer. If there isn't then we 359 * give up completely. This is done to avoid crashing the 360 * automounter itself (which would be a bad thing to do). 361 */ 362 #define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN) 363 static char expand_error[] = "No space to expand \"%s\""; 364 365 char expbuf[MAXPATHLEN+1]; 366 char nbuf[NLEN+1]; 367 char *ep = expbuf; 368 char *cp = *p->opt; 369 char *dp; 370 #ifdef DEBUG 371 char *cp_orig = *p->opt; 372 #endif /* DEBUG */ 373 struct opt *op; 374 375 while (dp = strchr(cp, '$')) { 376 char ch; 377 /* 378 * First copy up to the $ 379 */ 380 { int len = dp - cp; 381 if (BUFSPACE(ep, len)) { 382 strncpy(ep, cp, len); 383 ep += len; 384 } else { 385 plog(XLOG_ERROR, expand_error, *p->opt); 386 goto out; 387 } 388 } 389 cp = dp + 1; 390 ch = *cp++; 391 if (ch == '$') { 392 if (BUFSPACE(ep, 1)) { 393 *ep++ = '$'; 394 } else { 395 plog(XLOG_ERROR, expand_error, *p->opt); 396 goto out; 397 } 398 } else if (ch == '{') { 399 /* Expansion... */ 400 enum { E_All, E_Dir, E_File, E_Domain, E_Host } todo; 401 /* 402 * Find closing brace 403 */ 404 char *br_p = strchr(cp, '}'); 405 int len; 406 /* 407 * Check we found it 408 */ 409 if (!br_p) { 410 /* 411 * Just give up 412 */ 413 plog(XLOG_USER, "No closing '}' in \"%s\"", *p->opt); 414 goto out; 415 } 416 len = br_p - cp; 417 /* 418 * Figure out which part of the variable to grab. 419 */ 420 if (*cp == '/') { 421 /* 422 * Just take the last component 423 */ 424 todo = E_File; 425 cp++; 426 --len; 427 } else if (br_p[-1] == '/') { 428 /* 429 * Take all but the last component 430 */ 431 todo = E_Dir; 432 --len; 433 } else if (*cp == '.') { 434 /* 435 * Take domain name 436 */ 437 todo = E_Domain; 438 cp++; 439 --len; 440 } else if (br_p[-1] == '.') { 441 /* 442 * Take host name 443 */ 444 todo = E_Host; 445 --len; 446 } else { 447 /* 448 * Take the whole lot 449 */ 450 todo = E_All; 451 } 452 /* 453 * Truncate if too long. Since it won't 454 * match anyway it doesn't matter that 455 * it has been cut short. 456 */ 457 if (len > NLEN) 458 len = NLEN; 459 /* 460 * Put the string into another buffer so 461 * we can do comparisons. 462 */ 463 strncpy(nbuf, cp, len); 464 nbuf[len] = '\0'; 465 /* 466 * Advance cp 467 */ 468 cp = br_p + 1; 469 /* 470 * Search the option array 471 */ 472 for (op = opt_fields; op->name; op++) { 473 /* 474 * Check for match 475 */ 476 if (len == op->nlen && STREQ(op->name, nbuf)) { 477 char xbuf[NLEN+3]; 478 char *val; 479 /* 480 * Found expansion. Copy 481 * the correct value field. 482 */ 483 if (!(!op->sel_p == !sel_p)) { 484 /* 485 * Copy the string across unexpanded 486 */ 487 sprintf(xbuf, "${%s%s%s}", 488 todo == E_File ? "/" : 489 todo == E_Domain ? "." : "", 490 nbuf, 491 todo == E_Dir ? "/" : 492 todo == E_Host ? "." : ""); 493 val = xbuf; 494 /* 495 * Make sure expansion doesn't 496 * munge the value! 497 */ 498 todo = E_All; 499 } else if (op->sel_p) { 500 val = *op->sel_p; 501 } else { 502 val = *op->optp; 503 } 504 if (val) { 505 /* 506 * Do expansion: 507 * ${/var} means take just the last part 508 * ${var/} means take all but the last part 509 * ${.var} means take all but first part 510 * ${var.} means take just the first part 511 * ${var} means take the whole lot 512 */ 513 int vlen = strlen(val); 514 char *vptr = val; 515 switch (todo) { 516 case E_Dir: 517 vptr = strrchr(val, '/'); 518 if (vptr) 519 vlen = vptr - val; 520 vptr = val; 521 break; 522 case E_File: 523 vptr = strrchr(val, '/'); 524 if (vptr) { 525 vptr++; 526 vlen = strlen(vptr); 527 } else 528 vptr = val; 529 break; 530 case E_Domain: 531 vptr = strchr(val, '.'); 532 if (vptr) { 533 vptr++; 534 vlen = strlen(vptr); 535 } else { 536 vptr = ""; 537 vlen = 0; 538 } 539 break; 540 case E_Host: 541 vptr = strchr(val, '.'); 542 if (vptr) 543 vlen = vptr - val; 544 vptr = val; 545 break; 546 case E_All: 547 break; 548 } 549 #ifdef DEBUG 550 /*dlog("Expanding \"%s\" to \"%s\"", nbuf, val);*/ 551 #endif /* DEBUG */ 552 if (BUFSPACE(ep, vlen)) { 553 strcpy(ep, vptr); 554 ep += vlen; 555 } else { 556 plog(XLOG_ERROR, expand_error, *p->opt); 557 goto out; 558 } 559 } 560 /* 561 * Done with this variable 562 */ 563 break; 564 } 565 } 566 /* 567 * Check that the search was succesful 568 */ 569 if (!op->name) { 570 /* 571 * If it wasn't then scan the 572 * environment for that name 573 * and use any value found 574 */ 575 char *env = getenv(nbuf); 576 if (env) { 577 int vlen = strlen(env); 578 579 if (BUFSPACE(ep, vlen)) { 580 strcpy(ep, env); 581 ep += vlen; 582 } else { 583 plog(XLOG_ERROR, expand_error, *p->opt); 584 goto out; 585 } 586 #ifdef DEBUG 587 Debug(D_STR) 588 plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env); 589 #endif /* DEBUG */ 590 } else { 591 plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf); 592 } 593 } 594 } else { 595 /* 596 * Error, error 597 */ 598 plog(XLOG_USER, "Unknown $ sequence in \"%s\"", *p->opt); 599 } 600 } 601 602 out: 603 /* 604 * Handle common case - no expansion 605 */ 606 if (cp == *p->opt) { 607 *p->opt = strdup(cp); 608 } else { 609 /* 610 * Finish off the expansion 611 */ 612 if (BUFSPACE(ep, strlen(cp))) { 613 strcpy(ep, cp); 614 /*ep += strlen(ep);*/ 615 } else { 616 plog(XLOG_ERROR, expand_error, *p->opt); 617 } 618 619 /* 620 * Save the exansion 621 */ 622 *p->opt = strdup(expbuf); 623 } 624 625 normalize_slash(*p->opt); 626 627 #ifdef DEBUG 628 Debug(D_STR) { 629 plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig); 630 plog(XLOG_DEBUG, "... is \"%s\"", *p->opt); 631 } 632 #endif /* DEBUG */ 633 } 634 635 /* 636 * Wrapper for expand_op 637 */ 638 static void expand_opts P((opt_apply*, int)); 639 static void expand_opts(p, sel_p) 640 opt_apply *p; 641 int sel_p; 642 { 643 if (*p->opt) { 644 expand_op(p, sel_p); 645 } else if (p->val) { 646 /* 647 * Do double expansion, remembering 648 * to free the string from the first 649 * expansion... 650 */ 651 char *s = *p->opt = expand_key(p->val); 652 expand_op(p, sel_p); 653 free(s); 654 } 655 } 656 657 /* 658 * Apply a function to a list of options 659 */ 660 static void apply_opts(op, ppp, b) 661 void (*op)(); 662 opt_apply ppp[]; 663 int b; 664 { 665 opt_apply *pp; 666 for (pp = ppp; pp->opt; pp++) 667 (*op)(pp, b); 668 } 669 670 /* 671 * Free the option table 672 */ 673 void free_opts(fo) 674 am_opts *fo; 675 { 676 /* 677 * Copy in the structure we are playing with 678 */ 679 fs_static = *fo; 680 681 /* 682 * Free previously allocated memory 683 */ 684 apply_opts(free_op, to_free, FALSE); 685 } 686 687 /* 688 * Expand lookup key 689 */ 690 char *expand_key(key) 691 char *key; 692 { 693 opt_apply oa; 694 695 oa.opt = &key; oa.val = 0; 696 expand_opts(&oa, TRUE); 697 698 return key; 699 } 700 701 /* 702 * Remove trailing /'s from a string 703 * unless the string is a single / (Steven Glassman) 704 */ 705 void deslashify P((char *s)); 706 void deslashify(s) 707 char *s; 708 { 709 if (s && *s) { 710 char *sl = s + strlen(s); 711 while (*--sl == '/' && sl > s) 712 *sl = '\0'; 713 } 714 } 715 716 int eval_fs_opts(fo, opts, g_opts, path, key, map) 717 am_opts *fo; 718 char *opts, *g_opts, *path, *key, *map; 719 { 720 int ok = TRUE; 721 722 free_opts(fo); 723 724 /* 725 * Clear out the option table 726 */ 727 bzero((voidp) &fs_static, sizeof(fs_static)); 728 bzero((voidp) vars, sizeof(vars)); 729 bzero((voidp) fo, sizeof(*fo)); 730 731 /* 732 * Set key, map & path before expansion 733 */ 734 opt_key = key; 735 opt_map = map; 736 opt_path = path; 737 738 /* 739 * Expand global options 740 */ 741 fs_static.fs_glob = expand_key(g_opts); 742 743 /* 744 * Expand local options 745 */ 746 fs_static.fs_local = expand_key(opts); 747 748 /* 749 * Expand default (global) options 750 */ 751 if (!eval_opts(fs_static.fs_glob, key)) 752 ok = FALSE; 753 754 /* 755 * Expand local options 756 */ 757 if (ok && !eval_opts(fs_static.fs_local, key)) 758 ok = FALSE; 759 760 /* 761 * Normalise remote host name. 762 * 1. Expand variables 763 * 2. Normalize relative to host tables 764 * 3. Strip local domains from the remote host 765 * name before using it in other expansions. 766 * This makes mount point names and other things 767 * much shorter, while allowing cross domain 768 * sharing of mount maps. 769 */ 770 apply_opts(expand_opts, rhost_expansion, FALSE); 771 if (ok && fs_static.opt_rhost && *fs_static.opt_rhost) 772 host_normalize(&fs_static.opt_rhost); 773 774 /* 775 * Macro expand the options. 776 * Do this regardless of whether we are accepting 777 * this mount - otherwise nasty things happen 778 * with memory allocation. 779 */ 780 apply_opts(expand_opts, expansions, FALSE); 781 782 /* 783 * Strip trailing slashes from local pathname... 784 */ 785 deslashify(fs_static.opt_fs); 786 787 /* 788 * ok... copy the data back out. 789 */ 790 *fo = fs_static; 791 792 /* 793 * Clear defined options 794 */ 795 opt_key = opt_map = opt_path = nullstr; 796 797 return ok; 798 } 799