BSD 3 development
[unix-history] / .ref-BSD-2 / src / ex / exrecover.c
CommitLineData
d4285918
BJ
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
6#undef BUFSIZ
7#undef EOF
8#undef NULL
9
10#include <stdio.h>
11#include <sys/dir.h>
12
13/*
14 * Ex recovery program
15 * exrecover dir name
16 * exrecover -r
17 *
18 * This program searches through the specified directory and then
19 * the directory /usr/preserve looking for an instance of the specified
20 * file from a crashed editor or a crashed system.
21 * If this file is found, it is unscrambled and written to
22 * the standard output.
23 *
24 * If this program terminates without a "broken pipe" diagnostic
25 * (i.e. the editor doesn't die right away) then the buffer we are
26 * writing from is removed when we finish. This is potentially a mistake
27 * as there is not enough handshaking to guarantee that the file has actually
28 * been recovered, but should suffice for most cases.
29 */
30
31/*
32 * For lint's sake...
33 */
34#ifndef lint
35#define ignorl(a) a
36#endif
37
38/*
39 * This directory definition also appears (obviously) in expreserve.c.
40 * Change both if you change either.
41 */
42char mydir[] = "/usr/preserve";
43
44/*
45 * Limit on the number of printed entries
46 * when an, e.g. ``ex -r'' command is given.
47 */
48#define NENTRY 50
49
50char *ctime();
51char nb[BUFSIZ];
52int vercnt; /* Count number of versions of file found */
53
54main(argc, argv)
55 int argc;
56 char *argv[];
57{
58 register char *cp;
59 register int b, i;
60
61 /*
62 * Initialize as though the editor had just started.
63 */
64 fendcore = (line *) sbrk(0);
65 dot = zero = dol = fendcore;
66 one = zero + 1;
67 endcore = fendcore - 2;
68 iblock = oblock = -1;
69
70 /*
71 * If given only a -r argument, then list the saved files.
72 */
73 if (argc == 2 && eq(argv[1], "-r")) {
74 listfiles(mydir);
75 exit(0);
76 }
77 if (argc != 3)
78 error(" Wrong number of arguments to exrecover", 0);
79
80 CP(file, argv[2]);
81
82 /*
83 * Search for this file.
84 */
85 findtmp(argv[1]);
86
87 /*
88 * Got (one of the versions of) it, write it back to the editor.
89 */
90 cp = ctime(&H.Time);
91 cp[19] = 0;
92 fprintf(stderr, " [Dated: %s", cp);
93 fprintf(stderr, vercnt > 1 ? ", newest of %d saved]" : "]", vercnt);
94 H.Flines++;
95
96 /*
97 * Allocate space for the line pointers from the temp file.
98 */
99 if ((int) sbrk((int) (H.Flines * sizeof (line))) == -1)
100 /*
101 * Good grief.
102 */
103 error(" Not enough core for lines", 0);
104#ifdef DEBUG
105 fprintf(stderr, "%d lines\n", H.Flines);
106#endif
107
108 /*
109 * Now go get the blocks of seek pointers which are scattered
110 * throughout the temp file, reconstructing the incore
111 * line pointers at point of crash.
112 */
113 b = 0;
114 while (H.Flines > 0) {
115 ignorl(lseek(tfile, (long) blocks[b] * BUFSIZ, 0));
116 i = H.Flines < BUFSIZ / sizeof (line) ?
117 H.Flines * sizeof (line) : BUFSIZ;
118 if (read(tfile, (char *) dot, i) != i) {
119 perror(nb);
120 exit(1);
121 }
122 dot += i / sizeof (line);
123 H.Flines -= i / sizeof (line);
124 b++;
125 }
126 dot--; dol = dot;
127
128 /*
129 * Sigh... due to sandbagging some lines may really not be there.
130 * Find and discard such. This shouldn't happen much.
131 */
132 scrapbad();
133
134 /*
135 * Now if there were any lines in the recovered file
136 * write them to the standard output.
137 */
138 if (dol > zero) {
139 addr1 = one; addr2 = dol; io = 1;
140 putfile();
141 }
142
143 /*
144 * Trash the saved buffer.
145 * Hopefully the system won't crash before the editor
146 * syncs the new recovered buffer; i.e. for an instant here
147 * you may lose if the system crashes because this file
148 * is gone, but the editor hasn't completed reading the recovered
149 * file from the pipe from us to it.
150 *
151 * This doesn't work if we are coming from an non-absolute path
152 * name since we may have chdir'ed but what the hay, noone really
153 * ever edits with temporaries in "." anyways.
154 */
155 if (nb[0] == '/')
156 ignore(unlink(nb));
157
158 /*
159 * Adieu.
160 */
161 exit(0);
162}
163
164/*
165 * Print an error message (notably not in error
166 * message file). If terminal is in RAW mode, then
167 * we should be writing output for "vi", so don't print
168 * a newline which would screw up the screen.
169 */
170/*VARARGS2*/
171error(str, inf)
172 char *str;
173 int inf;
174{
175
176 fprintf(stderr, str, inf);
177 gtty(2, &tty);
178 if ((tty.sg_flags & RAW) == 0)
179 fprintf(stderr, "\n");
180 exit(1);
181}
182
183/*
184 * Here we save the information about files, when
185 * you ask us what files we have saved for you.
186 * We buffer file name, number of lines, and the time
187 * at which the file was saved.
188 */
189struct svfile {
190 char sf_name[FNSIZE + 1];
191 int sf_lines;
192 char sf_entry[DIRSIZ + 1];
193 time_t sf_time;
194};
195
196listfiles(dirname)
197 char *dirname;
198{
199 register FILE *dir;
200 struct direct dirent;
201 int ecount, qucmp();
202 register int f;
203 char *cp;
204 struct svfile *fp, svbuf[NENTRY];
205
206 /*
207 * Open /usr/preserve, and go there to make things quick.
208 */
209 dir = fopen(dirname, "r");
210 if (dir == NULL) {
211 perror(dirname);
212 return;
213 }
214 if (chdir(dirname) < 0) {
215 perror(dirname);
216 return;
217 }
218
219 /*
220 * Look at the candidate files in /usr/preserve.
221 */
222 fp = &svbuf[0];
223 ecount = 0;
224 while (fread((char *) &dirent, sizeof dirent, 1, dir) == 1) {
225 if (dirent.d_ino == 0)
226 continue;
227 if (dirent.d_name[0] != 'E')
228 continue;
229#ifdef DEBUG
230 fprintf(stderr, "considering %s\n", dirent.d_name);
231#endif
232 /*
233 * Name begins with E; open it and
234 * make sure the uid in the header is our uid.
235 * If not, then don't bother with this file, it can't
236 * be ours.
237 */
238 f = open(dirent.d_name, 0);
239 if (f < 0) {
240#ifdef DEBUG
241 fprintf(stderr, "open failed\n");
242#endif
243 continue;
244 }
245 if (read(f, (char *) &H, sizeof H) != sizeof H) {
246#ifdef DEBUG
247 fprintf(stderr, "culdnt read hedr\n");
248#endif
249 ignore(close(f));
250 continue;
251 }
252 ignore(close(f));
253 if (getuid() != H.Uid) {
254#ifdef DEBUG
255 fprintf(stderr, "uid wrong\n");
256#endif
257 continue;
258 }
259
260 /*
261 * Saved the day!
262 */
263 enter(fp++, dirent.d_name, ecount);
264 ecount++;
265#ifdef DEBUG
266 fprintf(stderr, "entered file %s\n", dirent.d_name);
267#endif
268 }
269 ignore(fclose(dir));
270
271 /*
272 * If any files were saved, then sort them and print
273 * them out.
274 */
275 if (ecount == 0) {
276 fprintf(stderr, "No files saved.\n");
277 return;
278 }
279 qsort(&svbuf[0], ecount, sizeof svbuf[0], qucmp);
280 for (fp = &svbuf[0]; fp < &svbuf[ecount]; fp++) {
281 cp = ctime(&fp->sf_time);
282 cp[10] = 0;
283 fprintf(stderr, "On %s at ", cp);
284 cp[16] = 0;
285 fprintf(stderr, &cp[11]);
286 fprintf(stderr, " saved %d lines of file \"%s\"\n",
287 fp->sf_lines, fp->sf_name);
288 }
289}
290
291/*
292 * Enter a new file into the saved file information.
293 */
294enter(fp, fname, count)
295 struct svfile *fp;
296 char *fname;
297{
298 register char *cp, *cp2;
299 register struct svfile *f, *fl;
300 time_t curtime, itol();
301
302 f = 0;
303 if (count >= NENTRY) {
304 /*
305 * My god, a huge number of saved files.
306 * Would you work on a system that crashed this
307 * often? Hope not. So lets trash the oldest
308 * as the most useless.
309 *
310 * (I wonder if this code has ever run?)
311 */
312 fl = fp - count + NENTRY - 1;
313 curtime = fl->sf_time;
314 for (f = fl; --f > fp-count; )
315 if (f->sf_time < curtime)
316 curtime = f->sf_time;
317 for (f = fl; --f > fp-count; )
318 if (f->sf_time == curtime)
319 break;
320 fp = f;
321 }
322
323 /*
324 * Gotcha.
325 */
326 fp->sf_time = H.Time;
327 fp->sf_lines = H.Flines;
328 for (cp2 = fp->sf_name, cp = savedfile; *cp;)
329 *cp2++ = *cp++;
330 for (cp2 = fp->sf_entry, cp = fname; *cp && cp-fname < 14;)
331 *cp2++ = *cp++;
332 *cp2++ = 0;
333}
334
335/*
336 * Do the qsort compare to sort the entries first by file name,
337 * then by modify time.
338 */
339qucmp(p1, p2)
340 struct svfile *p1, *p2;
341{
342 register int t;
343
344 if (t = strcmp(p1->sf_name, p2->sf_name))
345 return(t);
346 if (p1->sf_time > p2->sf_time)
347 return(-1);
348 return(p1->sf_time < p2->sf_time);
349}
350
351/*
352 * Scratch for search.
353 */
354char bestnb[BUFSIZ]; /* Name of the best one */
355long besttime; /* Time at which the best file was saved */
356int bestfd; /* Keep best file open so it dont vanish */
357
358/*
359 * Look for a file, both in the users directory option value
360 * (i.e. usually /tmp) and in /usr/preserve.
361 * Want to find the newest so we search on and on.
362 */
363findtmp(dir)
364 char *dir;
365{
366
367 /*
368 * No name or file so far.
369 */
370 bestnb[0] = 0;
371 bestfd = -1;
372
373 /*
374 * Search /usr/preserve and, if we can get there, /tmp
375 * (actually the users "directory" option).
376 */
377 searchdir(dir);
378 if (chdir(mydir) == 0)
379 searchdir(mydir);
380 if (bestfd != -1) {
381 /*
382 * Gotcha.
383 * Put the file (which is already open) in the file
384 * used by the temp file routines, and save its
385 * name for later unlinking.
386 */
387 tfile = bestfd;
388 CP(nb, bestnb);
389 ignorl(lseek(tfile, 0l, 0));
390
391 /*
392 * Gotta be able to read the header or fall through
393 * to lossage.
394 */
395 if (read(tfile, (char *) &H, sizeof H) == sizeof H)
396 return;
397 }
398
399 /*
400 * Extreme lossage...
401 */
402 error(" File not found", 0);
403}
404
405/*
406 * Search for the file in directory dirname.
407 *
408 * Don't chdir here, because the users directory
409 * may be ".", and we would move away before we searched it.
410 * Note that we actually chdir elsewhere (because it is too slow
411 * to look around in /usr/preserve without chdir'ing there) so we
412 * can't win, because we don't know the name of '.' and if the path
413 * name of the file we want to unlink is relative, rather than absolute
414 * we won't be able to find it again.
415 */
416searchdir(dirname)
417 char *dirname;
418{
419 struct direct dirent;
420 register FILE *dir;
421 char dbuf[BUFSIZ];
422
423 dir = fopen(dirname, "r");
424 if (dir == NULL)
425 return;
426 setbuf(dir, dbuf);
427 while (fread((char *) &dirent, sizeof dirent, 1, dir) == 1) {
428 if (dirent.d_ino == 0)
429 continue;
430 if (dirent.d_name[0] != 'E' || dirent.d_name[DIRSIZ - 1] != 0)
431 continue;
432 /*
433 * Got a file in the directory starting with E...
434 * Save a consed up name for the file to unlink
435 * later, and check that this is really a file
436 * we are looking for.
437 */
438 ignore(strcat(strcat(strcpy(nb, dirname), "/"), dirent.d_name));
439 if (yeah(nb)) {
440 /*
441 * Well, it is the file we are looking for.
442 * Is it more recent than any version we found before?
443 */
444 if (H.Time > besttime) {
445 /*
446 * A winner.
447 */
448 ignore(close(bestfd));
449 bestfd = dup(tfile);
450 besttime = H.Time;
451 CP(bestnb, nb);
452 }
453 /*
454 * Count versions so user can be told there are
455 * ``yet more pages to be turned''.
456 */
457 vercnt++;
458 }
459 ignore(close(tfile));
460 }
461 ignore(fclose(dir));
462}
463
464/*
465 * Given a candidate file to be recovered, see
466 * if its really an editor temporary and of this
467 * user and the file specified.
468 */
469yeah(name)
470 char *name;
471{
472
473 tfile = open(name, 2);
474 if (tfile < 0)
475 return (0);
476 if (read(tfile, (char *) &H, sizeof H) != sizeof H) {
477nope:
478 ignore(close(tfile));
479 return (0);
480 }
481 if (!eq(savedfile, file))
482 goto nope;
483 if (getuid() != H.Uid)
484 goto nope;
485 /*
486 * This is old and stupid code, which
487 * puts a word LOST in the header block, so that lost lines
488 * can be made to point at it.
489 */
490 ignorl(lseek(tfile, 504l, 0));
491 ignore(write(tfile, "LOST", 5));
492 return (1);
493}
494
495preserve()
496{
497
498}
499
500/*
501 * Find the true end of the scratch file, and ``LOSE''
502 * lines which point into thin air. This lossage occurs
503 * due to the sandbagging of i/o which can cause blocks to
504 * be written in a non-obvious order, different from the order
505 * in which the editor tried to write them.
506 *
507 * Lines which are lost are replaced with the text LOST so
508 * they are easy to find. We work hard at pretty formatting here
509 * as lines tend to be lost in blocks.
510 *
511 * This only seems to happen on very heavily loaded systems, and
512 * not very often.
513 */
514scrapbad()
515{
516 register line *ip;
517 struct stat stbuf;
518 off_t size, maxt;
519 int bno, cnt, bad, was;
520 char bk[BUFSIZ];
521
522 ignore(fstat(tfile, &stbuf));
523 size = stbuf.st_size;
524 maxt = (size >> SHFT) | 7;
525 bno = (maxt >> OFFBTS) & BLKMSK;
526#ifdef DEBUG
527 fprintf(stderr, "size %ld, maxt %o, bno %d\n", size, maxt, bno);
528#endif
529
530 /*
531 * Look for a null separating two lines in the temp file;
532 * if last line was split across blocks, then it is lost
533 * if the last block is.
534 */
535 while (bno > 0) {
536 ignorl(lseek(tfile, (long) BUFSIZ * bno, 0));
537 cnt = read(tfile, (char *) bk, BUFSIZ);
538 while (cnt > 0)
539 if (bk[--cnt] == 0)
540 goto null;
541 bno--;
542 }
543null:
544
545 /*
546 * Magically calculate the largest valid pointer in the temp file,
547 * consing it up from the block number and the count.
548 */
549 maxt = ((bno << OFFBTS) | (cnt >> SHFT)) & ~1;
550#ifdef DEBUG
551 fprintf(stderr, "bno %d, cnt %d, maxt %o\n", bno, cnt, maxt);
552#endif
553
554 /*
555 * Now cycle through the line pointers,
556 * trashing the Lusers.
557 */
558 was = bad = 0;
559 for (ip = one; ip <= dol; ip++)
560 if (*ip > maxt) {
561#ifdef DEBUG
562 fprintf(stderr, "%d bad, %o > %o\n", ip - zero, *ip, maxt);
563#endif
564 if (was == 0)
565 was = ip - zero;
566 *ip = 504 >> SHFT;
567 } else if (was) {
568 if (bad == 0)
569 fprintf(stderr, " [Lost line(s):");
570 fprintf(stderr, " %d", was);
571 if ((ip - 1) - zero > was)
572 fprintf(stderr, "-%d", (ip - 1) - zero);
573 bad++;
574 was = 0;
575 }
576 if (was != 0) {
577 if (bad == 0)
578 fprintf(stderr, " [Lost line(s):");
579 fprintf(stderr, " %d", was);
580 if (dol - zero != was)
581 fprintf(stderr, "-%d", dol - zero);
582 bad++;
583 }
584 if (bad)
585 fprintf(stderr, "]");
586}
587
588/*
589 * Aw shucks, if we only had a (void) cast.
590 */
591#ifdef lint
592Ignorl(a)
593 long a;
594{
595
596 a = a;
597}
598
599Ignore(a)
600 char *a;
601{
602
603 a = a;
604}
605
606Ignorf(a)
607 int (*a)();
608{
609
610 a = a;
611}
612
613ignorl(a)
614 long a;
615{
616
617 a = a;
618}
619#endif
620
621int cntch, cntln, cntodd, cntnull;
622/*
623 * Following routines stolen mercilessly from ex.
624 */
625putfile()
626{
627 line *a1;
628 register char *fp, *lp;
629 register int nib;
630
631 a1 = addr1;
632 clrstats();
633 cntln = addr2 - a1 + 1;
634 if (cntln == 0)
635 return;
636 nib = BUFSIZ;
637 fp = genbuf;
638 do {
639 getline(*a1++);
640 lp = linebuf;
641 for (;;) {
642 if (--nib < 0) {
643 nib = fp - genbuf;
644 if (write(io, genbuf, nib) != nib)
645 wrerror();
646 cntch += nib;
647 nib = 511;
648 fp = genbuf;
649 }
650 if ((*fp++ = *lp++) == 0) {
651 fp[-1] = '\n';
652 break;
653 }
654 }
655 } while (a1 <= addr2);
656 nib = fp - genbuf;
657 if (write(io, genbuf, nib) != nib)
658 wrerror();
659 cntch += nib;
660}
661
662wrerror()
663{
664
665 syserror();
666}
667
668clrstats()
669{
670
671 ninbuf = 0;
672 cntch = 0;
673 cntln = 0;
674 cntnull = 0;
675 cntodd = 0;
676}
677
678#define READ 0
679#define WRITE 1
680
681getline(tl)
682 line tl;
683{
684 register char *bp, *lp;
685 register int nl;
686
687 lp = linebuf;
688 bp = getblock(tl, READ);
689 nl = nleft;
690 tl &= ~OFFMSK;
691 while (*lp++ = *bp++)
692 if (--nl == 0) {
693 bp = getblock(tl += INCRMT, READ);
694 nl = nleft;
695 }
696}
697
698int read();
699int write();
700
701char *
702getblock(atl, iof)
703 line atl;
704 int iof;
705{
706 register int bno, off;
707
708 bno = (atl >> OFFBTS) & BLKMSK;
709 off = (atl << SHFT) & LBTMSK;
710 if (bno >= NMBLKS)
711 error(" Tmp file too large");
712 nleft = BUFSIZ - off;
713 if (bno == iblock) {
714 ichanged |= iof;
715 return (ibuff + off);
716 }
717 if (bno == oblock)
718 return (obuff + off);
719 if (iof == READ) {
720 if (ichanged)
721 blkio(iblock, ibuff, write);
722 ichanged = 0;
723 iblock = bno;
724 blkio(bno, ibuff, read);
725 return (ibuff + off);
726 }
727 if (oblock >= 0)
728 blkio(oblock, obuff, write);
729 oblock = bno;
730 return (obuff + off);
731}
732
733blkio(b, buf, iofcn)
734 short b;
735 char *buf;
736 int (*iofcn)();
737{
738
739 lseek(tfile, (long) (unsigned) b * BUFSIZ, 0);
740 if ((*iofcn)(tfile, buf, BUFSIZ) != 512)
741 syserror();
742}
743
744syserror()
745{
746 extern int sys_nerr;
747 extern char *sys_errlist[];
748
749 dirtcnt = 0;
750 write(2, " ", 1);
751 if (errno >= 0 && errno <= sys_nerr)
752 error(sys_errlist[errno]);
753 else
754 error("System error %d", errno);
755 exit(1);
756}