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