1/* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22/* 23 * ns_files.c 24 * 25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 29/* 30 * Portions Copyright 2007-2011 Apple Inc. 31 */ 32 33#pragma ident "@(#)ns_files.c 1.49 05/06/08 SMI" 34 35#include <stdio.h> 36#include <stdlib.h> 37#include <syslog.h> 38#include <string.h> 39#include <ctype.h> 40#include <sys/stat.h> 41#include <assert.h> 42#include <errno.h> 43#include <fcntl.h> 44#include <unistd.h> 45#include <sys/types.h> 46#include <sys/wait.h> 47#include "autofs.h" 48#include "automount.h" 49 50static int read_execout(const char *key, char **lp, char *fname, char *line, 51 int linesz); 52static int read_execoutreaddir(struct dir_entry **list, char *fname, 53 char *linebuf, int linebufsz); 54static FILE *file_open(const char *, char *, char **, char ***); 55 56/* 57 * Initialize the stack 58 */ 59void 60init_files(char **stack, char ***stkptr) 61{ 62 /* 63 * The call is bogus for automountd since the stack is 64 * is more appropriately initialized in the thread-private 65 * routines 66 */ 67 if (stack == NULL && stkptr == NULL) 68 return; 69 (void) stack_op(INIT, NULL, stack, stkptr); 70} 71 72int 73getmapent_files(key, mapname, ml, stack, stkptr, iswildcard, isrestricted) 74 const char *key; 75 const char *mapname; 76 struct mapline *ml; 77 char **stack, ***stkptr; 78 bool_t *iswildcard; 79 bool_t isrestricted; 80{ 81 int nserr; 82 FILE *fp; 83 char word[MAXPATHLEN+1], wordq[MAXPATHLEN+1]; 84 char linebuf[LINESZ], lineqbuf[LINESZ]; 85 char *lp, *lq; 86 struct stat stbuf; 87 char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ 88 int syntaxok = 1; 89 90 if (iswildcard) 91 *iswildcard = FALSE; 92 if ((fp = file_open(mapname, fname, stack, stkptr)) == NULL) { 93 nserr = __NSW_UNAVAIL; 94 goto done; 95 } 96 97 if (stat(fname, &stbuf) < 0) { 98 nserr = __NSW_UNAVAIL; 99 goto done; 100 } 101 102 /* 103 * If the file has its execute bit on then 104 * assume it's an executable map. 105 * Execute it and pass the key as an argument. 106 * Expect to get a map entry on the stdout. 107 * Ignore the "x" bit on restricted maps. 108 */ 109 if (!isrestricted && (stbuf.st_mode & S_IXUSR)) { 110 int rc; 111 112 if (trace > 1) { 113 trace_prt(1, 114 "\tExecutable map: map=%s key=%s\n", 115 fname, key); 116 } 117 118 rc = read_execout(key, &lp, fname, ml->linebuf, LINESZ); 119 120 if (rc != 0) { 121 nserr = __NSW_UNAVAIL; 122 goto done; 123 } 124 125 if (lp == NULL || strlen(ml->linebuf) == 0) { 126 nserr = __NSW_NOTFOUND; 127 goto done; 128 } 129 130 unquote(ml->linebuf, ml->lineqbuf); 131 nserr = __NSW_SUCCESS; 132 goto done; 133 } 134 135 136 /* 137 * It's just a normal map file. 138 * Search for the entry with the required key. 139 */ 140 for (;;) { 141 lp = get_line(fp, fname, linebuf, sizeof (linebuf)); 142 if (lp == NULL) { 143 nserr = __NSW_NOTFOUND; 144 goto done; 145 } 146 if (verbose && syntaxok && isspace(*(uchar_t *)lp)) { 147 syntaxok = 0; 148 syslog(LOG_ERR, 149 "leading space in map entry \"%s\" in %s", 150 lp, mapname); 151 } 152 lq = lineqbuf; 153 unquote(lp, lq); 154 if ((getword(word, wordq, &lp, &lq, ' ', sizeof (word)) 155 == -1) || (word[0] == '\0')) 156 continue; 157 if (strcmp(word, key) == 0) 158 break; 159 if (word[0] == '*' && word[1] == '\0') { 160 if (iswildcard) 161 *iswildcard = TRUE; 162 break; 163 } 164 if (word[0] == '+') { 165 nserr = getmapent(key, word+1, ml, stack, stkptr, 166 iswildcard, isrestricted); 167 if (nserr == __NSW_SUCCESS) 168 goto done; 169 continue; 170 } 171 172 /* 173 * sanity check each map entry key against 174 * the lookup key as the map is searched. 175 */ 176 if (verbose && syntaxok) { /* sanity check entry */ 177 if (*key == '/') { 178 if (*word != '/') { 179 syntaxok = 0; 180 syslog(LOG_ERR, 181 "bad key \"%s\" in direct map %s\n", 182 word, mapname); 183 } 184 } else { 185 if (strchr(word, '/')) { 186 syntaxok = 0; 187 syslog(LOG_ERR, 188 "bad key \"%s\" in indirect map %s\n", 189 word, mapname); 190 } 191 } 192 } 193 } 194 195 (void) strcpy(ml->linebuf, lp); 196 (void) strcpy(ml->lineqbuf, lq); 197 nserr = __NSW_SUCCESS; 198done: 199 if (fp) { 200 (void) stack_op(POP, (char *)NULL, stack, stkptr); 201 (void) fclose(fp); 202 } 203 204 205 return (nserr); 206} 207 208int 209getmapkeys_files(mapname, list, error, cache_time, stack, stkptr) 210 char *mapname; 211 struct dir_entry **list; 212 int *error; 213 int *cache_time; 214 char **stack, ***stkptr; 215{ 216 FILE *fp = NULL; 217 char word[MAXPATHLEN+1], wordq[MAXPATHLEN+1]; 218 char linebuf[LINESZ], lineqbuf[LINESZ]; 219 char *lp, *lq; 220 struct stat stbuf; 221 char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ 222 int syntaxok = 1; 223 int nserr; 224 int err; 225 struct dir_entry *last = NULL; 226 227 if (trace > 1) 228 trace_prt(1, "getmapkeys_files %s\n", mapname); 229 230 *cache_time = RDDIR_CACHE_TIME; 231 if ((fp = file_open(mapname, fname, stack, stkptr)) == NULL) { 232 *error = ENOENT; 233 nserr = __NSW_UNAVAIL; 234 goto done; 235 } 236 if (fseek(fp, 0L, SEEK_SET) == -1) { 237 *error = ENOENT; 238 nserr = __NSW_UNAVAIL; 239 goto done; 240 } 241 242 if (stat(fname, &stbuf) < 0) { 243 *error = ENOENT; 244 nserr = __NSW_UNAVAIL; 245 goto done; 246 } 247 248 /* 249 * If the file has its execute bit on then 250 * assume it's an executable map. 251 * Execute it and pass it no argument; if it returns an error 252 * (perhaps because it was written for versions of the automounter 253 * that didn't support listing executable maps, and thus doesn't 254 * properly handle not being passed a key), just return no 255 * entries, otherwise expect to get a newline-separated list 256 * of names on the stdout. 257 */ 258 if (stbuf.st_mode & S_IXUSR) { 259 int rc; 260 261 if (trace > 1) { 262 trace_prt(1, 263 "\tExecutable map: map=%s\n", 264 fname); 265 } 266 267 rc = read_execoutreaddir(list, fname, linebuf, LINESZ); 268 269 if (rc != 0) { 270 *error = rc; 271 nserr = __NSW_UNAVAIL; 272 goto done; 273 } 274 275 *error = 0; 276 nserr = __NSW_SUCCESS; 277 goto done; 278 } 279 /* 280 * It's just a normal map file. 281 * List entries one line at a time. 282 */ 283 for (;;) { 284 lp = get_line(fp, fname, linebuf, sizeof (linebuf)); 285 if (lp == NULL) { 286 nserr = __NSW_SUCCESS; 287 goto done; 288 } 289 if (syntaxok && isspace(*(uchar_t *)lp)) { 290 syntaxok = 0; 291 syslog(LOG_ERR, 292 "leading space in map entry \"%s\" in %s", 293 lp, mapname); 294 } 295 lq = lineqbuf; 296 unquote(lp, lq); 297 if ((getword(word, wordq, &lp, &lq, ' ', MAXFILENAMELEN) 298 == -1) || (word[0] == '\0')) 299 continue; 300 /* 301 * Wildcard entries should be ignored and this should be 302 * the last entry read to corroborate the search through 303 * files, i.e., search for key until a wildcard is reached. 304 */ 305 if (word[0] == '*' && word[1] == '\0') 306 break; 307 if (word[0] == '+') { 308 /* 309 * Name switch here 310 */ 311 getmapkeys(word+1, list, error, cache_time, 312 stack, stkptr); 313 /* 314 * the list may have been updated, therefore 315 * our 'last' may no longer be valid 316 */ 317 last = NULL; 318 continue; 319 } 320 321 /* 322 * A return value of -1 means the name is invalid, 323 * so nothing was added to the list. 324 */ 325 err = add_dir_entry(word, lp, lq, list, &last); 326 if (err != -1) { 327 if (err != 0) { 328 /* 329 * XXX - if we've gotten any entries, *error 330 * will be zeroed out. In addition, nobody 331 * checks our return value in any case. 332 */ 333 *error = err; 334 nserr = __NSW_UNAVAIL; 335 goto done; 336 } 337 assert(last != NULL); 338 } 339 } 340 341 nserr = __NSW_SUCCESS; 342done: 343 if (fp) { 344 (void) stack_op(POP, (char *)NULL, stack, stkptr); 345 (void) fclose(fp); 346 } 347 348 if (*list != NULL) { 349 /* 350 * list of entries found 351 */ 352 *error = 0; 353 } 354 return (nserr); 355} 356 357int 358loadmaster_files(mastermap, defopts, stack, stkptr) 359 char *mastermap; 360 char *defopts; 361 char **stack, ***stkptr; 362{ 363 FILE *fp; 364 int done = 0; 365 char *line, *dir, *map, *opts; 366 char linebuf[LINESZ]; 367 char lineq[LINESZ]; 368 char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ 369 370 371 if ((fp = file_open(mastermap, fname, stack, stkptr)) == NULL) 372 return (__NSW_UNAVAIL); 373 374 while ((line = get_line(fp, fname, linebuf, 375 sizeof (linebuf))) != NULL) { 376 unquote(line, lineq); 377 switch (macro_expand("", line, lineq, LINESZ)) { 378 379 case MEXPAND_OK: 380 break; 381 382 case MEXPAND_LINE_TOO_LONG: 383 syslog(LOG_ERR, 384 "map %s: line too long (max %d chars)", 385 mastermap, LINESZ - 1); 386 continue; 387 388 case MEXPAND_VARNAME_TOO_LONG: 389 syslog(LOG_ERR, 390 "map %s: variable name too long", 391 mastermap); 392 continue; 393 } 394 dir = line; 395 while (*dir && isspace(*dir)) 396 dir++; 397 if (*dir == '\0') 398 continue; 399 map = dir; 400 401 while (*map && !isspace(*map)) map++; 402 if (*map) 403 *map++ = '\0'; 404 405 if (*dir == '+') { 406 opts = map; 407 while (*opts && isspace(*opts)) 408 opts++; 409 if (*opts != '-') 410 opts = defopts; 411 else 412 opts++; 413 /* 414 * Check for no embedded blanks. 415 */ 416 if (strcspn(opts, " \t") == strlen(opts)) { 417 dir++; 418 (void) loadmaster_map(dir, opts, stack, stkptr); 419 } else { 420 continue; 421 } 422 423 } else { 424 while (*map && isspace(*map)) 425 map++; 426 if (*map == '\0') 427 continue; 428 opts = map; 429 while (*opts && !isspace(*opts)) 430 opts++; 431 if (*opts) { 432 *opts++ = '\0'; 433 while (*opts && isspace(*opts)) 434 opts++; 435 } 436 if (*opts != '-') 437 opts = defopts; 438 else 439 opts++; 440 /* 441 * Check for no embedded blanks. 442 */ 443 if (strcspn(opts, " \t") == strlen(opts)) { 444 dirinit(dir, map, opts, 0, stack, stkptr); 445 } else { 446 continue; 447 } 448 } 449 done++; 450 } 451 452 (void) stack_op(POP, (char *)NULL, stack, stkptr); 453 (void) fclose(fp); 454 455 return (done ? __NSW_SUCCESS : __NSW_NOTFOUND); 456} 457 458int 459loaddirect_files(map, local_map, opts, stack, stkptr) 460 char *map, *local_map, *opts; 461 char **stack, ***stkptr; 462{ 463 FILE *fp; 464 int done = 0; 465 char *line, *p1, *p2; 466 char linebuf[LINESZ]; 467 char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ 468 469 if ((fp = file_open(map, fname, stack, stkptr)) == NULL) 470 return (__NSW_UNAVAIL); 471 472 while ((line = get_line(fp, fname, linebuf, 473 sizeof (linebuf))) != NULL) { 474 p1 = line; 475 while (*p1 && isspace(*p1)) 476 p1++; 477 if (*p1 == '\0') 478 continue; 479 p2 = p1; 480 while (*p2 && !isspace(*p2)) 481 p2++; 482 *p2 = '\0'; 483 if (*p1 == '+') { 484 p1++; 485 (void) loaddirect_map(p1, local_map, opts, stack, 486 stkptr); 487 } else { 488 dirinit(p1, local_map, opts, 1, stack, stkptr); 489 } 490 done++; 491 } 492 493 (void) stack_op(POP, (char *)NULL, stack, stkptr); 494 (void) fclose(fp); 495 496 return (done ? __NSW_SUCCESS : __NSW_NOTFOUND); 497} 498 499/* 500 * This procedure opens the file and pushes it onto the 501 * the stack. Only if a file is opened successfully, is 502 * it pushed onto the stack 503 */ 504static FILE * 505file_open(map, fname, stack, stkptr) 506 const char *map; 507 char *fname; 508 char **stack, ***stkptr; 509{ 510 FILE *fp; 511 512 if (*map != '/') { 513 /* prepend an "/etc" */ 514 (void) strlcpy(fname, "/etc/", MAXFILENAMELEN); 515 (void) strlcat(fname, map, MAXFILENAMELEN); 516 } else 517 (void) strlcpy(fname, map, MAXFILENAMELEN); 518 519 fp = fopen(fname, "r"); 520 521 if (fp != NULL) { 522 if (!stack_op(PUSH, fname, stack, stkptr)) { 523 (void) fclose(fp); 524 return (NULL); 525 } 526 } 527 return (fp); 528} 529 530/* 531 * reimplemnted to be MT-HOT. 532 */ 533int 534stack_op(op, name, stack, stkptr) 535 int op; 536 char *name; 537 char **stack, ***stkptr; 538{ 539 char **ptr = NULL; 540 char **stk_top = &stack[STACKSIZ - 1]; 541 542 /* 543 * the stackptr points to the next empty slot 544 * for PUSH: put the element and increment stkptr 545 * for POP: decrement stkptr and free 546 */ 547 548 switch (op) { 549 case INIT: 550 for (ptr = stack; ptr != stk_top; ptr++) 551 *ptr = (char *)NULL; 552 *stkptr = stack; 553 return (1); 554 case ERASE: 555 for (ptr = stack; ptr != stk_top; ptr++) 556 if (*ptr) { 557 if (trace > 1) 558 trace_prt(1, " ERASE %s\n", *ptr); 559 free (*ptr); 560 *ptr = (char *)NULL; 561 } 562 *stkptr = stack; 563 return (1); 564 case PUSH: 565 if (*stkptr == stk_top) 566 return (0); 567 for (ptr = stack; ptr != *stkptr; ptr++) 568 if (*ptr && (strcmp(*ptr, name) == 0)) { 569 return (0); 570 } 571 if (trace > 1) 572 trace_prt(1, " PUSH %s\n", name); 573 if ((**stkptr = strdup(name)) == NULL) { 574 syslog(LOG_ERR, "stack_op: Memory alloc failed : %m"); 575 return (0); 576 } 577 (*stkptr)++; 578 return (1); 579 case POP: 580 if (*stkptr != stack) 581 (*stkptr)--; 582 else 583 syslog(LOG_ERR, "Attempt to pop empty stack\n"); 584 585 if (*stkptr && **stkptr) { 586 if (trace > 1) 587 trace_prt(1, " POP %s\n", **stkptr); 588 free (**stkptr); 589 **stkptr = (char *)NULL; 590 } 591 return (1); 592 default: 593 return (0); 594 } 595} 596 597#define READ_EXECOUT_ARGS 3 598 599/* 600 * read_execout(char *key, char **lp, char *fname, char *line, int linesz) 601 * A simpler, multithreaded implementation of popen(). Used due to 602 * non multithreaded implementation of popen() (it calls vfork()) and a 603 * significant bug in execl(). 604 * Returns 0 on OK or -1 on error. 605 */ 606static int 607read_execout(const char *key, char **lp, char *fname, char *line, int linesz) 608{ 609 int p[2]; 610 int status = 0; 611 int child_pid; 612 char *args[READ_EXECOUT_ARGS]; 613 FILE *fp0; 614 615 if (pipe(p) < 0) { 616 syslog(LOG_ERR, "read_execout: Cannot create pipe"); 617 return (-1); 618 } 619 620 /* setup args for execv */ 621 if (((args[0] = strdup(fname)) == NULL) || 622 ((args[1] = strdup(key)) == NULL)) { 623 if (args[0] != NULL) 624 free(args[0]); 625 syslog(LOG_ERR, "read_execout: Memory allocation failed"); 626 return (-1); 627 } 628 args[2] = NULL; 629 630 if (trace > 3) 631 trace_prt(1, "\tread_execout: forking .....\n"); 632 633 switch ((child_pid = fork1())) { 634 case -1: 635 syslog(LOG_ERR, "read_execout: Cannot fork"); 636 return (-1); 637 case 0: 638 /* 639 * Child 640 */ 641 close(p[0]); 642 close(1); 643 if (fcntl(p[1], F_DUPFD, 1) != 1) { 644 syslog(LOG_ERR, 645 "read_execout: dup of stdout failed"); 646 _exit(-1); 647 } 648 close(p[1]); 649 execv(fname, &args[0]); 650 _exit(-1); 651 default: 652 /* 653 * Parent 654 */ 655 close(p[1]); 656 657 /* 658 * wait for child to complete. Note we read after the 659 * child exits to guarantee a full pipe. 660 */ 661 while (waitpid(child_pid, &status, 0) < 0) { 662 /* if waitpid fails with EINTR, restart */ 663 if (errno != EINTR) { 664 status = -1; 665 break; 666 } 667 } 668 if (status != -1) { 669 if ((fp0 = fdopen(p[0], "r")) != NULL) { 670 *lp = get_line(fp0, fname, line, linesz); 671 fclose(fp0); 672 } else { 673 close(p[0]); 674 status = -1; 675 } 676 } else { 677 close(p[0]); 678 } 679 680 /* free args */ 681 free(args[0]); 682 free(args[1]); 683 684 if (trace > 3) 685 trace_prt(1, "\tread_execout: map=%s key=%s line=%s\n", 686 fname, key, line); 687 688 return (status); 689 } 690} 691 692#define READ_EXECOUTREADDIR_ARGS 2 693 694/* 695 * read_execoutreaddir(struct dir_entry **list, char *fname, char *linebuf, int linebufsz) 696 * A simpler, multithreaded implementation of popen(). Used due to 697 * non multithreaded implementation of popen() (it calls vfork()) and a 698 * significant bug in execl(). 699 * Returns 0 on OK or -1 on error. 700 */ 701static int 702read_execoutreaddir(struct dir_entry **list, char *fname, char *linebuf, int linebufsz) 703{ 704 int error = 0; 705 int p[2]; 706 int status = 0; 707 int child_pid; 708 char *args[READ_EXECOUT_ARGS]; 709 FILE *fp0; 710 size_t linelen; 711 struct dir_entry *last = NULL; 712 713 if (pipe(p) < 0) { 714 error = errno; 715 syslog(LOG_ERR, "read_execoutreaddir: Cannot create pipe: %m"); 716 return (error); 717 } 718 719 /* setup args for execv */ 720 if ((args[0] = strdup(fname)) == NULL) { 721 error = errno; 722 syslog(LOG_ERR, "read_execoutreaddir: Memory allocation failed"); 723 return (error); 724 } 725 args[1] = NULL; 726 727 if (trace > 3) 728 trace_prt(1, "\tread_execoutreaddir: forking .....\n"); 729 730 switch ((child_pid = fork1())) { 731 case -1: 732 error = errno; 733 syslog(LOG_ERR, "read_execoutreaddir: Cannot fork"); 734 break; 735 case 0: 736 /* 737 * Child 738 */ 739 close(p[0]); 740 close(1); 741 if (fcntl(p[1], F_DUPFD, 1) != 1) { 742 syslog(LOG_ERR, 743 "read_execoutreaddir: dup of stdout failed"); 744 _exit(-1); 745 } 746 close(p[1]); 747 execv(fname, &args[0]); 748 _exit(-1); 749 break; 750 default: 751 /* 752 * Parent 753 */ 754 close(p[1]); 755 756 if ((fp0 = fdopen(p[0], "r")) != NULL) { 757 /* 758 * Read lines from the pipe. 759 */ 760 while (fgets(linebuf, linebufsz, fp0) != NULL) { 761 linelen = strlen(linebuf); 762 if (linelen == 0) 763 continue; /* empty line */ 764 if (linebuf[linelen - 1] != '\n') { 765 syslog(LOG_ERR, 766 "read_execoutreaddir: Line too long\n"); 767 error = ENAMETOOLONG; 768 break; 769 } 770 linebuf[linelen - 1] = '\0'; /* remove NL */ 771 /* 772 * A return value of -1 means the name is 773 * invalid, so nothing was added to the list. 774 */ 775 error = add_dir_entry(linebuf, NULL, NULL, list, 776 &last); 777 if (error != -1) { 778 if (error != 0) 779 break; 780 assert(last != NULL); 781 } 782 } 783 if (ferror(fp0)) 784 error = errno; 785 fclose(fp0); 786 } else { 787 error = errno; 788 close(p[0]); 789 } 790 791 /* 792 * wait for child to complete. 793 */ 794 while (waitpid(child_pid, &status, 0) < 0) { 795 /* if waitpid fails with EINTR, restart */ 796 if (errno != EINTR) { 797 status = -1; 798 break; 799 } 800 } 801 802 /* free args */ 803 free(args[0]); 804 805 if (trace > 3) 806 trace_prt(1, "\tread_execoutreaddir: map=%s\n", 807 fname); 808 break; 809 } 810 return (error); 811} 812