Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

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