1b8ba871bSPeter Wemm /*-
2b8ba871bSPeter Wemm * Copyright (c) 1993, 1994
3b8ba871bSPeter Wemm * The Regents of the University of California. All rights reserved.
4b8ba871bSPeter Wemm * Copyright (c) 1993, 1994, 1995, 1996
5b8ba871bSPeter Wemm * Keith Bostic. All rights reserved.
6b8ba871bSPeter Wemm *
7b8ba871bSPeter Wemm * See the LICENSE file for redistribution information.
8b8ba871bSPeter Wemm */
9b8ba871bSPeter Wemm
10b8ba871bSPeter Wemm #include "config.h"
11b8ba871bSPeter Wemm
12f0957ccaSPeter Wemm #include <sys/types.h>
13b8ba871bSPeter Wemm #include <sys/queue.h>
14b8ba871bSPeter Wemm #include <sys/stat.h>
15b8ba871bSPeter Wemm
16b8ba871bSPeter Wemm /*
17b8ba871bSPeter Wemm * We include <sys/file.h>, because the open #defines were found there
18b8ba871bSPeter Wemm * on historical systems. We also include <fcntl.h> because the open(2)
19b8ba871bSPeter Wemm * #defines are found there on newer systems.
20b8ba871bSPeter Wemm */
21b8ba871bSPeter Wemm #include <sys/file.h>
22b8ba871bSPeter Wemm
23b8ba871bSPeter Wemm #include <bitstring.h>
24b8ba871bSPeter Wemm #include <dirent.h>
25b8ba871bSPeter Wemm #include <errno.h>
26b8ba871bSPeter Wemm #include <fcntl.h>
27b8ba871bSPeter Wemm #include <limits.h>
28b8ba871bSPeter Wemm #include <pwd.h>
29f0957ccaSPeter Wemm #include <netinet/in.h> /* Required by resolv.h. */
30f0957ccaSPeter Wemm #include <resolv.h>
31b8ba871bSPeter Wemm #include <stdio.h>
32b8ba871bSPeter Wemm #include <stdlib.h>
33b8ba871bSPeter Wemm #include <string.h>
34b8ba871bSPeter Wemm #include <time.h>
35b8ba871bSPeter Wemm #include <unistd.h>
36b8ba871bSPeter Wemm
37f0957ccaSPeter Wemm #include "../ex/version.h"
38b8ba871bSPeter Wemm #include "common.h"
39b8ba871bSPeter Wemm #include "pathnames.h"
40b8ba871bSPeter Wemm
41b8ba871bSPeter Wemm /*
42b8ba871bSPeter Wemm * Recovery code.
43b8ba871bSPeter Wemm *
44b8ba871bSPeter Wemm * The basic scheme is as follows. In the EXF structure, we maintain full
45b8ba871bSPeter Wemm * paths of a b+tree file and a mail recovery file. The former is the file
46b8ba871bSPeter Wemm * used as backing store by the DB package. The latter is the file that
47b8ba871bSPeter Wemm * contains an email message to be sent to the user if we crash. The two
48b8ba871bSPeter Wemm * simple states of recovery are:
49b8ba871bSPeter Wemm *
50b8ba871bSPeter Wemm * + first starting the edit session:
51b8ba871bSPeter Wemm * the b+tree file exists and is mode 700, the mail recovery
52b8ba871bSPeter Wemm * file doesn't exist.
53b8ba871bSPeter Wemm * + after the file has been modified:
54b8ba871bSPeter Wemm * the b+tree file exists and is mode 600, the mail recovery
55b8ba871bSPeter Wemm * file exists, and is exclusively locked.
56b8ba871bSPeter Wemm *
57b8ba871bSPeter Wemm * In the EXF structure we maintain a file descriptor that is the locked
58f0957ccaSPeter Wemm * file descriptor for the mail recovery file.
59b8ba871bSPeter Wemm *
60b8ba871bSPeter Wemm * To find out if a recovery file/backing file pair are in use, try to get
61b8ba871bSPeter Wemm * a lock on the recovery file.
62b8ba871bSPeter Wemm *
63b8ba871bSPeter Wemm * To find out if a backing file can be deleted at boot time, check for an
64b8ba871bSPeter Wemm * owner execute bit. (Yes, I know it's ugly, but it's either that or put
65b8ba871bSPeter Wemm * special stuff into the backing file itself, or correlate the files at
66b8ba871bSPeter Wemm * boot time, neither of which looks like fun.) Note also that there's a
67b8ba871bSPeter Wemm * window between when the file is created and the X bit is set. It's small,
68b8ba871bSPeter Wemm * but it's there. To fix the window, check for 0 length files as well.
69b8ba871bSPeter Wemm *
70b8ba871bSPeter Wemm * To find out if a file can be recovered, check the F_RCV_ON bit. Note,
71b8ba871bSPeter Wemm * this DOES NOT mean that any initialization has been done, only that we
72b8ba871bSPeter Wemm * haven't yet failed at setting up or doing recovery.
73b8ba871bSPeter Wemm *
74b8ba871bSPeter Wemm * To preserve a recovery file/backing file pair, set the F_RCV_NORM bit.
75b8ba871bSPeter Wemm * If that bit is not set when ending a file session:
76b8ba871bSPeter Wemm * If the EXF structure paths (rcv_path and rcv_mpath) are not NULL,
77b8ba871bSPeter Wemm * they are unlink(2)'d, and free(3)'d.
78b8ba871bSPeter Wemm * If the EXF file descriptor (rcv_fd) is not -1, it is closed.
79b8ba871bSPeter Wemm *
80b8ba871bSPeter Wemm * The backing b+tree file is set up when a file is first edited, so that
81b8ba871bSPeter Wemm * the DB package can use it for on-disk caching and/or to snapshot the
82b8ba871bSPeter Wemm * file. When the file is first modified, the mail recovery file is created,
83b8ba871bSPeter Wemm * the backing file permissions are updated, the file is sync(2)'d to disk,
84b8ba871bSPeter Wemm * and the timer is started. Then, at RCV_PERIOD second intervals, the
85b8ba871bSPeter Wemm * b+tree file is synced to disk. RCV_PERIOD is measured using SIGALRM, which
86b8ba871bSPeter Wemm * means that the data structures (SCR, EXF, the underlying tree structures)
87b8ba871bSPeter Wemm * must be consistent when the signal arrives.
88b8ba871bSPeter Wemm *
89f0957ccaSPeter Wemm * The recovery mail file contains normal mail headers, with two additional
90b8ba871bSPeter Wemm *
91f0957ccaSPeter Wemm * X-vi-data: <file|path>;<base64 encoded path>
92b8ba871bSPeter Wemm *
93f0957ccaSPeter Wemm * MIME headers; the folding character is limited to ' '.
94b8ba871bSPeter Wemm *
95f0957ccaSPeter Wemm * Btree files are named "vi.XXXXXX" and recovery files are named
96f0957ccaSPeter Wemm * "recover.XXXXXX".
97b8ba871bSPeter Wemm */
98b8ba871bSPeter Wemm
99f0957ccaSPeter Wemm #define VI_DHEADER "X-vi-data:"
100b8ba871bSPeter Wemm
101c271fa92SBaptiste Daroussin static int rcv_copy(SCR *, int, char *);
102c271fa92SBaptiste Daroussin static void rcv_email(SCR *, char *);
103c271fa92SBaptiste Daroussin static int rcv_mailfile(SCR *, int, char *);
104c271fa92SBaptiste Daroussin static int rcv_mktemp(SCR *, char *, char *);
105c271fa92SBaptiste Daroussin static int rcv_dlnwrite(SCR *, const char *, const char *, FILE *);
106c271fa92SBaptiste Daroussin static int rcv_dlnread(SCR *, char **, char **, FILE *);
107b8ba871bSPeter Wemm
108b8ba871bSPeter Wemm /*
109b8ba871bSPeter Wemm * rcv_tmp --
110b8ba871bSPeter Wemm * Build a file name that will be used as the recovery file.
111b8ba871bSPeter Wemm *
112c271fa92SBaptiste Daroussin * PUBLIC: int rcv_tmp(SCR *, EXF *, char *);
113b8ba871bSPeter Wemm */
114b8ba871bSPeter Wemm int
rcv_tmp(SCR * sp,EXF * ep,char * name)115110d525eSBaptiste Daroussin rcv_tmp(SCR *sp, EXF *ep, char *name)
116b8ba871bSPeter Wemm {
117b8ba871bSPeter Wemm struct stat sb;
118b8ba871bSPeter Wemm int fd;
119f0957ccaSPeter Wemm char *dp, *path;
120b8ba871bSPeter Wemm
121b8ba871bSPeter Wemm /*
122b8ba871bSPeter Wemm * !!!
123b8ba871bSPeter Wemm * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
124b8ba871bSPeter Wemm *
125b8ba871bSPeter Wemm *
126b8ba871bSPeter Wemm * If the recovery directory doesn't exist, try and create it. As
127b8ba871bSPeter Wemm * the recovery files are themselves protected from reading/writing
128b8ba871bSPeter Wemm * by other than the owner, the worst that can happen is that a user
129b8ba871bSPeter Wemm * would have permission to remove other user's recovery files. If
130b8ba871bSPeter Wemm * the sticky bit has the BSD semantics, that too will be impossible.
131b8ba871bSPeter Wemm */
132b8ba871bSPeter Wemm if (opts_empty(sp, O_RECDIR, 0))
133b8ba871bSPeter Wemm goto err;
134b8ba871bSPeter Wemm dp = O_STR(sp, O_RECDIR);
135b8ba871bSPeter Wemm if (stat(dp, &sb)) {
136b8ba871bSPeter Wemm if (errno != ENOENT || mkdir(dp, 0)) {
137b8ba871bSPeter Wemm msgq(sp, M_SYSERR, "%s", dp);
138b8ba871bSPeter Wemm goto err;
139b8ba871bSPeter Wemm }
140b8ba871bSPeter Wemm (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX);
141b8ba871bSPeter Wemm }
142b8ba871bSPeter Wemm
143f0957ccaSPeter Wemm if ((path = join(dp, "vi.XXXXXX")) == NULL)
144f0957ccaSPeter Wemm goto err;
145f0957ccaSPeter Wemm if ((fd = rcv_mktemp(sp, path, dp)) == -1) {
146f0957ccaSPeter Wemm free(path);
147b8ba871bSPeter Wemm goto err;
148b8ba871bSPeter Wemm }
149f0957ccaSPeter Wemm (void)fchmod(fd, S_IRWXU);
150b8ba871bSPeter Wemm (void)close(fd);
151b8ba871bSPeter Wemm
152f0957ccaSPeter Wemm ep->rcv_path = path;
153f0957ccaSPeter Wemm if (0) {
154b8ba871bSPeter Wemm err: msgq(sp, M_ERR,
155b8ba871bSPeter Wemm "056|Modifications not recoverable if the session fails");
156b8ba871bSPeter Wemm return (1);
157b8ba871bSPeter Wemm }
158b8ba871bSPeter Wemm
159b8ba871bSPeter Wemm /* We believe the file is recoverable. */
160b8ba871bSPeter Wemm F_SET(ep, F_RCV_ON);
161b8ba871bSPeter Wemm return (0);
162b8ba871bSPeter Wemm }
163b8ba871bSPeter Wemm
164b8ba871bSPeter Wemm /*
165b8ba871bSPeter Wemm * rcv_init --
166b8ba871bSPeter Wemm * Force the file to be snapshotted for recovery.
167b8ba871bSPeter Wemm *
168c271fa92SBaptiste Daroussin * PUBLIC: int rcv_init(SCR *);
169b8ba871bSPeter Wemm */
170b8ba871bSPeter Wemm int
rcv_init(SCR * sp)171f0957ccaSPeter Wemm rcv_init(SCR *sp)
172b8ba871bSPeter Wemm {
173b8ba871bSPeter Wemm EXF *ep;
174b8ba871bSPeter Wemm recno_t lno;
175b8ba871bSPeter Wemm
176b8ba871bSPeter Wemm ep = sp->ep;
177b8ba871bSPeter Wemm
178b8ba871bSPeter Wemm /* Only do this once. */
179b8ba871bSPeter Wemm F_CLR(ep, F_FIRSTMODIFY);
180b8ba871bSPeter Wemm
181b8ba871bSPeter Wemm /* If we already know the file isn't recoverable, we're done. */
182b8ba871bSPeter Wemm if (!F_ISSET(ep, F_RCV_ON))
183b8ba871bSPeter Wemm return (0);
184b8ba871bSPeter Wemm
185b8ba871bSPeter Wemm /* Turn off recoverability until we figure out if this will work. */
186b8ba871bSPeter Wemm F_CLR(ep, F_RCV_ON);
187b8ba871bSPeter Wemm
188b8ba871bSPeter Wemm /* Test if we're recovering a file, not editing one. */
189b8ba871bSPeter Wemm if (ep->rcv_mpath == NULL) {
190b8ba871bSPeter Wemm /* Build a file to mail to the user. */
191b8ba871bSPeter Wemm if (rcv_mailfile(sp, 0, NULL))
192b8ba871bSPeter Wemm goto err;
193b8ba871bSPeter Wemm
194b8ba871bSPeter Wemm /* Force a read of the entire file. */
195b8ba871bSPeter Wemm if (db_last(sp, &lno))
196b8ba871bSPeter Wemm goto err;
197b8ba871bSPeter Wemm
198b8ba871bSPeter Wemm /* Turn on a busy message, and sync it to backing store. */
199b8ba871bSPeter Wemm sp->gp->scr_busy(sp,
200b8ba871bSPeter Wemm "057|Copying file for recovery...", BUSY_ON);
201b8ba871bSPeter Wemm if (ep->db->sync(ep->db, R_RECNOSYNC)) {
202b8ba871bSPeter Wemm msgq_str(sp, M_SYSERR, ep->rcv_path,
203b8ba871bSPeter Wemm "058|Preservation failed: %s");
204b8ba871bSPeter Wemm sp->gp->scr_busy(sp, NULL, BUSY_OFF);
205b8ba871bSPeter Wemm goto err;
206b8ba871bSPeter Wemm }
207b8ba871bSPeter Wemm sp->gp->scr_busy(sp, NULL, BUSY_OFF);
208b8ba871bSPeter Wemm }
209b8ba871bSPeter Wemm
210b8ba871bSPeter Wemm /* Turn off the owner execute bit. */
211b8ba871bSPeter Wemm (void)chmod(ep->rcv_path, S_IRUSR | S_IWUSR);
212b8ba871bSPeter Wemm
213b8ba871bSPeter Wemm /* We believe the file is recoverable. */
214b8ba871bSPeter Wemm F_SET(ep, F_RCV_ON);
215b8ba871bSPeter Wemm return (0);
216b8ba871bSPeter Wemm
217b8ba871bSPeter Wemm err: msgq(sp, M_ERR,
218b8ba871bSPeter Wemm "059|Modifications not recoverable if the session fails");
219b8ba871bSPeter Wemm return (1);
220b8ba871bSPeter Wemm }
221b8ba871bSPeter Wemm
222b8ba871bSPeter Wemm /*
223b8ba871bSPeter Wemm * rcv_sync --
224b8ba871bSPeter Wemm * Sync the file, optionally:
225b8ba871bSPeter Wemm * flagging the backup file to be preserved
226b8ba871bSPeter Wemm * snapshotting the backup file and send email to the user
227b8ba871bSPeter Wemm * sending email to the user if the file was modified
228b8ba871bSPeter Wemm * ending the file session
229b8ba871bSPeter Wemm *
230c271fa92SBaptiste Daroussin * PUBLIC: int rcv_sync(SCR *, u_int);
231b8ba871bSPeter Wemm */
232b8ba871bSPeter Wemm int
rcv_sync(SCR * sp,u_int flags)233110d525eSBaptiste Daroussin rcv_sync(SCR *sp, u_int flags)
234b8ba871bSPeter Wemm {
235b8ba871bSPeter Wemm EXF *ep;
236b8ba871bSPeter Wemm int fd, rval;
237f0957ccaSPeter Wemm char *dp, *buf;
238b8ba871bSPeter Wemm
239b8ba871bSPeter Wemm /* Make sure that there's something to recover/sync. */
240b8ba871bSPeter Wemm ep = sp->ep;
241b8ba871bSPeter Wemm if (ep == NULL || !F_ISSET(ep, F_RCV_ON))
242b8ba871bSPeter Wemm return (0);
243b8ba871bSPeter Wemm
244b8ba871bSPeter Wemm /* Sync the file if it's been modified. */
245b8ba871bSPeter Wemm if (F_ISSET(ep, F_MODIFIED)) {
246b8ba871bSPeter Wemm if (ep->db->sync(ep->db, R_RECNOSYNC)) {
247b8ba871bSPeter Wemm F_CLR(ep, F_RCV_ON | F_RCV_NORM);
248b8ba871bSPeter Wemm msgq_str(sp, M_SYSERR,
249b8ba871bSPeter Wemm ep->rcv_path, "060|File backup failed: %s");
250b8ba871bSPeter Wemm return (1);
251b8ba871bSPeter Wemm }
252b8ba871bSPeter Wemm
253b8ba871bSPeter Wemm /* REQUEST: don't remove backing file on exit. */
254b8ba871bSPeter Wemm if (LF_ISSET(RCV_PRESERVE))
255b8ba871bSPeter Wemm F_SET(ep, F_RCV_NORM);
256b8ba871bSPeter Wemm
257b8ba871bSPeter Wemm /* REQUEST: send email. */
258b8ba871bSPeter Wemm if (LF_ISSET(RCV_EMAIL))
259b8ba871bSPeter Wemm rcv_email(sp, ep->rcv_mpath);
260b8ba871bSPeter Wemm }
261b8ba871bSPeter Wemm
262b8ba871bSPeter Wemm /*
263b8ba871bSPeter Wemm * !!!
264b8ba871bSPeter Wemm * Each time the user exec's :preserve, we have to snapshot all of
265b8ba871bSPeter Wemm * the recovery information, i.e. it's like the user re-edited the
266b8ba871bSPeter Wemm * file. We copy the DB(3) backing file, and then create a new mail
267b8ba871bSPeter Wemm * recovery file, it's simpler than exiting and reopening all of the
268b8ba871bSPeter Wemm * underlying files.
269b8ba871bSPeter Wemm *
270b8ba871bSPeter Wemm * REQUEST: snapshot the file.
271b8ba871bSPeter Wemm */
272b8ba871bSPeter Wemm rval = 0;
273b8ba871bSPeter Wemm if (LF_ISSET(RCV_SNAPSHOT)) {
274b8ba871bSPeter Wemm if (opts_empty(sp, O_RECDIR, 0))
275b8ba871bSPeter Wemm goto err;
276b8ba871bSPeter Wemm dp = O_STR(sp, O_RECDIR);
277f0957ccaSPeter Wemm if ((buf = join(dp, "vi.XXXXXX")) == NULL) {
278f0957ccaSPeter Wemm msgq(sp, M_SYSERR, NULL);
279b8ba871bSPeter Wemm goto err;
280f0957ccaSPeter Wemm }
281f0957ccaSPeter Wemm if ((fd = rcv_mktemp(sp, buf, dp)) == -1) {
282f0957ccaSPeter Wemm free(buf);
283f0957ccaSPeter Wemm goto err;
284f0957ccaSPeter Wemm }
285b8ba871bSPeter Wemm sp->gp->scr_busy(sp,
286b8ba871bSPeter Wemm "061|Copying file for recovery...", BUSY_ON);
287b8ba871bSPeter Wemm if (rcv_copy(sp, fd, ep->rcv_path) ||
288b8ba871bSPeter Wemm close(fd) || rcv_mailfile(sp, 1, buf)) {
289b8ba871bSPeter Wemm (void)unlink(buf);
290b8ba871bSPeter Wemm (void)close(fd);
291b8ba871bSPeter Wemm rval = 1;
292b8ba871bSPeter Wemm }
293f0957ccaSPeter Wemm free(buf);
294b8ba871bSPeter Wemm sp->gp->scr_busy(sp, NULL, BUSY_OFF);
295b8ba871bSPeter Wemm }
296b8ba871bSPeter Wemm if (0) {
297b8ba871bSPeter Wemm err: rval = 1;
298b8ba871bSPeter Wemm }
299b8ba871bSPeter Wemm
300b8ba871bSPeter Wemm /* REQUEST: end the file session. */
301b8ba871bSPeter Wemm if (LF_ISSET(RCV_ENDSESSION) && file_end(sp, NULL, 1))
302b8ba871bSPeter Wemm rval = 1;
303b8ba871bSPeter Wemm
304b8ba871bSPeter Wemm return (rval);
305b8ba871bSPeter Wemm }
306b8ba871bSPeter Wemm
307b8ba871bSPeter Wemm /*
308b8ba871bSPeter Wemm * rcv_mailfile --
309b8ba871bSPeter Wemm * Build the file to mail to the user.
310b8ba871bSPeter Wemm */
311b8ba871bSPeter Wemm static int
rcv_mailfile(SCR * sp,int issync,char * cp_path)312110d525eSBaptiste Daroussin rcv_mailfile(SCR *sp, int issync, char *cp_path)
313b8ba871bSPeter Wemm {
314b8ba871bSPeter Wemm EXF *ep;
315b8ba871bSPeter Wemm GS *gp;
316b8ba871bSPeter Wemm struct passwd *pw;
317f0957ccaSPeter Wemm int len;
318b8ba871bSPeter Wemm time_t now;
319b8ba871bSPeter Wemm uid_t uid;
320b8ba871bSPeter Wemm int fd;
321f0957ccaSPeter Wemm FILE *fp;
322f0957ccaSPeter Wemm char *dp, *p, *t, *qt, *buf, *mpath;
323b8ba871bSPeter Wemm char *t1, *t2, *t3;
324f0957ccaSPeter Wemm int st;
325b8ba871bSPeter Wemm
326b8ba871bSPeter Wemm /*
327b8ba871bSPeter Wemm * XXX
328f0957ccaSPeter Wemm * MAXHOSTNAMELEN/HOST_NAME_MAX are deprecated. We try sysconf(3)
329f0957ccaSPeter Wemm * first, then fallback to _POSIX_HOST_NAME_MAX.
330b8ba871bSPeter Wemm */
331f0957ccaSPeter Wemm char *host;
332f0957ccaSPeter Wemm long hostmax = sysconf(_SC_HOST_NAME_MAX);
333f0957ccaSPeter Wemm if (hostmax < 0)
334f0957ccaSPeter Wemm hostmax = _POSIX_HOST_NAME_MAX;
335b8ba871bSPeter Wemm
336b8ba871bSPeter Wemm gp = sp->gp;
337b8ba871bSPeter Wemm if ((pw = getpwuid(uid = getuid())) == NULL) {
338b8ba871bSPeter Wemm msgq(sp, M_ERR,
339b8ba871bSPeter Wemm "062|Information on user id %u not found", uid);
340b8ba871bSPeter Wemm return (1);
341b8ba871bSPeter Wemm }
342b8ba871bSPeter Wemm
343b8ba871bSPeter Wemm if (opts_empty(sp, O_RECDIR, 0))
344b8ba871bSPeter Wemm return (1);
345b8ba871bSPeter Wemm dp = O_STR(sp, O_RECDIR);
346f0957ccaSPeter Wemm if ((mpath = join(dp, "recover.XXXXXX")) == NULL) {
347f0957ccaSPeter Wemm msgq(sp, M_SYSERR, NULL);
348b8ba871bSPeter Wemm return (1);
349f0957ccaSPeter Wemm }
350f0957ccaSPeter Wemm if ((fd = rcv_mktemp(sp, mpath, dp)) == -1) {
351f0957ccaSPeter Wemm free(mpath);
352f0957ccaSPeter Wemm return (1);
353f0957ccaSPeter Wemm }
354f0957ccaSPeter Wemm if ((fp = fdopen(fd, "w")) == NULL) {
355f0957ccaSPeter Wemm free(mpath);
356f0957ccaSPeter Wemm close(fd);
357f0957ccaSPeter Wemm return (1);
358f0957ccaSPeter Wemm }
359b8ba871bSPeter Wemm
360b8ba871bSPeter Wemm /*
361b8ba871bSPeter Wemm * XXX
362b8ba871bSPeter Wemm * We keep an open lock on the file so that the recover option can
363b8ba871bSPeter Wemm * distinguish between files that are live and those that need to
364b8ba871bSPeter Wemm * be recovered. There's an obvious window between the mkstemp call
365b8ba871bSPeter Wemm * and the lock, but it's pretty small.
366b8ba871bSPeter Wemm */
367b8ba871bSPeter Wemm ep = sp->ep;
368f0957ccaSPeter Wemm if (file_lock(sp, NULL, fd, 1) != LOCK_SUCCESS)
369b8ba871bSPeter Wemm msgq(sp, M_SYSERR, "063|Unable to lock recovery file");
370b8ba871bSPeter Wemm if (!issync) {
371b8ba871bSPeter Wemm /* Save the recover file descriptor, and mail path. */
372f0957ccaSPeter Wemm ep->rcv_fd = dup(fd);
373f0957ccaSPeter Wemm ep->rcv_mpath = mpath;
374b8ba871bSPeter Wemm cp_path = ep->rcv_path;
375b8ba871bSPeter Wemm }
376b8ba871bSPeter Wemm
377b8ba871bSPeter Wemm t = sp->frp->name;
378b8ba871bSPeter Wemm if ((p = strrchr(t, '/')) == NULL)
379b8ba871bSPeter Wemm p = t;
380b8ba871bSPeter Wemm else
381b8ba871bSPeter Wemm ++p;
382b8ba871bSPeter Wemm (void)time(&now);
383f0957ccaSPeter Wemm
384f0957ccaSPeter Wemm if ((st = rcv_dlnwrite(sp, "file", t, fp))) {
385f0957ccaSPeter Wemm if (st == 1)
386f0957ccaSPeter Wemm goto werr;
387f0957ccaSPeter Wemm goto err;
388f0957ccaSPeter Wemm }
389f0957ccaSPeter Wemm if ((st = rcv_dlnwrite(sp, "path", cp_path, fp))) {
390f0957ccaSPeter Wemm if (st == 1)
391f0957ccaSPeter Wemm goto werr;
392f0957ccaSPeter Wemm goto err;
393f0957ccaSPeter Wemm }
394f0957ccaSPeter Wemm
395110d525eSBaptiste Daroussin MALLOC(sp, host, hostmax + 1);
396f0957ccaSPeter Wemm if (host == NULL)
397f0957ccaSPeter Wemm goto err;
398f0957ccaSPeter Wemm (void)gethostname(host, hostmax + 1);
399f0957ccaSPeter Wemm
400f0957ccaSPeter Wemm len = fprintf(fp, "%s%s%s\n%s%s%s%s\n%s%.40s\n%s\n\n",
401f0957ccaSPeter Wemm "From: root@", host, " (Nvi recovery program)",
402f0957ccaSPeter Wemm "To: ", pw->pw_name, "@", host,
403b8ba871bSPeter Wemm "Subject: Nvi saved the file ", p,
404b8ba871bSPeter Wemm "Precedence: bulk"); /* For vacation(1). */
405f0957ccaSPeter Wemm if (len < 0) {
406f0957ccaSPeter Wemm free(host);
407b8ba871bSPeter Wemm goto werr;
408f0957ccaSPeter Wemm }
409b8ba871bSPeter Wemm
410f0957ccaSPeter Wemm if ((qt = quote(t)) == NULL) {
411f0957ccaSPeter Wemm free(host);
412f0957ccaSPeter Wemm msgq(sp, M_SYSERR, NULL);
413f0957ccaSPeter Wemm goto err;
414f0957ccaSPeter Wemm }
415f0957ccaSPeter Wemm len = asprintf(&buf, "%s%.24s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n\n",
416b8ba871bSPeter Wemm "On ", ctime(&now), ", the user ", pw->pw_name,
417b8ba871bSPeter Wemm " was editing a file named ", t, " on the machine ",
418b8ba871bSPeter Wemm host, ", when it was saved for recovery. ",
419b8ba871bSPeter Wemm "You can recover most, if not all, of the changes ",
420110d525eSBaptiste Daroussin "to this file using the -r option to ", getprogname(), ":\n\n\t",
421110d525eSBaptiste Daroussin getprogname(), " -r ", qt);
422f0957ccaSPeter Wemm free(qt);
423f0957ccaSPeter Wemm free(host);
424755cc40cSBaptiste Daroussin if (len == -1) {
425f0957ccaSPeter Wemm msgq(sp, M_SYSERR, NULL);
426b8ba871bSPeter Wemm goto err;
427b8ba871bSPeter Wemm }
428b8ba871bSPeter Wemm
429b8ba871bSPeter Wemm /*
430b8ba871bSPeter Wemm * Format the message. (Yes, I know it's silly.)
431b8ba871bSPeter Wemm * Requires that the message end in a <newline>.
432b8ba871bSPeter Wemm */
433b8ba871bSPeter Wemm #define FMTCOLS 60
434b8ba871bSPeter Wemm for (t1 = buf; len > 0; len -= t2 - t1, t1 = t2) {
435b8ba871bSPeter Wemm /* Check for a short length. */
436b8ba871bSPeter Wemm if (len <= FMTCOLS) {
437b8ba871bSPeter Wemm t2 = t1 + (len - 1);
438b8ba871bSPeter Wemm goto wout;
439b8ba871bSPeter Wemm }
440b8ba871bSPeter Wemm
441b8ba871bSPeter Wemm /* Check for a required <newline>. */
442b8ba871bSPeter Wemm t2 = strchr(t1, '\n');
443b8ba871bSPeter Wemm if (t2 - t1 <= FMTCOLS)
444b8ba871bSPeter Wemm goto wout;
445b8ba871bSPeter Wemm
446b8ba871bSPeter Wemm /* Find the closest space, if any. */
447b8ba871bSPeter Wemm for (t3 = t2; t2 > t1; --t2)
448b8ba871bSPeter Wemm if (*t2 == ' ') {
449b8ba871bSPeter Wemm if (t2 - t1 <= FMTCOLS)
450b8ba871bSPeter Wemm goto wout;
451b8ba871bSPeter Wemm t3 = t2;
452b8ba871bSPeter Wemm }
453b8ba871bSPeter Wemm t2 = t3;
454b8ba871bSPeter Wemm
455b8ba871bSPeter Wemm /* t2 points to the last character to display. */
456b8ba871bSPeter Wemm wout: *t2++ = '\n';
457b8ba871bSPeter Wemm
458b8ba871bSPeter Wemm /* t2 points one after the last character to display. */
459f0957ccaSPeter Wemm if (fwrite(t1, 1, t2 - t1, fp) != t2 - t1) {
460f0957ccaSPeter Wemm free(buf);
461b8ba871bSPeter Wemm goto werr;
462b8ba871bSPeter Wemm }
463f0957ccaSPeter Wemm }
464b8ba871bSPeter Wemm
465b8ba871bSPeter Wemm if (issync) {
466f0957ccaSPeter Wemm fflush(fp);
467b8ba871bSPeter Wemm rcv_email(sp, mpath);
468f0957ccaSPeter Wemm free(mpath);
469f0957ccaSPeter Wemm }
470f0957ccaSPeter Wemm if (fclose(fp)) {
471f0957ccaSPeter Wemm free(buf);
472b8ba871bSPeter Wemm werr: msgq(sp, M_SYSERR, "065|Recovery file");
473b8ba871bSPeter Wemm goto err;
474b8ba871bSPeter Wemm }
475f0957ccaSPeter Wemm free(buf);
476b8ba871bSPeter Wemm return (0);
477b8ba871bSPeter Wemm
478b8ba871bSPeter Wemm err: if (!issync)
479b8ba871bSPeter Wemm ep->rcv_fd = -1;
480f0957ccaSPeter Wemm if (fp != NULL)
481f0957ccaSPeter Wemm (void)fclose(fp);
482b8ba871bSPeter Wemm return (1);
483b8ba871bSPeter Wemm }
484b8ba871bSPeter Wemm
485b8ba871bSPeter Wemm /*
486b8ba871bSPeter Wemm * people making love
487b8ba871bSPeter Wemm * never exactly the same
488b8ba871bSPeter Wemm * just like a snowflake
489b8ba871bSPeter Wemm *
490b8ba871bSPeter Wemm * rcv_list --
491b8ba871bSPeter Wemm * List the files that can be recovered by this user.
492b8ba871bSPeter Wemm *
493c271fa92SBaptiste Daroussin * PUBLIC: int rcv_list(SCR *);
494b8ba871bSPeter Wemm */
495b8ba871bSPeter Wemm int
rcv_list(SCR * sp)496f0957ccaSPeter Wemm rcv_list(SCR *sp)
497b8ba871bSPeter Wemm {
498b8ba871bSPeter Wemm struct dirent *dp;
499b8ba871bSPeter Wemm struct stat sb;
500b8ba871bSPeter Wemm DIR *dirp;
501b8ba871bSPeter Wemm FILE *fp;
502b8ba871bSPeter Wemm int found;
503f0957ccaSPeter Wemm char *p, *file, *path;
504f0957ccaSPeter Wemm char *dtype, *data;
505f0957ccaSPeter Wemm int st;
506b8ba871bSPeter Wemm
507b8ba871bSPeter Wemm /* Open the recovery directory for reading. */
508b8ba871bSPeter Wemm if (opts_empty(sp, O_RECDIR, 0))
509b8ba871bSPeter Wemm return (1);
510b8ba871bSPeter Wemm p = O_STR(sp, O_RECDIR);
511b8ba871bSPeter Wemm if (chdir(p) || (dirp = opendir(".")) == NULL) {
512b8ba871bSPeter Wemm msgq_str(sp, M_SYSERR, p, "recdir: %s");
513b8ba871bSPeter Wemm return (1);
514b8ba871bSPeter Wemm }
515b8ba871bSPeter Wemm
516b8ba871bSPeter Wemm /* Read the directory. */
517b8ba871bSPeter Wemm for (found = 0; (dp = readdir(dirp)) != NULL;) {
518b8ba871bSPeter Wemm if (strncmp(dp->d_name, "recover.", 8))
519b8ba871bSPeter Wemm continue;
520b8ba871bSPeter Wemm
521f0957ccaSPeter Wemm /* If it's readable, it's recoverable. */
522f0957ccaSPeter Wemm if ((fp = fopen(dp->d_name, "r")) == NULL)
523b8ba871bSPeter Wemm continue;
524b8ba871bSPeter Wemm
525f0957ccaSPeter Wemm switch (file_lock(sp, NULL, fileno(fp), 1)) {
526b8ba871bSPeter Wemm case LOCK_FAILED:
527b8ba871bSPeter Wemm /*
528b8ba871bSPeter Wemm * XXX
529b8ba871bSPeter Wemm * Assume that a lock can't be acquired, but that we
530b8ba871bSPeter Wemm * should permit recovery anyway. If this is wrong,
531b8ba871bSPeter Wemm * and someone else is using the file, we're going to
532b8ba871bSPeter Wemm * die horribly.
533b8ba871bSPeter Wemm */
534b8ba871bSPeter Wemm break;
535b8ba871bSPeter Wemm case LOCK_SUCCESS:
536b8ba871bSPeter Wemm break;
537b8ba871bSPeter Wemm case LOCK_UNAVAIL:
538b8ba871bSPeter Wemm /* If it's locked, it's live. */
539b8ba871bSPeter Wemm (void)fclose(fp);
540b8ba871bSPeter Wemm continue;
541b8ba871bSPeter Wemm }
542b8ba871bSPeter Wemm
543b8ba871bSPeter Wemm /* Check the headers. */
544f0957ccaSPeter Wemm for (file = NULL, path = NULL;
545f0957ccaSPeter Wemm file == NULL || path == NULL;) {
546f0957ccaSPeter Wemm if ((st = rcv_dlnread(sp, &dtype, &data, fp))) {
547f0957ccaSPeter Wemm if (st == 1)
548b8ba871bSPeter Wemm msgq_str(sp, M_ERR, dp->d_name,
549b8ba871bSPeter Wemm "066|%s: malformed recovery file");
550b8ba871bSPeter Wemm goto next;
551b8ba871bSPeter Wemm }
552f0957ccaSPeter Wemm if (dtype == NULL)
553f0957ccaSPeter Wemm continue;
554f0957ccaSPeter Wemm if (!strcmp(dtype, "file"))
555f0957ccaSPeter Wemm file = data;
556f0957ccaSPeter Wemm else if (!strcmp(dtype, "path"))
557f0957ccaSPeter Wemm path = data;
558f0957ccaSPeter Wemm else
559f0957ccaSPeter Wemm free(data);
560f0957ccaSPeter Wemm }
561b8ba871bSPeter Wemm
562b8ba871bSPeter Wemm /*
563b8ba871bSPeter Wemm * If the file doesn't exist, it's an orphaned recovery file,
564b8ba871bSPeter Wemm * toss it.
565b8ba871bSPeter Wemm *
566b8ba871bSPeter Wemm * XXX
567b8ba871bSPeter Wemm * This can occur if the backup file was deleted and we crashed
568b8ba871bSPeter Wemm * before deleting the email file.
569b8ba871bSPeter Wemm */
570b8ba871bSPeter Wemm errno = 0;
571f0957ccaSPeter Wemm if (stat(path, &sb) &&
572b8ba871bSPeter Wemm errno == ENOENT) {
573b8ba871bSPeter Wemm (void)unlink(dp->d_name);
574b8ba871bSPeter Wemm goto next;
575b8ba871bSPeter Wemm }
576b8ba871bSPeter Wemm
577b8ba871bSPeter Wemm /* Get the last modification time and display. */
578b8ba871bSPeter Wemm (void)fstat(fileno(fp), &sb);
579b8ba871bSPeter Wemm (void)printf("%.24s: %s\n",
580f0957ccaSPeter Wemm ctime(&sb.st_mtime), file);
581b8ba871bSPeter Wemm found = 1;
582b8ba871bSPeter Wemm
583b8ba871bSPeter Wemm /* Close, discarding lock. */
584b8ba871bSPeter Wemm next: (void)fclose(fp);
585f0957ccaSPeter Wemm free(file);
586f0957ccaSPeter Wemm free(path);
587b8ba871bSPeter Wemm }
588b8ba871bSPeter Wemm if (found == 0)
589110d525eSBaptiste Daroussin (void)printf("%s: No files to recover\n", getprogname());
590b8ba871bSPeter Wemm (void)closedir(dirp);
591b8ba871bSPeter Wemm return (0);
592b8ba871bSPeter Wemm }
593b8ba871bSPeter Wemm
594b8ba871bSPeter Wemm /*
595b8ba871bSPeter Wemm * rcv_read --
596b8ba871bSPeter Wemm * Start a recovered file as the file to edit.
597b8ba871bSPeter Wemm *
598c271fa92SBaptiste Daroussin * PUBLIC: int rcv_read(SCR *, FREF *);
599b8ba871bSPeter Wemm */
600b8ba871bSPeter Wemm int
rcv_read(SCR * sp,FREF * frp)601110d525eSBaptiste Daroussin rcv_read(SCR *sp, FREF *frp)
602b8ba871bSPeter Wemm {
603b8ba871bSPeter Wemm struct dirent *dp;
604b8ba871bSPeter Wemm struct stat sb;
605b8ba871bSPeter Wemm DIR *dirp;
606f0957ccaSPeter Wemm FILE *fp;
607b8ba871bSPeter Wemm EXF *ep;
608f0957ccaSPeter Wemm struct timespec rec_mtim = { 0, 0 };
609f0957ccaSPeter Wemm int found, locked = 0, requested, sv_fd;
610b8ba871bSPeter Wemm char *name, *p, *t, *rp, *recp, *pathp;
611f0957ccaSPeter Wemm char *file, *path, *recpath;
612f0957ccaSPeter Wemm char *dtype, *data;
613f0957ccaSPeter Wemm int st;
614b8ba871bSPeter Wemm
615b8ba871bSPeter Wemm if (opts_empty(sp, O_RECDIR, 0))
616b8ba871bSPeter Wemm return (1);
617b8ba871bSPeter Wemm rp = O_STR(sp, O_RECDIR);
618b8ba871bSPeter Wemm if ((dirp = opendir(rp)) == NULL) {
619c271fa92SBaptiste Daroussin msgq_str(sp, M_SYSERR, rp, "%s");
620b8ba871bSPeter Wemm return (1);
621b8ba871bSPeter Wemm }
622b8ba871bSPeter Wemm
623b8ba871bSPeter Wemm name = frp->name;
624b8ba871bSPeter Wemm sv_fd = -1;
625b8ba871bSPeter Wemm recp = pathp = NULL;
626b8ba871bSPeter Wemm for (found = requested = 0; (dp = readdir(dirp)) != NULL;) {
627b8ba871bSPeter Wemm if (strncmp(dp->d_name, "recover.", 8))
628b8ba871bSPeter Wemm continue;
629f0957ccaSPeter Wemm if ((recpath = join(rp, dp->d_name)) == NULL) {
630f0957ccaSPeter Wemm msgq(sp, M_SYSERR, NULL);
631b8ba871bSPeter Wemm continue;
632f0957ccaSPeter Wemm }
633b8ba871bSPeter Wemm
634f0957ccaSPeter Wemm /* If it's readable, it's recoverable. */
635f0957ccaSPeter Wemm if ((fp = fopen(recpath, "r")) == NULL) {
636f0957ccaSPeter Wemm free(recpath);
637f0957ccaSPeter Wemm continue;
638f0957ccaSPeter Wemm }
639f0957ccaSPeter Wemm
640f0957ccaSPeter Wemm switch (file_lock(sp, NULL, fileno(fp), 1)) {
641b8ba871bSPeter Wemm case LOCK_FAILED:
642b8ba871bSPeter Wemm /*
643b8ba871bSPeter Wemm * XXX
644b8ba871bSPeter Wemm * Assume that a lock can't be acquired, but that we
645b8ba871bSPeter Wemm * should permit recovery anyway. If this is wrong,
646b8ba871bSPeter Wemm * and someone else is using the file, we're going to
647b8ba871bSPeter Wemm * die horribly.
648b8ba871bSPeter Wemm */
649b8ba871bSPeter Wemm locked = 0;
650b8ba871bSPeter Wemm break;
651b8ba871bSPeter Wemm case LOCK_SUCCESS:
652b8ba871bSPeter Wemm locked = 1;
653b8ba871bSPeter Wemm break;
654b8ba871bSPeter Wemm case LOCK_UNAVAIL:
655b8ba871bSPeter Wemm /* If it's locked, it's live. */
656f0957ccaSPeter Wemm (void)fclose(fp);
657b8ba871bSPeter Wemm continue;
658b8ba871bSPeter Wemm }
659b8ba871bSPeter Wemm
660b8ba871bSPeter Wemm /* Check the headers. */
661f0957ccaSPeter Wemm for (file = NULL, path = NULL;
662f0957ccaSPeter Wemm file == NULL || path == NULL;) {
663f0957ccaSPeter Wemm if ((st = rcv_dlnread(sp, &dtype, &data, fp))) {
664f0957ccaSPeter Wemm if (st == 1)
665f0957ccaSPeter Wemm msgq_str(sp, M_ERR, dp->d_name,
666b8ba871bSPeter Wemm "067|%s: malformed recovery file");
667b8ba871bSPeter Wemm goto next;
668b8ba871bSPeter Wemm }
669f0957ccaSPeter Wemm if (dtype == NULL)
670f0957ccaSPeter Wemm continue;
671f0957ccaSPeter Wemm if (!strcmp(dtype, "file"))
672f0957ccaSPeter Wemm file = data;
673f0957ccaSPeter Wemm else if (!strcmp(dtype, "path"))
674f0957ccaSPeter Wemm path = data;
675f0957ccaSPeter Wemm else
676f0957ccaSPeter Wemm free(data);
677f0957ccaSPeter Wemm }
678b8ba871bSPeter Wemm ++found;
679b8ba871bSPeter Wemm
680b8ba871bSPeter Wemm /*
681b8ba871bSPeter Wemm * If the file doesn't exist, it's an orphaned recovery file,
682b8ba871bSPeter Wemm * toss it.
683b8ba871bSPeter Wemm *
684b8ba871bSPeter Wemm * XXX
685b8ba871bSPeter Wemm * This can occur if the backup file was deleted and we crashed
686b8ba871bSPeter Wemm * before deleting the email file.
687b8ba871bSPeter Wemm */
688b8ba871bSPeter Wemm errno = 0;
689f0957ccaSPeter Wemm if (stat(path, &sb) &&
690b8ba871bSPeter Wemm errno == ENOENT) {
691b8ba871bSPeter Wemm (void)unlink(dp->d_name);
692b8ba871bSPeter Wemm goto next;
693b8ba871bSPeter Wemm }
694b8ba871bSPeter Wemm
695b8ba871bSPeter Wemm /* Check the file name. */
696f0957ccaSPeter Wemm if (strcmp(file, name))
697b8ba871bSPeter Wemm goto next;
698b8ba871bSPeter Wemm
699b8ba871bSPeter Wemm ++requested;
700b8ba871bSPeter Wemm
701f0957ccaSPeter Wemm /* If we've found more than one, take the most recent. */
702f0957ccaSPeter Wemm (void)fstat(fileno(fp), &sb);
703f0957ccaSPeter Wemm if (recp == NULL ||
704*6680e5a5SBaptiste Daroussin #if defined HAVE_STRUCT_STAT_ST_MTIMESPEC
705*6680e5a5SBaptiste Daroussin timespeccmp(&rec_mtim, &sb.st_mtimespec, <)) {
706*6680e5a5SBaptiste Daroussin #elif defined HAVE_STRUCT_STAT_ST_MTIM
707755cc40cSBaptiste Daroussin timespeccmp(&rec_mtim, &sb.st_mtim, <)) {
708*6680e5a5SBaptiste Daroussin #else
709*6680e5a5SBaptiste Daroussin rec_mtim.tv_sec < sb.st_mtime) {
710*6680e5a5SBaptiste Daroussin #endif
711b8ba871bSPeter Wemm p = recp;
712b8ba871bSPeter Wemm t = pathp;
713f0957ccaSPeter Wemm recp = recpath;
714f0957ccaSPeter Wemm pathp = path;
715b8ba871bSPeter Wemm if (p != NULL) {
716b8ba871bSPeter Wemm free(p);
717b8ba871bSPeter Wemm free(t);
718b8ba871bSPeter Wemm }
719*6680e5a5SBaptiste Daroussin #if defined HAVE_STRUCT_STAT_ST_MTIMESPEC
720*6680e5a5SBaptiste Daroussin rec_mtim = sb.st_mtimespec;
721*6680e5a5SBaptiste Daroussin #elif defined HAVE_STRUCT_STAT_ST_MTIM
722755cc40cSBaptiste Daroussin rec_mtim = sb.st_mtim;
723*6680e5a5SBaptiste Daroussin #else
724*6680e5a5SBaptiste Daroussin rec_mtim.tv_sec = sb.st_mtime;
725*6680e5a5SBaptiste Daroussin #endif
726b8ba871bSPeter Wemm if (sv_fd != -1)
727b8ba871bSPeter Wemm (void)close(sv_fd);
728f0957ccaSPeter Wemm sv_fd = dup(fileno(fp));
729f0957ccaSPeter Wemm } else {
730f0957ccaSPeter Wemm next: free(recpath);
731f0957ccaSPeter Wemm free(path);
732f0957ccaSPeter Wemm }
733f0957ccaSPeter Wemm (void)fclose(fp);
734f0957ccaSPeter Wemm free(file);
735b8ba871bSPeter Wemm }
736b8ba871bSPeter Wemm (void)closedir(dirp);
737b8ba871bSPeter Wemm
738b8ba871bSPeter Wemm if (recp == NULL) {
739b8ba871bSPeter Wemm msgq_str(sp, M_INFO, name,
740b8ba871bSPeter Wemm "068|No files named %s, readable by you, to recover");
741b8ba871bSPeter Wemm return (1);
742b8ba871bSPeter Wemm }
743b8ba871bSPeter Wemm if (found) {
744b8ba871bSPeter Wemm if (requested > 1)
745b8ba871bSPeter Wemm msgq(sp, M_INFO,
746b8ba871bSPeter Wemm "069|There are older versions of this file for you to recover");
747b8ba871bSPeter Wemm if (found > requested)
748b8ba871bSPeter Wemm msgq(sp, M_INFO,
749b8ba871bSPeter Wemm "070|There are other files for you to recover");
750b8ba871bSPeter Wemm }
751b8ba871bSPeter Wemm
752b8ba871bSPeter Wemm /*
753b8ba871bSPeter Wemm * Create the FREF structure, start the btree file.
754b8ba871bSPeter Wemm *
755b8ba871bSPeter Wemm * XXX
756b8ba871bSPeter Wemm * file_init() is going to set ep->rcv_path.
757b8ba871bSPeter Wemm */
758f0957ccaSPeter Wemm if (file_init(sp, frp, pathp, 0)) {
759b8ba871bSPeter Wemm free(recp);
760b8ba871bSPeter Wemm free(pathp);
761b8ba871bSPeter Wemm (void)close(sv_fd);
762b8ba871bSPeter Wemm return (1);
763b8ba871bSPeter Wemm }
764f0957ccaSPeter Wemm free(pathp);
765b8ba871bSPeter Wemm
766b8ba871bSPeter Wemm /*
767b8ba871bSPeter Wemm * We keep an open lock on the file so that the recover option can
768b8ba871bSPeter Wemm * distinguish between files that are live and those that need to
769b8ba871bSPeter Wemm * be recovered. The lock is already acquired, just copy it.
770b8ba871bSPeter Wemm */
771b8ba871bSPeter Wemm ep = sp->ep;
772b8ba871bSPeter Wemm ep->rcv_mpath = recp;
773b8ba871bSPeter Wemm ep->rcv_fd = sv_fd;
774b8ba871bSPeter Wemm if (!locked)
775b8ba871bSPeter Wemm F_SET(frp, FR_UNLOCKED);
776b8ba871bSPeter Wemm
777b8ba871bSPeter Wemm /* We believe the file is recoverable. */
778b8ba871bSPeter Wemm F_SET(ep, F_RCV_ON);
779b8ba871bSPeter Wemm return (0);
780b8ba871bSPeter Wemm }
781b8ba871bSPeter Wemm
782b8ba871bSPeter Wemm /*
783b8ba871bSPeter Wemm * rcv_copy --
784b8ba871bSPeter Wemm * Copy a recovery file.
785b8ba871bSPeter Wemm */
786b8ba871bSPeter Wemm static int
787110d525eSBaptiste Daroussin rcv_copy(SCR *sp, int wfd, char *fname)
788b8ba871bSPeter Wemm {
789b8ba871bSPeter Wemm int nr, nw, off, rfd;
790b8ba871bSPeter Wemm char buf[8 * 1024];
791b8ba871bSPeter Wemm
792b8ba871bSPeter Wemm if ((rfd = open(fname, O_RDONLY, 0)) == -1)
793b8ba871bSPeter Wemm goto err;
794b8ba871bSPeter Wemm while ((nr = read(rfd, buf, sizeof(buf))) > 0)
795b8ba871bSPeter Wemm for (off = 0; nr; nr -= nw, off += nw)
796b8ba871bSPeter Wemm if ((nw = write(wfd, buf + off, nr)) < 0)
797b8ba871bSPeter Wemm goto err;
798b8ba871bSPeter Wemm if (nr == 0)
799b8ba871bSPeter Wemm return (0);
800b8ba871bSPeter Wemm
801b8ba871bSPeter Wemm err: msgq_str(sp, M_SYSERR, fname, "%s");
802b8ba871bSPeter Wemm return (1);
803b8ba871bSPeter Wemm }
804b8ba871bSPeter Wemm
805b8ba871bSPeter Wemm /*
806b8ba871bSPeter Wemm * rcv_mktemp --
807b8ba871bSPeter Wemm * Paranoid make temporary file routine.
808b8ba871bSPeter Wemm */
809b8ba871bSPeter Wemm static int
810110d525eSBaptiste Daroussin rcv_mktemp(SCR *sp, char *path, char *dname)
811b8ba871bSPeter Wemm {
812b8ba871bSPeter Wemm int fd;
813b8ba871bSPeter Wemm
814b8ba871bSPeter Wemm if ((fd = mkstemp(path)) == -1)
815b8ba871bSPeter Wemm msgq_str(sp, M_SYSERR, dname, "%s");
816b8ba871bSPeter Wemm return (fd);
817b8ba871bSPeter Wemm }
818b8ba871bSPeter Wemm
819b8ba871bSPeter Wemm /*
820b8ba871bSPeter Wemm * rcv_email --
821b8ba871bSPeter Wemm * Send email.
822b8ba871bSPeter Wemm */
823b8ba871bSPeter Wemm static void
824110d525eSBaptiste Daroussin rcv_email(SCR *sp, char *fname)
825b8ba871bSPeter Wemm {
826f0957ccaSPeter Wemm char *buf;
827b8ba871bSPeter Wemm
828755cc40cSBaptiste Daroussin if (asprintf(&buf, _PATH_SENDMAIL " -odb -t < %s", fname) == -1) {
829f0957ccaSPeter Wemm msgq_str(sp, M_ERR, strerror(errno),
830f0957ccaSPeter Wemm "071|not sending email: %s");
831f0957ccaSPeter Wemm return;
832b8ba871bSPeter Wemm }
833f0957ccaSPeter Wemm (void)system(buf);
834f0957ccaSPeter Wemm free(buf);
835f0957ccaSPeter Wemm }
836f0957ccaSPeter Wemm
837f0957ccaSPeter Wemm /*
838f0957ccaSPeter Wemm * rcv_dlnwrite --
839f0957ccaSPeter Wemm * Encode a string into an X-vi-data line and write it.
840f0957ccaSPeter Wemm */
841f0957ccaSPeter Wemm static int
842110d525eSBaptiste Daroussin rcv_dlnwrite(SCR *sp, const char *dtype, const char *src, FILE *fp)
843f0957ccaSPeter Wemm {
844f0957ccaSPeter Wemm char *bp = NULL, *p;
845f0957ccaSPeter Wemm size_t blen = 0;
846f0957ccaSPeter Wemm size_t dlen, len;
847f0957ccaSPeter Wemm int plen, xlen;
848f0957ccaSPeter Wemm
849f0957ccaSPeter Wemm len = strlen(src);
850f0957ccaSPeter Wemm dlen = strlen(dtype);
851f0957ccaSPeter Wemm GET_SPACE_GOTOC(sp, bp, blen, (len + 2) / 3 * 4 + dlen + 2);
852f0957ccaSPeter Wemm (void)memcpy(bp, dtype, dlen);
853f0957ccaSPeter Wemm bp[dlen] = ';';
854f0957ccaSPeter Wemm if ((xlen = b64_ntop((u_char *)src,
855f0957ccaSPeter Wemm len, bp + dlen + 1, blen)) == -1)
856f0957ccaSPeter Wemm goto err;
857f0957ccaSPeter Wemm xlen += dlen + 1;
858f0957ccaSPeter Wemm
859f0957ccaSPeter Wemm /* Output as an MIME folding header. */
860f0957ccaSPeter Wemm if ((plen = fprintf(fp, VI_DHEADER " %.*s\n",
861f0957ccaSPeter Wemm FMTCOLS - (int)sizeof(VI_DHEADER), bp)) < 0)
862f0957ccaSPeter Wemm goto err;
863f0957ccaSPeter Wemm plen -= (int)sizeof(VI_DHEADER) + 1;
864f0957ccaSPeter Wemm for (p = bp, xlen -= plen; xlen > 0; xlen -= plen) {
865f0957ccaSPeter Wemm p += plen;
866f0957ccaSPeter Wemm if ((plen = fprintf(fp, " %.*s\n", FMTCOLS - 1, p)) < 0)
867f0957ccaSPeter Wemm goto err;
868f0957ccaSPeter Wemm plen -= 2;
869f0957ccaSPeter Wemm }
870f0957ccaSPeter Wemm FREE_SPACE(sp, bp, blen);
871f0957ccaSPeter Wemm return (0);
872f0957ccaSPeter Wemm
873f0957ccaSPeter Wemm err: FREE_SPACE(sp, bp, blen);
874f0957ccaSPeter Wemm return (1);
875f0957ccaSPeter Wemm alloc_err:
876f0957ccaSPeter Wemm msgq(sp, M_SYSERR, NULL);
877f0957ccaSPeter Wemm return (-1);
878f0957ccaSPeter Wemm }
879f0957ccaSPeter Wemm
880f0957ccaSPeter Wemm /*
881f0957ccaSPeter Wemm * rcv_dlnread --
882f0957ccaSPeter Wemm * Read an X-vi-data line and decode it.
883f0957ccaSPeter Wemm */
884f0957ccaSPeter Wemm static int
885110d525eSBaptiste Daroussin rcv_dlnread(SCR *sp, char **dtypep,
886f0957ccaSPeter Wemm char **datap, /* free *datap if != NULL after use. */
887f0957ccaSPeter Wemm FILE *fp)
888f0957ccaSPeter Wemm {
889f0957ccaSPeter Wemm int ch;
890f0957ccaSPeter Wemm char buf[1024];
891f0957ccaSPeter Wemm char *bp = NULL, *p, *src;
892f0957ccaSPeter Wemm size_t blen = 0;
893f0957ccaSPeter Wemm size_t len, off, dlen;
894f0957ccaSPeter Wemm char *dtype, *data;
895f0957ccaSPeter Wemm int xlen;
896f0957ccaSPeter Wemm
897f0957ccaSPeter Wemm if (fgets(buf, sizeof(buf), fp) == NULL)
898f0957ccaSPeter Wemm return (1);
899f0957ccaSPeter Wemm if (strncmp(buf, VI_DHEADER, sizeof(VI_DHEADER) - 1)) {
900f0957ccaSPeter Wemm *dtypep = NULL;
901f0957ccaSPeter Wemm *datap = NULL;
902f0957ccaSPeter Wemm return (0);
903f0957ccaSPeter Wemm }
904f0957ccaSPeter Wemm
905f0957ccaSPeter Wemm /* Fetch an MIME folding header. */
906f0957ccaSPeter Wemm len = strlen(buf) - sizeof(VI_DHEADER) + 1;
907f0957ccaSPeter Wemm GET_SPACE_GOTOC(sp, bp, blen, len);
908f0957ccaSPeter Wemm (void)memcpy(bp, buf + sizeof(VI_DHEADER) - 1, len);
909f0957ccaSPeter Wemm p = bp + len;
910f0957ccaSPeter Wemm while ((ch = fgetc(fp)) == ' ') {
911f0957ccaSPeter Wemm if (fgets(buf, sizeof(buf), fp) == NULL)
912f0957ccaSPeter Wemm goto err;
913f0957ccaSPeter Wemm off = strlen(buf);
914f0957ccaSPeter Wemm len += off;
915f0957ccaSPeter Wemm ADD_SPACE_GOTOC(sp, bp, blen, len);
916f0957ccaSPeter Wemm p = bp + len - off;
917f0957ccaSPeter Wemm (void)memcpy(p, buf, off);
918f0957ccaSPeter Wemm }
919f0957ccaSPeter Wemm bp[len] = '\0';
920f0957ccaSPeter Wemm (void)ungetc(ch, fp);
921f0957ccaSPeter Wemm
922f0957ccaSPeter Wemm for (p = bp; *p == ' ' || *p == '\n'; p++);
923f0957ccaSPeter Wemm if ((src = strchr(p, ';')) == NULL)
924f0957ccaSPeter Wemm goto err;
925f0957ccaSPeter Wemm dlen = src - p;
926f0957ccaSPeter Wemm src += 1;
927f0957ccaSPeter Wemm len -= src - bp;
928f0957ccaSPeter Wemm
929f0957ccaSPeter Wemm /* Memory looks like: "<data>\0<dtype>\0". */
930110d525eSBaptiste Daroussin MALLOC(sp, data, dlen + len / 4 * 3 + 2);
931f0957ccaSPeter Wemm if (data == NULL)
932f0957ccaSPeter Wemm goto err;
933f0957ccaSPeter Wemm if ((xlen = (b64_pton(p + dlen + 1,
934f0957ccaSPeter Wemm (u_char *)data, len / 4 * 3 + 1))) == -1) {
935f0957ccaSPeter Wemm free(data);
936f0957ccaSPeter Wemm goto err;
937f0957ccaSPeter Wemm }
938f0957ccaSPeter Wemm data[xlen] = '\0';
939f0957ccaSPeter Wemm dtype = data + xlen + 1;
940f0957ccaSPeter Wemm (void)memcpy(dtype, p, dlen);
941f0957ccaSPeter Wemm dtype[dlen] = '\0';
942f0957ccaSPeter Wemm FREE_SPACE(sp, bp, blen);
943f0957ccaSPeter Wemm *dtypep = dtype;
944f0957ccaSPeter Wemm *datap = data;
945f0957ccaSPeter Wemm return (0);
946f0957ccaSPeter Wemm
947f0957ccaSPeter Wemm err: FREE_SPACE(sp, bp, blen);
948f0957ccaSPeter Wemm return (1);
949f0957ccaSPeter Wemm alloc_err:
950f0957ccaSPeter Wemm msgq(sp, M_SYSERR, NULL);
951f0957ccaSPeter Wemm return (-1);
952b8ba871bSPeter Wemm }
953