1 /* $NetBSD: opts.c,v 1.1.1.2 2009/03/20 20:26:50 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2009 Erez Zadok 5 * Copyright (c) 1989 Jan-Simon Pendry 6 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 7 * Copyright (c) 1989 The Regents of the University of California. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Jan-Simon Pendry at Imperial College, London. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgment: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * 42 * File: am-utils/amd/opts.c 43 * 44 */ 45 46 #ifdef HAVE_CONFIG_H 47 # include <config.h> 48 #endif /* HAVE_CONFIG_H */ 49 #include <am_defs.h> 50 #include <amd.h> 51 52 /* 53 * MACROS: 54 */ 55 #define NLEN 16 /* Length of longest option name (conservative) */ 56 #define S(x) (x) , (sizeof(x)-1) 57 /* 58 * The BUFSPACE macros checks that there is enough space 59 * left in the expansion buffer. If there isn't then we 60 * give up completely. This is done to avoid crashing the 61 * automounter itself (which would be a bad thing to do). 62 */ 63 #define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN) 64 65 /* 66 * TYPEDEFS: 67 */ 68 typedef int (*IntFuncPtr) (char *); 69 typedef struct opt_apply opt_apply; 70 enum vs_opt { SelEQ, SelNE, VarAss }; 71 72 /* 73 * STRUCTURES 74 */ 75 struct opt { 76 char *name; /* Name of the option */ 77 int nlen; /* Length of option name */ 78 char **optp; /* Pointer to option value string */ 79 char **sel_p; /* Pointer to selector value string */ 80 int (*fxn_p)(char *); /* Pointer to boolean function */ 81 int case_insensitive; /* How to do selector comparisons */ 82 }; 83 84 struct opt_apply { 85 char **opt; 86 char *val; 87 }; 88 89 struct functable { 90 char *name; 91 IntFuncPtr func; 92 }; 93 94 /* 95 * FORWARD DEFINITION: 96 */ 97 static int f_in_network(char *); 98 static int f_xhost(char *); 99 static int f_netgrp(char *); 100 static int f_netgrpd(char *); 101 static int f_exists(char *); 102 static int f_false(char *); 103 static int f_true(char *); 104 static inline char *expand_options(char *key); 105 106 /* 107 * STATICS: 108 */ 109 static char NullStr[] = "<NULL>"; 110 static char nullstr[] = ""; 111 static char *opt_dkey = NullStr; 112 static char *opt_host = nullstr; /* XXX: was the global hostname */ 113 static char *opt_hostd = hostd; 114 static char *opt_key = nullstr; 115 static char *opt_keyd = nullstr; 116 static char *opt_map = nullstr; 117 static char *opt_path = nullstr; 118 char uid_str[SIZEOF_UID_STR], gid_str[SIZEOF_GID_STR]; 119 char *opt_uid = uid_str; 120 char *opt_gid = gid_str; 121 static char *vars[8]; 122 static char *literal_dollar = "$"; /* ${dollar}: a literal '$' in maps */ 123 124 /* 125 * GLOBALS 126 */ 127 static struct am_opts fs_static; /* copy of the options to play with */ 128 129 130 /* 131 * Options in some order corresponding to frequency of use so that 132 * first-match algorithm is sped up. 133 */ 134 static struct opt opt_fields[] = { 135 /* Name and length. 136 Option str. Selector str. boolean fxn. case sensitive */ 137 { S("opts"), 138 &fs_static.opt_opts, 0, 0, FALSE }, 139 { S("host"), 140 0, &opt_host, 0, TRUE }, 141 { S("hostd"), 142 0, &opt_hostd, 0, TRUE }, 143 { S("type"), 144 &fs_static.opt_type, 0, 0, FALSE }, 145 { S("rhost"), 146 &fs_static.opt_rhost, 0, 0, TRUE }, 147 { S("rfs"), 148 &fs_static.opt_rfs, 0, 0, FALSE }, 149 { S("fs"), 150 &fs_static.opt_fs, 0, 0, FALSE }, 151 { S("key"), 152 0, &opt_key, 0, FALSE }, 153 { S("map"), 154 0, &opt_map, 0, FALSE }, 155 { S("sublink"), 156 &fs_static.opt_sublink, 0, 0, FALSE }, 157 { S("arch"), 158 0, &gopt.arch, 0, TRUE }, 159 { S("dev"), 160 &fs_static.opt_dev, 0, 0, FALSE }, 161 { S("pref"), 162 &fs_static.opt_pref, 0, 0, FALSE }, 163 { S("path"), 164 0, &opt_path, 0, FALSE }, 165 { S("autodir"), 166 0, &gopt.auto_dir, 0, FALSE }, 167 { S("delay"), 168 &fs_static.opt_delay, 0, 0, FALSE }, 169 { S("domain"), 170 0, &hostdomain, 0, TRUE }, 171 { S("karch"), 172 0, &gopt.karch, 0, TRUE }, 173 { S("cluster"), 174 0, &gopt.cluster, 0, TRUE }, 175 { S("wire"), 176 0, 0, f_in_network, TRUE }, 177 { S("network"), 178 0, 0, f_in_network, TRUE }, 179 { S("netnumber"), 180 0, 0, f_in_network, TRUE }, 181 { S("byte"), 182 0, &endian, 0, TRUE }, 183 { S("os"), 184 0, &gopt.op_sys, 0, TRUE }, 185 { S("osver"), 186 0, &gopt.op_sys_ver, 0, TRUE }, 187 { S("full_os"), 188 0, &gopt.op_sys_full, 0, TRUE }, 189 { S("vendor"), 190 0, &gopt.op_sys_vendor, 0, TRUE }, 191 { S("remopts"), 192 &fs_static.opt_remopts, 0, 0, FALSE }, 193 { S("mount"), 194 &fs_static.opt_mount, 0, 0, FALSE }, 195 { S("unmount"), 196 &fs_static.opt_unmount, 0, 0, FALSE }, 197 { S("umount"), 198 &fs_static.opt_umount, 0, 0, FALSE }, 199 { S("cache"), 200 &fs_static.opt_cache, 0, 0, FALSE }, 201 { S("user"), 202 &fs_static.opt_user, 0, 0, FALSE }, 203 { S("group"), 204 &fs_static.opt_group, 0, 0, FALSE }, 205 { S(".key"), 206 0, &opt_dkey, 0, FALSE }, 207 { S("key."), 208 0, &opt_keyd, 0, FALSE }, 209 { S("maptype"), 210 &fs_static.opt_maptype, 0, 0, FALSE }, 211 { S("cachedir"), 212 &fs_static.opt_cachedir, 0, 0, FALSE }, 213 { S("addopts"), 214 &fs_static.opt_addopts, 0, 0, FALSE }, 215 { S("uid"), 216 0, &opt_uid, 0, FALSE }, 217 { S("gid"), 218 0, &opt_gid, 0, FALSE }, 219 { S("mount_type"), 220 &fs_static.opt_mount_type, 0, 0, FALSE }, 221 { S("dollar"), 222 &literal_dollar, 0, 0, FALSE }, 223 { S("var0"), 224 &vars[0], 0, 0, FALSE }, 225 { S("var1"), 226 &vars[1], 0, 0, FALSE }, 227 { S("var2"), 228 &vars[2], 0, 0, FALSE }, 229 { S("var3"), 230 &vars[3], 0, 0, FALSE }, 231 { S("var4"), 232 &vars[4], 0, 0, FALSE }, 233 { S("var5"), 234 &vars[5], 0, 0, FALSE }, 235 { S("var6"), 236 &vars[6], 0, 0, FALSE }, 237 { S("var7"), 238 &vars[7], 0, 0, FALSE }, 239 { 0, 0, 0, 0, 0, FALSE }, 240 }; 241 242 static struct functable functable[] = { 243 { "in_network", f_in_network }, 244 { "xhost", f_xhost }, 245 { "netgrp", f_netgrp }, 246 { "netgrpd", f_netgrpd }, 247 { "exists", f_exists }, 248 { "false", f_false }, 249 { "true", f_true }, 250 { 0, 0 }, 251 }; 252 253 /* 254 * Specially expand the remote host name first 255 */ 256 static opt_apply rhost_expansion[] = 257 { 258 {&fs_static.opt_rhost, "${host}"}, 259 {0, 0}, 260 }; 261 262 /* 263 * List of options which need to be expanded 264 * Note that the order here _may_ be important. 265 */ 266 static opt_apply expansions[] = 267 { 268 {&fs_static.opt_sublink, 0}, 269 {&fs_static.opt_rfs, "${path}"}, 270 {&fs_static.opt_fs, "${autodir}/${rhost}${rfs}"}, 271 {&fs_static.opt_opts, "rw"}, 272 {&fs_static.opt_remopts, "${opts}"}, 273 {&fs_static.opt_mount, 0}, 274 {&fs_static.opt_unmount, 0}, 275 {&fs_static.opt_umount, 0}, 276 {&fs_static.opt_cachedir, 0}, 277 {&fs_static.opt_addopts, 0}, 278 {0, 0}, 279 }; 280 281 /* 282 * List of options which need to be free'ed before re-use 283 */ 284 static opt_apply to_free[] = 285 { 286 {&fs_static.fs_glob, 0}, 287 {&fs_static.fs_local, 0}, 288 {&fs_static.fs_mtab, 0}, 289 {&fs_static.opt_sublink, 0}, 290 {&fs_static.opt_rfs, 0}, 291 {&fs_static.opt_fs, 0}, 292 {&fs_static.opt_rhost, 0}, 293 {&fs_static.opt_opts, 0}, 294 {&fs_static.opt_remopts, 0}, 295 {&fs_static.opt_mount, 0}, 296 {&fs_static.opt_unmount, 0}, 297 {&fs_static.opt_umount, 0}, 298 {&fs_static.opt_cachedir, 0}, 299 {&fs_static.opt_addopts, 0}, 300 {&vars[0], 0}, 301 {&vars[1], 0}, 302 {&vars[2], 0}, 303 {&vars[3], 0}, 304 {&vars[4], 0}, 305 {&vars[5], 0}, 306 {&vars[6], 0}, 307 {&vars[7], 0}, 308 {0, 0}, 309 }; 310 311 312 /* 313 * expand backslash escape sequences 314 * (escaped slash is handled separately in normalize_slash) 315 */ 316 static char 317 backslash(char **p) 318 { 319 char c; 320 321 if ((*p)[1] == '\0') { 322 plog(XLOG_USER, "Empty backslash escape"); 323 return **p; 324 } 325 326 if (**p == '\\') { 327 (*p)++; 328 switch (**p) { 329 case 'g': 330 c = '\007'; /* Bell */ 331 break; 332 case 'b': 333 c = '\010'; /* Backspace */ 334 break; 335 case 't': 336 c = '\011'; /* Horizontal Tab */ 337 break; 338 case 'n': 339 c = '\012'; /* New Line */ 340 break; 341 case 'v': 342 c = '\013'; /* Vertical Tab */ 343 break; 344 case 'f': 345 c = '\014'; /* Form Feed */ 346 break; 347 case 'r': 348 c = '\015'; /* Carriage Return */ 349 break; 350 case 'e': 351 c = '\033'; /* Escape */ 352 break; 353 case '0': 354 case '1': 355 case '2': 356 case '3': 357 case '4': 358 case '5': 359 case '6': 360 case '7': 361 { 362 int cnt, val, ch; 363 364 for (cnt = 0, val = 0; cnt < 3; cnt++) { 365 ch = *(*p)++; 366 if (ch < '0' || ch > '7') { 367 (*p)--; 368 break; 369 } 370 val = (val << 3) | (ch - '0'); 371 } 372 373 if ((val & 0xffffff00) != 0) 374 plog(XLOG_USER, 375 "Too large character constant %u\n", 376 val); 377 c = (char) val; 378 --(*p); 379 } 380 break; 381 382 default: 383 c = **p; 384 break; 385 } 386 } else 387 c = **p; 388 389 return c; 390 } 391 392 393 /* 394 * Skip to next option in the string 395 */ 396 static char * 397 opt(char **p) 398 { 399 char *cp = *p; 400 char *dp = cp; 401 char *s = cp; 402 403 top: 404 while (*cp && *cp != ';') { 405 if (*cp == '"') { 406 /* 407 * Skip past string 408 */ 409 for (cp++; *cp && *cp != '"'; cp++) 410 if (*cp == '\\') 411 *dp++ = backslash(&cp); 412 else 413 *dp++ = *cp; 414 if (*cp) 415 cp++; 416 } else { 417 *dp++ = *cp++; 418 } 419 } 420 421 /* 422 * Skip past any remaining ';'s 423 */ 424 while (*cp == ';') 425 cp++; 426 427 /* 428 * If we have a zero length string 429 * and there are more fields, then 430 * parse the next one. This allows 431 * sequences of empty fields. 432 */ 433 if (*cp && dp == s) 434 goto top; 435 436 *dp = '\0'; 437 438 *p = cp; 439 return s; 440 } 441 442 443 /* 444 * These routines add a new style of selector; function-style boolean 445 * operators. To add new ones, just define functions as in true, false, 446 * exists (below) and add them to the functable, above. 447 * 448 * Usage example: Some people have X11R5 local, some go to a server. I do 449 * this: 450 * 451 * * exists(/usr/pkg/${key});type:=link;fs:=/usr/pkg/${key} || \ 452 * -type:=nfs;rfs=/usr/pkg/${key} \ 453 * rhost:=server1 \ 454 * rhost:=server2 455 * 456 * -Rens Troost <rens@imsi.com> 457 */ 458 static IntFuncPtr 459 functable_lookup(char *key) 460 { 461 struct functable *fp; 462 463 for (fp = functable; fp->name; fp++) 464 if (FSTREQ(fp->name, key)) 465 return (fp->func); 466 return (IntFuncPtr) NULL; 467 } 468 469 470 /* 471 * Fill in the global structure fs_static by 472 * cracking the string opts. opts may be 473 * scribbled on at will. Does NOT evaluate options. 474 * Returns 0 on error, 1 if no syntax errors were discovered. 475 */ 476 static int 477 split_opts(char *opts, char *mapkey) 478 { 479 char *o = opts; 480 char *f; 481 482 /* 483 * For each user-specified option 484 */ 485 for (f = opt(&o); *f; f = opt(&o)) { 486 struct opt *op; 487 char *eq = strchr(f, '='); 488 char *opt = NULL; 489 490 if (!eq) 491 continue; 492 493 if (*(eq-1) == '!' || 494 eq[1] == '=' || 495 eq[1] == '!') { /* != or == or =! */ 496 continue; /* we don't care about selectors */ 497 } 498 499 if (*(eq-1) == ':') { /* := */ 500 *(eq-1) = '\0'; 501 } else { 502 /* old style assignment */ 503 eq[0] = '\0'; 504 } 505 opt = eq + 1; 506 507 /* 508 * For each recognized option 509 */ 510 for (op = opt_fields; op->name; op++) { 511 /* 512 * Check whether they match 513 */ 514 if (FSTREQ(op->name, f)) { 515 if (op->sel_p) { 516 plog(XLOG_USER, "key %s: Can't assign to a selector (%s)", 517 mapkey, op->name); 518 return 0; 519 } 520 *op->optp = opt; /* actual assignment into fs_static */ 521 break; /* break out of for loop */ 522 } /* end of "if (FSTREQ(op->name, f))" statement */ 523 } /* end of "for (op = opt_fields..." statement */ 524 525 if (!op->name) 526 plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f); 527 } 528 529 return 1; 530 } 531 532 533 /* 534 * Just evaluate selectors, which were split by split_opts. 535 * Returns 0 on error or no match, 1 if matched. 536 */ 537 static int 538 eval_selectors(char *opts, char *mapkey) 539 { 540 char *o, *old_o; 541 char *f; 542 int ret = 0; 543 544 o = old_o = strdup(opts); 545 546 /* 547 * For each user-specified option 548 */ 549 for (f = opt(&o); *f; f = opt(&o)) { 550 struct opt *op; 551 enum vs_opt vs_opt; 552 char *eq = strchr(f, '='); 553 char *fx; 554 IntFuncPtr func; 555 char *opt = NULL; 556 char *arg; 557 558 if (!eq) { 559 /* 560 * No value, is it a function call? 561 */ 562 arg = strchr(f, '('); 563 564 if (!arg || arg[1] == '\0' || arg == f) { 565 /* 566 * No, just continue 567 */ 568 plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f); 569 continue; 570 } 571 572 /* null-terminate the argument */ 573 *arg++ = '\0'; 574 fx = strchr(arg, ')'); 575 if (!arg || fx == arg) { 576 plog(XLOG_USER, "key %s: Malformed function in \"%s\"", mapkey, f); 577 continue; 578 } 579 *fx = '\0'; 580 581 if (f[0] == '!') { 582 vs_opt = SelNE; 583 f++; 584 } else { 585 vs_opt = SelEQ; 586 } 587 /* 588 * look up f in functable and pass it arg. 589 * func must return 0 on failure, and 1 on success. 590 */ 591 if ((func = functable_lookup(f))) { 592 int funok; 593 594 /* this allocates memory, don't forget to free */ 595 arg = expand_options(arg); 596 funok = func(arg); 597 XFREE(arg); 598 599 if (vs_opt == SelNE) 600 funok = !funok; 601 if (!funok) 602 goto out; 603 604 continue; 605 } else { 606 plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f); 607 goto out; 608 } 609 } else { 610 if (eq[1] == '\0' || eq == f) { 611 /* misformed selector */ 612 plog(XLOG_USER, "key %s: Bad selector \"%s\"", mapkey, f); 613 continue; 614 } 615 } 616 617 /* 618 * Check what type of operation is happening 619 * !=, =! is SelNE 620 * == is SelEQ 621 * =, := is VarAss 622 */ 623 if (*(eq-1) == '!') { /* != */ 624 vs_opt = SelNE; 625 *(eq-1) = '\0'; 626 opt = eq + 1; 627 } else if (*(eq-1) == ':') { /* := */ 628 continue; 629 } else if (eq[1] == '=') { /* == */ 630 vs_opt = SelEQ; 631 eq[0] = '\0'; 632 opt = eq + 2; 633 } else if (eq[1] == '!') { /* =! */ 634 vs_opt = SelNE; 635 eq[0] = '\0'; 636 opt = eq + 2; 637 } else { 638 /* old style assignment */ 639 continue; 640 } 641 642 /* 643 * For each recognized option 644 */ 645 for (op = opt_fields; op->name; op++) { 646 /* 647 * Check whether they match 648 */ 649 if (FSTREQ(op->name, f)) { 650 opt = expand_options(opt); 651 652 if (op->sel_p != NULL) { 653 int selok; 654 if (op->case_insensitive) { 655 selok = STRCEQ(*op->sel_p, opt); 656 } else { 657 selok = STREQ(*op->sel_p, opt); 658 } 659 if (vs_opt == SelNE) 660 selok = !selok; 661 if (!selok) { 662 plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s", 663 mapkey, 664 op->name, 665 *op->sel_p, 666 vs_opt == SelNE ? "mis" : "", 667 opt); 668 XFREE(opt); 669 goto out; 670 } 671 XFREE(opt); 672 } 673 /* check if to apply a function */ 674 if (op->fxn_p) { 675 int funok; 676 677 funok = op->fxn_p(opt); 678 if (vs_opt == SelNE) 679 funok = !funok; 680 if (!funok) { 681 plog(XLOG_MAP, "key %s: map function %s did not %smatch %s", 682 mapkey, 683 op->name, 684 vs_opt == SelNE ? "mis" : "", 685 opt); 686 XFREE(opt); 687 goto out; 688 } 689 XFREE(opt); 690 } 691 break; /* break out of for loop */ 692 } 693 } 694 695 if (!op->name) 696 plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f); 697 } 698 699 /* all is ok */ 700 ret = 1; 701 702 out: 703 free(old_o); 704 return ret; 705 } 706 707 708 /* 709 * Skip to next option in the string, but don't scribble over the string. 710 * However, *p gets repointed to the start of the next string past ';'. 711 */ 712 static char * 713 opt_no_scribble(char **p) 714 { 715 char *cp = *p; 716 char *dp = cp; 717 char *s = cp; 718 719 top: 720 while (*cp && *cp != ';') { 721 if (*cp == '\"') { 722 /* 723 * Skip past string 724 */ 725 cp++; 726 while (*cp && *cp != '\"') 727 *dp++ = *cp++; 728 if (*cp) 729 cp++; 730 } else { 731 *dp++ = *cp++; 732 } 733 } 734 735 /* 736 * Skip past any remaining ';'s 737 */ 738 while (*cp == ';') 739 cp++; 740 741 /* 742 * If we have a zero length string 743 * and there are more fields, then 744 * parse the next one. This allows 745 * sequences of empty fields. 746 */ 747 if (*cp && dp == s) 748 goto top; 749 750 *p = cp; 751 return s; 752 } 753 754 755 /* 756 * Strip any selectors from a string. Selectors are all assumed to be 757 * first in the string. This is used for the new /defaults method which will 758 * use selectors as well. 759 */ 760 char * 761 strip_selectors(char *opts, char *mapkey) 762 { 763 /* 764 * Fill in the global structure fs_static by 765 * cracking the string opts. opts may be 766 * scribbled on at will. 767 */ 768 char *o = opts; 769 char *oo = opts; 770 char *f; 771 772 /* 773 * Scan options. Note that the opt() function scribbles on the opt string. 774 */ 775 while (*(f = opt_no_scribble(&o))) { 776 enum vs_opt vs_opt = VarAss; 777 char *eq = strchr(f, '='); 778 779 if (!eq || eq[1] == '\0' || eq == f) { 780 /* 781 * No option or assignment? Return as is. 782 */ 783 plog(XLOG_USER, "key %s: No option or assignment in \"%s\"", mapkey, f); 784 return o; 785 } 786 /* 787 * Check what type of operation is happening 788 * !=, =! is SelNE 789 * == is SelEQ 790 * := is VarAss 791 */ 792 if (*(eq-1) == '!') { /* != */ 793 vs_opt = SelNE; 794 } else if (*(eq-1) == ':') { /* := */ 795 vs_opt = VarAss; 796 } else if (eq[1] == '=') { /* == */ 797 vs_opt = SelEQ; 798 } else if (eq[1] == '!') { /* =! */ 799 vs_opt = SelNE; 800 } 801 switch (vs_opt) { 802 case SelEQ: 803 case SelNE: 804 /* Skip this selector, maybe there's another one following it */ 805 plog(XLOG_USER, "skipping selector to \"%s\"", o); 806 /* store previous match. it may have been the first assignment */ 807 oo = o; 808 break; 809 810 case VarAss: 811 /* found the first assignment, return the string starting with it */ 812 dlog("found first assignment past selectors \"%s\"", o); 813 return oo; 814 } 815 } 816 817 /* return the same string by default. should not happen. */ 818 return oo; 819 } 820 821 822 /***************************************************************************** 823 *** BOOLEAN FUNCTIONS (return 0 if false, 1 if true): *** 824 *****************************************************************************/ 825 826 /* test if arg is any of this host's network names or numbers */ 827 static int 828 f_in_network(char *arg) 829 { 830 int status; 831 832 if (!arg) 833 return 0; 834 835 status = is_network_member(arg); 836 dlog("%s is %son a local network", arg, (status ? "" : "not ")); 837 return status; 838 } 839 840 841 /* 842 * Test if arg is any of this host's names or aliases (CNAMES). 843 * Note: this function compares against the fully expanded host name (hostd). 844 * XXX: maybe we also need to compare against the stripped host name? 845 */ 846 static int 847 f_xhost(char *arg) 848 { 849 struct hostent *hp; 850 char **cp; 851 852 if (!arg) 853 return 0; 854 855 /* simple test: does it match main host name? */ 856 if (STREQ(arg, opt_hostd)) 857 return 1; 858 859 /* now find all of the names of "arg" and compare against opt_hostd */ 860 hp = gethostbyname(arg); 861 if (hp == NULL) { 862 #ifdef HAVE_HSTRERROR 863 plog(XLOG_ERROR, "gethostbyname xhost(%s): %s", arg, hstrerror(h_errno)); 864 #else /* not HAVE_HSTRERROR */ 865 plog(XLOG_ERROR, "gethostbyname xhost(%s): h_errno %d", arg, h_errno); 866 #endif /* not HAVE_HSTRERROR */ 867 return 0; 868 } 869 /* check primary name */ 870 if (hp->h_name) { 871 dlog("xhost: compare %s==%s", hp->h_name, opt_hostd); 872 if (STREQ(hp->h_name, opt_hostd)) { 873 plog(XLOG_INFO, "xhost(%s): matched h_name %s", arg, hp->h_name); 874 return 1; 875 } 876 } 877 /* check all aliases, if any */ 878 if (hp->h_aliases == NULL) { 879 dlog("gethostbyname(%s) has no aliases", arg); 880 return 0; 881 } 882 cp = hp->h_aliases; 883 while (*cp) { 884 dlog("xhost: compare alias %s==%s", *cp, opt_hostd); 885 if (STREQ(*cp, opt_hostd)) { 886 plog(XLOG_INFO, "xhost(%s): matched alias %s", arg, *cp); 887 return 1; 888 } 889 cp++; 890 } 891 /* nothing matched */ 892 return 0; 893 } 894 895 896 /* test if this host (short hostname form) is in netgroup (arg) */ 897 static int 898 f_netgrp(char *arg) 899 { 900 int status; 901 char *ptr, *nhost; 902 903 if ((ptr = strchr(arg, ',')) != NULL) { 904 *ptr = '\0'; 905 nhost = ptr + 1; 906 } else { 907 nhost = opt_host; 908 } 909 status = innetgr(arg, nhost, NULL, NULL); 910 dlog("netgrp = %s status = %d host = %s", arg, status, nhost); 911 if (ptr) 912 *ptr = ','; 913 return status; 914 } 915 916 917 /* test if this host (fully-qualified name) is in netgroup (arg) */ 918 static int 919 f_netgrpd(char *arg) 920 { 921 int status; 922 char *ptr, *nhost; 923 924 if ((ptr = strchr(arg, ',')) != NULL) { 925 *ptr = '\0'; 926 nhost = ptr + 1; 927 } else { 928 nhost = opt_hostd; 929 } 930 status = innetgr(arg, nhost, NULL, NULL); 931 dlog("netgrp = %s status = %d hostd = %s", arg, status, nhost); 932 if (ptr) 933 *ptr = ','; 934 return status; 935 } 936 937 938 /* test if file (arg) exists via lstat */ 939 static int 940 f_exists(char *arg) 941 { 942 struct stat buf; 943 944 if (lstat(arg, &buf) < 0) 945 return (0); 946 else 947 return (1); 948 } 949 950 951 /* always false */ 952 static int 953 f_false(char *arg) 954 { 955 return (0); 956 } 957 958 959 /* always true */ 960 static int 961 f_true(char *arg) 962 { 963 return (1); 964 } 965 966 967 /* 968 * Free an option 969 */ 970 static void 971 free_op(opt_apply *p, int b) 972 { 973 if (*p->opt) { 974 XFREE(*p->opt); 975 } 976 } 977 978 979 /* 980 * Normalize slashes in the string. 981 */ 982 void 983 normalize_slash(char *p) 984 { 985 char *f, *f0; 986 987 if (!(gopt.flags & CFM_NORMALIZE_SLASHES)) 988 return; 989 990 f0 = f = strchr(p, '/'); 991 if (f) { 992 char *t = f; 993 do { 994 /* assert(*f == '/'); */ 995 if (f == f0 && f[0] == '/' && f[1] == '/') { 996 /* copy double slash iff first */ 997 *t++ = *f++; 998 *t++ = *f++; 999 } else { 1000 /* copy a single / across */ 1001 *t++ = *f++; 1002 } 1003 1004 /* assert(f[-1] == '/'); */ 1005 /* skip past more /'s */ 1006 while (*f == '/') 1007 f++; 1008 1009 /* assert(*f != '/'); */ 1010 /* keep copying up to next / */ 1011 while (*f && *f != '/') { 1012 /* support escaped slashes '\/' */ 1013 if (f[0] == '\\' && f[1] == '/') 1014 f++; /* skip backslash */ 1015 *t++ = *f++; 1016 } 1017 1018 /* assert(*f == 0 || *f == '/'); */ 1019 1020 } while (*f); 1021 *t = '\0'; /* derived from fix by Steven Glassman */ 1022 } 1023 } 1024 1025 1026 /* 1027 * Macro-expand an option. Note that this does not 1028 * handle recursive expansions. They will go badly wrong. 1029 * If sel_p is true then old expand selectors, otherwise 1030 * don't expand selectors. 1031 */ 1032 static char * 1033 expand_op(char *opt, int sel_p) 1034 { 1035 #define EXPAND_ERROR "No space to expand \"%s\"" 1036 char expbuf[MAXPATHLEN + 1]; 1037 char nbuf[NLEN + 1]; 1038 char *ep = expbuf; 1039 char *cp = opt; 1040 char *dp; 1041 struct opt *op; 1042 char *cp_orig = opt; 1043 1044 while ((dp = strchr(cp, '$'))) { 1045 char ch; 1046 /* 1047 * First copy up to the $ 1048 */ 1049 { 1050 int len = dp - cp; 1051 1052 if (len > 0) { 1053 if (BUFSPACE(ep, len)) { 1054 /* 1055 * We use strncpy (not xstrlcpy) because 'ep' relies on its 1056 * semantics. BUFSPACE guarantees that ep can hold len. 1057 */ 1058 strncpy(ep, cp, len); 1059 ep += len; 1060 } else { 1061 plog(XLOG_ERROR, EXPAND_ERROR, opt); 1062 goto out; 1063 } 1064 } 1065 } 1066 1067 cp = dp + 1; 1068 ch = *cp++; 1069 if (ch == '$') { 1070 if (BUFSPACE(ep, 1)) { 1071 *ep++ = '$'; 1072 } else { 1073 plog(XLOG_ERROR, EXPAND_ERROR, opt); 1074 goto out; 1075 } 1076 } else if (ch == '{') { 1077 /* Expansion... */ 1078 enum { 1079 E_All, E_Dir, E_File, E_Domain, E_Host 1080 } todo; 1081 /* 1082 * Find closing brace 1083 */ 1084 char *br_p = strchr(cp, '}'); 1085 int len; 1086 1087 /* 1088 * Check we found it 1089 */ 1090 if (!br_p) { 1091 /* 1092 * Just give up 1093 */ 1094 plog(XLOG_USER, "No closing '}' in \"%s\"", opt); 1095 goto out; 1096 } 1097 len = br_p - cp; 1098 1099 /* 1100 * Figure out which part of the variable to grab. 1101 */ 1102 if (*cp == '/') { 1103 /* 1104 * Just take the last component 1105 */ 1106 todo = E_File; 1107 cp++; 1108 --len; 1109 } else if (*(br_p-1) == '/') { 1110 /* 1111 * Take all but the last component 1112 */ 1113 todo = E_Dir; 1114 --len; 1115 } else if (*cp == '.') { 1116 /* 1117 * Take domain name 1118 */ 1119 todo = E_Domain; 1120 cp++; 1121 --len; 1122 } else if (*(br_p-1) == '.') { 1123 /* 1124 * Take host name 1125 */ 1126 todo = E_Host; 1127 --len; 1128 } else { 1129 /* 1130 * Take the whole lot 1131 */ 1132 todo = E_All; 1133 } 1134 1135 /* 1136 * Truncate if too long. Since it won't 1137 * match anyway it doesn't matter that 1138 * it has been cut short. 1139 */ 1140 if (len > NLEN) 1141 len = NLEN; 1142 1143 /* 1144 * Put the string into another buffer so 1145 * we can do comparisons. 1146 * 1147 * We use strncpy here (not xstrlcpy) because the dest is meant 1148 * to be truncated and we don't want to log it as an error. The 1149 * use of the BUFSPACE macro above guarantees the safe use of 1150 * strncpy with nbuf. 1151 */ 1152 strncpy(nbuf, cp, len); 1153 nbuf[len] = '\0'; 1154 1155 /* 1156 * Advance cp 1157 */ 1158 cp = br_p + 1; 1159 1160 /* 1161 * Search the option array 1162 */ 1163 for (op = opt_fields; op->name; op++) { 1164 /* 1165 * Check for match 1166 */ 1167 if (len == op->nlen && STREQ(op->name, nbuf)) { 1168 char xbuf[NLEN + 3]; 1169 char *val; 1170 /* 1171 * Found expansion. Copy 1172 * the correct value field. 1173 */ 1174 if (!(!op->sel_p == !sel_p)) { 1175 /* 1176 * Copy the string across unexpanded 1177 */ 1178 xsnprintf(xbuf, sizeof(xbuf), "${%s%s%s}", 1179 todo == E_File ? "/" : 1180 todo == E_Domain ? "." : "", 1181 nbuf, 1182 todo == E_Dir ? "/" : 1183 todo == E_Host ? "." : ""); 1184 val = xbuf; 1185 /* 1186 * Make sure expansion doesn't 1187 * munge the value! 1188 */ 1189 todo = E_All; 1190 } else if (op->sel_p) { 1191 val = *op->sel_p; 1192 } else { 1193 val = *op->optp; 1194 } 1195 1196 if (val) { 1197 /* 1198 * Do expansion: 1199 * ${/var} means take just the last part 1200 * ${var/} means take all but the last part 1201 * ${.var} means take all but first part 1202 * ${var.} means take just the first part 1203 * ${var} means take the whole lot 1204 */ 1205 int vlen = strlen(val); 1206 char *vptr = val; 1207 switch (todo) { 1208 case E_Dir: 1209 vptr = strrchr(val, '/'); 1210 if (vptr) 1211 vlen = vptr - val; 1212 vptr = val; 1213 break; 1214 case E_File: 1215 vptr = strrchr(val, '/'); 1216 if (vptr) { 1217 vptr++; 1218 vlen = strlen(vptr); 1219 } else 1220 vptr = val; 1221 break; 1222 case E_Domain: 1223 vptr = strchr(val, '.'); 1224 if (vptr) { 1225 vptr++; 1226 vlen = strlen(vptr); 1227 } else { 1228 vptr = ""; 1229 vlen = 0; 1230 } 1231 break; 1232 case E_Host: 1233 vptr = strchr(val, '.'); 1234 if (vptr) 1235 vlen = vptr - val; 1236 vptr = val; 1237 break; 1238 case E_All: 1239 break; 1240 } 1241 1242 if (BUFSPACE(ep, vlen+1)) { 1243 /* 1244 * Don't call xstrlcpy() to truncate a string here. It causes 1245 * spurious xstrlcpy() syslog() errors. Use memcpy() and 1246 * explicitly terminate the string. 1247 */ 1248 memcpy(ep, vptr, vlen+1); 1249 ep += vlen; 1250 *ep = '\0'; 1251 } else { 1252 plog(XLOG_ERROR, EXPAND_ERROR, opt); 1253 goto out; 1254 } 1255 } 1256 /* 1257 * Done with this variable 1258 */ 1259 break; 1260 } 1261 } 1262 1263 /* 1264 * Check that the search was successful 1265 */ 1266 if (!op->name) { 1267 /* 1268 * If it wasn't then scan the 1269 * environment for that name 1270 * and use any value found 1271 */ 1272 char *env = getenv(nbuf); 1273 1274 if (env) { 1275 int vlen = strlen(env); 1276 1277 if (BUFSPACE(ep, vlen+1)) { 1278 xstrlcpy(ep, env, vlen+1); 1279 ep += vlen; 1280 } else { 1281 plog(XLOG_ERROR, EXPAND_ERROR, opt); 1282 goto out; 1283 } 1284 if (amuDebug(D_STR)) 1285 plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env); 1286 } else { 1287 plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf); 1288 } 1289 } 1290 } else { 1291 /* 1292 * Error, error 1293 */ 1294 plog(XLOG_USER, "Unknown $ sequence in \"%s\"", opt); 1295 } 1296 } 1297 1298 out: 1299 /* 1300 * Handle common case - no expansion 1301 */ 1302 if (cp == opt) { 1303 opt = strdup(cp); 1304 } else { 1305 /* 1306 * Finish off the expansion 1307 */ 1308 int vlen = strlen(cp); 1309 if (BUFSPACE(ep, vlen+1)) { 1310 xstrlcpy(ep, cp, vlen+1); 1311 /* ep += vlen; */ 1312 } else { 1313 plog(XLOG_ERROR, EXPAND_ERROR, opt); 1314 } 1315 1316 /* 1317 * Save the expansion 1318 */ 1319 opt = strdup(expbuf); 1320 } 1321 1322 normalize_slash(opt); 1323 1324 if (amuDebug(D_STR)) { 1325 plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig); 1326 plog(XLOG_DEBUG, "......... is \"%s\"", opt); 1327 } 1328 return opt; 1329 } 1330 1331 1332 /* 1333 * Wrapper for expand_op 1334 */ 1335 static void 1336 expand_opts(opt_apply *p, int sel_p) 1337 { 1338 if (*p->opt) { 1339 *p->opt = expand_op(*p->opt, sel_p); 1340 } else if (p->val) { 1341 /* 1342 * Do double expansion, remembering 1343 * to free the string from the first 1344 * expansion... 1345 */ 1346 char *s = expand_op(p->val, TRUE); 1347 *p->opt = expand_op(s, sel_p); 1348 XFREE(s); 1349 } 1350 } 1351 1352 1353 /* 1354 * Apply a function to a list of options 1355 */ 1356 static void 1357 apply_opts(void (*op) (opt_apply *, int), opt_apply ppp[], int b) 1358 { 1359 opt_apply *pp; 1360 1361 for (pp = ppp; pp->opt; pp++) 1362 (*op) (pp, b); 1363 } 1364 1365 1366 /* 1367 * Free the option table 1368 */ 1369 void 1370 free_opts(am_opts *fo) 1371 { 1372 /* 1373 * Copy in the structure we are playing with 1374 */ 1375 fs_static = *fo; 1376 1377 /* 1378 * Free previously allocated memory 1379 */ 1380 apply_opts(free_op, to_free, FALSE); 1381 } 1382 1383 1384 /* 1385 * Expand selectors (variables that cannot be assigned to or overridden) 1386 */ 1387 char * 1388 expand_selectors(char *key) 1389 { 1390 return expand_op(key, TRUE); 1391 } 1392 1393 1394 /* 1395 * Expand options (i.e. non-selectors, see above for definition) 1396 */ 1397 static inline char * 1398 expand_options(char *key) 1399 { 1400 return expand_op(key, FALSE); 1401 } 1402 1403 1404 /* 1405 * Remove trailing /'s from a string 1406 * unless the string is a single / (Steven Glassman) 1407 * or unless it is two slashes // (Kevin D. Bond) 1408 * or unless amd.conf says not to touch slashes. 1409 */ 1410 void 1411 deslashify(char *s) 1412 { 1413 if (!(gopt.flags & CFM_NORMALIZE_SLASHES)) 1414 return; 1415 1416 if (s && *s) { 1417 char *sl = s + strlen(s); 1418 1419 while (*--sl == '/' && sl > s) 1420 *sl = '\0'; 1421 } 1422 } 1423 1424 1425 int 1426 eval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path, char *key, char *map) 1427 { 1428 int ok = TRUE; 1429 1430 free_opts(fo); 1431 1432 /* 1433 * Clear out the option table 1434 */ 1435 memset((voidp) &fs_static, 0, sizeof(fs_static)); 1436 memset((voidp) vars, 0, sizeof(vars)); 1437 memset((voidp) fo, 0, sizeof(*fo)); 1438 1439 /* set hostname */ 1440 opt_host = (char *) am_get_hostname(); 1441 1442 /* 1443 * Set key, map & path before expansion 1444 */ 1445 opt_key = key; 1446 opt_map = map; 1447 opt_path = path; 1448 1449 opt_dkey = strchr(key, '.'); 1450 if (!opt_dkey) { 1451 opt_dkey = NullStr; 1452 opt_keyd = key; 1453 } else { 1454 opt_keyd = strnsave(key, opt_dkey - key); 1455 opt_dkey++; 1456 if (*opt_dkey == '\0') /* check for 'host.' */ 1457 opt_dkey = NullStr; 1458 } 1459 1460 /* 1461 * Expand global options 1462 */ 1463 fs_static.fs_glob = expand_selectors(g_opts); 1464 1465 /* 1466 * Expand local options 1467 */ 1468 fs_static.fs_local = expand_selectors(opts); 1469 1470 /* break global options into fs_static fields */ 1471 if ((ok = split_opts(fs_static.fs_glob, key))) { 1472 dlog("global split_opts ok"); 1473 /* 1474 * evaluate local selectors 1475 */ 1476 if ((ok = eval_selectors(fs_static.fs_local, key))) { 1477 dlog("local eval_selectors ok"); 1478 /* if the local selectors matched, then do the local overrides */ 1479 ok = split_opts(fs_static.fs_local, key); 1480 if (ok) 1481 dlog("local split_opts ok"); 1482 } 1483 } 1484 1485 /* 1486 * Normalize remote host name. 1487 * 1. Expand variables 1488 * 2. Normalize relative to host tables 1489 * 3. Strip local domains from the remote host 1490 * name before using it in other expansions. 1491 * This makes mount point names and other things 1492 * much shorter, while allowing cross domain 1493 * sharing of mount maps. 1494 */ 1495 apply_opts(expand_opts, rhost_expansion, FALSE); 1496 if (ok && fs_static.opt_rhost && *fs_static.opt_rhost) 1497 host_normalize(&fs_static.opt_rhost); 1498 1499 /* 1500 * Macro expand the options. 1501 * Do this regardless of whether we are accepting 1502 * this mount - otherwise nasty things happen 1503 * with memory allocation. 1504 */ 1505 apply_opts(expand_opts, expansions, FALSE); 1506 1507 /* 1508 * Strip trailing slashes from local pathname... 1509 */ 1510 deslashify(fs_static.opt_fs); 1511 1512 /* 1513 * ok... copy the data back out. 1514 */ 1515 *fo = fs_static; 1516 1517 /* 1518 * Clear defined options 1519 */ 1520 if (opt_keyd != key && opt_keyd != nullstr) 1521 XFREE(opt_keyd); 1522 opt_keyd = nullstr; 1523 opt_dkey = NullStr; 1524 opt_key = opt_map = opt_path = nullstr; 1525 1526 return ok; 1527 } 1528