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