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 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 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 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 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 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 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 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 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 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 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 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