|
|
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.12 (Berkeley) 5/28/90";
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:
27: char xstr[1]; /* make loader happy */
28: short tfile = -1; /* ditto */
29:
30: /*
31: *
32: * This program searches through the specified directory and then
33: * the directory _PATH_USRPRESERVE looking for an instance of the specified
34: * file from a crashed editor or a crashed system.
35: * If this file is found, it is unscrambled and written to
36: * the standard output.
37: *
38: * If this program terminates without a "broken pipe" diagnostic
39: * (i.e. the editor doesn't die right away) then the buffer we are
40: * writing from is removed when we finish. This is potentially a mistake
41: * as there is not enough handshaking to guarantee that the file has actually
42: * been recovered, but should suffice for most cases.
43: */
44:
45: /*
46: * For lint's sake...
47: */
48: #ifndef lint
49: #define ignorl(a) a
50: #endif
51:
52: /*
53: * Limit on the number of printed entries
54: * when an, e.g. ``ex -r'' command is given.
55: */
56: #define NENTRY 50
57:
58: char *ctime();
59: char nb[BUFSIZ];
60: int vercnt; /* Count number of versions of file found */
61:
62: main(argc, argv)
63: int argc;
64: char *argv[];
65: {
66: register char *cp;
67: register int b, i;
68:
69: /*
70: * Initialize as though the editor had just started.
71: */
72: fendcore = (line *) sbrk(0);
73: dot = zero = dol = fendcore;
74: one = zero + 1;
75: endcore = fendcore - 2;
76: iblock = oblock = -1;
77:
78: /*
79: * If given only a -r argument, then list the saved files.
80: */
81: if (argc == 2 && eq(argv[1], "-r")) {
82: listfiles(_PATH_PRESERVE);
83: exit(0);
84: }
85: if (argc != 3)
86: error(" Wrong number of arguments to exrecover", 0);
87:
88: CP(file, argv[2]);
89:
90: /*
91: * Search for this file.
92: */
93: findtmp(argv[1]);
94:
95: /*
96: * Got (one of the versions of) it, write it back to the editor.
97: */
98: cp = ctime(&H.Time);
99: cp[19] = 0;
100: fprintf(stderr, " [Dated: %s", cp);
101: fprintf(stderr, vercnt > 1 ? ", newest of %d saved]" : "]", vercnt);
102: H.Flines++;
103:
104: /*
105: * Allocate space for the line pointers from the temp file.
106: */
107: if ((int) sbrk((int) (H.Flines * sizeof (line))) == -1)
108: /*
109: * Good grief.
110: */
111: error(" Not enough core for lines", 0);
112: #ifdef DEBUG
113: fprintf(stderr, "%d lines\n", H.Flines);
114: #endif
115:
116: /*
117: * Now go get the blocks of seek pointers which are scattered
118: * throughout the temp file, reconstructing the incore
119: * line pointers at point of crash.
120: */
121: b = 0;
122: while (H.Flines > 0) {
123: ignorl(lseek(tfile, (long) blocks[b] * BUFSIZ, 0));
124: i = H.Flines < BUFSIZ / sizeof (line) ?
125: H.Flines * sizeof (line) : BUFSIZ;
126: if (read(tfile, (char *) dot, i) != i) {
127: perror(nb);
128: exit(1);
129: }
130: dot += i / sizeof (line);
131: H.Flines -= i / sizeof (line);
132: b++;
133: }
134: dot--; dol = dot;
135:
136: /*
137: * Sigh... due to sandbagging some lines may really not be there.
138: * Find and discard such. This shouldn't happen much.
139: */
140: scrapbad();
141:
142: /*
143: * Now if there were any lines in the recovered file
144: * write them to the standard output.
145: */
146: if (dol > zero) {
147: addr1 = one; addr2 = dol; io = 1;
148: putfile(0);
149: }
150:
151: /*
152: * Trash the saved buffer.
153: * Hopefully the system won't crash before the editor
154: * syncs the new recovered buffer; i.e. for an instant here
155: * you may lose if the system crashes because this file
156: * is gone, but the editor hasn't completed reading the recovered
157: * file from the pipe from us to it.
158: *
159: * This doesn't work if we are coming from an non-absolute path
160: * name since we may have chdir'ed but what the hay, noone really
161: * ever edits with temporaries in "." anyways.
162: */
163: if (nb[0] == '/')
164: ignore(unlink(nb));
165:
166: /*
167: * Adieu.
168: */
169: exit(0);
170: }
171:
172: /*
173: * Print an error message (notably not in error
174: * message file). If terminal is in RAW mode, then
175: * we should be writing output for "vi", so don't print
176: * a newline which would screw up the screen.
177: */
178: /*VARARGS2*/
179: error(str, inf)
180: char *str;
181: int inf;
182: {
183:
184: fprintf(stderr, str, inf);
185: (void)ioctl(2, TIOCGETP, &tty);
186: if ((tty.sg_flags & RAW) == 0)
187: fprintf(stderr, "\n");
188: exit(1);
189: }
190:
191: /*
192: * Here we save the information about files, when
193: * you ask us what files we have saved for you.
194: * We buffer file name, number of lines, and the time
195: * at which the file was saved.
196: */
197: struct svfile {
198: char sf_name[FNSIZE + 1];
199: int sf_lines;
200: char sf_entry[MAXNAMLEN + 1];
201: time_t sf_time;
202: };
203:
204: listfiles(dirname)
205: char *dirname;
206: {
207: register DIR *dir;
208: struct direct *dirent;
209: int ecount, qucmp();
210: register int f;
211: char *cp;
212: struct svfile *fp, svbuf[NENTRY];
213:
214: /*
215: * Open _PATH_PRESERVE, and go there to make things quick.
216: */
217: dir = opendir(dirname);
218: if (dir == NULL) {
219: perror(dirname);
220: return;
221: }
222: if (chdir(dirname) < 0) {
223: perror(dirname);
224: return;
225: }
226:
227: /*
228: * Look at the candidate files in _PATH_PRESERVE.
229: */
230: fp = &svbuf[0];
231: ecount = 0;
232: while ((dirent = readdir(dir)) != NULL) {
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(closedir(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 _PATH_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 _PATH_PRESERVE and, if we can get there, /tmp
381: * (actually the users "directory" option).
382: */
383: searchdir(dir);
384: if (chdir(_PATH_PRESERVE) == 0)
385: searchdir(_PATH_PRESERVE);
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 _PATH_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 DIR *dir;
427: char dbuf[BUFSIZ];
428:
429: dir = opendir(dirname);
430: if (dir == NULL)
431: return;
432: while ((dirent = readdir(dir)) != NULL) {
433: if (dirent->d_name[0] != 'E')
434: continue;
435: /*
436: * Got a file in the directory starting with E...
437: * Save a consed up name for the file to unlink
438: * later, and check that this is really a file
439: * we are looking for.
440: */
441: ignore(strcat(strcat(strcpy(nb, dirname), "/"), dirent->d_name));
442: if (yeah(nb)) {
443: /*
444: * Well, it is the file we are looking for.
445: * Is it more recent than any version we found before?
446: */
447: if (H.Time > besttime) {
448: /*
449: * A winner.
450: */
451: ignore(close(bestfd));
452: bestfd = dup(tfile);
453: besttime = H.Time;
454: CP(bestnb, nb);
455: }
456: /*
457: * Count versions so user can be told there are
458: * ``yet more pages to be turned''.
459: */
460: vercnt++;
461: }
462: ignore(close(tfile));
463: }
464: ignore(closedir(dir));
465: }
466:
467: /*
468: * Given a candidate file to be recovered, see
469: * if its really an editor temporary and of this
470: * user and the file specified.
471: */
472: yeah(name)
473: char *name;
474: {
475:
476: tfile = open(name, 2);
477: if (tfile < 0)
478: return (0);
479: if (read(tfile, (char *) &H, sizeof H) != sizeof H) {
480: nope:
481: ignore(close(tfile));
482: return (0);
483: }
484: if (!eq(savedfile, file))
485: goto nope;
486: if (getuid() != H.Uid)
487: goto nope;
488: /*
489: * This is old and stupid code, which
490: * puts a word LOST in the header block, so that lost lines
491: * can be made to point at it.
492: */
493: ignorl(lseek(tfile, (long)(BUFSIZ*HBLKS-8), 0));
494: ignore(write(tfile, "LOST", 5));
495: return (1);
496: }
497:
498: preserve()
499: {
500:
501: }
502:
503: /*
504: * Find the true end of the scratch file, and ``LOSE''
505: * lines which point into thin air. This lossage occurs
506: * due to the sandbagging of i/o which can cause blocks to
507: * be written in a non-obvious order, different from the order
508: * in which the editor tried to write them.
509: *
510: * Lines which are lost are replaced with the text LOST so
511: * they are easy to find. We work hard at pretty formatting here
512: * as lines tend to be lost in blocks.
513: *
514: * This only seems to happen on very heavily loaded systems, and
515: * not very often.
516: */
517: scrapbad()
518: {
519: register line *ip;
520: struct stat stbuf;
521: off_t size, maxt;
522: int bno, cnt, bad, was;
523: char bk[BUFSIZ];
524:
525: ignore(fstat(tfile, &stbuf));
526: size = stbuf.st_size;
527: maxt = (size >> SHFT) | (BNDRY-1);
528: bno = (maxt >> OFFBTS) & BLKMSK;
529: #ifdef DEBUG
530: fprintf(stderr, "size %ld, maxt %o, bno %d\n", size, maxt, bno);
531: #endif
532:
533: /*
534: * Look for a null separating two lines in the temp file;
535: * if last line was split across blocks, then it is lost
536: * if the last block is.
537: */
538: while (bno > 0) {
539: ignorl(lseek(tfile, (long) BUFSIZ * bno, 0));
540: cnt = read(tfile, (char *) bk, BUFSIZ);
541: while (cnt > 0)
542: if (bk[--cnt] == 0)
543: goto null;
544: bno--;
545: }
546: null:
547:
548: /*
549: * Magically calculate the largest valid pointer in the temp file,
550: * consing it up from the block number and the count.
551: */
552: maxt = ((bno << OFFBTS) | (cnt >> SHFT)) & ~1;
553: #ifdef DEBUG
554: fprintf(stderr, "bno %d, cnt %d, maxt %o\n", bno, cnt, maxt);
555: #endif
556:
557: /*
558: * Now cycle through the line pointers,
559: * trashing the Lusers.
560: */
561: was = bad = 0;
562: for (ip = one; ip <= dol; ip++)
563: if (*ip > maxt) {
564: #ifdef DEBUG
565: fprintf(stderr, "%d bad, %o > %o\n", ip - zero, *ip, maxt);
566: #endif
567: if (was == 0)
568: was = ip - zero;
569: *ip = ((HBLKS*BUFSIZ)-8) >> SHFT;
570: } else if (was) {
571: if (bad == 0)
572: fprintf(stderr, " [Lost line(s):");
573: fprintf(stderr, " %d", was);
574: if ((ip - 1) - zero > was)
575: fprintf(stderr, "-%d", (ip - 1) - zero);
576: bad++;
577: was = 0;
578: }
579: if (was != 0) {
580: if (bad == 0)
581: fprintf(stderr, " [Lost line(s):");
582: fprintf(stderr, " %d", was);
583: if (dol - zero != was)
584: fprintf(stderr, "-%d", dol - zero);
585: bad++;
586: }
587: if (bad)
588: fprintf(stderr, "]");
589: }
590:
591: /*
592: * Aw shucks, if we only had a (void) cast.
593: */
594: #ifdef lint
595: Ignorl(a)
596: long a;
597: {
598:
599: a = a;
600: }
601:
602: Ignore(a)
603: char *a;
604: {
605:
606: a = a;
607: }
608:
609: Ignorf(a)
610: int (*a)();
611: {
612:
613: a = a;
614: }
615:
616: ignorl(a)
617: long a;
618: {
619:
620: a = a;
621: }
622: #endif
623:
624: int cntch, cntln, cntodd, cntnull;
625: /*
626: * Following routines stolen mercilessly from ex.
627: */
628: putfile()
629: {
630: line *a1;
631: register char *fp, *lp;
632: register int nib;
633:
634: a1 = addr1;
635: clrstats();
636: cntln = addr2 - a1 + 1;
637: if (cntln == 0)
638: return;
639: nib = BUFSIZ;
640: fp = genbuf;
641: do {
642: getline(*a1++);
643: lp = linebuf;
644: for (;;) {
645: if (--nib < 0) {
646: nib = fp - genbuf;
647: if (write(io, genbuf, nib) != nib)
648: wrerror();
649: cntch += nib;
650: nib = 511;
651: fp = genbuf;
652: }
653: if ((*fp++ = *lp++) == 0) {
654: fp[-1] = '\n';
655: break;
656: }
657: }
658: } while (a1 <= addr2);
659: nib = fp - genbuf;
660: if (write(io, genbuf, nib) != nib)
661: wrerror();
662: cntch += nib;
663: }
664:
665: wrerror()
666: {
667:
668: syserror();
669: }
670:
671: clrstats()
672: {
673:
674: ninbuf = 0;
675: cntch = 0;
676: cntln = 0;
677: cntnull = 0;
678: cntodd = 0;
679: }
680:
681: #define READ 0
682: #define WRITE 1
683:
684: getline(tl)
685: line tl;
686: {
687: register char *bp, *lp;
688: register int nl;
689:
690: lp = linebuf;
691: bp = getblock(tl, READ);
692: nl = nleft;
693: tl &= ~OFFMSK;
694: while (*lp++ = *bp++)
695: if (--nl == 0) {
696: bp = getblock(tl += INCRMT, READ);
697: nl = nleft;
698: }
699: }
700:
701: int read();
702: int write();
703:
704: char *
705: getblock(atl, iof)
706: line atl;
707: int iof;
708: {
709: register int bno, off;
710:
711: bno = (atl >> OFFBTS) & BLKMSK;
712: off = (atl << SHFT) & LBTMSK;
713: if (bno >= NMBLKS)
714: error(" Tmp file too large");
715: nleft = BUFSIZ - off;
716: if (bno == iblock) {
717: ichanged |= iof;
718: return (ibuff + off);
719: }
720: if (bno == oblock)
721: return (obuff + off);
722: if (iof == READ) {
723: if (ichanged)
724: blkio(iblock, ibuff, write);
725: ichanged = 0;
726: iblock = bno;
727: blkio(bno, ibuff, read);
728: return (ibuff + off);
729: }
730: if (oblock >= 0)
731: blkio(oblock, obuff, write);
732: oblock = bno;
733: return (obuff + off);
734: }
735:
736: blkio(b, buf, iofcn)
737: short b;
738: char *buf;
739: int (*iofcn)();
740: {
741:
742: lseek(tfile, (long) (unsigned) b * BUFSIZ, 0);
743: if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ)
744: syserror();
745: }
746:
747: syserror()
748: {
749: char *strerror();
750:
751: dirtcnt = 0;
752: write(2, " ", 1);
753: error(strerror(errno));
754: exit(1);
755: }
756:
757: /*
758: * Must avoid stdio because expreserve uses sbrk to do memory
759: * allocation and stdio uses malloc.
760: */
761: fprintf(fp, fmt, a1, a2, a3, a4, a5)
762: FILE *fp;
763: char *fmt;
764: char *a1, *a2, *a3, *a4, *a5;
765: {
766: char buf[BUFSIZ];
767:
768: if (fp != stderr)
769: return;
770: sprintf(buf, fmt, a1, a2, a3, a4, a5);
771: write(2, buf, strlen(buf));
772: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.