1 /* $OpenBSD: log.c,v 1.12 2017/04/18 01:45:35 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 <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 free(ep->l_lp); 128 ep->l_lp = NULL; 129 ep->l_len = 0; 130 ep->l_cursor.lno = 1; /* XXX Any valid recno. */ 131 ep->l_cursor.cno = 0; 132 ep->l_high = ep->l_cur = 1; 133 return (0); 134 } 135 136 /* 137 * log_cursor -- 138 * Log the current cursor position, starting an event. 139 * 140 * PUBLIC: int log_cursor(SCR *); 141 */ 142 int 143 log_cursor(SCR *sp) 144 { 145 EXF *ep; 146 147 ep = sp->ep; 148 if (F_ISSET(ep, F_NOLOG)) 149 return (0); 150 151 /* 152 * If any changes were made since the last cursor init, 153 * put out the ending cursor record. 154 */ 155 if (ep->l_cursor.lno == OOBLNO) { 156 ep->l_cursor.lno = sp->lno; 157 ep->l_cursor.cno = sp->cno; 158 return (log_cursor1(sp, LOG_CURSOR_END)); 159 } 160 ep->l_cursor.lno = sp->lno; 161 ep->l_cursor.cno = sp->cno; 162 return (0); 163 } 164 165 /* 166 * log_cursor1 -- 167 * Actually push a cursor record out. 168 */ 169 static int 170 log_cursor1(SCR *sp, int type) 171 { 172 DBT data, key; 173 EXF *ep; 174 175 ep = sp->ep; 176 BINC_RET(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK)); 177 ep->l_lp[0] = type; 178 memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK)); 179 180 key.data = &ep->l_cur; 181 key.size = sizeof(recno_t); 182 data.data = ep->l_lp; 183 data.size = sizeof(u_char) + sizeof(MARK); 184 if (ep->log->put(ep->log, &key, &data, 0) == -1) 185 LOG_ERR; 186 187 #if defined(DEBUG) && 0 188 TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur, 189 type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end", 190 sp->lno, sp->cno); 191 #endif 192 /* Reset high water mark. */ 193 ep->l_high = ++ep->l_cur; 194 195 return (0); 196 } 197 198 /* 199 * log_line -- 200 * Log a line change. 201 * 202 * PUBLIC: int log_line(SCR *, recno_t, u_int); 203 */ 204 int 205 log_line(SCR *sp, recno_t lno, u_int action) 206 { 207 DBT data, key; 208 EXF *ep; 209 size_t len; 210 char *lp; 211 212 ep = sp->ep; 213 if (F_ISSET(ep, F_NOLOG)) 214 return (0); 215 216 /* 217 * XXX 218 * 219 * Kluge for vi. Clear the EXF undo flag so that the 220 * next 'u' command does a roll-back, regardless. 221 */ 222 F_CLR(ep, F_UNDO); 223 224 /* Put out one initial cursor record per set of changes. */ 225 if (ep->l_cursor.lno != OOBLNO) { 226 if (log_cursor1(sp, LOG_CURSOR_INIT)) 227 return (1); 228 ep->l_cursor.lno = OOBLNO; 229 } 230 231 /* 232 * Put out the changes. If it's a LOG_LINE_RESET_B call, it's a 233 * special case, avoid the caches. Also, if it fails and it's 234 * line 1, it just means that the user started with an empty file, 235 * so fake an empty length line. 236 */ 237 if (action == LOG_LINE_RESET_B) { 238 if (db_get(sp, lno, DBG_NOCACHE, &lp, &len)) { 239 if (lno != 1) { 240 db_err(sp, lno); 241 return (1); 242 } 243 len = 0; 244 lp = ""; 245 } 246 } else 247 if (db_get(sp, lno, DBG_FATAL, &lp, &len)) 248 return (1); 249 BINC_RET(sp, 250 ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t)); 251 ep->l_lp[0] = action; 252 memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t)); 253 memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len); 254 255 key.data = &ep->l_cur; 256 key.size = sizeof(recno_t); 257 data.data = ep->l_lp; 258 data.size = len + sizeof(u_char) + sizeof(recno_t); 259 if (ep->log->put(ep->log, &key, &data, 0) == -1) 260 LOG_ERR; 261 262 #if defined(DEBUG) && 0 263 switch (action) { 264 case LOG_LINE_APPEND: 265 TRACE(sp, "%u: log_line: append: %lu {%u}\n", 266 ep->l_cur, lno, len); 267 break; 268 case LOG_LINE_DELETE: 269 TRACE(sp, "%lu: log_line: delete: %lu {%u}\n", 270 ep->l_cur, lno, len); 271 break; 272 case LOG_LINE_INSERT: 273 TRACE(sp, "%lu: log_line: insert: %lu {%u}\n", 274 ep->l_cur, lno, len); 275 break; 276 case LOG_LINE_RESET_F: 277 TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n", 278 ep->l_cur, lno, len); 279 break; 280 case LOG_LINE_RESET_B: 281 TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n", 282 ep->l_cur, lno, len); 283 break; 284 } 285 #endif 286 /* Reset high water mark. */ 287 ep->l_high = ++ep->l_cur; 288 289 return (0); 290 } 291 292 /* 293 * log_mark -- 294 * Log a mark position. For the log to work, we assume that there 295 * aren't any operations that just put out a log record -- this 296 * would mean that undo operations would only reset marks, and not 297 * cause any other change. 298 * 299 * PUBLIC: int log_mark(SCR *, LMARK *); 300 */ 301 int 302 log_mark(SCR *sp, LMARK *lmp) 303 { 304 DBT data, key; 305 EXF *ep; 306 307 ep = sp->ep; 308 if (F_ISSET(ep, F_NOLOG)) 309 return (0); 310 311 /* Put out one initial cursor record per set of changes. */ 312 if (ep->l_cursor.lno != OOBLNO) { 313 if (log_cursor1(sp, LOG_CURSOR_INIT)) 314 return (1); 315 ep->l_cursor.lno = OOBLNO; 316 } 317 318 BINC_RET(sp, ep->l_lp, 319 ep->l_len, sizeof(u_char) + sizeof(LMARK)); 320 ep->l_lp[0] = LOG_MARK; 321 memmove(ep->l_lp + sizeof(u_char), lmp, sizeof(LMARK)); 322 323 key.data = &ep->l_cur; 324 key.size = sizeof(recno_t); 325 data.data = ep->l_lp; 326 data.size = sizeof(u_char) + sizeof(LMARK); 327 if (ep->log->put(ep->log, &key, &data, 0) == -1) 328 LOG_ERR; 329 330 #if defined(DEBUG) && 0 331 TRACE(sp, "%lu: mark %c: %lu/%u\n", 332 ep->l_cur, lmp->name, lmp->lno, lmp->cno); 333 #endif 334 /* Reset high water mark. */ 335 ep->l_high = ++ep->l_cur; 336 return (0); 337 } 338 339 /* 340 * Log_backward -- 341 * Roll the log backward one operation. 342 * 343 * PUBLIC: int log_backward(SCR *, MARK *); 344 */ 345 int 346 log_backward(SCR *sp, MARK *rp) 347 { 348 DBT key, data; 349 EXF *ep; 350 LMARK lm; 351 MARK m; 352 recno_t lno; 353 int didop; 354 u_char *p; 355 356 ep = sp->ep; 357 if (F_ISSET(ep, F_NOLOG)) { 358 msgq(sp, M_ERR, 359 "Logging not being performed, undo not possible"); 360 return (1); 361 } 362 363 if (ep->l_cur == 1) { 364 msgq(sp, M_BERR, "No changes to undo"); 365 return (1); 366 } 367 368 F_SET(ep, F_NOLOG); /* Turn off logging. */ 369 370 key.data = &ep->l_cur; /* Initialize db request. */ 371 key.size = sizeof(recno_t); 372 for (didop = 0;;) { 373 --ep->l_cur; 374 if (ep->log->get(ep->log, &key, &data, 0)) 375 LOG_ERR; 376 #if defined(DEBUG) && 0 377 log_trace(sp, "log_backward", ep->l_cur, data.data); 378 #endif 379 switch (*(p = (u_char *)data.data)) { 380 case LOG_CURSOR_INIT: 381 if (didop) { 382 memmove(rp, p + sizeof(u_char), sizeof(MARK)); 383 F_CLR(ep, F_NOLOG); 384 return (0); 385 } 386 break; 387 case LOG_CURSOR_END: 388 break; 389 case LOG_LINE_APPEND: 390 case LOG_LINE_INSERT: 391 didop = 1; 392 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 393 if (db_delete(sp, lno)) 394 goto err; 395 ++sp->rptlines[L_DELETED]; 396 break; 397 case LOG_LINE_DELETE: 398 didop = 1; 399 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 400 if (db_insert(sp, lno, p + sizeof(u_char) + 401 sizeof(recno_t), data.size - sizeof(u_char) - 402 sizeof(recno_t))) 403 goto err; 404 ++sp->rptlines[L_ADDED]; 405 break; 406 case LOG_LINE_RESET_F: 407 break; 408 case LOG_LINE_RESET_B: 409 didop = 1; 410 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 411 if (db_set(sp, lno, p + sizeof(u_char) + 412 sizeof(recno_t), data.size - sizeof(u_char) - 413 sizeof(recno_t))) 414 goto err; 415 if (sp->rptlchange != lno) { 416 sp->rptlchange = lno; 417 ++sp->rptlines[L_CHANGED]; 418 } 419 break; 420 case LOG_MARK: 421 didop = 1; 422 memmove(&lm, p + sizeof(u_char), sizeof(LMARK)); 423 m.lno = lm.lno; 424 m.cno = lm.cno; 425 if (mark_set(sp, lm.name, &m, 0)) 426 goto err; 427 break; 428 default: 429 abort(); 430 } 431 } 432 433 err: F_CLR(ep, F_NOLOG); 434 return (1); 435 } 436 437 /* 438 * Log_setline -- 439 * Reset the line to its original appearance. 440 * 441 * XXX 442 * There's a bug in this code due to our not logging cursor movements 443 * unless a change was made. If you do a change, move off the line, 444 * then move back on and do a 'U', the line will be restored to the way 445 * it was before the original change. 446 * 447 * PUBLIC: int log_setline(SCR *); 448 */ 449 int 450 log_setline(SCR *sp) 451 { 452 DBT key, data; 453 EXF *ep; 454 LMARK lm; 455 MARK m; 456 recno_t lno; 457 u_char *p; 458 459 ep = sp->ep; 460 if (F_ISSET(ep, F_NOLOG)) { 461 msgq(sp, M_ERR, 462 "Logging not being performed, undo not possible"); 463 return (1); 464 } 465 466 if (ep->l_cur == 1) 467 return (1); 468 469 F_SET(ep, F_NOLOG); /* Turn off logging. */ 470 471 key.data = &ep->l_cur; /* Initialize db request. */ 472 key.size = sizeof(recno_t); 473 474 for (;;) { 475 --ep->l_cur; 476 if (ep->log->get(ep->log, &key, &data, 0)) 477 LOG_ERR; 478 #if defined(DEBUG) && 0 479 log_trace(sp, "log_setline", ep->l_cur, data.data); 480 #endif 481 switch (*(p = (u_char *)data.data)) { 482 case LOG_CURSOR_INIT: 483 memmove(&m, p + sizeof(u_char), sizeof(MARK)); 484 if (m.lno != sp->lno || ep->l_cur == 1) { 485 F_CLR(ep, F_NOLOG); 486 return (0); 487 } 488 break; 489 case LOG_CURSOR_END: 490 memmove(&m, p + sizeof(u_char), sizeof(MARK)); 491 if (m.lno != sp->lno) { 492 ++ep->l_cur; 493 F_CLR(ep, F_NOLOG); 494 return (0); 495 } 496 break; 497 case LOG_LINE_APPEND: 498 case LOG_LINE_INSERT: 499 case LOG_LINE_DELETE: 500 case LOG_LINE_RESET_F: 501 break; 502 case LOG_LINE_RESET_B: 503 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 504 if (lno == sp->lno && 505 db_set(sp, lno, p + sizeof(u_char) + 506 sizeof(recno_t), data.size - sizeof(u_char) - 507 sizeof(recno_t))) 508 goto err; 509 if (sp->rptlchange != lno) { 510 sp->rptlchange = lno; 511 ++sp->rptlines[L_CHANGED]; 512 } 513 case LOG_MARK: 514 memmove(&lm, p + sizeof(u_char), sizeof(LMARK)); 515 m.lno = lm.lno; 516 m.cno = lm.cno; 517 if (mark_set(sp, lm.name, &m, 0)) 518 goto err; 519 break; 520 default: 521 abort(); 522 } 523 } 524 525 err: F_CLR(ep, F_NOLOG); 526 return (1); 527 } 528 529 /* 530 * Log_forward -- 531 * Roll the log forward one operation. 532 * 533 * PUBLIC: int log_forward(SCR *, MARK *); 534 */ 535 int 536 log_forward(SCR *sp, MARK *rp) 537 { 538 DBT key, data; 539 EXF *ep; 540 LMARK lm; 541 MARK m; 542 recno_t lno; 543 int didop; 544 u_char *p; 545 546 ep = sp->ep; 547 if (F_ISSET(ep, F_NOLOG)) { 548 msgq(sp, M_ERR, 549 "Logging not being performed, roll-forward not possible"); 550 return (1); 551 } 552 553 if (ep->l_cur == ep->l_high) { 554 msgq(sp, M_BERR, "No changes to re-do"); 555 return (1); 556 } 557 558 F_SET(ep, F_NOLOG); /* Turn off logging. */ 559 560 key.data = &ep->l_cur; /* Initialize db request. */ 561 key.size = sizeof(recno_t); 562 for (didop = 0;;) { 563 ++ep->l_cur; 564 if (ep->log->get(ep->log, &key, &data, 0)) 565 LOG_ERR; 566 #if defined(DEBUG) && 0 567 log_trace(sp, "log_forward", ep->l_cur, data.data); 568 #endif 569 switch (*(p = (u_char *)data.data)) { 570 case LOG_CURSOR_END: 571 if (didop) { 572 ++ep->l_cur; 573 memmove(rp, p + sizeof(u_char), sizeof(MARK)); 574 F_CLR(ep, F_NOLOG); 575 return (0); 576 } 577 break; 578 case LOG_CURSOR_INIT: 579 break; 580 case LOG_LINE_APPEND: 581 case LOG_LINE_INSERT: 582 didop = 1; 583 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 584 if (db_insert(sp, lno, p + sizeof(u_char) + 585 sizeof(recno_t), data.size - sizeof(u_char) - 586 sizeof(recno_t))) 587 goto err; 588 ++sp->rptlines[L_ADDED]; 589 break; 590 case LOG_LINE_DELETE: 591 didop = 1; 592 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 593 if (db_delete(sp, lno)) 594 goto err; 595 ++sp->rptlines[L_DELETED]; 596 break; 597 case LOG_LINE_RESET_B: 598 break; 599 case LOG_LINE_RESET_F: 600 didop = 1; 601 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 602 if (db_set(sp, lno, p + sizeof(u_char) + 603 sizeof(recno_t), data.size - sizeof(u_char) - 604 sizeof(recno_t))) 605 goto err; 606 if (sp->rptlchange != lno) { 607 sp->rptlchange = lno; 608 ++sp->rptlines[L_CHANGED]; 609 } 610 break; 611 case LOG_MARK: 612 didop = 1; 613 memmove(&lm, p + sizeof(u_char), sizeof(LMARK)); 614 m.lno = lm.lno; 615 m.cno = lm.cno; 616 if (mark_set(sp, lm.name, &m, 0)) 617 goto err; 618 break; 619 default: 620 abort(); 621 } 622 } 623 624 err: F_CLR(ep, F_NOLOG); 625 return (1); 626 } 627 628 /* 629 * log_err -- 630 * Try and restart the log on failure, i.e. if we run out of memory. 631 */ 632 static void 633 log_err(SCR *sp, char *file, int line) 634 { 635 EXF *ep; 636 637 msgq(sp, M_SYSERR, "%s/%d: log put error", basename(file), line); 638 ep = sp->ep; 639 (void)ep->log->close(ep->log); 640 if (!log_init(sp, ep)) 641 msgq(sp, M_ERR, "Log restarted"); 642 } 643 644 #if defined(DEBUG) && 0 645 static void 646 log_trace(SCR *sp, char *msg, recno_t rno, u_char *p) 647 { 648 LMARK lm; 649 MARK m; 650 recno_t lno; 651 652 switch (*p) { 653 case LOG_CURSOR_INIT: 654 memmove(&m, p + sizeof(u_char), sizeof(MARK)); 655 TRACE(sp, "%lu: %s: C_INIT: %u/%u\n", rno, msg, m.lno, m.cno); 656 break; 657 case LOG_CURSOR_END: 658 memmove(&m, p + sizeof(u_char), sizeof(MARK)); 659 TRACE(sp, "%lu: %s: C_END: %u/%u\n", rno, msg, m.lno, m.cno); 660 break; 661 case LOG_LINE_APPEND: 662 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 663 TRACE(sp, "%lu: %s: APPEND: %lu\n", rno, msg, lno); 664 break; 665 case LOG_LINE_INSERT: 666 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 667 TRACE(sp, "%lu: %s: INSERT: %lu\n", rno, msg, lno); 668 break; 669 case LOG_LINE_DELETE: 670 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 671 TRACE(sp, "%lu: %s: DELETE: %lu\n", rno, msg, lno); 672 break; 673 case LOG_LINE_RESET_F: 674 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 675 TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno); 676 break; 677 case LOG_LINE_RESET_B: 678 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 679 TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno); 680 break; 681 case LOG_MARK: 682 memmove(&lm, p + sizeof(u_char), sizeof(LMARK)); 683 TRACE(sp, 684 "%lu: %s: MARK: %u/%u\n", rno, msg, lm.lno, lm.cno); 685 break; 686 default: 687 abort(); 688 } 689 } 690 #endif 691