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