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