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