1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 * Copyright (c) 2016 by Delphix. All rights reserved. 5 */ 6 7 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 8 /* All Rights Reserved */ 9 10 /* 11 * Copyright (c) 1980 Regents of the University of California. 12 * All rights reserved. The Berkeley Software License Agreement 13 * specifies the terms and conditions for redistribution. 14 */ 15 16 #include "sh.h" 17 #include "sh.dir.h" 18 #include "sh.tconst.h" 19 20 /* 21 * C Shell - directory management 22 */ 23 24 struct directory *dfind(tchar *); 25 tchar *dfollow(tchar *); 26 tchar *dcanon(tchar *, tchar *); 27 void dtildepr(tchar *, tchar *); 28 void dfree(struct directory *); 29 void dnewcwd(struct directory *); 30 31 int didchdir; 32 bool loginsh; 33 bool havhash2; 34 struct varent shvhed; 35 struct directory *dcwd; 36 struct directory dhead; /* "head" of loop */ 37 int printd; /* force name to be printed */ 38 static tchar *fakev[] = { S_dirs, NOSTR }; 39 char xhash2[HSHSIZ / 8]; 40 41 /* 42 * dinit - initialize current working directory 43 */ 44 void 45 dinit(tchar *hp) 46 { 47 tchar *cp; 48 struct directory *dp; 49 tchar path[MAXPATHLEN]; 50 51 #ifdef TRACE 52 tprintf("TRACE- dinit()\n"); 53 #endif 54 /* 55 * If this is a login shell, we should have a home directory. But, 56 * if we got here via 'su - <user>' where the user has no directory 57 * in their passwd file, then su has passed HOME=<nothing>, so hp is 58 * non-null, but has zero length. Thus, we do not know the current 59 * working directory based on the home directory. 60 */ 61 if (loginsh && hp && *hp) 62 cp = hp; 63 else { 64 cp = getwd_(path); 65 if (cp == NULL) { 66 printf("Warning: cannot determine current directory\n"); 67 cp = S_DOT; 68 } 69 } 70 dp = (struct directory *)xcalloc(sizeof (struct directory), 1); 71 dp->di_name = savestr(cp); 72 dp->di_count = 0; 73 dhead.di_next = dhead.di_prev = dp; 74 dp->di_next = dp->di_prev = &dhead; 75 printd = 0; 76 dnewcwd(dp); 77 } 78 79 /* 80 * dodirs - list all directories in directory loop 81 */ 82 void 83 dodirs(tchar **v) 84 { 85 struct directory *dp; 86 bool lflag; 87 tchar *hp = value(S_home); 88 89 #ifdef TRACE 90 tprintf("TRACE- dodirs()\n"); 91 #endif 92 if (*hp == '\0') 93 hp = NOSTR; 94 if (*++v != NOSTR) 95 if (eq(*v, S_MINl /* "-l" */) && *++v == NOSTR) 96 lflag = 1; 97 else 98 error("Usage: dirs [ -l ]"); 99 else 100 lflag = 0; 101 dp = dcwd; 102 do { 103 if (dp == &dhead) 104 continue; 105 if (!lflag && hp != NOSTR) { 106 dtildepr(hp, dp->di_name); 107 } else 108 printf("%t", dp->di_name); 109 printf(" "); 110 } while ((dp = dp->di_prev) != dcwd); 111 printf("\n"); 112 } 113 114 void 115 dtildepr(tchar *home, tchar *dir) 116 { 117 118 #ifdef TRACE 119 tprintf("TRACE- dtildepr()\n"); 120 #endif 121 if (!eq(home, S_SLASH /* "/" */) && prefix(home, dir)) 122 printf("~%t", dir + strlen_(home)); 123 else 124 printf("%t", dir); 125 } 126 127 /* 128 * dochngd - implement chdir command. 129 */ 130 void 131 dochngd(tchar **v) 132 { 133 tchar *cp; 134 struct directory *dp; 135 136 #ifdef TRACE 137 tprintf("TRACE- dochngd()\n"); 138 #endif 139 printd = 0; 140 if (*++v == NOSTR) { 141 if ((cp = value(S_home)) == NOSTR || *cp == 0) 142 bferr("No home directory"); 143 if (chdir_(cp) < 0) 144 bferr("Can't change to home directory"); 145 cp = savestr(cp); 146 } else if ((dp = dfind(*v)) != 0) { 147 printd = 1; 148 if (chdir_(dp->di_name) < 0) 149 Perror(dp->di_name); 150 dcwd->di_prev->di_next = dcwd->di_next; 151 dcwd->di_next->di_prev = dcwd->di_prev; 152 goto flushcwd; 153 } else 154 cp = dfollow(*v); 155 dp = (struct directory *)xcalloc(sizeof (struct directory), 1); 156 dp->di_name = cp; 157 dp->di_count = 0; 158 dp->di_next = dcwd->di_next; 159 dp->di_prev = dcwd->di_prev; 160 dp->di_prev->di_next = dp; 161 dp->di_next->di_prev = dp; 162 flushcwd: 163 dfree(dcwd); 164 dnewcwd(dp); 165 } 166 167 /* 168 * dfollow - change to arg directory; fall back on cdpath if not valid 169 */ 170 tchar * 171 dfollow(tchar *cp) 172 { 173 tchar *dp; 174 struct varent *c; 175 int cdhashval, cdhashval1; 176 int index; 177 int slash; /* slashes in the argument */ 178 tchar *fullpath; 179 tchar *slashcp; /* cp string prepended with a slash */ 180 181 #ifdef TRACE 182 tprintf("TRACE- dfollow()\n"); 183 #endif 184 cp = globone(cp); 185 if (chdir_(cp) >= 0) 186 goto gotcha; 187 188 /* 189 * If the directory argument has a slash in it, 190 * for example, directory/directory, then can't 191 * find that in the cache table. 192 */ 193 slash = any('/', cp); 194 195 /* 196 * Try interpreting wrt successive components of cdpath. 197 * cdpath caching is turned off or directory argument 198 * has a slash in it. 199 */ 200 if (cp[0] != '/' 201 && !prefix(S_DOTSLA /* "./" */, cp) 202 && !prefix(S_DOTDOTSLA /* "../" */, cp) 203 && (c = adrof(S_cdpath)) 204 && (!havhash2 || slash)) { 205 tchar **cdp; 206 tchar *p; 207 tchar buf[MAXPATHLEN]; 208 209 for (cdp = c->vec; *cdp; cdp++) { 210 for (dp = buf, p = *cdp; *dp++ = *p++; ) 211 ; 212 dp[-1] = '/'; 213 for (p = cp; *dp++ = *p++; ) 214 ; 215 if (chdir_(buf) >= 0) { 216 printd = 1; 217 xfree(cp); 218 cp = savestr(buf); 219 goto gotcha; 220 } 221 } 222 } 223 224 /* cdpath caching turned on */ 225 if (cp[0] != '/' 226 && !prefix(S_DOTSLA /* "./" */, cp) 227 && !prefix(S_DOTDOTSLA /* "../" */, cp) 228 && (c = adrof(S_cdpath)) 229 && havhash2 && !slash) { 230 tchar **pv; 231 232 /* If no cdpath or no paths in cdpath, leave */ 233 if (c == 0 || c->vec[0] == 0) 234 pv = justabs; 235 else 236 pv = c->vec; 237 238 slashcp = strspl(S_SLASH, cp); 239 240 cdhashval = hashname(cp); 241 242 /* index points to next path component to test */ 243 index = 0; 244 245 /* 246 * Look at each path in cdpath until get a match. 247 * Only look at those path beginning with a slash 248 */ 249 do { 250 /* only check cache for absolute pathnames */ 251 if (pv[0][0] == '/') { 252 cdhashval1 = hash(cdhashval, index); 253 if (bit(xhash2, cdhashval1)) { 254 /* 255 * concatenate found path with 256 * arg directory 257 */ 258 fullpath = strspl(*pv, slashcp); 259 if (chdir_(fullpath) >= 0) { 260 printd = 1; 261 xfree(cp); 262 cp = savestr(fullpath); 263 xfree(slashcp); 264 xfree(fullpath); 265 goto gotcha; 266 } 267 } 268 } 269 /* 270 * relative pathnames are not cached, and must be 271 * checked manually 272 */ 273 else { 274 tchar *p; 275 tchar buf[MAXPATHLEN]; 276 277 for (dp = buf, p = *pv; *dp++ = *p++; ) 278 ; 279 dp[-1] = '/'; 280 for (p = cp; *dp++ = *p++; ) 281 ; 282 if (chdir_(buf) >= 0) { 283 printd = 1; 284 xfree(cp); 285 cp = savestr(buf); 286 xfree(slashcp); 287 goto gotcha; 288 } 289 } 290 pv++; 291 index++; 292 } while (*pv); 293 } 294 295 /* 296 * Try dereferencing the variable named by the argument. 297 */ 298 dp = value(cp); 299 if ((dp[0] == '/' || dp[0] == '.') && chdir_(dp) >= 0) { 300 xfree(cp); 301 cp = savestr(dp); 302 printd = 1; 303 goto gotcha; 304 } 305 xfree(cp); /* XXX, use after free */ 306 Perror(cp); 307 308 gotcha: 309 if (*cp != '/') { 310 tchar *p, *q; 311 int cwdlen; 312 int len; 313 314 /* 315 * All in the name of efficiency? 316 */ 317 318 if ((cwdlen = (strlen_(dcwd->di_name))) == 1) { 319 if (*dcwd->di_name == '/') /* root */ 320 cwdlen = 0; 321 else 322 { 323 /* 324 * if we are here, when the shell started 325 * it was unable to getwd(), lets try it again 326 */ 327 tchar path[MAXPATHLEN]; 328 329 p = getwd_(path); 330 if (p == NULL) 331 error("cannot determine current directory"); 332 else 333 { 334 xfree(dcwd->di_name); 335 dcwd->di_name = savestr(p); 336 xfree(cp); 337 cp = savestr(p); 338 return dcanon(cp, cp); 339 } 340 341 } 342 } 343 /* 344 * 345 * for (p = cp; *p++;) 346 * ; 347 * dp = (tchar *)xalloc((unsigned) (cwdlen + (p - cp) + 1)*sizeof (tchar)) 348 */ 349 len = strlen_(cp); 350 dp = (tchar *)xalloc((unsigned)(cwdlen + len + 2) * sizeof (tchar)); 351 for (p = dp, q = dcwd->di_name; *p++ = *q++; ) 352 ; 353 if (cwdlen) 354 p[-1] = '/'; 355 else 356 p--; /* don't add a / after root */ 357 for (q = cp; *p++ = *q++; ) 358 ; 359 xfree(cp); 360 cp = dp; 361 dp += cwdlen; 362 } else 363 dp = cp; 364 return dcanon(cp, dp); 365 } 366 367 /* 368 * dopushd - push new directory onto directory stack. 369 * with no arguments exchange top and second. 370 * with numeric argument (+n) bring it to top. 371 */ 372 void 373 dopushd(tchar **v) 374 { 375 struct directory *dp; 376 377 #ifdef TRACE 378 tprintf("TRACE- dopushd()\n"); 379 #endif 380 printd = 1; 381 if (*++v == NOSTR) { 382 if ((dp = dcwd->di_prev) == &dhead) 383 dp = dhead.di_prev; 384 if (dp == dcwd) 385 bferr("No other directory"); 386 if (chdir_(dp->di_name) < 0) 387 Perror(dp->di_name); 388 dp->di_prev->di_next = dp->di_next; 389 dp->di_next->di_prev = dp->di_prev; 390 dp->di_next = dcwd->di_next; 391 dp->di_prev = dcwd; 392 dcwd->di_next->di_prev = dp; 393 dcwd->di_next = dp; 394 } else if (dp = dfind(*v)) { 395 if (chdir_(dp->di_name) < 0) 396 Perror(dp->di_name); 397 } else { 398 tchar *cp; 399 400 cp = dfollow(*v); 401 dp = (struct directory *)xcalloc(sizeof (struct directory), 1); 402 dp->di_name = cp; 403 dp->di_count = 0; 404 dp->di_prev = dcwd; 405 dp->di_next = dcwd->di_next; 406 dcwd->di_next = dp; 407 dp->di_next->di_prev = dp; 408 } 409 dnewcwd(dp); 410 } 411 412 /* 413 * dfind - find a directory if specified by numeric (+n) argument 414 */ 415 struct directory * 416 dfind(tchar *cp) 417 { 418 struct directory *dp; 419 int i; 420 tchar *ep; 421 422 #ifdef TRACE 423 tprintf("TRACE- dfind()\n"); 424 #endif 425 if (*cp++ != '+') 426 return (0); 427 for (ep = cp; digit(*ep); ep++) 428 continue; 429 if (*ep) 430 return (0); 431 i = getn(cp); 432 if (i <= 0) 433 return (0); 434 for (dp = dcwd; i != 0; i--) { 435 if ((dp = dp->di_prev) == &dhead) 436 dp = dp->di_prev; 437 if (dp == dcwd) 438 bferr("Directory stack not that deep"); 439 } 440 return (dp); 441 } 442 443 /* 444 * dopopd - pop a directory out of the directory stack 445 * with a numeric argument just discard it. 446 */ 447 void 448 dopopd(tchar **v) 449 { 450 struct directory *dp, *p; 451 452 #ifdef TRACE 453 tprintf("TRACE- dopopd()\n"); 454 #endif 455 printd = 1; 456 if (*++v == NOSTR) 457 dp = dcwd; 458 else if ((dp = dfind(*v)) == 0) 459 bferr("Invalid argument"); 460 if (dp->di_prev == &dhead && dp->di_next == &dhead) 461 bferr("Directory stack empty"); 462 if (dp == dcwd) { 463 if ((p = dp->di_prev) == &dhead) 464 p = dhead.di_prev; 465 if (chdir_(p->di_name) < 0) 466 Perror(p->di_name); 467 } 468 dp->di_prev->di_next = dp->di_next; 469 dp->di_next->di_prev = dp->di_prev; 470 if (dp == dcwd) 471 dnewcwd(p); 472 else 473 dodirs(fakev); 474 dfree(dp); 475 } 476 477 /* 478 * dfree - free the directory (or keep it if it still has ref count) 479 */ 480 void 481 dfree(struct directory *dp) 482 { 483 484 #ifdef TRACE 485 tprintf("TRACE- dfree()\n"); 486 #endif 487 if (dp->di_count != 0) 488 dp->di_next = dp->di_prev = 0; 489 else 490 xfree(dp->di_name), xfree((tchar *)dp); 491 } 492 493 /* 494 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 495 * We are of course assuming that the file system is standardly 496 * constructed (always have ..'s, directories have links). 497 * 498 * If the hardpaths shell variable is set, resolve the 499 * resulting pathname to contain no symbolic link components. 500 */ 501 tchar * 502 dcanon(tchar *cp, tchar *p) 503 { 504 tchar *sp; /* rightmost component currently under 505 consideration */ 506 tchar *p1, /* general purpose */ 507 *p2; 508 bool slash, dotdot, hardpaths; 509 510 #ifdef TRACE 511 tprintf("TRACE- dcannon()\n"); 512 #endif 513 514 if (*cp != '/') 515 abort(); 516 517 if (hardpaths = (adrof(S_hardpaths) != NULL)) { 518 /* 519 * Be paranoid: don't trust the initial prefix 520 * to be symlink-free. 521 */ 522 p = cp; 523 } 524 525 /* 526 * Loop invariant: cp points to the overall path start, 527 * p to its as yet uncanonicalized trailing suffix. 528 */ 529 while (*p) { /* for each component */ 530 sp = p; /* save slash address */ 531 532 while (*++p == '/') /* flush extra slashes */ 533 ; 534 if (p != ++sp) 535 for (p1 = sp, p2 = p; *p1++ = *p2++; ) 536 ; 537 538 p = sp; /* save start of component */ 539 slash = 0; 540 if (*p) 541 while (*++p) /* find next slash or end of path */ 542 if (*p == '/') { 543 slash = 1; 544 *p = '\0'; 545 break; 546 } 547 548 if (*sp == '\0') { 549 /* component is null */ 550 if (--sp == cp) /* if path is one tchar (i.e. /) */ 551 break; 552 else 553 *sp = '\0'; 554 continue; 555 } 556 557 if (sp[0] == '.' && sp[1] == '\0') { 558 /* Squeeze out component consisting of "." */ 559 if (slash) { 560 for (p1 = sp, p2 = p + 1; *p1++ = *p2++; ) 561 ; 562 p = --sp; 563 } else if (--sp != cp) 564 *sp = '\0'; 565 continue; 566 } 567 568 /* 569 * At this point we have a path of the form "x/yz", 570 * where "x" is null or rooted at "/", "y" is a single 571 * component, and "z" is possibly null. The pointer cp 572 * points to the start of "x", sp to the start of "y", 573 * and p to the beginning of "z", which has been forced 574 * to a null. 575 */ 576 /* 577 * Process symbolic link component. Provided that either 578 * the hardpaths shell variable is set or "y" is really 579 * ".." we replace the symlink with its contents. The 580 * second condition for replacement is necessary to make 581 * the command "cd x/.." produce the same results as the 582 * sequence "cd x; cd ..". 583 * 584 * Note that the two conditions correspond to different 585 * potential symlinks. When hardpaths is set, we must 586 * check "x/y"; otherwise, when "y" is known to be "..", 587 * we check "x". 588 */ 589 dotdot = sp[0] == '.' && sp[1] == '.' && sp[2] == '\0'; 590 if (hardpaths || dotdot) { 591 tchar link[MAXPATHLEN]; 592 int cc; 593 tchar *newcp; 594 595 /* 596 * Isolate the end of the component that is to 597 * be checked for symlink-hood. 598 */ 599 sp--; 600 if (! hardpaths) 601 *sp = '\0'; 602 603 /* 604 * See whether the component is really a symlink by 605 * trying to read it. If the read succeeds, it is. 606 */ 607 if ((hardpaths || sp > cp) && 608 (cc = readlink_(cp, link, MAXPATHLEN)) >= 0) { 609 /* 610 * readlink_ put null, so we don't need this. 611 */ 612 /* link[cc] = '\0'; */ 613 614 /* Restore path. */ 615 if (slash) 616 *p = '/'; 617 618 /* 619 * Point p at the start of the trailing 620 * path following the symlink component. 621 * It's already there is hardpaths is set. 622 */ 623 if (! hardpaths) { 624 /* Restore path as well. */ 625 *(p = sp) = '/'; 626 } 627 628 /* 629 * Find length of p. 630 */ 631 for (p1 = p; *p1++; ) 632 ; 633 634 if (*link != '/') { 635 /* 636 * Relative path: replace the symlink 637 * component with its value. First, 638 * set sp to point to the slash at 639 * its beginning. If hardpaths is 640 * set, this is already the case. 641 */ 642 if (! hardpaths) { 643 while (*--sp != '/') 644 ; 645 } 646 647 /* 648 * Terminate the leading part of the 649 * path, including trailing slash. 650 */ 651 sp++; 652 *sp = '\0'; 653 654 /* 655 * New length is: "x/" + link + "z" 656 */ 657 p1 = newcp = (tchar *)xalloc((unsigned) 658 ((sp - cp) + cc + (p1 - p)) * sizeof (tchar)); 659 /* 660 * Copy new path into newcp 661 */ 662 for (p2 = cp; *p1++ = *p2++; ) 663 ; 664 for (p1--, p2 = link; *p1++ = *p2++; ) 665 ; 666 for (p1--, p2 = p; *p1++ = *p2++; ) 667 ; 668 /* 669 * Restart canonicalization at 670 * expanded "/y". 671 */ 672 p = sp - cp - 1 + newcp; 673 } else { 674 /* 675 * New length is: link + "z" 676 */ 677 p1 = newcp = (tchar *)xalloc((unsigned) 678 (cc + (p1 - p))*sizeof (tchar)); 679 /* 680 * Copy new path into newcp 681 */ 682 for (p2 = link; *p1++ = *p2++; ) 683 ; 684 for (p1--, p2 = p; *p1++ = *p2++; ) 685 ; 686 /* 687 * Restart canonicalization at beginning 688 */ 689 p = newcp; 690 } 691 xfree(cp); 692 cp = newcp; 693 continue; /* canonicalize the link */ 694 } 695 696 /* The component wasn't a symlink after all. */ 697 if (! hardpaths) 698 *sp = '/'; 699 } 700 701 if (dotdot) { 702 if (sp != cp) 703 while (*--sp != '/') 704 ; 705 if (slash) { 706 for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++; ) 707 ; 708 p = sp; 709 } else if (cp == sp) 710 *++sp = '\0'; 711 else 712 *sp = '\0'; 713 continue; 714 } 715 716 if (slash) 717 *p = '/'; 718 } 719 return cp; 720 } 721 722 /* 723 * dnewcwd - make a new directory in the loop the current one 724 * and export its name to the PWD environment variable. 725 */ 726 void 727 dnewcwd(struct directory *dp) 728 { 729 730 #ifdef TRACE 731 tprintf("TRACE- dnewcwd()\n"); 732 #endif 733 dcwd = dp; 734 #ifdef notdef 735 /* 736 * If we have a fast version of getwd available 737 * and hardpaths is set, it would be reasonable 738 * here to verify that dcwd->di_name really does 739 * name the current directory. Later... 740 */ 741 #endif /* notdef */ 742 743 didchdir = 1; 744 set(S_cwd, savestr(dcwd->di_name)); 745 didchdir = 0; 746 local_setenv(S_PWD, dcwd->di_name); 747 if (printd) 748 dodirs(fakev); 749 } 750