From 2fb7cb8cbbb50dfbc703b5a9667a45c076d3837a Mon Sep 17 00:00:00 2001 From: Bill Joy Date: Sat, 5 Jan 1980 08:45:40 -0800 Subject: [PATCH] BSD 3 development Work on file usr/src/cmd/ex/exrecover.c Synthesized-from: 3bsd --- usr/src/cmd/ex/exrecover.c | 757 +++++++++++++++++++++++++++++++++++++ 1 file changed, 757 insertions(+) create mode 100644 usr/src/cmd/ex/exrecover.c diff --git a/usr/src/cmd/ex/exrecover.c b/usr/src/cmd/ex/exrecover.c new file mode 100644 index 0000000000..9781fb9f8d --- /dev/null +++ b/usr/src/cmd/ex/exrecover.c @@ -0,0 +1,757 @@ +/* Copyright (c) 1979 Regents of the University of California */ +#include "ex.h" +#include "ex_temp.h" +#include "ex_tty.h" +#include "local/uparm.h" + +#undef BUFSIZ +#undef EOF +#undef NULL + +#include +#include + +/* + * Ex recovery program + * exrecover dir name + * exrecover -r + * + * This program searches through the specified directory and then + * the directory usrpath(preserve) looking for an instance of the specified + * file from a crashed editor or a crashed system. + * If this file is found, it is unscrambled and written to + * the standard output. + * + * If this program terminates without a "broken pipe" diagnostic + * (i.e. the editor doesn't die right away) then the buffer we are + * writing from is removed when we finish. This is potentially a mistake + * as there is not enough handshaking to guarantee that the file has actually + * been recovered, but should suffice for most cases. + */ + +/* + * For lint's sake... + */ +#ifndef lint +#define ignorl(a) a +#endif + +/* + * This directory definition also appears (obviously) in expreserve.c. + * Change both if you change either. + */ +char mydir[] = usrpath(preserve); + +/* + * Limit on the number of printed entries + * when an, e.g. ``ex -r'' command is given. + */ +#define NENTRY 50 + +char *ctime(); +char nb[BUFSIZ]; +int vercnt; /* Count number of versions of file found */ + +main(argc, argv) + int argc; + char *argv[]; +{ + register char *cp; + register int b, i; + + /* + * Initialize as though the editor had just started. + */ + fendcore = (line *) sbrk(0); + dot = zero = dol = fendcore; + one = zero + 1; + endcore = fendcore - 2; + iblock = oblock = -1; + + /* + * If given only a -r argument, then list the saved files. + */ + if (argc == 2 && eq(argv[1], "-r")) { + listfiles(mydir); + exit(0); + } + if (argc != 3) + error(" Wrong number of arguments to exrecover", 0); + + CP(file, argv[2]); + + /* + * Search for this file. + */ + findtmp(argv[1]); + + /* + * Got (one of the versions of) it, write it back to the editor. + */ + cp = ctime(&H.Time); + cp[19] = 0; + fprintf(stderr, " [Dated: %s", cp); + fprintf(stderr, vercnt > 1 ? ", newest of %d saved]" : "]", vercnt); + H.Flines++; + + /* + * Allocate space for the line pointers from the temp file. + */ + if ((int) sbrk((int) (H.Flines * sizeof (line))) == -1) + /* + * Good grief. + */ + error(" Not enough core for lines", 0); +#ifdef DEBUG + fprintf(stderr, "%d lines\n", H.Flines); +#endif + + /* + * Now go get the blocks of seek pointers which are scattered + * throughout the temp file, reconstructing the incore + * line pointers at point of crash. + */ + b = 0; + while (H.Flines > 0) { + ignorl(lseek(tfile, (long) blocks[b] * BUFSIZ, 0)); + i = H.Flines < BUFSIZ / sizeof (line) ? + H.Flines * sizeof (line) : BUFSIZ; + if (read(tfile, (char *) dot, i) != i) { + perror(nb); + exit(1); + } + dot += i / sizeof (line); + H.Flines -= i / sizeof (line); + b++; + } + dot--; dol = dot; + + /* + * Sigh... due to sandbagging some lines may really not be there. + * Find and discard such. This shouldn't happen much. + */ + scrapbad(); + + /* + * Now if there were any lines in the recovered file + * write them to the standard output. + */ + if (dol > zero) { + addr1 = one; addr2 = dol; io = 1; + putfile(); + } + + /* + * Trash the saved buffer. + * Hopefully the system won't crash before the editor + * syncs the new recovered buffer; i.e. for an instant here + * you may lose if the system crashes because this file + * is gone, but the editor hasn't completed reading the recovered + * file from the pipe from us to it. + * + * This doesn't work if we are coming from an non-absolute path + * name since we may have chdir'ed but what the hay, noone really + * ever edits with temporaries in "." anyways. + */ + if (nb[0] == '/') + ignore(unlink(nb)); + + /* + * Adieu. + */ + exit(0); +} + +/* + * Print an error message (notably not in error + * message file). If terminal is in RAW mode, then + * we should be writing output for "vi", so don't print + * a newline which would screw up the screen. + */ +/*VARARGS2*/ +error(str, inf) + char *str; + int inf; +{ + + fprintf(stderr, str, inf); + gtty(2, &tty); + if ((tty.sg_flags & RAW) == 0) + fprintf(stderr, "\n"); + exit(1); +} + +/* + * Here we save the information about files, when + * you ask us what files we have saved for you. + * We buffer file name, number of lines, and the time + * at which the file was saved. + */ +struct svfile { + char sf_name[FNSIZE + 1]; + int sf_lines; + char sf_entry[DIRSIZ + 1]; + time_t sf_time; +}; + +listfiles(dirname) + char *dirname; +{ + register FILE *dir; + struct direct dirent; + int ecount, qucmp(); + register int f; + char *cp; + struct svfile *fp, svbuf[NENTRY]; + + /* + * Open usrpath(preserve), and go there to make things quick. + */ + dir = fopen(dirname, "r"); + if (dir == NULL) { + perror(dirname); + return; + } + if (chdir(dirname) < 0) { + perror(dirname); + return; + } + + /* + * Look at the candidate files in usrpath(preserve). + */ + fp = &svbuf[0]; + ecount = 0; + while (fread((char *) &dirent, sizeof dirent, 1, dir) == 1) { + if (dirent.d_ino == 0) + continue; + if (dirent.d_name[0] != 'E') + continue; +#ifdef DEBUG + fprintf(stderr, "considering %s\n", dirent.d_name); +#endif + /* + * Name begins with E; open it and + * make sure the uid in the header is our uid. + * If not, then don't bother with this file, it can't + * be ours. + */ + f = open(dirent.d_name, 0); + if (f < 0) { +#ifdef DEBUG + fprintf(stderr, "open failed\n"); +#endif + continue; + } + if (read(f, (char *) &H, sizeof H) != sizeof H) { +#ifdef DEBUG + fprintf(stderr, "culdnt read hedr\n"); +#endif + ignore(close(f)); + continue; + } + ignore(close(f)); + if (getuid() != H.Uid) { +#ifdef DEBUG + fprintf(stderr, "uid wrong\n"); +#endif + continue; + } + + /* + * Saved the day! + */ + enter(fp++, dirent.d_name, ecount); + ecount++; +#ifdef DEBUG + fprintf(stderr, "entered file %s\n", dirent.d_name); +#endif + } + ignore(fclose(dir)); + + /* + * If any files were saved, then sort them and print + * them out. + */ + if (ecount == 0) { + fprintf(stderr, "No files saved.\n"); + return; + } + qsort(&svbuf[0], ecount, sizeof svbuf[0], qucmp); + for (fp = &svbuf[0]; fp < &svbuf[ecount]; fp++) { + cp = ctime(&fp->sf_time); + cp[10] = 0; + fprintf(stderr, "On %s at ", cp); + cp[16] = 0; + fprintf(stderr, &cp[11]); + fprintf(stderr, " saved %d lines of file \"%s\"\n", + fp->sf_lines, fp->sf_name); + } +} + +/* + * Enter a new file into the saved file information. + */ +enter(fp, fname, count) + struct svfile *fp; + char *fname; +{ + register char *cp, *cp2; + register struct svfile *f, *fl; + time_t curtime, itol(); + + f = 0; + if (count >= NENTRY) { + /* + * My god, a huge number of saved files. + * Would you work on a system that crashed this + * often? Hope not. So lets trash the oldest + * as the most useless. + * + * (I wonder if this code has ever run?) + */ + fl = fp - count + NENTRY - 1; + curtime = fl->sf_time; + for (f = fl; --f > fp-count; ) + if (f->sf_time < curtime) + curtime = f->sf_time; + for (f = fl; --f > fp-count; ) + if (f->sf_time == curtime) + break; + fp = f; + } + + /* + * Gotcha. + */ + fp->sf_time = H.Time; + fp->sf_lines = H.Flines; + for (cp2 = fp->sf_name, cp = savedfile; *cp;) + *cp2++ = *cp++; + for (cp2 = fp->sf_entry, cp = fname; *cp && cp-fname < 14;) + *cp2++ = *cp++; + *cp2++ = 0; +} + +/* + * Do the qsort compare to sort the entries first by file name, + * then by modify time. + */ +qucmp(p1, p2) + struct svfile *p1, *p2; +{ + register int t; + + if (t = strcmp(p1->sf_name, p2->sf_name)) + return(t); + if (p1->sf_time > p2->sf_time) + return(-1); + return(p1->sf_time < p2->sf_time); +} + +/* + * Scratch for search. + */ +char bestnb[BUFSIZ]; /* Name of the best one */ +long besttime; /* Time at which the best file was saved */ +int bestfd; /* Keep best file open so it dont vanish */ + +/* + * Look for a file, both in the users directory option value + * (i.e. usually /tmp) and in usrpath(preserve). + * Want to find the newest so we search on and on. + */ +findtmp(dir) + char *dir; +{ + + /* + * No name or file so far. + */ + bestnb[0] = 0; + bestfd = -1; + + /* + * Search usrpath(preserve) and, if we can get there, /tmp + * (actually the users "directory" option). + */ + searchdir(dir); + if (chdir(mydir) == 0) + searchdir(mydir); + if (bestfd != -1) { + /* + * Gotcha. + * Put the file (which is already open) in the file + * used by the temp file routines, and save its + * name for later unlinking. + */ + tfile = bestfd; + CP(nb, bestnb); + ignorl(lseek(tfile, 0l, 0)); + + /* + * Gotta be able to read the header or fall through + * to lossage. + */ + if (read(tfile, (char *) &H, sizeof H) == sizeof H) + return; + } + + /* + * Extreme lossage... + */ + error(" File not found", 0); +} + +/* + * Search for the file in directory dirname. + * + * Don't chdir here, because the users directory + * may be ".", and we would move away before we searched it. + * Note that we actually chdir elsewhere (because it is too slow + * to look around in usrpath(preserve) without chdir'ing there) so we + * can't win, because we don't know the name of '.' and if the path + * name of the file we want to unlink is relative, rather than absolute + * we won't be able to find it again. + */ +searchdir(dirname) + char *dirname; +{ + struct direct dirent; + register FILE *dir; + char dbuf[BUFSIZ]; + + dir = fopen(dirname, "r"); + if (dir == NULL) + return; + setbuf(dir, dbuf); + while (fread((char *) &dirent, sizeof dirent, 1, dir) == 1) { + if (dirent.d_ino == 0) + continue; + if (dirent.d_name[0] != 'E' || dirent.d_name[DIRSIZ - 1] != 0) + continue; + /* + * Got a file in the directory starting with E... + * Save a consed up name for the file to unlink + * later, and check that this is really a file + * we are looking for. + */ + ignore(strcat(strcat(strcpy(nb, dirname), "/"), dirent.d_name)); + if (yeah(nb)) { + /* + * Well, it is the file we are looking for. + * Is it more recent than any version we found before? + */ + if (H.Time > besttime) { + /* + * A winner. + */ + ignore(close(bestfd)); + bestfd = dup(tfile); + besttime = H.Time; + CP(bestnb, nb); + } + /* + * Count versions so user can be told there are + * ``yet more pages to be turned''. + */ + vercnt++; + } + ignore(close(tfile)); + } + ignore(fclose(dir)); +} + +/* + * Given a candidate file to be recovered, see + * if its really an editor temporary and of this + * user and the file specified. + */ +yeah(name) + char *name; +{ + + tfile = open(name, 2); + if (tfile < 0) + return (0); + if (read(tfile, (char *) &H, sizeof H) != sizeof H) { +nope: + ignore(close(tfile)); + return (0); + } + if (!eq(savedfile, file)) + goto nope; + if (getuid() != H.Uid) + goto nope; + /* + * This is old and stupid code, which + * puts a word LOST in the header block, so that lost lines + * can be made to point at it. + */ + ignorl(lseek(tfile, (long)(BUFSIZ*HBLKS-8), 0)); + ignore(write(tfile, "LOST", 5)); + return (1); +} + +preserve() +{ + +} + +/* + * Find the true end of the scratch file, and ``LOSE'' + * lines which point into thin air. This lossage occurs + * due to the sandbagging of i/o which can cause blocks to + * be written in a non-obvious order, different from the order + * in which the editor tried to write them. + * + * Lines which are lost are replaced with the text LOST so + * they are easy to find. We work hard at pretty formatting here + * as lines tend to be lost in blocks. + * + * This only seems to happen on very heavily loaded systems, and + * not very often. + */ +scrapbad() +{ + register line *ip; + struct stat stbuf; + off_t size, maxt; + int bno, cnt, bad, was; + char bk[BUFSIZ]; + + ignore(fstat(tfile, &stbuf)); + size = stbuf.st_size; + maxt = (size >> SHFT) | (BNDRY-1); + bno = (maxt >> OFFBTS) & BLKMSK; +#ifdef DEBUG + fprintf(stderr, "size %ld, maxt %o, bno %d\n", size, maxt, bno); +#endif + + /* + * Look for a null separating two lines in the temp file; + * if last line was split across blocks, then it is lost + * if the last block is. + */ + while (bno > 0) { + ignorl(lseek(tfile, (long) BUFSIZ * bno, 0)); + cnt = read(tfile, (char *) bk, BUFSIZ); + while (cnt > 0) + if (bk[--cnt] == 0) + goto null; + bno--; + } +null: + + /* + * Magically calculate the largest valid pointer in the temp file, + * consing it up from the block number and the count. + */ + maxt = ((bno << OFFBTS) | (cnt >> SHFT)) & ~1; +#ifdef DEBUG + fprintf(stderr, "bno %d, cnt %d, maxt %o\n", bno, cnt, maxt); +#endif + + /* + * Now cycle through the line pointers, + * trashing the Lusers. + */ + was = bad = 0; + for (ip = one; ip <= dol; ip++) + if (*ip > maxt) { +#ifdef DEBUG + fprintf(stderr, "%d bad, %o > %o\n", ip - zero, *ip, maxt); +#endif + if (was == 0) + was = ip - zero; + *ip = ((HBLKS*BUFSIZ)-8) >> SHFT; + } else if (was) { + if (bad == 0) + fprintf(stderr, " [Lost line(s):"); + fprintf(stderr, " %d", was); + if ((ip - 1) - zero > was) + fprintf(stderr, "-%d", (ip - 1) - zero); + bad++; + was = 0; + } + if (was != 0) { + if (bad == 0) + fprintf(stderr, " [Lost line(s):"); + fprintf(stderr, " %d", was); + if (dol - zero != was) + fprintf(stderr, "-%d", dol - zero); + bad++; + } + if (bad) + fprintf(stderr, "]"); +} + +/* + * Aw shucks, if we only had a (void) cast. + */ +#ifdef lint +Ignorl(a) + long a; +{ + + a = a; +} + +Ignore(a) + char *a; +{ + + a = a; +} + +Ignorf(a) + int (*a)(); +{ + + a = a; +} + +ignorl(a) + long a; +{ + + a = a; +} +#endif + +int cntch, cntln, cntodd, cntnull; +/* + * Following routines stolen mercilessly from ex. + */ +putfile() +{ + line *a1; + register char *fp, *lp; + register int nib; + + a1 = addr1; + clrstats(); + cntln = addr2 - a1 + 1; + if (cntln == 0) + return; + nib = BUFSIZ; + fp = genbuf; + do { + getline(*a1++); + lp = linebuf; + for (;;) { + if (--nib < 0) { + nib = fp - genbuf; + if (write(io, genbuf, nib) != nib) + wrerror(); + cntch += nib; + nib = 511; + fp = genbuf; + } + if ((*fp++ = *lp++) == 0) { + fp[-1] = '\n'; + break; + } + } + } while (a1 <= addr2); + nib = fp - genbuf; + if (write(io, genbuf, nib) != nib) + wrerror(); + cntch += nib; +} + +wrerror() +{ + + syserror(); +} + +clrstats() +{ + + ninbuf = 0; + cntch = 0; + cntln = 0; + cntnull = 0; + cntodd = 0; +} + +#define READ 0 +#define WRITE 1 + +getline(tl) + line tl; +{ + register char *bp, *lp; + register int nl; + + lp = linebuf; + bp = getblock(tl, READ); + nl = nleft; + tl &= ~OFFMSK; + while (*lp++ = *bp++) + if (--nl == 0) { + bp = getblock(tl += INCRMT, READ); + nl = nleft; + } +} + +int read(); +int write(); + +char * +getblock(atl, iof) + line atl; + int iof; +{ + register int bno, off; + + bno = (atl >> OFFBTS) & BLKMSK; + off = (atl << SHFT) & LBTMSK; + if (bno >= NMBLKS) + error(" Tmp file too large"); + nleft = BUFSIZ - off; + if (bno == iblock) { + ichanged |= iof; + return (ibuff + off); + } + if (bno == oblock) + return (obuff + off); + if (iof == READ) { + if (ichanged) + blkio(iblock, ibuff, write); + ichanged = 0; + iblock = bno; + blkio(bno, ibuff, read); + return (ibuff + off); + } + if (oblock >= 0) + blkio(oblock, obuff, write); + oblock = bno; + return (obuff + off); +} + +blkio(b, buf, iofcn) + short b; + char *buf; + int (*iofcn)(); +{ + + lseek(tfile, (long) (unsigned) b * BUFSIZ, 0); + if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ) + syserror(); +} + +syserror() +{ + extern int sys_nerr; + extern char *sys_errlist[]; + + dirtcnt = 0; + write(2, " ", 1); + if (errno >= 0 && errno <= sys_nerr) + error(sys_errlist[errno]); + else + error("System error %d", errno); + exit(1); +} -- 2.20.1