1: /* Copyright (c) 1981 Regents of the University of California */ 2: static char *sccsid = "@(#)exrecover.c 7.4 10/16/81"; 3: #include <stdio.h> /* mjm: BUFSIZ: stdio = 512, VMUNIX = 1024 */ 4: #undef BUFSIZ /* mjm: BUFSIZ different */ 5: #undef EOF /* mjm: EOF and NULL effectively the same */ 6: #undef NULL 7: 8: #include "ex.h" 9: #include "ex_temp.h" 10: #include "ex_tty.h" 11: #include "local/uparm.h" 12: #include "sys/dir.h" 13: 14: char xstr[1]; /* make loader happy */ 15: short tfile = -1; /* ditto */ 16: 17: /* 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(0); 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: *cp2++ = 0; 337: for (cp2 = fp->sf_entry, cp = fname; *cp && cp-fname < 14;) 338: *cp2++ = *cp++; 339: *cp2++ = 0; 340: } 341: 342: /* 343: * Do the qsort compare to sort the entries first by file name, 344: * then by modify time. 345: */ 346: qucmp(p1, p2) 347: struct svfile *p1, *p2; 348: { 349: register int t; 350: 351: if (t = strcmp(p1->sf_name, p2->sf_name)) 352: return(t); 353: if (p1->sf_time > p2->sf_time) 354: return(-1); 355: return(p1->sf_time < p2->sf_time); 356: } 357: 358: /* 359: * Scratch for search. 360: */ 361: char bestnb[BUFSIZ]; /* Name of the best one */ 362: long besttime; /* Time at which the best file was saved */ 363: int bestfd; /* Keep best file open so it dont vanish */ 364: 365: /* 366: * Look for a file, both in the users directory option value 367: * (i.e. usually /tmp) and in usrpath(preserve). 368: * Want to find the newest so we search on and on. 369: */ 370: findtmp(dir) 371: char *dir; 372: { 373: 374: /* 375: * No name or file so far. 376: */ 377: bestnb[0] = 0; 378: bestfd = -1; 379: 380: /* 381: * Search usrpath(preserve) and, if we can get there, /tmp 382: * (actually the users "directory" option). 383: */ 384: searchdir(dir); 385: if (chdir(mydir) == 0) 386: searchdir(mydir); 387: if (bestfd != -1) { 388: /* 389: * Gotcha. 390: * Put the file (which is already open) in the file 391: * used by the temp file routines, and save its 392: * name for later unlinking. 393: */ 394: tfile = bestfd; 395: CP(nb, bestnb); 396: ignorl(lseek(tfile, 0l, 0)); 397: 398: /* 399: * Gotta be able to read the header or fall through 400: * to lossage. 401: */ 402: if (read(tfile, (char *) &H, sizeof H) == sizeof H) 403: return; 404: } 405: 406: /* 407: * Extreme lossage... 408: */ 409: error(" File not found", 0); 410: } 411: 412: /* 413: * Search for the file in directory dirname. 414: * 415: * Don't chdir here, because the users directory 416: * may be ".", and we would move away before we searched it. 417: * Note that we actually chdir elsewhere (because it is too slow 418: * to look around in usrpath(preserve) without chdir'ing there) so we 419: * can't win, because we don't know the name of '.' and if the path 420: * name of the file we want to unlink is relative, rather than absolute 421: * we won't be able to find it again. 422: */ 423: searchdir(dirname) 424: char *dirname; 425: { 426: struct direct dirent; 427: register FILE *dir; 428: char dbuf[BUFSIZ]; 429: 430: dir = fopen(dirname, "r"); 431: if (dir == NULL) 432: return; 433: /* setbuf(dir, dbuf); this breaks UNIX/370. */ 434: while (fread((char *) &dirent, sizeof dirent, 1, dir) == 1) { 435: if (dirent.d_ino == 0) 436: continue; 437: if (dirent.d_name[0] != 'E' || dirent.d_name[DIRSIZ - 1] != 0) 438: continue; 439: /* 440: * Got a file in the directory starting with E... 441: * Save a consed up name for the file to unlink 442: * later, and check that this is really a file 443: * we are looking for. 444: */ 445: ignore(strcat(strcat(strcpy(nb, dirname), "/"), dirent.d_name)); 446: if (yeah(nb)) { 447: /* 448: * Well, it is the file we are looking for. 449: * Is it more recent than any version we found before? 450: */ 451: if (H.Time > besttime) { 452: /* 453: * A winner. 454: */ 455: ignore(close(bestfd)); 456: bestfd = dup(tfile); 457: besttime = H.Time; 458: CP(bestnb, nb); 459: } 460: /* 461: * Count versions so user can be told there are 462: * ``yet more pages to be turned''. 463: */ 464: vercnt++; 465: } 466: ignore(close(tfile)); 467: } 468: ignore(fclose(dir)); 469: } 470: 471: /* 472: * Given a candidate file to be recovered, see 473: * if its really an editor temporary and of this 474: * user and the file specified. 475: */ 476: yeah(name) 477: char *name; 478: { 479: 480: tfile = open(name, 2); 481: if (tfile < 0) 482: return (0); 483: if (read(tfile, (char *) &H, sizeof H) != sizeof H) { 484: nope: 485: ignore(close(tfile)); 486: return (0); 487: } 488: if (!eq(savedfile, file)) 489: goto nope; 490: if (getuid() != H.Uid) 491: goto nope; 492: /* 493: * This is old and stupid code, which 494: * puts a word LOST in the header block, so that lost lines 495: * can be made to point at it. 496: */ 497: ignorl(lseek(tfile, (long)(BUFSIZ*HBLKS-8), 0)); 498: ignore(write(tfile, "LOST", 5)); 499: return (1); 500: } 501: 502: preserve() 503: { 504: 505: } 506: 507: /* 508: * Find the true end of the scratch file, and ``LOSE'' 509: * lines which point into thin air. This lossage occurs 510: * due to the sandbagging of i/o which can cause blocks to 511: * be written in a non-obvious order, different from the order 512: * in which the editor tried to write them. 513: * 514: * Lines which are lost are replaced with the text LOST so 515: * they are easy to find. We work hard at pretty formatting here 516: * as lines tend to be lost in blocks. 517: * 518: * This only seems to happen on very heavily loaded systems, and 519: * not very often. 520: */ 521: scrapbad() 522: { 523: register line *ip; 524: struct stat stbuf; 525: off_t size, maxt; 526: int bno, cnt, bad, was; 527: char bk[BUFSIZ]; 528: 529: ignore(fstat(tfile, &stbuf)); 530: size = stbuf.st_size; 531: maxt = (size >> SHFT) | (BNDRY-1); 532: bno = (maxt >> OFFBTS) & BLKMSK; 533: #ifdef DEBUG 534: fprintf(stderr, "size %ld, maxt %o, bno %d\n", size, maxt, bno); 535: #endif 536: 537: /* 538: * Look for a null separating two lines in the temp file; 539: * if last line was split across blocks, then it is lost 540: * if the last block is. 541: */ 542: while (bno > 0) { 543: ignorl(lseek(tfile, (long) BUFSIZ * bno, 0)); 544: cnt = read(tfile, (char *) bk, BUFSIZ); 545: while (cnt > 0) 546: if (bk[--cnt] == 0) 547: goto null; 548: bno--; 549: } 550: null: 551: 552: /* 553: * Magically calculate the largest valid pointer in the temp file, 554: * consing it up from the block number and the count. 555: */ 556: maxt = ((bno << OFFBTS) | (cnt >> SHFT)) & ~1; 557: #ifdef DEBUG 558: fprintf(stderr, "bno %d, cnt %d, maxt %o\n", bno, cnt, maxt); 559: #endif 560: 561: /* 562: * Now cycle through the line pointers, 563: * trashing the Lusers. 564: */ 565: was = bad = 0; 566: for (ip = one; ip <= dol; ip++) 567: if (*ip > maxt) { 568: #ifdef DEBUG 569: fprintf(stderr, "%d bad, %o > %o\n", ip - zero, *ip, maxt); 570: #endif 571: if (was == 0) 572: was = ip - zero; 573: *ip = ((HBLKS*BUFSIZ)-8) >> SHFT; 574: } else if (was) { 575: if (bad == 0) 576: fprintf(stderr, " [Lost line(s):"); 577: fprintf(stderr, " %d", was); 578: if ((ip - 1) - zero > was) 579: fprintf(stderr, "-%d", (ip - 1) - zero); 580: bad++; 581: was = 0; 582: } 583: if (was != 0) { 584: if (bad == 0) 585: fprintf(stderr, " [Lost line(s):"); 586: fprintf(stderr, " %d", was); 587: if (dol - zero != was) 588: fprintf(stderr, "-%d", dol - zero); 589: bad++; 590: } 591: if (bad) 592: fprintf(stderr, "]"); 593: } 594: 595: /* 596: * Aw shucks, if we only had a (void) cast. 597: */ 598: #ifdef lint 599: Ignorl(a) 600: long a; 601: { 602: 603: a = a; 604: } 605: 606: Ignore(a) 607: char *a; 608: { 609: 610: a = a; 611: } 612: 613: Ignorf(a) 614: int (*a)(); 615: { 616: 617: a = a; 618: } 619: 620: ignorl(a) 621: long a; 622: { 623: 624: a = a; 625: } 626: #endif 627: 628: int cntch, cntln, cntodd, cntnull; 629: /* 630: * Following routines stolen mercilessly from ex. 631: */ 632: putfile() 633: { 634: line *a1; 635: register char *fp, *lp; 636: register int nib; 637: 638: a1 = addr1; 639: clrstats(); 640: cntln = addr2 - a1 + 1; 641: if (cntln == 0) 642: return; 643: nib = BUFSIZ; 644: fp = genbuf; 645: do { 646: getline(*a1++); 647: lp = linebuf; 648: for (;;) { 649: if (--nib < 0) { 650: nib = fp - genbuf; 651: if (write(io, genbuf, nib) != nib) 652: wrerror(); 653: cntch += nib; 654: nib = 511; 655: fp = genbuf; 656: } 657: if ((*fp++ = *lp++) == 0) { 658: fp[-1] = '\n'; 659: break; 660: } 661: } 662: } while (a1 <= addr2); 663: nib = fp - genbuf; 664: if (write(io, genbuf, nib) != nib) 665: wrerror(); 666: cntch += nib; 667: } 668: 669: wrerror() 670: { 671: 672: syserror(); 673: } 674: 675: clrstats() 676: { 677: 678: ninbuf = 0; 679: cntch = 0; 680: cntln = 0; 681: cntnull = 0; 682: cntodd = 0; 683: } 684: 685: #define READ 0 686: #define WRITE 1 687: 688: getline(tl) 689: line tl; 690: { 691: register char *bp, *lp; 692: register int nl; 693: 694: lp = linebuf; 695: bp = getblock(tl, READ); 696: nl = nleft; 697: tl &= ~OFFMSK; 698: while (*lp++ = *bp++) 699: if (--nl == 0) { 700: bp = getblock(tl += INCRMT, READ); 701: nl = nleft; 702: } 703: } 704: 705: int read(); 706: int write(); 707: 708: char * 709: getblock(atl, iof) 710: line atl; 711: int iof; 712: { 713: register int bno, off; 714: 715: bno = (atl >> OFFBTS) & BLKMSK; 716: off = (atl << SHFT) & LBTMSK; 717: if (bno >= NMBLKS) 718: error(" Tmp file too large"); 719: nleft = BUFSIZ - off; 720: if (bno == iblock) { 721: ichanged |= iof; 722: return (ibuff + off); 723: } 724: if (bno == oblock) 725: return (obuff + off); 726: if (iof == READ) { 727: if (ichanged) 728: blkio(iblock, ibuff, write); 729: ichanged = 0; 730: iblock = bno; 731: blkio(bno, ibuff, read); 732: return (ibuff + off); 733: } 734: if (oblock >= 0) 735: blkio(oblock, obuff, write); 736: oblock = bno; 737: return (obuff + off); 738: } 739: 740: blkio(b, buf, iofcn) 741: short b; 742: char *buf; 743: int (*iofcn)(); 744: { 745: 746: lseek(tfile, (long) (unsigned) b * BUFSIZ, 0); 747: if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ) 748: syserror(); 749: } 750: 751: syserror() 752: { 753: extern int sys_nerr; 754: extern char *sys_errlist[]; 755: 756: dirtcnt = 0; 757: write(2, " ", 1); 758: if (errno >= 0 && errno <= sys_nerr) 759: error(sys_errlist[errno]); 760: else 761: error("System error %d", errno); 762: exit(1); 763: }