|
|
1.1 root 1: /* Copyright (c) 1979 Regents of the University of California */
2: #include "ex.h"
3: #include "ex_temp.h"
4: #include "ex_tty.h"
5: #include "local/uparm.h"
6:
7: #undef BUFSIZ
8: #undef EOF
9: #undef NULL
10:
11: #include <stdio.h>
12: #include <sys/dir.h>
13:
14: /*
15: * Ex recovery program
16: * exrecover dir name
17: * exrecover -r
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();
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: gtty(2, &tty);
179: if ((tty.sg_flags & RAW) == 0)
180: fprintf(stderr, "\n");
181: exit(1);
182: }
183:
184: /*
185: * Here we save the information about files, when
186: * you ask us what files we have saved for you.
187: * We buffer file name, number of lines, and the time
188: * at which the file was saved.
189: */
190: struct svfile {
191: char sf_name[FNSIZE + 1];
192: int sf_lines;
193: char sf_entry[DIRSIZ + 1];
194: time_t sf_time;
195: };
196:
197: listfiles(dirname)
198: char *dirname;
199: {
200: register FILE *dir;
201: struct direct dirent;
202: int ecount, qucmp();
203: register int f;
204: char *cp;
205: struct svfile *fp, svbuf[NENTRY];
206:
207: /*
208: * Open usrpath(preserve), and go there to make things quick.
209: */
210: dir = fopen(dirname, "r");
211: if (dir == NULL) {
212: perror(dirname);
213: return;
214: }
215: if (chdir(dirname) < 0) {
216: perror(dirname);
217: return;
218: }
219:
220: /*
221: * Look at the candidate files in usrpath(preserve).
222: */
223: fp = &svbuf[0];
224: ecount = 0;
225: while (fread((char *) &dirent, sizeof dirent, 1, dir) == 1) {
226: if (dirent.d_ino == 0)
227: continue;
228: if (dirent.d_name[0] != 'E')
229: continue;
230: #ifdef DEBUG
231: fprintf(stderr, "considering %s\n", dirent.d_name);
232: #endif
233: /*
234: * Name begins with E; open it and
235: * make sure the uid in the header is our uid.
236: * If not, then don't bother with this file, it can't
237: * be ours.
238: */
239: f = open(dirent.d_name, 0);
240: if (f < 0) {
241: #ifdef DEBUG
242: fprintf(stderr, "open failed\n");
243: #endif
244: continue;
245: }
246: if (read(f, (char *) &H, sizeof H) != sizeof H) {
247: #ifdef DEBUG
248: fprintf(stderr, "culdnt read hedr\n");
249: #endif
250: ignore(close(f));
251: continue;
252: }
253: ignore(close(f));
254: if (getuid() != H.Uid) {
255: #ifdef DEBUG
256: fprintf(stderr, "uid wrong\n");
257: #endif
258: continue;
259: }
260:
261: /*
262: * Saved the day!
263: */
264: enter(fp++, dirent.d_name, ecount);
265: ecount++;
266: #ifdef DEBUG
267: fprintf(stderr, "entered file %s\n", dirent.d_name);
268: #endif
269: }
270: ignore(fclose(dir));
271:
272: /*
273: * If any files were saved, then sort them and print
274: * them out.
275: */
276: if (ecount == 0) {
277: fprintf(stderr, "No files saved.\n");
278: return;
279: }
280: qsort(&svbuf[0], ecount, sizeof svbuf[0], qucmp);
281: for (fp = &svbuf[0]; fp < &svbuf[ecount]; fp++) {
282: cp = ctime(&fp->sf_time);
283: cp[10] = 0;
284: fprintf(stderr, "On %s at ", cp);
285: cp[16] = 0;
286: fprintf(stderr, &cp[11]);
287: fprintf(stderr, " saved %d lines of file \"%s\"\n",
288: fp->sf_lines, fp->sf_name);
289: }
290: }
291:
292: /*
293: * Enter a new file into the saved file information.
294: */
295: enter(fp, fname, count)
296: struct svfile *fp;
297: char *fname;
298: {
299: register char *cp, *cp2;
300: register struct svfile *f, *fl;
301: time_t curtime, itol();
302:
303: f = 0;
304: if (count >= NENTRY) {
305: /*
306: * My god, a huge number of saved files.
307: * Would you work on a system that crashed this
308: * often? Hope not. So lets trash the oldest
309: * as the most useless.
310: *
311: * (I wonder if this code has ever run?)
312: */
313: fl = fp - count + NENTRY - 1;
314: curtime = fl->sf_time;
315: for (f = fl; --f > fp-count; )
316: if (f->sf_time < curtime)
317: curtime = f->sf_time;
318: for (f = fl; --f > fp-count; )
319: if (f->sf_time == curtime)
320: break;
321: fp = f;
322: }
323:
324: /*
325: * Gotcha.
326: */
327: fp->sf_time = H.Time;
328: fp->sf_lines = H.Flines;
329: for (cp2 = fp->sf_name, cp = savedfile; *cp;)
330: *cp2++ = *cp++;
331: for (cp2 = fp->sf_entry, cp = fname; *cp && cp-fname < 14;)
332: *cp2++ = *cp++;
333: *cp2++ = 0;
334: }
335:
336: /*
337: * Do the qsort compare to sort the entries first by file name,
338: * then by modify time.
339: */
340: qucmp(p1, p2)
341: struct svfile *p1, *p2;
342: {
343: register int t;
344:
345: if (t = strcmp(p1->sf_name, p2->sf_name))
346: return(t);
347: if (p1->sf_time > p2->sf_time)
348: return(-1);
349: return(p1->sf_time < p2->sf_time);
350: }
351:
352: /*
353: * Scratch for search.
354: */
355: char bestnb[BUFSIZ]; /* Name of the best one */
356: long besttime; /* Time at which the best file was saved */
357: int bestfd; /* Keep best file open so it dont vanish */
358:
359: /*
360: * Look for a file, both in the users directory option value
361: * (i.e. usually /tmp) and in usrpath(preserve).
362: * Want to find the newest so we search on and on.
363: */
364: findtmp(dir)
365: char *dir;
366: {
367:
368: /*
369: * No name or file so far.
370: */
371: bestnb[0] = 0;
372: bestfd = -1;
373:
374: /*
375: * Search usrpath(preserve) and, if we can get there, /tmp
376: * (actually the users "directory" option).
377: */
378: searchdir(dir);
379: if (chdir(mydir) == 0)
380: searchdir(mydir);
381: if (bestfd != -1) {
382: /*
383: * Gotcha.
384: * Put the file (which is already open) in the file
385: * used by the temp file routines, and save its
386: * name for later unlinking.
387: */
388: tfile = bestfd;
389: CP(nb, bestnb);
390: ignorl(lseek(tfile, 0l, 0));
391:
392: /*
393: * Gotta be able to read the header or fall through
394: * to lossage.
395: */
396: if (read(tfile, (char *) &H, sizeof H) == sizeof H)
397: return;
398: }
399:
400: /*
401: * Extreme lossage...
402: */
403: error(" File not found", 0);
404: }
405:
406: /*
407: * Search for the file in directory dirname.
408: *
409: * Don't chdir here, because the users directory
410: * may be ".", and we would move away before we searched it.
411: * Note that we actually chdir elsewhere (because it is too slow
412: * to look around in usrpath(preserve) without chdir'ing there) so we
413: * can't win, because we don't know the name of '.' and if the path
414: * name of the file we want to unlink is relative, rather than absolute
415: * we won't be able to find it again.
416: */
417: searchdir(dirname)
418: char *dirname;
419: {
420: struct direct dirent;
421: register FILE *dir;
422: char dbuf[BUFSIZ];
423:
424: dir = fopen(dirname, "r");
425: if (dir == NULL)
426: return;
427: setbuf(dir, dbuf);
428: while (fread((char *) &dirent, sizeof dirent, 1, dir) == 1) {
429: if (dirent.d_ino == 0)
430: continue;
431: if (dirent.d_name[0] != 'E' || dirent.d_name[DIRSIZ - 1] != 0)
432: continue;
433: /*
434: * Got a file in the directory starting with E...
435: * Save a consed up name for the file to unlink
436: * later, and check that this is really a file
437: * we are looking for.
438: */
439: ignore(strcat(strcat(strcpy(nb, dirname), "/"), dirent.d_name));
440: if (yeah(nb)) {
441: /*
442: * Well, it is the file we are looking for.
443: * Is it more recent than any version we found before?
444: */
445: if (H.Time > besttime) {
446: /*
447: * A winner.
448: */
449: ignore(close(bestfd));
450: bestfd = dup(tfile);
451: besttime = H.Time;
452: CP(bestnb, nb);
453: }
454: /*
455: * Count versions so user can be told there are
456: * ``yet more pages to be turned''.
457: */
458: vercnt++;
459: }
460: ignore(close(tfile));
461: }
462: ignore(fclose(dir));
463: }
464:
465: /*
466: * Given a candidate file to be recovered, see
467: * if its really an editor temporary and of this
468: * user and the file specified.
469: */
470: yeah(name)
471: char *name;
472: {
473:
474: tfile = open(name, 2);
475: if (tfile < 0)
476: return (0);
477: if (read(tfile, (char *) &H, sizeof H) != sizeof H) {
478: nope:
479: ignore(close(tfile));
480: return (0);
481: }
482: if (!eq(savedfile, file))
483: goto nope;
484: if (getuid() != H.Uid)
485: goto nope;
486: /*
487: * This is old and stupid code, which
488: * puts a word LOST in the header block, so that lost lines
489: * can be made to point at it.
490: */
491: ignorl(lseek(tfile, (long)(BUFSIZ*HBLKS-8), 0));
492: ignore(write(tfile, "LOST", 5));
493: return (1);
494: }
495:
496: preserve()
497: {
498:
499: }
500:
501: /*
502: * Find the true end of the scratch file, and ``LOSE''
503: * lines which point into thin air. This lossage occurs
504: * due to the sandbagging of i/o which can cause blocks to
505: * be written in a non-obvious order, different from the order
506: * in which the editor tried to write them.
507: *
508: * Lines which are lost are replaced with the text LOST so
509: * they are easy to find. We work hard at pretty formatting here
510: * as lines tend to be lost in blocks.
511: *
512: * This only seems to happen on very heavily loaded systems, and
513: * not very often.
514: */
515: scrapbad()
516: {
517: register line *ip;
518: struct stat stbuf;
519: off_t size, maxt;
520: int bno, cnt, bad, was;
521: char bk[BUFSIZ];
522:
523: ignore(fstat(tfile, &stbuf));
524: size = stbuf.st_size;
525: maxt = (size >> SHFT) | (BNDRY-1);
526: bno = (maxt >> OFFBTS) & BLKMSK;
527: #ifdef DEBUG
528: fprintf(stderr, "size %ld, maxt %o, bno %d\n", size, maxt, bno);
529: #endif
530:
531: /*
532: * Look for a null separating two lines in the temp file;
533: * if last line was split across blocks, then it is lost
534: * if the last block is.
535: */
536: while (bno > 0) {
537: ignorl(lseek(tfile, (long) BUFSIZ * bno, 0));
538: cnt = read(tfile, (char *) bk, BUFSIZ);
539: while (cnt > 0)
540: if (bk[--cnt] == 0)
541: goto null;
542: bno--;
543: }
544: null:
545:
546: /*
547: * Magically calculate the largest valid pointer in the temp file,
548: * consing it up from the block number and the count.
549: */
550: maxt = ((bno << OFFBTS) | (cnt >> SHFT)) & ~1;
551: #ifdef DEBUG
552: fprintf(stderr, "bno %d, cnt %d, maxt %o\n", bno, cnt, maxt);
553: #endif
554:
555: /*
556: * Now cycle through the line pointers,
557: * trashing the Lusers.
558: */
559: was = bad = 0;
560: for (ip = one; ip <= dol; ip++)
561: if (*ip > maxt) {
562: #ifdef DEBUG
563: fprintf(stderr, "%d bad, %o > %o\n", ip - zero, *ip, maxt);
564: #endif
565: if (was == 0)
566: was = ip - zero;
567: *ip = ((HBLKS*BUFSIZ)-8) >> SHFT;
568: } else if (was) {
569: if (bad == 0)
570: fprintf(stderr, " [Lost line(s):");
571: fprintf(stderr, " %d", was);
572: if ((ip - 1) - zero > was)
573: fprintf(stderr, "-%d", (ip - 1) - zero);
574: bad++;
575: was = 0;
576: }
577: if (was != 0) {
578: if (bad == 0)
579: fprintf(stderr, " [Lost line(s):");
580: fprintf(stderr, " %d", was);
581: if (dol - zero != was)
582: fprintf(stderr, "-%d", dol - zero);
583: bad++;
584: }
585: if (bad)
586: fprintf(stderr, "]");
587: }
588:
589: /*
590: * Aw shucks, if we only had a (void) cast.
591: */
592: #ifdef lint
593: Ignorl(a)
594: long a;
595: {
596:
597: a = a;
598: }
599:
600: Ignore(a)
601: char *a;
602: {
603:
604: a = a;
605: }
606:
607: Ignorf(a)
608: int (*a)();
609: {
610:
611: a = a;
612: }
613:
614: ignorl(a)
615: long a;
616: {
617:
618: a = a;
619: }
620: #endif
621:
622: int cntch, cntln, cntodd, cntnull;
623: /*
624: * Following routines stolen mercilessly from ex.
625: */
626: putfile()
627: {
628: line *a1;
629: register char *fp, *lp;
630: register int nib;
631:
632: a1 = addr1;
633: clrstats();
634: cntln = addr2 - a1 + 1;
635: if (cntln == 0)
636: return;
637: nib = BUFSIZ;
638: fp = genbuf;
639: do {
640: getline(*a1++);
641: lp = linebuf;
642: for (;;) {
643: if (--nib < 0) {
644: nib = fp - genbuf;
645: if (write(io, genbuf, nib) != nib)
646: wrerror();
647: cntch += nib;
648: nib = 511;
649: fp = genbuf;
650: }
651: if ((*fp++ = *lp++) == 0) {
652: fp[-1] = '\n';
653: break;
654: }
655: }
656: } while (a1 <= addr2);
657: nib = fp - genbuf;
658: if (write(io, genbuf, nib) != nib)
659: wrerror();
660: cntch += nib;
661: }
662:
663: wrerror()
664: {
665:
666: syserror();
667: }
668:
669: clrstats()
670: {
671:
672: ninbuf = 0;
673: cntch = 0;
674: cntln = 0;
675: cntnull = 0;
676: cntodd = 0;
677: }
678:
679: #define READ 0
680: #define WRITE 1
681:
682: getline(tl)
683: line tl;
684: {
685: register char *bp, *lp;
686: register int nl;
687:
688: lp = linebuf;
689: bp = getblock(tl, READ);
690: nl = nleft;
691: tl &= ~OFFMSK;
692: while (*lp++ = *bp++)
693: if (--nl == 0) {
694: bp = getblock(tl += INCRMT, READ);
695: nl = nleft;
696: }
697: }
698:
699: int read();
700: int write();
701:
702: char *
703: getblock(atl, iof)
704: line atl;
705: int iof;
706: {
707: register int bno, off;
708:
709: bno = (atl >> OFFBTS) & BLKMSK;
710: off = (atl << SHFT) & LBTMSK;
711: if (bno >= NMBLKS)
712: error(" Tmp file too large");
713: nleft = BUFSIZ - off;
714: if (bno == iblock) {
715: ichanged |= iof;
716: return (ibuff + off);
717: }
718: if (bno == oblock)
719: return (obuff + off);
720: if (iof == READ) {
721: if (ichanged)
722: blkio(iblock, ibuff, write);
723: ichanged = 0;
724: iblock = bno;
725: blkio(bno, ibuff, read);
726: return (ibuff + off);
727: }
728: if (oblock >= 0)
729: blkio(oblock, obuff, write);
730: oblock = bno;
731: return (obuff + off);
732: }
733:
734: blkio(b, buf, iofcn)
735: short b;
736: char *buf;
737: int (*iofcn)();
738: {
739:
740: lseek(tfile, (long) (unsigned) b * BUFSIZ, 0);
741: if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ)
742: syserror();
743: }
744:
745: syserror()
746: {
747: extern int sys_nerr;
748: extern char *sys_errlist[];
749:
750: dirtcnt = 0;
751: write(2, " ", 1);
752: if (errno >= 0 && errno <= sys_nerr)
753: error(sys_errlist[errno]);
754: else
755: error("System error %d", errno);
756: exit(1);
757: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.