BSD 3 development
authorBill Joy <wnj@ucbvax.Berkeley.EDU>
Sat, 5 Jan 1980 16:45:40 +0000 (08:45 -0800)
committerBill Joy <wnj@ucbvax.Berkeley.EDU>
Sat, 5 Jan 1980 16:45:40 +0000 (08:45 -0800)
Work on file usr/src/cmd/ex/exrecover.c

Synthesized-from: 3bsd

usr/src/cmd/ex/exrecover.c [new file with mode: 0644]

diff --git a/usr/src/cmd/ex/exrecover.c b/usr/src/cmd/ex/exrecover.c
new file mode 100644 (file)
index 0000000..9781fb9
--- /dev/null
@@ -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 <stdio.h>
+#include <sys/dir.h>
+
+/*
+ * 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);
+}