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 * Copyright (c) 1988-1999 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <ctype.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <unistd.h> 33 #include <locale.h> 34 #include <syslog.h> 35 #include <errno.h> 36 #include <string.h> 37 #include <stdarg.h> 38 #include <dirent.h> 39 #include <thread.h> 40 #include <sys/param.h> 41 #include <sys/time.h> 42 #include <sys/vfs.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <sys/mnttab.h> 46 #include <sys/mntent.h> 47 #include <sys/mount.h> 48 #include <sys/signal.h> 49 #include <sys/utsname.h> 50 #include <sys/systeminfo.h> 51 #include <sys/tiuser.h> 52 #include <sys/utsname.h> 53 #include <rpc/rpc.h> 54 #include <rpcsvc/nfs_prot.h> 55 #include <assert.h> 56 #include "automount.h" 57 58 static char *check_hier(char *); 59 static int natisa(char *, size_t); 60 61 struct mntlist *current_mounts; 62 63 static bool_t nodirect_map = FALSE; 64 65 void 66 dirinit(char *mntpnt, char *map, char *opts, int direct, char **stack, 67 char ***stkptr) 68 { 69 struct autodir *dir; 70 char *p; 71 72 if (strcmp(map, "-null") == 0) { 73 if (strcmp(mntpnt, "/-") == 0) 74 nodirect_map = TRUE; 75 goto enter; 76 } 77 78 p = mntpnt + (strlen(mntpnt) - 1); 79 if (*p == '/') 80 *p = '\0'; /* trim trailing / */ 81 if (*mntpnt != '/') { 82 pr_msg("dir %s must start with '/'", mntpnt); 83 return; 84 } 85 if (p = check_hier(mntpnt)) { 86 pr_msg("hierarchical mountpoint: %s and %s", 87 p, mntpnt); 88 return; 89 } 90 91 /* 92 * If it's a direct map then call dirinit 93 * for every map entry. 94 */ 95 if ((strcmp(mntpnt, "/-") == 0) && !(nodirect_map)) { 96 (void) loaddirect_map(map, map, opts, stack, stkptr); 97 return; 98 } 99 100 enter: 101 dir = (struct autodir *)malloc(sizeof (*dir)); 102 if (dir == NULL) 103 goto alloc_failed; 104 dir->dir_name = strdup(mntpnt); 105 if (dir->dir_name == NULL) 106 goto alloc_failed; 107 dir->dir_map = strdup(map); 108 if (dir->dir_map == NULL) 109 goto alloc_failed; 110 dir->dir_opts = strdup(opts); 111 if (dir->dir_opts == NULL) 112 goto alloc_failed; 113 dir->dir_direct = direct; 114 dir->dir_remount = 0; 115 dir->dir_next = NULL; 116 117 /* 118 * Append to dir chain 119 */ 120 if (dir_head == NULL) 121 dir_head = dir; 122 else 123 dir_tail->dir_next = dir; 124 125 dir->dir_prev = dir_tail; 126 dir_tail = dir; 127 128 return; 129 130 alloc_failed: 131 if (dir != NULL) { 132 if (dir->dir_opts) 133 free(dir->dir_opts); 134 if (dir->dir_map) 135 free(dir->dir_map); 136 if (dir->dir_name) 137 free(dir->dir_name); 138 free(dir); 139 } 140 pr_msg("dirinit: memory allocation failed"); 141 } 142 143 /* 144 * Check whether the mount point is a 145 * subdirectory or a parent directory 146 * of any previously mounted automount 147 * mount point. 148 */ 149 static char * 150 check_hier(mntpnt) 151 char *mntpnt; 152 { 153 register struct autodir *dir; 154 register char *p, *q; 155 156 for (dir = dir_head; dir; dir = dir->dir_next) { 157 p = dir->dir_name; 158 q = mntpnt; 159 for (; *p == *q; p++, q++) 160 if (*p == '\0') 161 break; 162 if (*p == '/' && *q == '\0') 163 return (dir->dir_name); 164 if (*p == '\0' && *q == '/') 165 return (dir->dir_name); 166 if (*p == '\0' && *q == '\0') 167 return (NULL); 168 } 169 return (NULL); /* it's not a subdir or parent */ 170 } 171 172 /* 173 * Gets the next token from the string "p" and copies 174 * it into "w". Both "wq" and "w" are quote vectors 175 * for "w" and "p". Delim is the character to be used 176 * as a delimiter for the scan. A space means "whitespace". 177 * The call to getword must provide buffers w and wq of size at 178 * least wordsz. getword() will pass strings of maximum length 179 * (wordsz-1), since it needs to null terminate the string. 180 * Returns 0 on ok and -1 on error. 181 */ 182 int 183 getword(char *w, char *wq, char **p, char **pq, char delim, int wordsz) 184 { 185 char *tmp = w; 186 char *tmpq = wq; 187 int count = wordsz; 188 189 if (wordsz <= 0) { 190 if (verbose) 191 syslog(LOG_ERR, 192 "getword: input word size %d must be > 0", wordsz); 193 return (-1); 194 } 195 196 while ((delim == ' ' ? isspace(**p) : **p == delim) && **pq == ' ') 197 (*p)++, (*pq)++; 198 199 while (**p && 200 !((delim == ' ' ? isspace(**p) : **p == delim) && 201 **pq == ' ')) { 202 if (--count <= 0) { 203 *tmp = '\0'; 204 *tmpq = '\0'; 205 syslog(LOG_ERR, 206 "maximum word length (%d) exceeded", wordsz); 207 return (-1); 208 } 209 *w++ = *(*p)++; 210 *wq++ = *(*pq)++; 211 } 212 *w = '\0'; 213 *wq = '\0'; 214 215 return (0); 216 } 217 218 /* 219 * get_line attempts to get a line from the map, upto LINESZ. A line in 220 * the map is a concatenation of lines if the continuation symbol '\' 221 * is used at the end of the line. Returns line on success, a NULL on 222 * EOF, and an empty string on lines > linesz. 223 */ 224 char * 225 get_line(FILE *fp, char *map, char *line, int linesz) 226 { 227 register char *p = line; 228 register int len; 229 int excess = 0; 230 231 *p = '\0'; 232 233 for (;;) { 234 if (fgets(p, linesz - (p-line), fp) == NULL) { 235 return (*line ? line : NULL); /* EOF */ 236 } 237 238 len = strlen(line); 239 if (len <= 0) { 240 p = line; 241 continue; 242 } 243 p = &line[len - 1]; 244 245 /* 246 * Is input line too long? 247 */ 248 if (*p != '\n') { 249 excess = 1; 250 /* 251 * Perhaps last char read was '\'. Reinsert it 252 * into the stream to ease the parsing when we 253 * read the rest of the line to discard. 254 */ 255 (void) ungetc(*p, fp); 256 break; 257 } 258 trim: 259 /* trim trailing white space */ 260 while (p >= line && isspace(*(uchar_t *)p)) 261 *p-- = '\0'; 262 if (p < line) { /* empty line */ 263 p = line; 264 continue; 265 } 266 267 if (*p == '\\') { /* continuation */ 268 *p = '\0'; 269 continue; 270 } 271 272 /* 273 * Ignore comments. Comments start with '#' 274 * which must be preceded by a whitespace, unless 275 * if '#' is the first character in the line. 276 */ 277 p = line; 278 while (p = strchr(p, '#')) { 279 if (p == line || isspace(*(p-1))) { 280 *p-- = '\0'; 281 goto trim; 282 } 283 p++; 284 } 285 break; 286 } 287 if (excess) { 288 int c; 289 290 /* 291 * discard rest of line and return an empty string. 292 * done to set the stream to the correct place when 293 * we are done with this line. 294 */ 295 while ((c = getc(fp)) != EOF) { 296 *p = c; 297 if (*p == '\n') /* end of the long line */ 298 break; 299 else if (*p == '\\') { /* continuation */ 300 if (getc(fp) == EOF) /* ignore next char */ 301 break; 302 } 303 } 304 syslog(LOG_ERR, 305 "map %s: line too long (max %d chars)", 306 map, linesz-1); 307 *line = '\0'; 308 } 309 310 return (line); 311 } 312 313 /* 314 * Gets the retry=n entry from opts. 315 * Returns 0 if retry=n is not present in option string, 316 * retry=n is invalid, or when option string is NULL. 317 */ 318 int 319 get_retry(char *opts) 320 { 321 int retry = 0; 322 char buf[MAXOPTSLEN]; 323 char *p, *pb, *lasts; 324 325 if (opts == NULL) 326 return (retry); 327 328 (void) strcpy(buf, opts); 329 pb = buf; 330 while (p = (char *)strtok_r(pb, ",", &lasts)) { 331 pb = NULL; 332 if (strncmp(p, "retry=", 6) == 0) 333 retry = atoi(p+6); 334 } 335 return (retry > 0 ? retry : 0); 336 } 337 338 /* 339 * Returns zero if "opt" is found in mnt->mnt_opts, setting 340 * *sval to whatever follows the equal sign after "opt". 341 * str_opt allocates a string long enough to store the value of 342 * "opt" plus a terminating null character and returns it as *sval. 343 * It is the responsability of the caller to deallocate *sval. 344 * *sval will be equal to NULL upon return if either "opt=" is not found, 345 * or "opt=" has no value associated with it. 346 * 347 * stropt will return -1 on error. 348 */ 349 int 350 str_opt(struct mnttab *mnt, char *opt, char **sval) 351 { 352 char *str, *comma; 353 354 /* 355 * is "opt" in the options field? 356 */ 357 if (str = hasmntopt(mnt, opt)) { 358 str += strlen(opt); 359 if (*str++ != '=' || 360 (*str == ',' || *str == '\0')) { 361 syslog(LOG_ERR, "Bad option field"); 362 return (-1); 363 } 364 comma = strchr(str, ','); 365 if (comma != NULL) 366 *comma = '\0'; 367 *sval = strdup(str); 368 if (comma != NULL) 369 *comma = ','; 370 if (*sval == NULL) 371 return (-1); 372 } else 373 *sval = NULL; 374 375 return (0); 376 } 377 378 /* 379 * Performs text expansions in the string "pline". 380 * "plineq" is the quote vector for "pline". 381 * An identifier prefixed by "$" is replaced by the 382 * corresponding environment variable string. A "&" 383 * is replaced by the key string for the map entry. 384 * 385 * This routine will return an error (non-zero) if *size* would be 386 * exceeded after expansion, indicating that the macro_expand failed. 387 * This is to prevent writing past the end of pline and plineq. 388 * Both pline and plineq are left untouched in such error case. 389 */ 390 int 391 macro_expand(key, pline, plineq, size) 392 char *key, *pline, *plineq; 393 int size; 394 { 395 register char *p, *q; 396 register char *bp, *bq; 397 register char *s; 398 char buffp[LINESZ], buffq[LINESZ]; 399 char namebuf[64], *pn; 400 int expand = 0; 401 struct utsname name; 402 char isaname[64]; 403 404 p = pline; q = plineq; 405 bp = buffp; bq = buffq; 406 407 while (*p) { 408 if (*p == '&' && *q == ' ') { /* insert key */ 409 /* 410 * make sure we don't overflow buffer 411 */ 412 if ((int)((bp - buffp) + strlen(key)) < size) { 413 for (s = key; *s; s++) { 414 *bp++ = *s; 415 *bq++ = ' '; 416 } 417 expand++; 418 p++; q++; 419 continue; 420 } else { 421 /* 422 * line too long... 423 */ 424 return (1); 425 } 426 } 427 428 if (*p == '$' && *q == ' ') { /* insert env var */ 429 p++; q++; 430 pn = namebuf; 431 if (*p == '{') { 432 p++; q++; 433 while (*p && *p != '}') { 434 *pn++ = *p++; 435 q++; 436 } 437 if (*p) { 438 p++; q++; 439 } 440 } else { 441 while (*p && (*p == '_' || isalnum(*p))) { 442 *pn++ = *p++; 443 q++; 444 } 445 } 446 *pn = '\0'; 447 448 s = getenv(namebuf); 449 if (!s) { 450 /* not found in env */ 451 if (strcmp(namebuf, "HOST") == 0) { 452 (void) uname(&name); 453 s = name.nodename; 454 } else if (strcmp(namebuf, "OSREL") == 0) { 455 (void) uname(&name); 456 s = name.release; 457 } else if (strcmp(namebuf, "OSNAME") == 0) { 458 (void) uname(&name); 459 s = name.sysname; 460 } else if (strcmp(namebuf, "OSVERS") == 0) { 461 (void) uname(&name); 462 s = name.version; 463 } else if (strcmp(namebuf, "NATISA") == 0) { 464 if (natisa(isaname, sizeof (isaname))) 465 s = isaname; 466 } 467 } 468 469 if (s) { 470 if ((int)((bp - buffp) + strlen(s)) < size) { 471 while (*s) { 472 *bp++ = *s++; 473 *bq++ = ' '; 474 } 475 } else { 476 /* 477 * line too long... 478 */ 479 return (1); 480 } 481 } 482 expand++; 483 continue; 484 } 485 /* 486 * Since buffp needs to be null terminated, we need to 487 * check that there's still room in the buffer to 488 * place at least two more characters, *p and the 489 * terminating null. 490 */ 491 if (bp - buffp == size - 1) { 492 /* 493 * There was not enough room for at least two more 494 * characters, return with an error. 495 */ 496 return (1); 497 } 498 /* 499 * The total number of characters so far better be less 500 * than the size of buffer passed in. 501 */ 502 *bp++ = *p++; 503 *bq++ = *q++; 504 505 } 506 if (!expand) 507 return (0); 508 *bp = '\0'; 509 *bq = '\0'; 510 /* 511 * We know buffp/buffq will fit in pline/plineq since we 512 * processed at most size characters. 513 */ 514 (void) strcpy(pline, buffp); 515 (void) strcpy(plineq, buffq); 516 517 return (0); 518 } 519 520 /* 521 * Removes quotes from the string "str" and returns 522 * the quoting information in "qbuf". e.g. 523 * original str: 'the "quick brown" f\ox' 524 * unquoted str: 'the quick brown fox' 525 * and the qbuf: ' ^^^^^^^^^^^ ^ ' 526 */ 527 void 528 unquote(str, qbuf) 529 char *str, *qbuf; 530 { 531 register int escaped, inquote, quoted; 532 register char *ip, *bp, *qp; 533 char buf[LINESZ]; 534 535 escaped = inquote = quoted = 0; 536 537 for (ip = str, bp = buf, qp = qbuf; *ip; ip++) { 538 if (!escaped) { 539 if (*ip == '\\') { 540 escaped = 1; 541 quoted++; 542 continue; 543 } else 544 if (*ip == '"') { 545 inquote = !inquote; 546 quoted++; 547 continue; 548 } 549 } 550 551 *bp++ = *ip; 552 *qp++ = (inquote || escaped) ? '^' : ' '; 553 escaped = 0; 554 } 555 *bp = '\0'; 556 *qp = '\0'; 557 if (quoted) 558 (void) strcpy(str, buf); 559 } 560 561 /* 562 * Removes trailing spaces from string "s". 563 */ 564 void 565 trim(s) 566 char *s; 567 { 568 char *p = &s[strlen(s) - 1]; 569 570 while (p >= s && isspace(*(uchar_t *)p)) 571 *p-- = '\0'; 572 } 573 574 /* 575 * try to allocate memory using malloc, if malloc fails, then flush the 576 * rddir caches, and retry. If the second allocation after the readdir 577 * caches have been flushed fails too, then return NULL to indicate 578 * memory could not be allocated. 579 */ 580 char * 581 auto_rddir_malloc(unsigned nbytes) 582 { 583 char *p; 584 int again = 0; 585 586 if ((p = malloc(nbytes)) == NULL) { 587 /* 588 * No memory, free rddir caches and try again 589 */ 590 mutex_lock(&cleanup_lock); 591 cond_signal(&cleanup_start_cv); 592 if (cond_wait(&cleanup_done_cv, &cleanup_lock)) { 593 mutex_unlock(&cleanup_lock); 594 syslog(LOG_ERR, "auto_rddir_malloc interrupted\n"); 595 } else { 596 mutex_unlock(&cleanup_lock); 597 again = 1; 598 } 599 } 600 601 if (again) 602 p = malloc(nbytes); 603 604 return (p); 605 } 606 607 /* 608 * try to strdup a string, if it fails, then flush the rddir caches, 609 * and retry. If the second strdup fails, return NULL to indicate failure. 610 */ 611 char * 612 auto_rddir_strdup(const char *s1) 613 { 614 char *s2; 615 int again = 0; 616 617 if ((s2 = strdup(s1)) == NULL) { 618 /* 619 * No memory, free rddir caches and try again 620 */ 621 mutex_lock(&cleanup_lock); 622 cond_signal(&cleanup_start_cv); 623 if (cond_wait(&cleanup_done_cv, &cleanup_lock)) { 624 mutex_unlock(&cleanup_lock); 625 syslog(LOG_ERR, "auto_rddir_strdup interrupted\n"); 626 } else { 627 mutex_unlock(&cleanup_lock); 628 again = 1; 629 } 630 } 631 632 if (again) 633 s2 = strdup(s1); 634 635 return (s2); 636 } 637 638 /* 639 * Returns a pointer to the entry corresponding to 'name' if found, 640 * otherwise it returns NULL. 641 */ 642 struct dir_entry * 643 btree_lookup(struct dir_entry *head, char *name) 644 { 645 register struct dir_entry *p; 646 register int direction; 647 648 for (p = head; p != NULL; ) { 649 direction = strcmp(name, p->name); 650 if (direction == 0) 651 return (p); 652 if (direction > 0) 653 p = p->right; 654 else p = p->left; 655 } 656 return (NULL); 657 } 658 659 /* 660 * Add entry to binary tree 661 * Duplicate entries are not added 662 */ 663 void 664 btree_enter(struct dir_entry **head, struct dir_entry *ent) 665 { 666 register struct dir_entry *p, *prev = NULL; 667 register int direction; 668 669 ent->right = ent->left = NULL; 670 if (*head == NULL) { 671 *head = ent; 672 return; 673 } 674 675 for (p = *head; p != NULL; ) { 676 prev = p; 677 direction = strcmp(ent->name, p->name); 678 if (direction == 0) { 679 /* 680 * entry already in btree 681 */ 682 return; 683 } 684 if (direction > 0) 685 p = p->right; 686 else p = p->left; 687 } 688 assert(prev != NULL); 689 if (direction > 0) 690 prev->right = ent; 691 else prev->left = ent; 692 } 693 694 /* 695 * If entry doesn't exist already, add it to the linear list 696 * after '*last' and to the binary tree list. 697 * If '*last == NULL' then the list is walked till the end. 698 * *last is always set to the new element after successful completion. 699 * if entry already exists '*last' is only updated if not previously 700 * provided. 701 */ 702 int 703 add_dir_entry(char *name, struct dir_entry **list, struct dir_entry **last) 704 { 705 struct dir_entry *e, *l; 706 707 if ((*list != NULL) && (*last == NULL)) { 708 /* 709 * walk the list to find last element 710 */ 711 for (l = *list; l != NULL; l = l->next) 712 *last = l; 713 } 714 715 if (btree_lookup(*list, name) == NULL) { 716 /* 717 * not a duplicate, add it to list 718 */ 719 /* LINTED pointer alignment */ 720 e = (struct dir_entry *) 721 auto_rddir_malloc(sizeof (struct dir_entry)); 722 if (e == NULL) 723 return (ENOMEM); 724 (void) memset((char *)e, 0, sizeof (*e)); 725 e->name = auto_rddir_strdup(name); 726 if (e->name == NULL) { 727 free(e); 728 return (ENOMEM); 729 } 730 e->next = NULL; 731 if (*list == NULL) { 732 /* 733 * list is empty 734 */ 735 *list = *last = e; 736 } else { 737 /* 738 * append to end of list 739 */ 740 assert(*last != NULL); 741 (*last)->next = e; 742 *last = e; 743 } 744 /* 745 * add to binary tree 746 */ 747 btree_enter(list, e); 748 } 749 return (0); 750 } 751 752 /* 753 * Print trace output. 754 * Like fprintf(stderr, fmt, ...) except that if "id" is nonzero, the output 755 * is preceeded by the ID of the calling thread. 756 */ 757 #define FMT_BUFSIZ 1024 758 759 void 760 trace_prt(int id, char *fmt, ...) 761 { 762 va_list args; 763 764 char buf[FMT_BUFSIZ]; 765 766 if (id) { 767 (void) sprintf(buf, "t%u\t%s", thr_self(), fmt); 768 fmt = buf; 769 } 770 va_start(args, fmt); 771 (void) vfprintf(stderr, fmt, args); 772 va_end(args); 773 } 774 775 /* 776 * Extract the isalist(5) for userland from the kernel. 777 */ 778 static char * 779 isalist(void) 780 { 781 char *buf; 782 size_t bufsize = BUFSIZ; /* wild guess */ 783 long ret; 784 785 buf = malloc(bufsize); 786 do { 787 ret = sysinfo(SI_ISALIST, buf, bufsize); 788 if (ret == -1l) 789 return (NULL); 790 if (ret > bufsize) { 791 bufsize = ret; 792 buf = realloc(buf, bufsize); 793 } else 794 break; 795 } while (buf != NULL); 796 797 return (buf); 798 } 799 800 /* 801 * Classify isa's as to bitness of the corresponding ABIs. 802 * isa's which have no "official" system ABI are returned 803 * unrecognised i.e. zero bits. 804 */ 805 static int 806 bitness(char *isaname) 807 { 808 if (strcmp(isaname, "sparc") == 0 || 809 strcmp(isaname, "i386") == 0) 810 return (32); 811 812 if (strcmp(isaname, "sparcv9") == 0) 813 return (64); 814 815 return (0); 816 } 817 818 /* 819 * Find the left-most element in the isalist that matches our idea of a 820 * system ABI. 821 * 822 * On machines with only one ABI, this is usually the same as uname -p. 823 */ 824 static int 825 natisa(char *buf, size_t bufsize) 826 { 827 int bits; 828 char *isa, *list; 829 char *lasts; 830 831 if ((list = isalist()) == NULL) 832 return (0); 833 834 for (isa = strtok_r(list, " ", &lasts); 835 isa; isa = strtok_r(0, " ", &lasts)) 836 if ((bits = bitness(isa)) != 0) 837 break; /* ignore "extension" architectures */ 838 839 if (isa == 0 || bits == 0) { 840 free(list); 841 return (0); /* can't figure it out :( */ 842 } 843 844 (void) strncpy(buf, isa, bufsize); 845 free(list); 846 847 return (1); 848 } 849