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