1 /* $OpenBSD: log.c,v 1.5 2009/10/27 23:59:47 deraadt Exp $ */
2
3 /*-
4 * Copyright (c) 1992, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 * Copyright (c) 1992, 1993, 1994, 1995, 1996
7 * Keith Bostic. All rights reserved.
8 *
9 * See the LICENSE file for redistribution information.
10 */
11
12 #include "config.h"
13
14 #include <sys/types.h>
15 #include <sys/queue.h>
16 #include <sys/stat.h>
17
18 #include <bitstring.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "common.h"
27
28 /*
29 * The log consists of records, each containing a type byte and a variable
30 * length byte string, as follows:
31 *
32 * LOG_CURSOR_INIT MARK
33 * LOG_CURSOR_END MARK
34 * LOG_LINE_APPEND recno_t char *
35 * LOG_LINE_DELETE recno_t char *
36 * LOG_LINE_INSERT recno_t char *
37 * LOG_LINE_RESET_F recno_t char *
38 * LOG_LINE_RESET_B recno_t char *
39 * LOG_MARK LMARK
40 *
41 * We do before image physical logging. This means that the editor layer
42 * MAY NOT modify records in place, even if simply deleting or overwriting
43 * characters. Since the smallest unit of logging is a line, we're using
44 * up lots of space. This may eventually have to be reduced, probably by
45 * doing logical logging, which is a much cooler database phrase.
46 *
47 * The implementation of the historic vi 'u' command, using roll-forward and
48 * roll-back, is simple. Each set of changes has a LOG_CURSOR_INIT record,
49 * followed by a number of other records, followed by a LOG_CURSOR_END record.
50 * LOG_LINE_RESET records come in pairs. The first is a LOG_LINE_RESET_B
51 * record, and is the line before the change. The second is LOG_LINE_RESET_F,
52 * and is the line after the change. Roll-back is done by backing up to the
53 * first LOG_CURSOR_INIT record before a change. Roll-forward is done in a
54 * similar fashion.
55 *
56 * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END
57 * record for a line different from the current one. It should be noted that
58 * this means that a subsequent 'u' command will make a change based on the
59 * new position of the log's cursor. This is okay, and, in fact, historic vi
60 * behaved that way.
61 */
62
63 static int log_cursor1(SCR *, int);
64 static void log_err(SCR *, char *, int);
65 #if defined(DEBUG) && 0
66 static void log_trace(SCR *, char *, recno_t, u_char *);
67 #endif
68
69 /* Try and restart the log on failure, i.e. if we run out of memory. */
70 #define LOG_ERR { \
71 log_err(sp, __FILE__, __LINE__); \
72 return (1); \
73 }
74
75 /*
76 * log_init --
77 * Initialize the logging subsystem.
78 *
79 * PUBLIC: int log_init(SCR *, EXF *);
80 */
81 int
log_init(sp,ep)82 log_init(sp, ep)
83 SCR *sp;
84 EXF *ep;
85 {
86 /*
87 * !!!
88 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
89 *
90 * Initialize the buffer. The logging subsystem has its own
91 * buffers because the global ones are almost by definition
92 * going to be in use when the log runs.
93 */
94 ep->l_lp = NULL;
95 ep->l_len = 0;
96 ep->l_cursor.lno = 1; /* XXX Any valid recno. */
97 ep->l_cursor.cno = 0;
98 ep->l_high = ep->l_cur = 1;
99
100 ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR,
101 S_IRUSR | S_IWUSR, DB_RECNO, NULL);
102 if (ep->log == NULL) {
103 msgq(sp, M_SYSERR, "009|Log file");
104 F_SET(ep, F_NOLOG);
105 return (1);
106 }
107
108 return (0);
109 }
110
111 /*
112 * log_end --
113 * Close the logging subsystem.
114 *
115 * PUBLIC: int log_end(SCR *, EXF *);
116 */
117 int
log_end(sp,ep)118 log_end(sp, ep)
119 SCR *sp;
120 EXF *ep;
121 {
122 /*
123 * !!!
124 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
125 */
126 if (ep->log != NULL) {
127 (void)(ep->log->close)(ep->log);
128 ep->log = NULL;
129 }
130 if (ep->l_lp != NULL) {
131 free(ep->l_lp);
132 ep->l_lp = NULL;
133 }
134 ep->l_len = 0;
135 ep->l_cursor.lno = 1; /* XXX Any valid recno. */
136 ep->l_cursor.cno = 0;
137 ep->l_high = ep->l_cur = 1;
138 return (0);
139 }
140
141 /*
142 * log_cursor --
143 * Log the current cursor position, starting an event.
144 *
145 * PUBLIC: int log_cursor(SCR *);
146 */
147 int
log_cursor(sp)148 log_cursor(sp)
149 SCR *sp;
150 {
151 EXF *ep;
152
153 ep = sp->ep;
154 if (F_ISSET(ep, F_NOLOG))
155 return (0);
156
157 /*
158 * If any changes were made since the last cursor init,
159 * put out the ending cursor record.
160 */
161 if (ep->l_cursor.lno == OOBLNO) {
162 ep->l_cursor.lno = sp->lno;
163 ep->l_cursor.cno = sp->cno;
164 return (log_cursor1(sp, LOG_CURSOR_END));
165 }
166 ep->l_cursor.lno = sp->lno;
167 ep->l_cursor.cno = sp->cno;
168 return (0);
169 }
170
171 /*
172 * log_cursor1 --
173 * Actually push a cursor record out.
174 */
175 static int
log_cursor1(sp,type)176 log_cursor1(sp, type)
177 SCR *sp;
178 int type;
179 {
180 DBT data, key;
181 EXF *ep;
182
183 ep = sp->ep;
184 BINC_RET(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK));
185 ep->l_lp[0] = type;
186 memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK));
187
188 key.data = &ep->l_cur;
189 key.size = sizeof(recno_t);
190 data.data = ep->l_lp;
191 data.size = sizeof(u_char) + sizeof(MARK);
192 if (ep->log->put(ep->log, &key, &data, 0) == -1)
193 LOG_ERR;
194
195 #if defined(DEBUG) && 0
196 TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur,
197 type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end",
198 sp->lno, sp->cno);
199 #endif
200 /* Reset high water mark. */
201 ep->l_high = ++ep->l_cur;
202
203 return (0);
204 }
205
206 /*
207 * log_line --
208 * Log a line change.
209 *
210 * PUBLIC: int log_line(SCR *, recno_t, u_int);
211 */
212 int
log_line(sp,lno,action)213 log_line(sp, lno, action)
214 SCR *sp;
215 recno_t lno;
216 u_int action;
217 {
218 DBT data, key;
219 EXF *ep;
220 size_t len;
221 char *lp;
222
223 ep = sp->ep;
224 if (F_ISSET(ep, F_NOLOG))
225 return (0);
226
227 /*
228 * XXX
229 *
230 * Kluge for vi. Clear the EXF undo flag so that the
231 * next 'u' command does a roll-back, regardless.
232 */
233 F_CLR(ep, F_UNDO);
234
235 /* Put out one initial cursor record per set of changes. */
236 if (ep->l_cursor.lno != OOBLNO) {
237 if (log_cursor1(sp, LOG_CURSOR_INIT))
238 return (1);
239 ep->l_cursor.lno = OOBLNO;
240 }
241
242 /*
243 * Put out the changes. If it's a LOG_LINE_RESET_B call, it's a
244 * special case, avoid the caches. Also, if it fails and it's
245 * line 1, it just means that the user started with an empty file,
246 * so fake an empty length line.
247 */
248 if (action == LOG_LINE_RESET_B) {
249 if (db_get(sp, lno, DBG_NOCACHE, &lp, &len)) {
250 if (lno != 1) {
251 db_err(sp, lno);
252 return (1);
253 }
254 len = 0;
255 lp = "";
256 }
257 } else
258 if (db_get(sp, lno, DBG_FATAL, &lp, &len))
259 return (1);
260 BINC_RET(sp,
261 ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t));
262 ep->l_lp[0] = action;
263 memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t));
264 memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len);
265
266 key.data = &ep->l_cur;
267 key.size = sizeof(recno_t);
268 data.data = ep->l_lp;
269 data.size = len + sizeof(u_char) + sizeof(recno_t);
270 if (ep->log->put(ep->log, &key, &data, 0) == -1)
271 LOG_ERR;
272
273 #if defined(DEBUG) && 0
274 switch (action) {
275 case LOG_LINE_APPEND:
276 TRACE(sp, "%u: log_line: append: %lu {%u}\n",
277 ep->l_cur, lno, len);
278 break;
279 case LOG_LINE_DELETE:
280 TRACE(sp, "%lu: log_line: delete: %lu {%u}\n",
281 ep->l_cur, lno, len);
282 break;
283 case LOG_LINE_INSERT:
284 TRACE(sp, "%lu: log_line: insert: %lu {%u}\n",
285 ep->l_cur, lno, len);
286 break;
287 case LOG_LINE_RESET_F:
288 TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n",
289 ep->l_cur, lno, len);
290 break;
291 case LOG_LINE_RESET_B:
292 TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n",
293 ep->l_cur, lno, len);
294 break;
295 }
296 #endif
297 /* Reset high water mark. */
298 ep->l_high = ++ep->l_cur;
299
300 return (0);
301 }
302
303 /*
304 * log_mark --
305 * Log a mark position. For the log to work, we assume that there
306 * aren't any operations that just put out a log record -- this
307 * would mean that undo operations would only reset marks, and not
308 * cause any other change.
309 *
310 * PUBLIC: int log_mark(SCR *, LMARK *);
311 */
312 int
log_mark(sp,lmp)313 log_mark(sp, lmp)
314 SCR *sp;
315 LMARK *lmp;
316 {
317 DBT data, key;
318 EXF *ep;
319
320 ep = sp->ep;
321 if (F_ISSET(ep, F_NOLOG))
322 return (0);
323
324 /* Put out one initial cursor record per set of changes. */
325 if (ep->l_cursor.lno != OOBLNO) {
326 if (log_cursor1(sp, LOG_CURSOR_INIT))
327 return (1);
328 ep->l_cursor.lno = OOBLNO;
329 }
330
331 BINC_RET(sp, ep->l_lp,
332 ep->l_len, sizeof(u_char) + sizeof(LMARK));
333 ep->l_lp[0] = LOG_MARK;
334 memmove(ep->l_lp + sizeof(u_char), lmp, sizeof(LMARK));
335
336 key.data = &ep->l_cur;
337 key.size = sizeof(recno_t);
338 data.data = ep->l_lp;
339 data.size = sizeof(u_char) + sizeof(LMARK);
340 if (ep->log->put(ep->log, &key, &data, 0) == -1)
341 LOG_ERR;
342
343 #if defined(DEBUG) && 0
344 TRACE(sp, "%lu: mark %c: %lu/%u\n",
345 ep->l_cur, lmp->name, lmp->lno, lmp->cno);
346 #endif
347 /* Reset high water mark. */
348 ep->l_high = ++ep->l_cur;
349 return (0);
350 }
351
352 /*
353 * Log_backward --
354 * Roll the log backward one operation.
355 *
356 * PUBLIC: int log_backward(SCR *, MARK *);
357 */
358 int
log_backward(sp,rp)359 log_backward(sp, rp)
360 SCR *sp;
361 MARK *rp;
362 {
363 DBT key, data;
364 EXF *ep;
365 LMARK lm;
366 MARK m;
367 recno_t lno;
368 int didop;
369 u_char *p;
370
371 ep = sp->ep;
372 if (F_ISSET(ep, F_NOLOG)) {
373 msgq(sp, M_ERR,
374 "010|Logging not being performed, undo not possible");
375 return (1);
376 }
377
378 if (ep->l_cur == 1) {
379 msgq(sp, M_BERR, "011|No changes to undo");
380 return (1);
381 }
382
383 F_SET(ep, F_NOLOG); /* Turn off logging. */
384
385 key.data = &ep->l_cur; /* Initialize db request. */
386 key.size = sizeof(recno_t);
387 for (didop = 0;;) {
388 --ep->l_cur;
389 if (ep->log->get(ep->log, &key, &data, 0))
390 LOG_ERR;
391 #if defined(DEBUG) && 0
392 log_trace(sp, "log_backward", ep->l_cur, data.data);
393 #endif
394 switch (*(p = (u_char *)data.data)) {
395 case LOG_CURSOR_INIT:
396 if (didop) {
397 memmove(rp, p + sizeof(u_char), sizeof(MARK));
398 F_CLR(ep, F_NOLOG);
399 return (0);
400 }
401 break;
402 case LOG_CURSOR_END:
403 break;
404 case LOG_LINE_APPEND:
405 case LOG_LINE_INSERT:
406 didop = 1;
407 memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
408 if (db_delete(sp, lno))
409 goto err;
410 ++sp->rptlines[L_DELETED];
411 break;
412 case LOG_LINE_DELETE:
413 didop = 1;
414 memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
415 if (db_insert(sp, lno, p + sizeof(u_char) +
416 sizeof(recno_t), data.size - sizeof(u_char) -
417 sizeof(recno_t)))
418 goto err;
419 ++sp->rptlines[L_ADDED];
420 break;
421 case LOG_LINE_RESET_F:
422 break;
423 case LOG_LINE_RESET_B:
424 didop = 1;
425 memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
426 if (db_set(sp, lno, p + sizeof(u_char) +
427 sizeof(recno_t), data.size - sizeof(u_char) -
428 sizeof(recno_t)))
429 goto err;
430 if (sp->rptlchange != lno) {
431 sp->rptlchange = lno;
432 ++sp->rptlines[L_CHANGED];
433 }
434 break;
435 case LOG_MARK:
436 didop = 1;
437 memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
438 m.lno = lm.lno;
439 m.cno = lm.cno;
440 if (mark_set(sp, lm.name, &m, 0))
441 goto err;
442 break;
443 default:
444 abort();
445 }
446 }
447
448 err: F_CLR(ep, F_NOLOG);
449 return (1);
450 }
451
452 /*
453 * Log_setline --
454 * Reset the line to its original appearance.
455 *
456 * XXX
457 * There's a bug in this code due to our not logging cursor movements
458 * unless a change was made. If you do a change, move off the line,
459 * then move back on and do a 'U', the line will be restored to the way
460 * it was before the original change.
461 *
462 * PUBLIC: int log_setline(SCR *);
463 */
464 int
log_setline(sp)465 log_setline(sp)
466 SCR *sp;
467 {
468 DBT key, data;
469 EXF *ep;
470 LMARK lm;
471 MARK m;
472 recno_t lno;
473 u_char *p;
474
475 ep = sp->ep;
476 if (F_ISSET(ep, F_NOLOG)) {
477 msgq(sp, M_ERR,
478 "012|Logging not being performed, undo not possible");
479 return (1);
480 }
481
482 if (ep->l_cur == 1)
483 return (1);
484
485 F_SET(ep, F_NOLOG); /* Turn off logging. */
486
487 key.data = &ep->l_cur; /* Initialize db request. */
488 key.size = sizeof(recno_t);
489
490 for (;;) {
491 --ep->l_cur;
492 if (ep->log->get(ep->log, &key, &data, 0))
493 LOG_ERR;
494 #if defined(DEBUG) && 0
495 log_trace(sp, "log_setline", ep->l_cur, data.data);
496 #endif
497 switch (*(p = (u_char *)data.data)) {
498 case LOG_CURSOR_INIT:
499 memmove(&m, p + sizeof(u_char), sizeof(MARK));
500 if (m.lno != sp->lno || ep->l_cur == 1) {
501 F_CLR(ep, F_NOLOG);
502 return (0);
503 }
504 break;
505 case LOG_CURSOR_END:
506 memmove(&m, p + sizeof(u_char), sizeof(MARK));
507 if (m.lno != sp->lno) {
508 ++ep->l_cur;
509 F_CLR(ep, F_NOLOG);
510 return (0);
511 }
512 break;
513 case LOG_LINE_APPEND:
514 case LOG_LINE_INSERT:
515 case LOG_LINE_DELETE:
516 case LOG_LINE_RESET_F:
517 break;
518 case LOG_LINE_RESET_B:
519 memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
520 if (lno == sp->lno &&
521 db_set(sp, lno, p + sizeof(u_char) +
522 sizeof(recno_t), data.size - sizeof(u_char) -
523 sizeof(recno_t)))
524 goto err;
525 if (sp->rptlchange != lno) {
526 sp->rptlchange = lno;
527 ++sp->rptlines[L_CHANGED];
528 }
529 case LOG_MARK:
530 memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
531 m.lno = lm.lno;
532 m.cno = lm.cno;
533 if (mark_set(sp, lm.name, &m, 0))
534 goto err;
535 break;
536 default:
537 abort();
538 }
539 }
540
541 err: F_CLR(ep, F_NOLOG);
542 return (1);
543 }
544
545 /*
546 * Log_forward --
547 * Roll the log forward one operation.
548 *
549 * PUBLIC: int log_forward(SCR *, MARK *);
550 */
551 int
log_forward(sp,rp)552 log_forward(sp, rp)
553 SCR *sp;
554 MARK *rp;
555 {
556 DBT key, data;
557 EXF *ep;
558 LMARK lm;
559 MARK m;
560 recno_t lno;
561 int didop;
562 u_char *p;
563
564 ep = sp->ep;
565 if (F_ISSET(ep, F_NOLOG)) {
566 msgq(sp, M_ERR,
567 "013|Logging not being performed, roll-forward not possible");
568 return (1);
569 }
570
571 if (ep->l_cur == ep->l_high) {
572 msgq(sp, M_BERR, "014|No changes to re-do");
573 return (1);
574 }
575
576 F_SET(ep, F_NOLOG); /* Turn off logging. */
577
578 key.data = &ep->l_cur; /* Initialize db request. */
579 key.size = sizeof(recno_t);
580 for (didop = 0;;) {
581 ++ep->l_cur;
582 if (ep->log->get(ep->log, &key, &data, 0))
583 LOG_ERR;
584 #if defined(DEBUG) && 0
585 log_trace(sp, "log_forward", ep->l_cur, data.data);
586 #endif
587 switch (*(p = (u_char *)data.data)) {
588 case LOG_CURSOR_END:
589 if (didop) {
590 ++ep->l_cur;
591 memmove(rp, p + sizeof(u_char), sizeof(MARK));
592 F_CLR(ep, F_NOLOG);
593 return (0);
594 }
595 break;
596 case LOG_CURSOR_INIT:
597 break;
598 case LOG_LINE_APPEND:
599 case LOG_LINE_INSERT:
600 didop = 1;
601 memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
602 if (db_insert(sp, lno, p + sizeof(u_char) +
603 sizeof(recno_t), data.size - sizeof(u_char) -
604 sizeof(recno_t)))
605 goto err;
606 ++sp->rptlines[L_ADDED];
607 break;
608 case LOG_LINE_DELETE:
609 didop = 1;
610 memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
611 if (db_delete(sp, lno))
612 goto err;
613 ++sp->rptlines[L_DELETED];
614 break;
615 case LOG_LINE_RESET_B:
616 break;
617 case LOG_LINE_RESET_F:
618 didop = 1;
619 memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
620 if (db_set(sp, lno, p + sizeof(u_char) +
621 sizeof(recno_t), data.size - sizeof(u_char) -
622 sizeof(recno_t)))
623 goto err;
624 if (sp->rptlchange != lno) {
625 sp->rptlchange = lno;
626 ++sp->rptlines[L_CHANGED];
627 }
628 break;
629 case LOG_MARK:
630 didop = 1;
631 memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
632 m.lno = lm.lno;
633 m.cno = lm.cno;
634 if (mark_set(sp, lm.name, &m, 0))
635 goto err;
636 break;
637 default:
638 abort();
639 }
640 }
641
642 err: F_CLR(ep, F_NOLOG);
643 return (1);
644 }
645
646 /*
647 * log_err --
648 * Try and restart the log on failure, i.e. if we run out of memory.
649 */
650 static void
log_err(sp,file,line)651 log_err(sp, file, line)
652 SCR *sp;
653 char *file;
654 int line;
655 {
656 EXF *ep;
657
658 msgq(sp, M_SYSERR, "015|%s/%d: log put error", tail(file), line);
659 ep = sp->ep;
660 (void)ep->log->close(ep->log);
661 if (!log_init(sp, ep))
662 msgq(sp, M_ERR, "267|Log restarted");
663 }
664
665 #if defined(DEBUG) && 0
666 static void
log_trace(sp,msg,rno,p)667 log_trace(sp, msg, rno, p)
668 SCR *sp;
669 char *msg;
670 recno_t rno;
671 u_char *p;
672 {
673 LMARK lm;
674 MARK m;
675 recno_t lno;
676
677 switch (*p) {
678 case LOG_CURSOR_INIT:
679 memmove(&m, p + sizeof(u_char), sizeof(MARK));
680 TRACE(sp, "%lu: %s: C_INIT: %u/%u\n", rno, msg, m.lno, m.cno);
681 break;
682 case LOG_CURSOR_END:
683 memmove(&m, p + sizeof(u_char), sizeof(MARK));
684 TRACE(sp, "%lu: %s: C_END: %u/%u\n", rno, msg, m.lno, m.cno);
685 break;
686 case LOG_LINE_APPEND:
687 memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
688 TRACE(sp, "%lu: %s: APPEND: %lu\n", rno, msg, lno);
689 break;
690 case LOG_LINE_INSERT:
691 memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
692 TRACE(sp, "%lu: %s: INSERT: %lu\n", rno, msg, lno);
693 break;
694 case LOG_LINE_DELETE:
695 memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
696 TRACE(sp, "%lu: %s: DELETE: %lu\n", rno, msg, lno);
697 break;
698 case LOG_LINE_RESET_F:
699 memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
700 TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno);
701 break;
702 case LOG_LINE_RESET_B:
703 memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
704 TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno);
705 break;
706 case LOG_MARK:
707 memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
708 TRACE(sp,
709 "%lu: %s: MARK: %u/%u\n", rno, msg, lm.lno, lm.cno);
710 break;
711 default:
712 abort();
713 }
714 }
715 #endif
716