Remove building with NOCRYPTO option
[minix.git] / external / bsd / nvi / dist / common / recover.c
blob3dfabb363e56d1bb2eee085eb4afdc768591d883
1 /* $NetBSD: recover.c,v 1.5 2014/01/26 21:43:45 christos Exp $ */
2 /*-
3 * Copyright (c) 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 * Copyright (c) 1993, 1994, 1995, 1996
6 * Keith Bostic. All rights reserved.
8 * See the LICENSE file for redistribution information.
9 */
11 #include "config.h"
13 #include <sys/cdefs.h>
14 #if 0
15 #ifndef lint
16 static const char sccsid[] = "Id: recover.c,v 10.31 2001/11/01 15:24:44 skimo Exp (Berkeley) Date: 2001/11/01 15:24:44 ";
17 #endif /* not lint */
18 #else
19 __RCSID("$NetBSD: recover.c,v 1.5 2014/01/26 21:43:45 christos Exp $");
20 #endif
22 #include <sys/param.h>
23 #include <sys/types.h> /* XXX: param.h may not have included types.h */
24 #include <sys/queue.h>
25 #include <sys/stat.h>
28 * We include <sys/file.h>, because the open #defines were found there
29 * on historical systems. We also include <fcntl.h> because the open(2)
30 * #defines are found there on newer systems.
32 #include <sys/file.h>
34 #include <bitstring.h>
35 #include <dirent.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <limits.h>
39 #include <pwd.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <time.h>
44 #include <unistd.h>
46 #include "common.h"
47 #include "pathnames.h"
50 * Recovery code.
52 * The basic scheme is as follows. In the EXF structure, we maintain full
53 * paths of a b+tree file and a mail recovery file. The former is the file
54 * used as backing store by the DB package. The latter is the file that
55 * contains an email message to be sent to the user if we crash. The two
56 * simple states of recovery are:
58 * + first starting the edit session:
59 * the b+tree file exists and is mode 700, the mail recovery
60 * file doesn't exist.
61 * + after the file has been modified:
62 * the b+tree file exists and is mode 600, the mail recovery
63 * file exists, and is exclusively locked.
65 * In the EXF structure we maintain a file descriptor that is the locked
66 * file descriptor for the mail recovery file. NOTE: we sometimes have to
67 * do locking with fcntl(2). This is a problem because if you close(2) any
68 * file descriptor associated with the file, ALL of the locks go away. Be
69 * sure to remember that if you have to modify the recovery code. (It has
70 * been rhetorically asked of what the designers could have been thinking
71 * when they did that interface. The answer is simple: they weren't.)
73 * To find out if a recovery file/backing file pair are in use, try to get
74 * a lock on the recovery file.
76 * To find out if a backing file can be deleted at boot time, check for an
77 * owner execute bit. (Yes, I know it's ugly, but it's either that or put
78 * special stuff into the backing file itself, or correlate the files at
79 * boot time, neither of which looks like fun.) Note also that there's a
80 * window between when the file is created and the X bit is set. It's small,
81 * but it's there. To fix the window, check for 0 length files as well.
83 * To find out if a file can be recovered, check the F_RCV_ON bit. Note,
84 * this DOES NOT mean that any initialization has been done, only that we
85 * haven't yet failed at setting up or doing recovery.
87 * To preserve a recovery file/backing file pair, set the F_RCV_NORM bit.
88 * If that bit is not set when ending a file session:
89 * If the EXF structure paths (rcv_path and rcv_mpath) are not NULL,
90 * they are unlink(2)'d, and free(3)'d.
91 * If the EXF file descriptor (rcv_fd) is not -1, it is closed.
93 * The backing b+tree file is set up when a file is first edited, so that
94 * the DB package can use it for on-disk caching and/or to snapshot the
95 * file. When the file is first modified, the mail recovery file is created,
96 * the backing file permissions are updated, the file is sync(2)'d to disk,
97 * and the timer is started. Then, at RCV_PERIOD second intervals, the
98 * b+tree file is synced to disk. RCV_PERIOD is measured using SIGALRM, which
99 * means that the data structures (SCR, EXF, the underlying tree structures)
100 * must be consistent when the signal arrives.
102 * The recovery mail file contains normal mail headers, with two additions,
103 * which occur in THIS order, as the FIRST TWO headers:
105 * X-vi-recover-file: file_name
106 * X-vi-recover-path: recover_path
108 * Since newlines delimit the headers, this means that file names cannot have
109 * newlines in them, but that's probably okay. As these files aren't intended
110 * to be long-lived, changing their format won't be too painful.
112 * Btree files are named "vi.XXXX" and recovery files are named "recover.XXXX".
115 #define VI_FHEADER "X-vi-recover-file: "
116 #define VI_PHEADER "X-vi-recover-path: "
118 static int rcv_copy __P((SCR *, int, char *));
119 static void rcv_email __P((SCR *, char *));
120 static char *rcv_gets __P((char *, size_t, int));
121 static int rcv_mailfile __P((SCR *, int, char *));
122 static int rcv_mktemp __P((SCR *, char *, const char *, int));
125 * rcv_tmp --
126 * Build a file name that will be used as the recovery file.
128 * PUBLIC: int rcv_tmp __P((SCR *, EXF *, char *));
131 rcv_tmp(SCR *sp, EXF *ep, char *name)
133 struct stat sb;
134 int fd;
135 char path[MAXPATHLEN];
136 const char *dp;
139 * !!!
140 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
143 * If the recovery directory doesn't exist, try and create it. As
144 * the recovery files are themselves protected from reading/writing
145 * by other than the owner, the worst that can happen is that a user
146 * would have permission to remove other user's recovery files. If
147 * the sticky bit has the BSD semantics, that too will be impossible.
149 if (opts_empty(sp, O_RECDIR, 0))
150 goto err;
151 dp = O_STR(sp, O_RECDIR);
152 if (stat(dp, &sb)) {
153 if (errno != ENOENT || mkdir(dp, 0)) {
154 msgq(sp, M_SYSERR, "%s", dp);
155 goto err;
157 (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX);
160 /* Newlines delimit the mail messages. */
161 if (strchr(name, '\n')) {
162 msgq(sp, M_ERR,
163 "055|Files with newlines in the name are unrecoverable");
164 goto err;
167 (void)snprintf(path, sizeof(path), "%s/vi.XXXXXX", dp);
168 if ((fd = rcv_mktemp(sp, path, dp, S_IRWXU)) == -1)
169 goto err;
170 (void)close(fd);
172 if ((ep->rcv_path = strdup(path)) == NULL) {
173 msgq(sp, M_SYSERR, NULL);
174 (void)unlink(path);
175 err: msgq(sp, M_ERR,
176 "056|Modifications not recoverable if the session fails");
177 return (1);
180 /* We believe the file is recoverable. */
181 F_SET(ep, F_RCV_ON);
182 return (0);
186 * rcv_init --
187 * Force the file to be snapshotted for recovery.
189 * PUBLIC: int rcv_init __P((SCR *));
192 rcv_init(SCR *sp)
194 EXF *ep;
195 db_recno_t lno;
197 ep = sp->ep;
199 /* Only do this once. */
200 F_CLR(ep, F_FIRSTMODIFY);
202 /* If we already know the file isn't recoverable, we're done. */
203 if (!F_ISSET(ep, F_RCV_ON))
204 return (0);
206 /* Turn off recoverability until we figure out if this will work. */
207 F_CLR(ep, F_RCV_ON);
209 /* Test if we're recovering a file, not editing one. */
210 if (ep->rcv_mpath == NULL) {
211 /* Build a file to mail to the user. */
212 if (rcv_mailfile(sp, 0, NULL))
213 goto err;
215 /* Force a read of the entire file. */
216 if (db_last(sp, &lno))
217 goto err;
219 /* Turn on a busy message, and sync it to backing store. */
220 sp->gp->scr_busy(sp,
221 "057|Copying file for recovery...", BUSY_ON);
222 if (ep->db->sync(ep->db, 0)) {
223 msgq_str(sp, M_SYSERR, ep->rcv_path,
224 "058|Preservation failed: %s");
225 sp->gp->scr_busy(sp, NULL, BUSY_OFF);
226 goto err;
228 sp->gp->scr_busy(sp, NULL, BUSY_OFF);
231 /* Turn off the owner execute bit. */
232 (void)chmod(ep->rcv_path, S_IRUSR | S_IWUSR);
234 /* We believe the file is recoverable. */
235 F_SET(ep, F_RCV_ON);
236 return (0);
238 err: msgq(sp, M_ERR,
239 "059|Modifications not recoverable if the session fails");
240 return (1);
244 * rcv_sync --
245 * Sync the file, optionally:
246 * flagging the backup file to be preserved
247 * snapshotting the backup file and send email to the user
248 * sending email to the user if the file was modified
249 * ending the file session
251 * PUBLIC: int rcv_sync __P((SCR *, u_int));
254 rcv_sync(SCR *sp, u_int flags)
256 EXF *ep;
257 int fd, rval;
258 char buf[1024];
259 const char *dp;
261 /* Make sure that there's something to recover/sync. */
262 ep = sp->ep;
263 if (ep == NULL || !F_ISSET(ep, F_RCV_ON))
264 return (0);
266 /* Sync the file if it's been modified. */
267 if (F_ISSET(ep, F_MODIFIED)) {
269 * If we are using a db1 version of the database,
270 * we want to sync the underlying btree not the
271 * recno tree which is transient anyway.
273 #ifndef R_RECNOSYNC
274 #define R_RECNOSYNC 0
275 #endif
276 if (ep->db->sync(ep->db, R_RECNOSYNC)) {
277 F_CLR(ep, F_RCV_ON | F_RCV_NORM);
278 msgq_str(sp, M_SYSERR,
279 ep->rcv_path, "060|File backup failed: %s");
280 return (1);
283 /* REQUEST: don't remove backing file on exit. */
284 if (LF_ISSET(RCV_PRESERVE))
285 F_SET(ep, F_RCV_NORM);
287 /* REQUEST: send email. */
288 if (LF_ISSET(RCV_EMAIL))
289 rcv_email(sp, ep->rcv_mpath);
293 * !!!
294 * Each time the user exec's :preserve, we have to snapshot all of
295 * the recovery information, i.e. it's like the user re-edited the
296 * file. We copy the DB(3) backing file, and then create a new mail
297 * recovery file, it's simpler than exiting and reopening all of the
298 * underlying files.
300 * REQUEST: snapshot the file.
302 rval = 0;
303 if (LF_ISSET(RCV_SNAPSHOT)) {
304 if (opts_empty(sp, O_RECDIR, 0))
305 goto err;
306 dp = O_STR(sp, O_RECDIR);
307 (void)snprintf(buf, sizeof(buf), "%s/vi.XXXXXX", dp);
308 if ((fd = rcv_mktemp(sp, buf, dp, S_IRUSR | S_IWUSR)) == -1)
309 goto err;
310 sp->gp->scr_busy(sp,
311 "061|Copying file for recovery...", BUSY_ON);
312 if (rcv_copy(sp, fd, ep->rcv_path) ||
313 close(fd) || rcv_mailfile(sp, 1, buf)) {
314 (void)unlink(buf);
315 (void)close(fd);
316 rval = 1;
318 sp->gp->scr_busy(sp, NULL, BUSY_OFF);
320 if (0) {
321 err: rval = 1;
324 /* REQUEST: end the file session. */
325 if (LF_ISSET(RCV_ENDSESSION) && file_end(sp, NULL, 1))
326 rval = 1;
328 return (rval);
332 * rcv_mailfile --
333 * Build the file to mail to the user.
335 static int
336 rcv_mailfile(SCR *sp, int issync, char *cp_path)
338 EXF *ep;
339 GS *gp;
340 struct passwd *pw;
341 size_t len;
342 time_t now;
343 uid_t uid;
344 int fd;
345 char *p, *t, buf[4096], mpath[MAXPATHLEN];
346 const char *dp;
347 char *t1, *t2, *t3;
350 * XXX
351 * MAXHOSTNAMELEN is in various places on various systems, including
352 * <netdb.h> and <sys/socket.h>. If not found, use a large default.
354 #ifndef MAXHOSTNAMELEN
355 #define MAXHOSTNAMELEN 1024
356 #endif
357 char host[MAXHOSTNAMELEN];
359 gp = sp->gp;
360 if ((pw = getpwuid(uid = getuid())) == NULL) {
361 msgq(sp, M_ERR,
362 "062|Information on user id %u not found", uid);
363 return (1);
366 if (opts_empty(sp, O_RECDIR, 0))
367 return (1);
368 dp = O_STR(sp, O_RECDIR);
369 (void)snprintf(mpath, sizeof(mpath), "%s/recover.XXXXXX", dp);
370 if ((fd = rcv_mktemp(sp, mpath, dp, S_IRUSR | S_IWUSR)) == -1)
371 return (1);
374 * XXX
375 * We keep an open lock on the file so that the recover option can
376 * distinguish between files that are live and those that need to
377 * be recovered. There's an obvious window between the mkstemp call
378 * and the lock, but it's pretty small.
380 ep = sp->ep;
381 if (file_lock(sp, NULL, NULL, fd, 1) != LOCK_SUCCESS)
382 msgq(sp, M_SYSERR, "063|Unable to lock recovery file");
383 if (!issync) {
384 /* Save the recover file descriptor, and mail path. */
385 ep->rcv_fd = fd;
386 if ((ep->rcv_mpath = strdup(mpath)) == NULL) {
387 msgq(sp, M_SYSERR, NULL);
388 goto err;
390 cp_path = ep->rcv_path;
394 * XXX
395 * We can't use stdio(3) here. The problem is that we may be using
396 * fcntl(2), so if ANY file descriptor into the file is closed, the
397 * lock is lost. So, we could never close the FILE *, even if we
398 * dup'd the fd first.
400 t = sp->frp->name;
401 if ((p = strrchr(t, '/')) == NULL)
402 p = t;
403 else
404 ++p;
405 (void)time(&now);
406 (void)gethostname(host, sizeof(host));
407 len = snprintf(buf, sizeof(buf),
408 "%s%s\n%s%s\n%s\n%s\n%s%s\n%s%s\n%s\n\n",
409 VI_FHEADER, t, /* Non-standard. */
410 VI_PHEADER, cp_path, /* Non-standard. */
411 "Reply-To: root",
412 "From: root (Nvi recovery program)",
413 "To: ", pw->pw_name,
414 "Subject: Nvi saved the file ", p,
415 "Precedence: bulk"); /* For vacation(1). */
416 if (len > sizeof(buf) - 1)
417 goto lerr;
418 if ((size_t)write(fd, buf, len) != len)
419 goto werr;
421 len = snprintf(buf, sizeof(buf),
422 "%s%.24s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n\n",
423 "On ", ctime(&now), ", the user ", pw->pw_name,
424 " was editing a file named ", t, " on the machine ",
425 host, ", when it was saved for recovery. ",
426 "You can recover most, if not all, of the changes ",
427 "to this file using the -r option to ", gp->progname, ":\n\n\t",
428 gp->progname, " -r ", t);
429 if (len > sizeof(buf) - 1) {
430 lerr: msgq(sp, M_ERR, "064|Recovery file buffer overrun");
431 goto err;
435 * Format the message. (Yes, I know it's silly.)
436 * Requires that the message end in a <newline>.
438 #define FMTCOLS 60
439 for (t1 = buf; len > 0; len -= t2 - t1, t1 = t2) {
440 /* Check for a short length. */
441 if (len <= FMTCOLS) {
442 t2 = t1 + (len - 1);
443 goto wout;
446 /* Check for a required <newline>. */
447 t2 = strchr(t1, '\n');
448 if (t2 - t1 <= FMTCOLS)
449 goto wout;
451 /* Find the closest space, if any. */
452 for (t3 = t2; t2 > t1; --t2)
453 if (*t2 == ' ') {
454 if (t2 - t1 <= FMTCOLS)
455 goto wout;
456 t3 = t2;
458 t2 = t3;
460 /* t2 points to the last character to display. */
461 wout: *t2++ = '\n';
463 /* t2 points one after the last character to display. */
464 if (write(fd, t1, t2 - t1) != t2 - t1)
465 goto werr;
468 if (issync) {
469 rcv_email(sp, mpath);
470 if (close(fd)) {
471 werr: msgq(sp, M_SYSERR, "065|Recovery file");
472 goto err;
475 return (0);
477 err: if (!issync)
478 ep->rcv_fd = -1;
479 if (fd != -1)
480 (void)close(fd);
481 return (1);
485 * people making love
486 * never exactly the same
487 * just like a snowflake
489 * rcv_list --
490 * List the files that can be recovered by this user.
492 * PUBLIC: int rcv_list __P((SCR *));
495 rcv_list(SCR *sp)
497 struct dirent *dp;
498 struct stat sb;
499 DIR *dirp;
500 FILE *fp;
501 int found;
502 char *p, *t;
503 const char *d;
504 char file[MAXPATHLEN], path[MAXPATHLEN];
506 /* Open the recovery directory for reading. */
507 if (opts_empty(sp, O_RECDIR, 0))
508 return (1);
509 d = O_STR(sp, O_RECDIR);
510 if (chdir(d) || (dirp = opendir(".")) == NULL) {
511 msgq_str(sp, M_SYSERR, d, "recdir: %s");
512 return (1);
515 /* Read the directory. */
516 for (found = 0; (dp = readdir(dirp)) != NULL;) {
517 if (strncmp(dp->d_name, "recover.", 8))
518 continue;
521 * If it's readable, it's recoverable.
523 * XXX
524 * Should be "r", we don't want to write the file. However,
525 * if we're using fcntl(2), there's no way to lock a file
526 * descriptor that's not open for writing.
528 if ((fp = fopen(dp->d_name, "r+")) == NULL)
529 continue;
531 switch (file_lock(sp, NULL, NULL, fileno(fp), 1)) {
532 case LOCK_FAILED:
534 * XXX
535 * Assume that a lock can't be acquired, but that we
536 * should permit recovery anyway. If this is wrong,
537 * and someone else is using the file, we're going to
538 * die horribly.
540 break;
541 case LOCK_SUCCESS:
542 break;
543 case LOCK_UNAVAIL:
544 /* If it's locked, it's live. */
545 (void)fclose(fp);
546 continue;
549 /* Check the headers. */
550 if (fgets(file, sizeof(file), fp) == NULL ||
551 strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
552 (p = strchr(file, '\n')) == NULL ||
553 fgets(path, sizeof(path), fp) == NULL ||
554 strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) ||
555 (t = strchr(path, '\n')) == NULL) {
556 msgq_str(sp, M_ERR, dp->d_name,
557 "066|%s: malformed recovery file");
558 goto next;
560 *p = *t = '\0';
563 * If the file doesn't exist, it's an orphaned recovery file,
564 * toss it.
566 * XXX
567 * This can occur if the backup file was deleted and we crashed
568 * before deleting the email file.
570 errno = 0;
571 if (stat(path + sizeof(VI_PHEADER) - 1, &sb) &&
572 errno == ENOENT) {
573 (void)unlink(dp->d_name);
574 goto next;
577 /* Get the last modification time and display. */
578 (void)fstat(fileno(fp), &sb);
579 (void)printf("%.24s: %s\n",
580 ctime(&sb.st_mtime), file + sizeof(VI_FHEADER) - 1);
581 found = 1;
583 /* Close, discarding lock. */
584 next: (void)fclose(fp);
586 if (found == 0)
587 (void)printf("%s: No files to recover\n", sp->gp->progname);
588 (void)closedir(dirp);
589 return (0);
593 * rcv_read --
594 * Start a recovered file as the file to edit.
596 * PUBLIC: int rcv_read __P((SCR *, FREF *));
599 rcv_read(SCR *sp, FREF *frp)
601 struct dirent *dp;
602 struct stat sb;
603 DIR *dirp;
604 EXF *ep;
605 time_t rec_mtime;
606 int fd, found, locked = 0, requested, sv_fd;
607 char *name, *p, *t, *recp, *pathp;
608 const char *rp;
609 char file[MAXPATHLEN], path[MAXPATHLEN], recpath[MAXPATHLEN];
611 if (opts_empty(sp, O_RECDIR, 0))
612 return (1);
613 rp = O_STR(sp, O_RECDIR);
614 if ((dirp = opendir(rp)) == NULL) {
615 msgq_str(sp, M_ERR, rp, "%s");
616 return (1);
619 name = frp->name;
620 sv_fd = -1;
621 rec_mtime = 0;
622 recp = pathp = NULL;
623 for (found = requested = 0; (dp = readdir(dirp)) != NULL;) {
624 if (strncmp(dp->d_name, "recover.", 8))
625 continue;
626 (void)snprintf(recpath,
627 sizeof(recpath), "%s/%s", rp, dp->d_name);
630 * If it's readable, it's recoverable. It would be very
631 * nice to use stdio(3), but, we can't because that would
632 * require closing and then reopening the file so that we
633 * could have a lock and still close the FP. Another tip
634 * of the hat to fcntl(2).
636 * XXX
637 * Should be O_RDONLY, we don't want to write it. However,
638 * if we're using fcntl(2), there's no way to lock a file
639 * descriptor that's not open for writing.
641 if ((fd = open(recpath, O_RDWR, 0)) == -1)
642 continue;
644 switch (file_lock(sp, NULL, NULL, fd, 1)) {
645 case LOCK_FAILED:
647 * XXX
648 * Assume that a lock can't be acquired, but that we
649 * should permit recovery anyway. If this is wrong,
650 * and someone else is using the file, we're going to
651 * die horribly.
653 locked = 0;
654 break;
655 case LOCK_SUCCESS:
656 locked = 1;
657 break;
658 case LOCK_UNAVAIL:
659 /* If it's locked, it's live. */
660 (void)close(fd);
661 continue;
664 /* Check the headers. */
665 if (rcv_gets(file, sizeof(file), fd) == NULL ||
666 strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
667 (p = strchr(file, '\n')) == NULL ||
668 rcv_gets(path, sizeof(path), fd) == NULL ||
669 strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) ||
670 (t = strchr(path, '\n')) == NULL) {
671 msgq_str(sp, M_ERR, recpath,
672 "067|%s: malformed recovery file");
673 goto next;
675 *p = *t = '\0';
676 ++found;
679 * If the file doesn't exist, it's an orphaned recovery file,
680 * toss it.
682 * XXX
683 * This can occur if the backup file was deleted and we crashed
684 * before deleting the email file.
686 errno = 0;
687 if (stat(path + sizeof(VI_PHEADER) - 1, &sb) &&
688 errno == ENOENT) {
689 (void)unlink(dp->d_name);
690 goto next;
693 /* Check the file name. */
694 if (strcmp(file + sizeof(VI_FHEADER) - 1, name))
695 goto next;
697 ++requested;
700 * If we've found more than one, take the most recent.
702 * XXX
703 * Since we're using st_mtime, for portability reasons,
704 * we only get a single second granularity, instead of
705 * getting it right.
707 (void)fstat(fd, &sb);
708 if (recp == NULL || rec_mtime < sb.st_mtime) {
709 p = recp;
710 t = pathp;
711 if ((recp = strdup(recpath)) == NULL) {
712 msgq(sp, M_SYSERR, NULL);
713 recp = p;
714 goto next;
716 if ((pathp = strdup(path)) == NULL) {
717 msgq(sp, M_SYSERR, NULL);
718 free(recp);
719 recp = p;
720 pathp = t;
721 goto next;
723 if (p != NULL) {
724 free(p);
725 free(t);
727 rec_mtime = sb.st_mtime;
728 if (sv_fd != -1)
729 (void)close(sv_fd);
730 sv_fd = fd;
731 } else
732 next: (void)close(fd);
734 (void)closedir(dirp);
736 if (recp == NULL) {
737 msgq_str(sp, M_INFO, name,
738 "068|No files named %s, readable by you, to recover");
739 return (1);
741 if (found) {
742 if (requested > 1)
743 msgq(sp, M_INFO,
744 "069|There are older versions of this file for you to recover");
745 if (found > requested)
746 msgq(sp, M_INFO,
747 "070|There are other files for you to recover");
751 * Create the FREF structure, start the btree file.
753 * XXX
754 * file_init() is going to set ep->rcv_path.
756 if (file_init(sp, frp, pathp + sizeof(VI_PHEADER) - 1, 0)) {
757 free(recp);
758 free(pathp);
759 (void)close(sv_fd);
760 return (1);
764 * We keep an open lock on the file so that the recover option can
765 * distinguish between files that are live and those that need to
766 * be recovered. The lock is already acquired, just copy it.
768 ep = sp->ep;
769 ep->rcv_mpath = recp;
770 ep->rcv_fd = sv_fd;
771 if (!locked)
772 F_SET(frp, FR_UNLOCKED);
774 /* We believe the file is recoverable. */
775 F_SET(ep, F_RCV_ON);
776 free(pathp);
777 return (0);
781 * rcv_copy --
782 * Copy a recovery file.
784 static int
785 rcv_copy(SCR *sp, int wfd, char *fname)
787 int nr, nw, off, rfd;
788 char buf[8 * 1024];
790 if ((rfd = open(fname, O_RDONLY, 0)) == -1)
791 goto err;
792 while ((nr = read(rfd, buf, sizeof(buf))) > 0)
793 for (off = 0; nr; nr -= nw, off += nw)
794 if ((nw = write(wfd, buf + off, nr)) < 0)
795 goto err;
796 if (nr == 0)
797 return (0);
799 err: msgq_str(sp, M_SYSERR, fname, "%s");
800 return (1);
804 * rcv_gets --
805 * Fgets(3) for a file descriptor.
807 static char *
808 rcv_gets(char *buf, size_t len, int fd)
810 int nr;
811 char *p;
813 if ((nr = read(fd, buf, len - 1)) == -1)
814 return (NULL);
815 if ((p = strchr(buf, '\n')) == NULL)
816 return (NULL);
817 (void)lseek(fd, (off_t)((p - buf) + 1), SEEK_SET);
818 return (buf);
822 * rcv_mktemp --
823 * Paranoid make temporary file routine.
825 static int
826 rcv_mktemp(SCR *sp, char *path, const char *dname, int perms)
828 int fd;
831 * !!!
832 * We expect mkstemp(3) to set the permissions correctly. On
833 * historic System V systems, mkstemp didn't. Do it here, on
834 * GP's.
836 * XXX
837 * The variable perms should really be a mode_t, and it would
838 * be nice to use fchmod(2) instead of chmod(2), here.
840 if ((fd = mkstemp(path)) == -1)
841 msgq_str(sp, M_SYSERR, dname, "%s");
842 else
843 (void)chmod(path, perms);
844 return (fd);
848 * rcv_email --
849 * Send email.
851 static void
852 rcv_email(SCR *sp, char *fname)
854 struct stat sb;
855 char buf[MAXPATHLEN * 2 + 20];
857 if (_PATH_SENDMAIL[0] != '/' || stat(_PATH_SENDMAIL, &sb))
858 msgq_str(sp, M_SYSERR,
859 _PATH_SENDMAIL, "071|not sending email: %s");
860 else {
862 * !!!
863 * If you need to port this to a system that doesn't have
864 * sendmail, the -t flag causes sendmail to read the message
865 * for the recipients instead of specifying them some other
866 * way.
868 (void)snprintf(buf, sizeof(buf),
869 "%s -t < %s", _PATH_SENDMAIL, fname);
870 (void)system(buf);