|
|
1.1 root 1: /* Copyright (c) 1980 Regents of the University of California */
2: static char *sccsid = "@(#)exrecover.c 6.1 10/18/80";
3: #include "ex.h"
4: #include "ex_temp.h"
5: #include "ex_tty.h"
6: #include "local/uparm.h"
7:
8: #undef BUFSIZ
9: #undef EOF
10: #undef NULL
11:
12: #include <stdio.h>
13: #include <sys/dir.h>
14:
15: /*
16: * Ex recovery program
17: * exrecover dir name
18: * exrecover -r
19: *
20: * This program searches through the specified directory and then
21: * the directory usrpath(preserve) looking for an instance of the specified
22: * file from a crashed editor or a crashed system.
23: * If this file is found, it is unscrambled and written to
24: * the standard output.
25: *
26: * If this program terminates without a "broken pipe" diagnostic
27: * (i.e. the editor doesn't die right away) then the buffer we are
28: * writing from is removed when we finish. This is potentially a mistake
29: * as there is not enough handshaking to guarantee that the file has actually
30: * been recovered, but should suffice for most cases.
31: */
32:
33: /*
34: * For lint's sake...
35: */
36: #ifndef lint
37: #define ignorl(a) a
38: #endif
39:
40: /*
41: * This directory definition also appears (obviously) in expreserve.c.
42: * Change both if you change either.
43: */
44: char mydir[] = usrpath(preserve);
45:
46: /*
47: * Limit on the number of printed entries
48: * when an, e.g. ``ex -r'' command is given.
49: */
50: #define NENTRY 50
51:
52: char *ctime();
53: char nb[BUFSIZ];
54: int vercnt; /* Count number of versions of file found */
55:
56: main(argc, argv)
57: int argc;
58: char *argv[];
59: {
60: register char *cp;
61: register int b, i;
62:
63: /*
64: * Initialize as though the editor had just started.
65: */
66: fendcore = (line *) sbrk(0);
67: dot = zero = dol = fendcore;
68: one = zero + 1;
69: endcore = fendcore - 2;
70: iblock = oblock = -1;
71:
72: /*
73: * If given only a -r argument, then list the saved files.
74: */
75: if (argc == 2 && eq(argv[1], "-r")) {
76: listfiles(mydir);
77: exit(0);
78: }
79: if (argc != 3)
80: error(" Wrong number of arguments to exrecover", 0);
81:
82: CP(file, argv[2]);
83:
84: /*
85: * Search for this file.
86: */
87: findtmp(argv[1]);
88:
89: /*
90: * Got (one of the versions of) it, write it back to the editor.
91: */
92: cp = ctime(&H.Time);
93: cp[19] = 0;
94: fprintf(stderr, " [Dated: %s", cp);
95: fprintf(stderr, vercnt > 1 ? ", newest of %d saved]" : "]", vercnt);
96: H.Flines++;
97:
98: /*
99: * Allocate space for the line pointers from the temp file.
100: */
101: if ((int) sbrk((int) (H.Flines * sizeof (line))) == -1)
102: /*
103: * Good grief.
104: */
105: error(" Not enough core for lines", 0);
106: #ifdef DEBUG
107: fprintf(stderr, "%d lines\n", H.Flines);
108: #endif
109:
110: /*
111: * Now go get the blocks of seek pointers which are scattered
112: * throughout the temp file, reconstructing the incore
113: * line pointers at point of crash.
114: */
115: b = 0;
116: while (H.Flines > 0) {
117: ignorl(lseek(tfile, (long) blocks[b] * BUFSIZ, 0));
118: i = H.Flines < BUFSIZ / sizeof (line) ?
119: H.Flines * sizeof (line) : BUFSIZ;
120: if (read(tfile, (char *) dot, i) != i) {
121: perror(nb);
122: exit(1);
123: }
124: dot += i / sizeof (line);
125: H.Flines -= i / sizeof (line);
126: b++;
127: }
128: dot--; dol = dot;
129:
130: /*
131: * Sigh... due to sandbagging some lines may really not be there.
132: * Find and discard such. This shouldn't happen much.
133: */
134: scrapbad();
135:
136: /*
137: * Now if there were any lines in the recovered file
138: * write them to the standard output.
139: */
140: if (dol > zero) {
141: addr1 = one; addr2 = dol; io = 1;
142: putfile();
143: }
144:
145: /*
146: * Trash the saved buffer.
147: * Hopefully the system won't crash before the editor
148: * syncs the new recovered buffer; i.e. for an instant here
149: * you may lose if the system crashes because this file
150: * is gone, but the editor hasn't completed reading the recovered
151: * file from the pipe from us to it.
152: *
153: * This doesn't work if we are coming from an non-absolute path
154: * name since we may have chdir'ed but what the hay, noone really
155: * ever edits with temporaries in "." anyways.
156: */
157: if (nb[0] == '/')
158: ignore(unlink(nb));
159:
160: /*
161: * Adieu.
162: */
163: exit(0);
164: }
165:
166: /*
167: * Print an error message (notably not in error
168: * message file). If terminal is in RAW mode, then
169: * we should be writing output for "vi", so don't print
170: * a newline which would screw up the screen.
171: */
172: /*VARARGS2*/
173: error(str, inf)
174: char *str;
175: int inf;
176: {
177:
178: fprintf(stderr, str, inf);
179: #ifndef USG3TTY
180: gtty(2, &tty);
181: if ((tty.sg_flags & RAW) == 0)
182: #else
183: ioctl(2, TCGETA, &tty);
184: if (tty.c_lflag & ICANON)
185: #endif
186: fprintf(stderr, "\n");
187: exit(1);
188: }
189:
190: /*
191: * Here we save the information about files, when
192: * you ask us what files we have saved for you.
193: * We buffer file name, number of lines, and the time
194: * at which the file was saved.
195: */
196: struct svfile {
197: char sf_name[FNSIZE + 1];
198: int sf_lines;
199: char sf_entry[DIRSIZ + 1];
200: time_t sf_time;
201: };
202:
203: listfiles(dirname)
204: char *dirname;
205: {
206: register FILE *dir;
207: struct direct dirent;
208: int ecount, qucmp();
209: register int f;
210: char *cp;
211: struct svfile *fp, svbuf[NENTRY];
212:
213: /*
214: * Open usrpath(preserve), and go there to make things quick.
215: */
216: dir = fopen(dirname, "r");
217: if (dir == NULL) {
218: perror(dirname);
219: return;
220: }
221: if (chdir(dirname) < 0) {
222: perror(dirname);
223: return;
224: }
225:
226: /*
227: * Look at the candidate files in usrpath(preserve).
228: */
229: fp = &svbuf[0];
230: ecount = 0;
231: while (fread((char *) &dirent, sizeof dirent, 1, dir) == 1) {
232: if (dirent.d_ino == 0)
233: continue;
234: if (dirent.d_name[0] != 'E')
235: continue;
236: #ifdef DEBUG
237: fprintf(stderr, "considering %s\n", dirent.d_name);
238: #endif
239: /*
240: * Name begins with E; open it and
241: * make sure the uid in the header is our uid.
242: * If not, then don't bother with this file, it can't
243: * be ours.
244: */
245: f = open(dirent.d_name, 0);
246: if (f < 0) {
247: #ifdef DEBUG
248: fprintf(stderr, "open failed\n");
249: #endif
250: continue;
251: }
252: if (read(f, (char *) &H, sizeof H) != sizeof H) {
253: #ifdef DEBUG
254: fprintf(stderr, "culdnt read hedr\n");
255: #endif
256: ignore(close(f));
257: continue;
258: }
259: ignore(close(f));
260: if (getuid() != H.Uid) {
261: #ifdef DEBUG
262: fprintf(stderr, "uid wrong\n");
263: #endif
264: continue;
265: }
266:
267: /*
268: * Saved the day!
269: */
270: enter(fp++, dirent.d_name, ecount);
271: ecount++;
272: #ifdef DEBUG
273: fprintf(stderr, "entered file %s\n", dirent.d_name);
274: #endif
275: }
276: ignore(fclose(dir));
277:
278: /*
279: * If any files were saved, then sort them and print
280: * them out.
281: */
282: if (ecount == 0) {
283: fprintf(stderr, "No files saved.\n");
284: return;
285: }
286: qsort(&svbuf[0], ecount, sizeof svbuf[0], qucmp);
287: for (fp = &svbuf[0]; fp < &svbuf[ecount]; fp++) {
288: cp = ctime(&fp->sf_time);
289: cp[10] = 0;
290: fprintf(stderr, "On %s at ", cp);
291: cp[16] = 0;
292: fprintf(stderr, &cp[11]);
293: fprintf(stderr, " saved %d lines of file \"%s\"\n",
294: fp->sf_lines, fp->sf_name);
295: }
296: }
297:
298: /*
299: * Enter a new file into the saved file information.
300: */
301: enter(fp, fname, count)
302: struct svfile *fp;
303: char *fname;
304: {
305: register char *cp, *cp2;
306: register struct svfile *f, *fl;
307: time_t curtime, itol();
308:
309: f = 0;
310: if (count >= NENTRY) {
311: /*
312: * My god, a huge number of saved files.
313: * Would you work on a system that crashed this
314: * often? Hope not. So lets trash the oldest
315: * as the most useless.
316: *
317: * (I wonder if this code has ever run?)
318: */
319: fl = fp - count + NENTRY - 1;
320: curtime = fl->sf_time;
321: for (f = fl; --f > fp-count; )
322: if (f->sf_time < curtime)
323: curtime = f->sf_time;
324: for (f = fl; --f > fp-count; )
325: if (f->sf_time == curtime)
326: break;
327: fp = f;
328: }
329:
330: /*
331: * Gotcha.
332: */
333: fp->sf_time = H.Time;
334: fp->sf_lines = H.Flines;
335: for (cp2 = fp->sf_name, cp = savedfile; *cp;)
336: *cp2++ = *cp++;
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);
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.