1: /* Copyright (c) 1979 Regents of the University of California */ 2: #include "ex.h" 3: #include "ex_temp.h" 4: #include "ex_tty.h" 5: #include "local/uparm.h" 6: 7: #undef BUFSIZ 8: #undef EOF 9: #undef NULL 10: 11: #include <stdio.h> 12: #include <sys/dir.h> 13: 14: /* 15: * Ex recovery program 16: * exrecover dir name 17: * exrecover -r 18: * 19: * This program searches through the specified directory and then 20: * the directory usrpath(preserve) looking for an instance of the specified 21: * file from a crashed editor or a crashed system. 22: * If this file is found, it is unscrambled and written to 23: * the standard output. 24: * 25: * If this program terminates without a "broken pipe" diagnostic 26: * (i.e. the editor doesn't die right away) then the buffer we are 27: * writing from is removed when we finish. This is potentially a mistake 28: * as there is not enough handshaking to guarantee that the file has actually 29: * been recovered, but should suffice for most cases. 30: */ 31: 32: /* 33: * For lint's sake... 34: */ 35: #ifndef lint 36: #define ignorl(a) a 37: #endif 38: 39: /* 40: * This directory definition also appears (obviously) in expreserve.c. 41: * Change both if you change either. 42: */ 43: char mydir[] = usrpath(preserve); 44: 45: /* 46: * Limit on the number of printed entries 47: * when an, e.g. ``ex -r'' command is given. 48: */ 49: #define NENTRY 50 50: 51: char *ctime(); 52: char nb[BUFSIZ]; 53: int vercnt; /* Count number of versions of file found */ 54: 55: main(argc, argv) 56: int argc; 57: char *argv[]; 58: { 59: register char *cp; 60: register int b, i; 61: 62: /* 63: * Initialize as though the editor had just started. 64: */ 65: fendcore = (line *) sbrk(0); 66: dot = zero = dol = fendcore; 67: one = zero + 1; 68: endcore = fendcore - 2; 69: iblock = oblock = -1; 70: 71: /* 72: * If given only a -r argument, then list the saved files. 73: */ 74: if (argc == 2 && eq(argv[1], "-r")) { 75: listfiles(mydir); 76: exit(0); 77: } 78: if (argc != 3) 79: error(" Wrong number of arguments to exrecover", 0); 80: 81: CP(file, argv[2]); 82: 83: /* 84: * Search for this file. 85: */ 86: findtmp(argv[1]); 87: 88: /* 89: * Got (one of the versions of) it, write it back to the editor. 90: */ 91: cp = ctime(&H.Time); 92: cp[19] = 0; 93: fprintf(stderr, " [Dated: %s", cp); 94: fprintf(stderr, vercnt > 1 ? ", newest of %d saved]" : "]", vercnt); 95: H.Flines++; 96: 97: /* 98: * Allocate space for the line pointers from the temp file. 99: */ 100: if ((int) sbrk((int) (H.Flines * sizeof (line))) == -1) 101: /* 102: * Good grief. 103: */ 104: error(" Not enough core for lines", 0); 105: #ifdef DEBUG 106: fprintf(stderr, "%d lines\n", H.Flines); 107: #endif 108: 109: /* 110: * Now go get the blocks of seek pointers which are scattered 111: * throughout the temp file, reconstructing the incore 112: * line pointers at point of crash. 113: */ 114: b = 0; 115: while (H.Flines > 0) { 116: ignorl(lseek(tfile, (long) blocks[b] * BUFSIZ, 0)); 117: i = H.Flines < BUFSIZ / sizeof (line) ? 118: H.Flines * sizeof (line) : BUFSIZ; 119: if (read(tfile, (char *) dot, i) != i) { 120: perror(nb); 121: exit(1); 122: } 123: dot += i / sizeof (line); 124: H.Flines -= i / sizeof (line); 125: b++; 126: } 127: dot--; dol = dot; 128: 129: /* 130: * Sigh... due to sandbagging some lines may really not be there. 131: * Find and discard such. This shouldn't happen much. 132: */ 133: scrapbad(); 134: 135: /* 136: * Now if there were any lines in the recovered file 137: * write them to the standard output. 138: */ 139: if (dol > zero) { 140: addr1 = one; addr2 = dol; io = 1; 141: putfile(); 142: } 143: 144: /* 145: * Trash the saved buffer. 146: * Hopefully the system won't crash before the editor 147: * syncs the new recovered buffer; i.e. for an instant here 148: * you may lose if the system crashes because this file 149: * is gone, but the editor hasn't completed reading the recovered 150: * file from the pipe from us to it. 151: * 152: * This doesn't work if we are coming from an non-absolute path 153: * name since we may have chdir'ed but what the hay, noone really 154: * ever edits with temporaries in "." anyways. 155: */ 156: if (nb[0] == '/') 157: ignore(unlink(nb)); 158: 159: /* 160: * Adieu. 161: */ 162: exit(0); 163: } 164: 165: /* 166: * Print an error message (notably not in error 167: * message file). If terminal is in RAW mode, then 168: * we should be writing output for "vi", so don't print 169: * a newline which would screw up the screen. 170: */ 171: /*VARARGS2*/ 172: error(str, inf) 173: char *str; 174: int inf; 175: { 176: 177: fprintf(stderr, str, inf); 178: #ifndef USG3TTY 179: gtty(2, &tty); 180: if ((tty.sg_flags & RAW) == 0) 181: #else 182: ioctl(2, TCGETA, &tty); 183: if (tty.c_lflag & ICANON) 184: #endif 185: fprintf(stderr, "\n"); 186: exit(1); 187: } 188: 189: /* 190: * Here we save the information about files, when 191: * you ask us what files we have saved for you. 192: * We buffer file name, number of lines, and the time 193: * at which the file was saved. 194: */ 195: struct svfile { 196: char sf_name[FNSIZE + 1]; 197: int sf_lines; 198: char sf_entry[DIRSIZ + 1]; 199: time_t sf_time; 200: }; 201: 202: listfiles(dirname) 203: char *dirname; 204: { 205: register FILE *dir; 206: struct direct dirent; 207: int ecount, qucmp(); 208: register int f; 209: char *cp; 210: struct svfile *fp, svbuf[NENTRY]; 211: 212: /* 213: * Open usrpath(preserve), and go there to make things quick. 214: */ 215: dir = fopen(dirname, "r"); 216: if (dir == NULL) { 217: perror(dirname); 218: return; 219: } 220: if (chdir(dirname) < 0) { 221: perror(dirname); 222: return; 223: } 224: 225: /* 226: * Look at the candidate files in usrpath(preserve). 227: */ 228: fp = &svbuf[0]; 229: ecount = 0; 230: while (fread((char *) &dirent, sizeof dirent, 1, dir) == 1) { 231: if (dirent.d_ino == 0) 232: continue; 233: if (dirent.d_name[0] != 'E') 234: continue; 235: #ifdef DEBUG 236: fprintf(stderr, "considering %s\n", dirent.d_name); 237: #endif 238: /* 239: * Name begins with E; open it and 240: * make sure the uid in the header is our uid. 241: * If not, then don't bother with this file, it can't 242: * be ours. 243: */ 244: f = open(dirent.d_name, 0); 245: if (f < 0) { 246: #ifdef DEBUG 247: fprintf(stderr, "open failed\n"); 248: #endif 249: continue; 250: } 251: if (read(f, (char *) &H, sizeof H) != sizeof H) { 252: #ifdef DEBUG 253: fprintf(stderr, "culdnt read hedr\n"); 254: #endif 255: ignore(close(f)); 256: continue; 257: } 258: ignore(close(f)); 259: if (getuid() != H.Uid) { 260: #ifdef DEBUG 261: fprintf(stderr, "uid wrong\n"); 262: #endif 263: continue; 264: } 265: 266: /* 267: * Saved the day! 268: */ 269: enter(fp++, dirent.d_name, ecount); 270: ecount++; 271: #ifdef DEBUG 272: fprintf(stderr, "entered file %s\n", dirent.d_name); 273: #endif 274: } 275: ignore(fclose(dir)); 276: 277: /* 278: * If any files were saved, then sort them and print 279: * them out. 280: */ 281: if (ecount == 0) { 282: fprintf(stderr, "No files saved.\n"); 283: return; 284: } 285: qsort(&svbuf[0], ecount, sizeof svbuf[0], qucmp); 286: for (fp = &svbuf[0]; fp < &svbuf[ecount]; fp++) { 287: cp = ctime(&fp->sf_time); 288: cp[10] = 0; 289: fprintf(stderr, "On %s at ", cp); 290: cp[16] = 0; 291: fprintf(stderr, &cp[11]); 292: fprintf(stderr, " saved %d lines of file \"%s\"\n", 293: fp->sf_lines, fp->sf_name); 294: } 295: } 296: 297: /* 298: * Enter a new file into the saved file information. 299: */ 300: enter(fp, fname, count) 301: struct svfile *fp; 302: char *fname; 303: { 304: register char *cp, *cp2; 305: register struct svfile *f, *fl; 306: time_t curtime, itol(); 307: 308: f = 0; 309: if (count >= NENTRY) { 310: /* 311: * My god, a huge number of saved files. 312: * Would you work on a system that crashed this 313: * often? Hope not. So lets trash the oldest 314: * as the most useless. 315: * 316: * (I wonder if this code has ever run?) 317: */ 318: fl = fp - count + NENTRY - 1; 319: curtime = fl->sf_time; 320: for (f = fl; --f > fp-count; ) 321: if (f->sf_time < curtime) 322: curtime = f->sf_time; 323: for (f = fl; --f > fp-count; ) 324: if (f->sf_time == curtime) 325: break; 326: fp = f; 327: } 328: 329: /* 330: * Gotcha. 331: */ 332: fp->sf_time = H.Time; 333: fp->sf_lines = H.Flines; 334: for (cp2 = fp->sf_name, cp = savedfile; *cp;) 335: *cp2++ = *cp++; 336: for (cp2 = fp->sf_entry, cp = fname; *cp && cp-fname < 14;) 337: *cp2++ = *cp++; 338: *cp2++ = 0; 339: } 340: 341: /* 342: * Do the qsort compare to sort the entries first by file name, 343: * then by modify time. 344: */ 345: qucmp(p1, p2) 346: struct svfile *p1, *p2; 347: { 348: register int t; 349: 350: if (t = strcmp(p1->sf_name, p2->sf_name)) 351: return(t); 352: if (p1->sf_time > p2->sf_time) 353: return(-1); 354: return(p1->sf_time < p2->sf_time); 355: } 356: 357: /* 358: * Scratch for search. 359: */ 360: char bestnb[BUFSIZ]; /* Name of the best one */ 361: long besttime; /* Time at which the best file was saved */ 362: int bestfd; /* Keep best file open so it dont vanish */ 363: 364: /* 365: * Look for a file, both in the users directory option value 366: * (i.e. usually /tmp) and in usrpath(preserve). 367: * Want to find the newest so we search on and on. 368: */ 369: findtmp(dir) 370: char *dir; 371: { 372: 373: /* 374: * No name or file so far. 375: */ 376: bestnb[0] = 0; 377: bestfd = -1; 378: 379: /* 380: * Search usrpath(preserve) and, if we can get there, /tmp 381: * (actually the users "directory" option). 382: */ 383: searchdir(dir); 384: if (chdir(mydir) == 0) 385: searchdir(mydir); 386: if (bestfd != -1) { 387: /* 388: * Gotcha. 389: * Put the file (which is already open) in the file 390: * used by the temp file routines, and save its 391: * name for later unlinking. 392: */ 393: tfile = bestfd; 394: CP(nb, bestnb); 395: ignorl(lseek(tfile, 0l, 0)); 396: 397: /* 398: * Gotta be able to read the header or fall through 399: * to lossage. 400: */ 401: if (read(tfile, (char *) &H, sizeof H) == sizeof H) 402: return; 403: } 404: 405: /* 406: * Extreme lossage... 407: */ 408: error(" File not found", 0); 409: } 410: 411: /* 412: * Search for the file in directory dirname. 413: * 414: * Don't chdir here, because the users directory 415: * may be ".", and we would move away before we searched it. 416: * Note that we actually chdir elsewhere (because it is too slow 417: * to look around in usrpath(preserve) without chdir'ing there) so we 418: * can't win, because we don't know the name of '.' and if the path 419: * name of the file we want to unlink is relative, rather than absolute 420: * we won't be able to find it again. 421: */ 422: searchdir(dirname) 423: char *dirname; 424: { 425: struct direct dirent; 426: register FILE *dir; 427: char dbuf[BUFSIZ]; 428: 429: dir = fopen(dirname, "r"); 430: if (dir == NULL) 431: return; 432: setbuf(dir, dbuf); 433: while (fread((char *) &dirent, sizeof dirent, 1, dir) == 1) { 434: if (dirent.d_ino == 0) 435: continue; 436: if (dirent.d_name[0] != 'E' || dirent.d_name[DIRSIZ - 1] != 0) 437: continue; 438: /* 439: * Got a file in the directory starting with E... 440: * Save a consed up name for the file to unlink 441: * later, and check that this is really a file 442: * we are looking for. 443: */ 444: ignore(strcat(strcat(strcpy(nb, dirname), "/"), dirent.d_name)); 445: if (yeah(nb)) { 446: /* 447: * Well, it is the file we are looking for. 448: * Is it more recent than any version we found before? 449: */ 450: if (H.Time > besttime) { 451: /* 452: * A winner. 453: */ 454: ignore(close(bestfd)); 455: bestfd = dup(tfile); 456: besttime = H.Time; 457: CP(bestnb, nb); 458: } 459: /* 460: * Count versions so user can be told there are 461: * ``yet more pages to be turned''. 462: */ 463: vercnt++; 464: } 465: ignore(close(tfile)); 466: } 467: ignore(fclose(dir)); 468: } 469: 470: /* 471: * Given a candidate file to be recovered, see 472: * if its really an editor temporary and of this 473: * user and the file specified. 474: */ 475: yeah(name) 476: char *name; 477: { 478: 479: tfile = open(name, 2); 480: if (tfile < 0) 481: return (0); 482: if (read(tfile, (char *) &H, sizeof H) != sizeof H) { 483: nope: 484: ignore(close(tfile)); 485: return (0); 486: } 487: if (!eq(savedfile, file)) 488: goto nope; 489: if (getuid() != H.Uid) 490: goto nope; 491: /* 492: * This is old and stupid code, which 493: * puts a word LOST in the header block, so that lost lines 494: * can be made to point at it. 495: */ 496: ignorl(lseek(tfile, 504l, 0)); 497: ignore(write(tfile, "LOST", 5)); 498: return (1); 499: } 500: 501: preserve() 502: { 503: 504: } 505: 506: /* 507: * Find the true end of the scratch file, and ``LOSE'' 508: * lines which point into thin air. This lossage occurs 509: * due to the sandbagging of i/o which can cause blocks to 510: * be written in a non-obvious order, different from the order 511: * in which the editor tried to write them. 512: * 513: * Lines which are lost are replaced with the text LOST so 514: * they are easy to find. We work hard at pretty formatting here 515: * as lines tend to be lost in blocks. 516: * 517: * This only seems to happen on very heavily loaded systems, and 518: * not very often. 519: */ 520: scrapbad() 521: { 522: register line *ip; 523: struct stat stbuf; 524: off_t size, maxt; 525: int bno, cnt, bad, was; 526: char bk[BUFSIZ]; 527: 528: ignore(fstat(tfile, &stbuf)); 529: size = stbuf.st_size; 530: maxt = (size >> SHFT) | 7; 531: bno = (maxt >> OFFBTS) & BLKMSK; 532: #ifdef DEBUG 533: fprintf(stderr, "size %ld, maxt %o, bno %d\n", size, maxt, bno); 534: #endif 535: 536: /* 537: * Look for a null separating two lines in the temp file; 538: * if last line was split across blocks, then it is lost 539: * if the last block is. 540: */ 541: while (bno > 0) { 542: ignorl(lseek(tfile, (long) BUFSIZ * bno, 0)); 543: cnt = read(tfile, (char *) bk, BUFSIZ); 544: while (cnt > 0) 545: if (bk[--cnt] == 0) 546: goto null; 547: bno--; 548: } 549: null: 550: 551: /* 552: * Magically calculate the largest valid pointer in the temp file, 553: * consing it up from the block number and the count. 554: */ 555: maxt = ((bno << OFFBTS) | (cnt >> SHFT)) & ~1; 556: #ifdef DEBUG 557: fprintf(stderr, "bno %d, cnt %d, maxt %o\n", bno, cnt, maxt); 558: #endif 559: 560: /* 561: * Now cycle through the line pointers, 562: * trashing the Lusers. 563: */ 564: was = bad = 0; 565: for (ip = one; ip <= dol; ip++) 566: if (*ip > maxt) { 567: #ifdef DEBUG 568: fprintf(stderr, "%d bad, %o > %o\n", ip - zero, *ip, maxt); 569: #endif 570: if (was == 0) 571: was = ip - zero; 572: *ip = 504 >> SHFT; 573: } else if (was) { 574: if (bad == 0) 575: fprintf(stderr, " [Lost line(s):"); 576: fprintf(stderr, " %d", was); 577: if ((ip - 1) - zero > was) 578: fprintf(stderr, "-%d", (ip - 1) - zero); 579: bad++; 580: was = 0; 581: } 582: if (was != 0) { 583: if (bad == 0) 584: fprintf(stderr, " [Lost line(s):"); 585: fprintf(stderr, " %d", was); 586: if (dol - zero != was) 587: fprintf(stderr, "-%d", dol - zero); 588: bad++; 589: } 590: if (bad) 591: fprintf(stderr, "]"); 592: } 593: 594: /* 595: * Aw shucks, if we only had a (void) cast. 596: */ 597: #ifdef lint 598: Ignorl(a) 599: long a; 600: { 601: 602: a = a; 603: } 604: 605: Ignore(a) 606: char *a; 607: { 608: 609: a = a; 610: } 611: 612: Ignorf(a) 613: int (*a)(); 614: { 615: 616: a = a; 617: } 618: 619: ignorl(a) 620: long a; 621: { 622: 623: a = a; 624: } 625: #endif 626: 627: int cntch, cntln, cntodd, cntnull; 628: /* 629: * Following routines stolen mercilessly from ex. 630: */ 631: putfile() 632: { 633: line *a1; 634: register char *fp, *lp; 635: register int nib; 636: 637: a1 = addr1; 638: clrstats(); 639: cntln = addr2 - a1 + 1; 640: if (cntln == 0) 641: return; 642: nib = BUFSIZ; 643: fp = genbuf; 644: do { 645: getline(*a1++); 646: lp = linebuf; 647: for (;;) { 648: if (--nib < 0) { 649: nib = fp - genbuf; 650: if (write(io, genbuf, nib) != nib) 651: wrerror(); 652: cntch += nib; 653: nib = 511; 654: fp = genbuf; 655: } 656: if ((*fp++ = *lp++) == 0) { 657: fp[-1] = '\n'; 658: break; 659: } 660: } 661: } while (a1 <= addr2); 662: nib = fp - genbuf; 663: if (write(io, genbuf, nib) != nib) 664: wrerror(); 665: cntch += nib; 666: } 667: 668: wrerror() 669: { 670: 671: syserror(); 672: } 673: 674: clrstats() 675: { 676: 677: ninbuf = 0; 678: cntch = 0; 679: cntln = 0; 680: cntnull = 0; 681: cntodd = 0; 682: } 683: 684: #define READ 0 685: #define WRITE 1 686: 687: getline(tl) 688: line tl; 689: { 690: register char *bp, *lp; 691: register int nl; 692: 693: lp = linebuf; 694: bp = getblock(tl, READ); 695: nl = nleft; 696: tl &= ~OFFMSK; 697: while (*lp++ = *bp++) 698: if (--nl == 0) { 699: bp = getblock(tl += INCRMT, READ); 700: nl = nleft; 701: } 702: } 703: 704: int read(); 705: int write(); 706: 707: char * 708: getblock(atl, iof) 709: line atl; 710: int iof; 711: { 712: register int bno, off; 713: 714: bno = (atl >> OFFBTS) & BLKMSK; 715: off = (atl << SHFT) & LBTMSK; 716: if (bno >= NMBLKS) 717: error(" Tmp file too large"); 718: nleft = BUFSIZ - off; 719: if (bno == iblock) { 720: ichanged |= iof; 721: return (ibuff + off); 722: } 723: if (bno == oblock) 724: return (obuff + off); 725: if (iof == READ) { 726: if (ichanged) 727: blkio(iblock, ibuff, write); 728: ichanged = 0; 729: iblock = bno; 730: blkio(bno, ibuff, read); 731: return (ibuff + off); 732: } 733: if (oblock >= 0) 734: blkio(oblock, obuff, write); 735: oblock = bno; 736: return (obuff + off); 737: } 738: 739: blkio(b, buf, iofcn) 740: short b; 741: char *buf; 742: int (*iofcn)(); 743: { 744: 745: lseek(tfile, (long) (unsigned) b * BUFSIZ, 0); 746: if ((*iofcn)(tfile, buf, BUFSIZ) != 512) 747: syserror(); 748: } 749: 750: syserror() 751: { 752: extern int sys_nerr; 753: extern char *sys_errlist[]; 754: 755: dirtcnt = 0; 756: write(2, " ", 1); 757: if (errno >= 0 && errno <= sys_nerr) 758: error(sys_errlist[errno]); 759: else 760: error("System error %d", errno); 761: exit(1); 762: }