📄 recover.c
字号:
/*- * Copyright (c) 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)recover.c 8.51 (Berkeley) 3/25/94";#endif /* not lint */#include <sys/param.h>#include <sys/queue.h>#include <sys/stat.h>#include <sys/time.h>/* * We include <sys/file.h>, because the flock(2) #defines were * found there on historical systems. We also include <fcntl.h> * because the open(2) #defines are found there on newer systems. */#include <sys/file.h>#include <netdb.h> /* MAXHOSTNAMELEN on some systems. */#include <bitstring.h>#include <dirent.h>#include <errno.h>#include <fcntl.h>#include <limits.h>#include <pwd.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <termios.h>#include <unistd.h>#include "compat.h"#include <db.h>#include <regex.h>#include <pathnames.h>#include "vi.h"/* * Recovery code. * * The basic scheme is there's a btree file, whose name we specify. The first * time a file is modified, and then at RCV_PERIOD intervals after that, the * btree file is synced to disk. Each time a keystroke is requested for a file * the terminal routines check to see if the file needs to be synced. This, of * course means that the data structures had better be consistent each time the * key routines are called. * * We don't use timers other than to flag that the file should be synced. This * would require that the SCR and EXF data structures be locked, the dbopen(3) * routines lock out the timers for each update, etc. It's just not worth it. * The only way we can lose in the current scheme is if the file is saved, then * the user types furiously for RCV_PERIOD - 1 seconds, and types nothing more. * Not likely. * * When a file is first modified, a file which can be handed off to the mailer * is created. The file contains normal headers, with two additions, which * occur in THIS order, as the FIRST TWO headers: * * Vi-recover-file: file_name * Vi-recover-path: recover_path * * Since newlines delimit the headers, this means that file names cannot * have newlines in them, but that's probably okay. * * Btree files are named "vi.XXXX" and recovery files are named "recover.XXXX". */#define VI_FHEADER "Vi-recover-file: "#define VI_PHEADER "Vi-recover-path: "static int rcv_mailfile __P((SCR *, EXF *));static void rcv_syncit __P((SCR *, int));/* * rcv_tmp -- * Build a file name that will be used as the recovery file. */intrcv_tmp(sp, ep, name) SCR *sp; EXF *ep; char *name;{ struct stat sb; int fd; char *dp, *p, path[MAXPATHLEN]; /* * If the recovery directory doesn't exist, try and create it. As * the recovery files are themselves protected from reading/writing * by other than the owner, the worst that can happen is that a user * would have permission to remove other users recovery files. If * the sticky bit has the BSD semantics, that too will be impossible. */ dp = O_STR(sp, O_RECDIR); if (stat(dp, &sb)) { if (errno != ENOENT || mkdir(dp, 0)) { msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno)); goto err; } (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX); } /* Newlines delimit the mail messages. */ for (p = name; *p; ++p) if (*p == '\n') { msgq(sp, M_ERR, "Files with newlines in the name are unrecoverable."); goto err; } (void)snprintf(path, sizeof(path), "%s/vi.XXXXXX", dp); /* * !!! * We depend on mkstemp(3) setting the permissions correctly. * For GP's, we do it ourselves, to keep the window as small * as possible. */ if ((fd = mkstemp(path)) == -1) { msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno)); goto err; } (void)chmod(path, S_IRUSR | S_IWUSR); (void)close(fd); if ((ep->rcv_path = strdup(path)) == NULL) { msgq(sp, M_SYSERR, NULL); (void)unlink(path);err: msgq(sp, M_ERR, "Modifications not recoverable if the session fails."); return (1); } /* We believe the file is recoverable. */ F_SET(ep, F_RCV_ON); return (0);}/* * rcv_init -- * Force the file to be snapshotted for recovery. */intrcv_init(sp, ep) SCR *sp; EXF *ep;{ recno_t lno; int btear; /* Only do this once. */ F_CLR(ep, F_FIRSTMODIFY); /* If we already know the file isn't recoverable, we're done. */ if (!F_ISSET(ep, F_RCV_ON)) return (0); /* Turn off recoverability until we figure out if this will work. */ F_CLR(ep, F_RCV_ON); /* If not recovering a file, build a file to mail to the user. */ if (ep->rcv_mpath == NULL && rcv_mailfile(sp, ep)) goto err; /* Force read of entire file. */ if (file_lline(sp, ep, &lno)) goto err; /* Turn on a busy message, and sync it to backing store. */ btear = F_ISSET(sp, S_EXSILENT) ? 0 : !busy_on(sp, "Copying file for recovery..."); if (ep->db->sync(ep->db, R_RECNOSYNC)) { msgq(sp, M_ERR, "Preservation failed: %s: %s", ep->rcv_path, strerror(errno)); if (btear) busy_off(sp); goto err; } if (btear) busy_off(sp); if (!F_ISSET(sp->gp, G_RECOVER_SET) && rcv_on(sp, ep)) {err: msgq(sp, M_ERR, "Modifications not recoverable if the session fails."); return (1); } /* We believe the file is recoverable. */ F_SET(ep, F_RCV_ON); return (0);}/* * rcv_mailfile -- * Build the file to mail to the user. */static intrcv_mailfile(sp, ep) SCR *sp; EXF *ep;{ struct passwd *pw; uid_t uid; FILE *fp; time_t now; int fd; char *p, *t, host[MAXHOSTNAMELEN], path[MAXPATHLEN]; if ((pw = getpwuid(uid = getuid())) == NULL) { msgq(sp, M_ERR, "Information on user id %u not found.", uid); return (1); } (void)snprintf(path, sizeof(path), "%s/recover.XXXXXX", O_STR(sp, O_RECDIR)); if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w")) == NULL) { msgq(sp, M_ERR, "Error: %s: %s", O_STR(sp, O_RECDIR), strerror(errno)); if (fd != -1) (void)close(fd); return (1); } /* * We keep an open lock on the file so that the recover option can * distinguish between files that are live and those that need to * be recovered. There's an obvious window between the mkstemp call * and the lock, but it's pretty small. */ if ((ep->rcv_fd = dup(fd)) == -1 || flock(ep->rcv_fd, LOCK_EX | LOCK_NB)) msgq(sp, M_SYSERR, "Unable to lock recovery file"); if ((ep->rcv_mpath = strdup(path)) == NULL) { msgq(sp, M_SYSERR, NULL); (void)fclose(fp); return (1); } t = FILENAME(sp->frp); if ((p = strrchr(t, '/')) == NULL) p = t; else ++p; (void)time(&now); (void)gethostname(host, sizeof(host)); (void)fprintf(fp, "%s%s\n%s%s\n%s\n%s\n%s%s\n%s%s\n%s\n\n", VI_FHEADER, p, /* Non-standard. */ VI_PHEADER, ep->rcv_path, /* Non-standard. */ "Reply-To: root", "From: root (Nvi recovery program)", "To: ", pw->pw_name, "Subject: Nvi saved the file ", p, "Precedence: bulk"); /* For vacation(1). */ (void)fprintf(fp, "%s%.24s%s%s\n%s%s", "On ", ctime(&now), ", the user ", pw->pw_name, "was editing a file named ", p); if (p != t) (void)fprintf(fp, " (%s)", t); (void)fprintf(fp, "\n%s%s%s\n", "on the machine ", host, ", when it was saved for\nrecovery."); (void)fprintf(fp, "\n%s\n%s\n%s\n\n", "You can recover most, if not all, of the changes", "to this file using the -l and -r options to nvi(1)", "or nex(1)."); if (fflush(fp) || ferror(fp)) { msgq(sp, M_SYSERR, NULL); (void)fclose(fp); return (1); } (void)fclose(fp); return (0);}/* * rcv_sync -- * Sync the backing file. */intrcv_sync(sp, ep)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -