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