|
|
1.1 root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.