1 /* 2 * Copyright (c) 2005 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sbin/jscan/dump_mirror.c,v 1.8 2005/11/06 12:32:56 swildner Exp $ 35 */ 36 37 #include "jscan.h" 38 #include <sys/vfscache.h> 39 40 static void dump_mirror_stream(struct jsession *ss, struct jstream *js); 41 static int dump_mirror_toprecord(struct jsession *ss, struct jstream *js, 42 off_t *off, off_t recsize, int level); 43 static int dump_mirror_subrecord(enum jdirection direction, struct jstream *js, 44 off_t *off, off_t recsize, int level, 45 struct jattr *jattr); 46 static int dump_mirror_payload(int16_t rectype, struct jstream *js, off_t off, 47 int recsize, int level, struct jattr *jattr); 48 static int dump_mirror_rebuild_redo(u_int16_t rectype, 49 struct jstream *js, struct jattr *jattr); 50 static int dump_mirror_rebuild_undo(u_int16_t rectype, 51 struct jstream *js, struct jattr *jattr); 52 static void undo_recreate(const char *filename, 53 struct jstream *js, struct jattr *jattr); 54 static void dosetattr(const char *filename, int fd, struct jattr *jattr); 55 56 void 57 dump_mirror(struct jsession *ss, struct jdata *jd) 58 { 59 struct jstream *js; 60 61 if ((js = jaddrecord(ss, jd)) != NULL) { 62 dump_mirror_stream(ss, js); 63 jscan_dispose(js); 64 } 65 jsession_update_transid(ss, jd->jd_transid); 66 } 67 68 static void 69 dump_mirror_stream(struct jsession *ss, struct jstream *js) 70 { 71 struct journal_rawrecbeg head; 72 int16_t sid; 73 mode_t save_umask; 74 75 save_umask = umask(0); 76 jsread(js, 0, &head, sizeof(head)); 77 78 sid = head.streamid & JREC_STREAMID_MASK; 79 if (sid >= JREC_STREAMID_JMIN && sid < JREC_STREAMID_JMAX) { 80 off_t off = sizeof(head); 81 dump_mirror_toprecord(ss, js, &off, 82 js->js_normalized_total - 83 sizeof(struct journal_rawrecbeg), 84 1); 85 } else { 86 switch(head.streamid & JREC_STREAMID_MASK) { 87 case JREC_STREAMID_SYNCPT & JREC_STREAMID_MASK: 88 break; 89 case JREC_STREAMID_PAD & JREC_STREAMID_MASK: 90 break; 91 case JREC_STREAMID_DISCONT & JREC_STREAMID_MASK: 92 break; 93 case JREC_STREAMID_ANNOTATE & JREC_STREAMID_MASK: 94 break; 95 default: 96 break; 97 } 98 } 99 umask(save_umask); 100 } 101 102 /* 103 * Execute a meta-transaction, e.g. something like 'WRITE'. Meta-transactions 104 * are almost universally nested. 105 */ 106 static int 107 dump_mirror_toprecord(struct jsession *ss, struct jstream *js, 108 off_t *off, off_t recsize, int level) 109 { 110 struct journal_subrecord sub; 111 struct jattr jattr; 112 int payload; 113 int subsize; 114 int error; 115 off_t base = *off; 116 117 error = 0; 118 bzero(&jattr, sizeof(jattr)); 119 jattr_reset(&jattr); 120 121 while (recsize > 0) { 122 if ((error = jsread(js, base, &sub, sizeof(sub))) != 0) 123 break; 124 if (sub.recsize == -1) { 125 if ((sub.rectype & JMASK_NESTED) == 0) { 126 printf("Record size of -1 only works for nested records\n"); 127 error = -1; 128 break; 129 } 130 payload = 0x7FFFFFFF; 131 subsize = 0x7FFFFFFF; 132 } else { 133 payload = sub.recsize - sizeof(sub); 134 subsize = (sub.recsize + 7) & ~7; 135 } 136 if (sub.rectype & JMASK_NESTED) { 137 *off = base + sizeof(sub); 138 error = dump_mirror_subrecord(ss->ss_direction, js, off, 139 payload, level + 1, &jattr); 140 } else if (sub.rectype & JMASK_SUBRECORD) { 141 *off = base + sizeof(sub) + payload; 142 } else if ((sub.rectype & JTYPE_MASK) == JLEAF_PAD) { 143 } else { 144 } 145 if (ss->ss_direction == JD_FORWARDS) 146 dump_mirror_rebuild_redo(sub.rectype, js, &jattr); 147 else 148 dump_mirror_rebuild_undo(sub.rectype, js, &jattr); 149 jattr_reset(&jattr); 150 if (error) 151 break; 152 if (sub.recsize == -1) { 153 if ((sub.rectype & JMASK_NESTED) == 0) { 154 printf("Record size of -1 only works for nested records\n"); 155 error = -1; 156 break; 157 } 158 recsize -= ((*off + 7) & ~7) - base; 159 base = (*off + 7) & ~7; 160 } else { 161 if (subsize == 0) 162 subsize = sizeof(sub); 163 recsize -= subsize; 164 base += subsize; 165 } 166 if (sub.rectype & JMASK_LAST) 167 break; 168 } 169 *off = base; 170 return(error); 171 } 172 173 /* 174 * Parse a meta-transaction's nested records. The highest subrecord layer 175 * starts at layer = 2 (the top layer specifying the command is layer = 1). 176 * 177 * The nested subrecord contains informational records containing primarily 178 * namespace data, and further subrecords containing nested 179 * audit, undo, and redo data. 180 */ 181 static int 182 dump_mirror_subrecord(enum jdirection direction, struct jstream *js, 183 off_t *off, off_t recsize, int level, 184 struct jattr *jattr) 185 { 186 struct journal_subrecord sub; 187 int payload; 188 int subsize; 189 int error; 190 int skip; 191 u_int16_t rectype; 192 off_t base = *off; 193 194 error = 0; 195 while (recsize > 0) { 196 if ((error = jsread(js, base, &sub, sizeof(sub))) != 0) 197 break; 198 rectype = sub.rectype & JTYPE_MASK; /* includes the nested bit */ 199 if (sub.recsize == -1) { 200 payload = 0x7FFFFFFF; 201 subsize = 0x7FFFFFFF; 202 } else { 203 payload = sub.recsize - sizeof(sub); 204 subsize = (sub.recsize + 7) & ~7; 205 } 206 207 skip = 1; 208 *off = base + sizeof(sub); 209 210 switch(rectype) { 211 case JTYPE_REDO: /* NESTED */ 212 /* 213 * Process redo information when scanning forwards. 214 */ 215 if (direction == JD_FORWARDS) { 216 error = dump_mirror_subrecord(direction, js, off, payload, 217 level + 1, jattr); 218 skip = 0; 219 } 220 break; 221 case JTYPE_UNDO: /* NESTED */ 222 /* 223 * Process undo information when scanning backwards. 224 */ 225 if (direction == JD_BACKWARDS) { 226 error = dump_mirror_subrecord(direction, js, off, payload, 227 level + 1, jattr); 228 skip = 0; 229 } 230 break; 231 case JTYPE_CRED: /* NESTED */ 232 /* 233 * Ignore audit information 234 */ 235 break; 236 default: /* NESTED or non-NESTED */ 237 /* 238 * Execute these. Nested records might contain attribute 239 * information under an UNDO or REDO parent, for example. 240 */ 241 if (rectype & JMASK_NESTED) { 242 error = dump_mirror_subrecord(direction, js, off, payload, 243 level + 1, jattr); 244 skip = 0; 245 } else if (rectype & JMASK_SUBRECORD) { 246 error = dump_mirror_payload(sub.rectype, js, *off, payload, 247 level, jattr); 248 } 249 break; 250 } 251 if (error) 252 break; 253 254 /* 255 * skip only applies to nested subrecords. If the record size 256 * is unknown the record MUST be a nested record, and if we have 257 * not processed it we must recurse to figure out the actual size. 258 */ 259 if (sub.recsize == -1) { 260 assert(sub.rectype & JMASK_NESTED); 261 if (skip) { 262 error = dump_mirror_subrecord(direction, js, off, payload, 263 level + 1, NULL); 264 } 265 recsize -= ((*off + 7) & ~7) - base; 266 base = (*off + 7) & ~7; 267 } else { 268 if (subsize == 0) 269 subsize = sizeof(sub); 270 recsize -= subsize; 271 base += subsize; 272 } 273 if (error) 274 break; 275 if (sub.rectype & JMASK_LAST) 276 break; 277 } 278 *off = base; 279 return(error); 280 } 281 282 static int 283 dump_mirror_payload(int16_t rectype, struct jstream *js, off_t off, 284 int recsize, int level __unused, struct jattr *jattr) 285 { 286 const char *buf; 287 struct jattr_data *data; 288 int error; 289 290 if (jattr == NULL) 291 return (0); 292 293 if ((rectype & ~JMASK_LAST) != JLEAF_FILEDATA) { 294 error = jsreadp(js, off, (const void **)&buf, recsize); 295 if (error) 296 return (error); 297 } else { 298 buf = NULL; 299 error = 0; 300 } 301 302 switch(rectype & ~JMASK_LAST) { 303 case JLEAF_PAD: 304 case JLEAF_ABORT: 305 break; 306 case JLEAF_SYMLINKDATA: 307 jattr->symlinkdata = dupdatastr(buf, recsize); 308 jattr->symlinklen = recsize; 309 break; 310 case JLEAF_FILEDATA: 311 if ((data = jattr->last_data) == NULL) { 312 jattr->data.off = off; 313 jattr->data.bytes = recsize; 314 jattr->last_data = &jattr->data; 315 } else { 316 data->next = malloc(sizeof(jattr->data)); 317 data = data->next; 318 data->off = off; 319 data->bytes = recsize; 320 data->next = NULL; 321 jattr->last_data = data; 322 } 323 break; 324 case JLEAF_PATH1: 325 jattr->path1 = dupdatapath(buf, recsize); 326 break; 327 case JLEAF_PATH2: 328 jattr->path2 = dupdatapath(buf, recsize); 329 break; 330 case JLEAF_PATH3: 331 jattr->path3 = dupdatapath(buf, recsize); 332 break; 333 case JLEAF_PATH4: 334 jattr->path4 = dupdatapath(buf, recsize); 335 break; 336 case JLEAF_UID: 337 jattr->uid = buf_to_int64(buf, recsize); 338 break; 339 case JLEAF_GID: 340 jattr->gid = buf_to_int64(buf, recsize); 341 break; 342 case JLEAF_VTYPE: 343 jattr->vtype = buf_to_int64(buf, recsize); 344 break; 345 case JLEAF_MODES: 346 jattr->modes = buf_to_int64(buf, recsize); 347 break; 348 case JLEAF_FFLAGS: 349 jattr->fflags = buf_to_int64(buf, recsize); 350 break; 351 case JLEAF_PID: 352 jattr->pid = buf_to_int64(buf, recsize); 353 break; 354 case JLEAF_PPID: 355 jattr->ppid = buf_to_int64(buf, recsize); 356 break; 357 case JLEAF_COMM: 358 jattr->comm = dupdatastr(buf, recsize); 359 break; 360 case JLEAF_ATTRNAME: 361 jattr->attrname = dupdatastr(buf, recsize); 362 break; 363 case JLEAF_PATH_REF: 364 jattr->pathref = dupdatapath(buf, recsize); 365 break; 366 case JLEAF_RESERVED_0F: 367 break; 368 case JLEAF_SEEKPOS: 369 jattr->seekpos = buf_to_int64(buf, recsize); 370 break; 371 case JLEAF_INUM: 372 jattr->inum = buf_to_int64(buf, recsize); 373 break; 374 case JLEAF_NLINK: 375 jattr->nlink = buf_to_int64(buf, recsize); 376 break; 377 case JLEAF_FSID: 378 jattr->fsid = buf_to_int64(buf, recsize); 379 break; 380 case JLEAF_SIZE: 381 jattr->size = buf_to_int64(buf, recsize); 382 break; 383 case JLEAF_ATIME: 384 jattr->atime = *(const struct timeval *)buf; 385 break; 386 case JLEAF_MTIME: 387 jattr->mtime = *(const struct timeval *)buf; 388 break; 389 case JLEAF_CTIME: 390 jattr->ctime = *(const struct timeval *)buf; 391 break; 392 case JLEAF_GEN: 393 jattr->gen = buf_to_int64(buf, recsize); 394 break; 395 case JLEAF_FLAGS: 396 jattr->flags = buf_to_int64(buf, recsize); 397 break; 398 case JLEAF_UDEV: 399 jattr->udev = buf_to_int64(buf, recsize); 400 break; 401 case JLEAF_FILEREV: 402 jattr->filerev = buf_to_int64(buf, recsize); 403 break; 404 default: 405 break; 406 } 407 return (0); 408 } 409 410 static int 411 dump_mirror_rebuild_redo(u_int16_t rectype, struct jstream *js, 412 struct jattr *jattr) 413 { 414 struct jattr_data *data; 415 int error = 0; 416 int fd; 417 418 if (verbose_opt > 2) { 419 fprintf(stderr, "REDO %04x %s %s\n", 420 js->js_head->streamid, type_to_name(rectype), 421 jattr->pathref ? jattr->pathref : jattr->path1); 422 } 423 switch(rectype) { 424 case JTYPE_SETATTR: 425 if (jattr->pathref) { 426 if (jattr->uid != (uid_t)-1) 427 chown(jattr->pathref, jattr->uid, -1); 428 if (jattr->gid != (gid_t)-1) 429 chown(jattr->pathref, -1, jattr->gid); 430 if (jattr->modes != (mode_t)-1) 431 chmod(jattr->pathref, jattr->modes); 432 if (jattr->fflags != -1) 433 chflags(jattr->pathref, jattr->fflags); 434 if (jattr->size != -1) 435 truncate(jattr->pathref, jattr->size); 436 } 437 break; 438 case JTYPE_WRITE: 439 case JTYPE_PUTPAGES: 440 if (jattr->pathref && jattr->seekpos != -1) { 441 if ((fd = open(jattr->pathref, O_RDWR)) >= 0) { 442 lseek(fd, jattr->seekpos, 0); 443 for (data = &jattr->data; data; data = data->next) { 444 if (data->bytes) 445 jsreadcallback(js, write, fd, data->off, data->bytes); 446 } 447 close(fd); 448 } 449 } 450 break; 451 case JTYPE_SETACL: 452 break; 453 case JTYPE_SETEXTATTR: 454 break; 455 case JTYPE_CREATE: 456 /* 457 * note: both path1 and pathref will exist. 458 */ 459 if (jattr->path1 && jattr->modes != (mode_t)-1) { 460 if ((fd = open(jattr->path1, O_CREAT, jattr->modes)) >= 0) { 461 dosetattr(jattr->path1, fd, jattr); 462 close(fd); 463 } 464 } 465 break; 466 case JTYPE_MKNOD: 467 /* XXX */ 468 break; 469 case JTYPE_LINK: 470 if (jattr->pathref && jattr->path1) { 471 link(jattr->pathref, jattr->path1); 472 } 473 break; 474 case JTYPE_SYMLINK: 475 if (jattr->symlinkdata && jattr->path1) { 476 symlink(jattr->symlinkdata, jattr->path1); 477 } 478 break; 479 case JTYPE_WHITEOUT: 480 break; 481 case JTYPE_REMOVE: 482 if (jattr->path1) { 483 remove(jattr->path1); 484 } 485 break; 486 case JTYPE_MKDIR: 487 if (jattr->path1 && jattr->modes != (mode_t)-1) { 488 mkdir(jattr->path1, jattr->modes); 489 } 490 break; 491 case JTYPE_RMDIR: 492 if (jattr->path1) { 493 rmdir(jattr->path1); 494 } 495 break; 496 case JTYPE_RENAME: 497 if (jattr->path1 && jattr->path2) { 498 rename(jattr->path1, jattr->path2); 499 } 500 break; 501 } 502 return(error); 503 } 504 505 /* 506 * UNDO function using parsed primary data and parsed UNDO data. This 507 * must typically 508 */ 509 static int 510 dump_mirror_rebuild_undo(u_int16_t rectype, struct jstream *js, 511 struct jattr *jattr) 512 { 513 struct jattr_data *data; 514 int error = 0; 515 int fd; 516 517 if (verbose_opt > 2) { 518 fprintf(stderr, "UNDO %04x %s %s\n", 519 js->js_head->streamid, type_to_name(rectype), 520 jattr->pathref ? jattr->pathref : jattr->path1); 521 } 522 switch(rectype) { 523 case JTYPE_SETATTR: 524 if (jattr->pathref) 525 dosetattr(jattr->pathref, -1, jattr); 526 break; 527 case JTYPE_WRITE: 528 case JTYPE_PUTPAGES: 529 if (jattr->pathref && jattr->seekpos != -1) { 530 if ((fd = open(jattr->pathref, O_RDWR)) >= 0) { 531 lseek(fd, jattr->seekpos, 0); 532 for (data = &jattr->data; data; data = data->next) { 533 if (data->bytes) 534 jsreadcallback(js, write, fd, data->off, data->bytes); 535 } 536 close(fd); 537 } 538 } 539 if (jattr->size != -1) 540 truncate(jattr->pathref, jattr->size); 541 break; 542 case JTYPE_SETACL: 543 break; 544 case JTYPE_SETEXTATTR: 545 break; 546 case JTYPE_CREATE: 547 /* 548 * note: both path1 and pathref will exist. 549 */ 550 if (jattr->path1) 551 remove(jattr->path1); 552 break; 553 case JTYPE_MKNOD: 554 if (jattr->path1) 555 remove(jattr->path1); 556 break; 557 case JTYPE_LINK: 558 if (jattr->path1) { 559 undo_recreate(jattr->path1, js, jattr); 560 } 561 break; 562 case JTYPE_SYMLINK: 563 if (jattr->symlinkdata && jattr->path1) { 564 undo_recreate(jattr->path1, js, jattr); 565 } 566 break; 567 case JTYPE_WHITEOUT: 568 /* XXX */ 569 break; 570 case JTYPE_REMOVE: 571 if (jattr->path1) { 572 undo_recreate(jattr->path1, js, jattr); 573 } 574 break; 575 case JTYPE_MKDIR: 576 if (jattr->path1) { 577 rmdir(jattr->path1); 578 } 579 break; 580 case JTYPE_RMDIR: 581 if (jattr->path1 && jattr->modes != (mode_t)-1) { 582 mkdir(jattr->path1, jattr->modes); 583 } 584 break; 585 case JTYPE_RENAME: 586 if (jattr->path2) { 587 undo_recreate(jattr->path2, js, jattr); 588 } 589 break; 590 } 591 return(error); 592 } 593 594 /* 595 * This is a helper function for undoing operations which completely destroy 596 * the file that had existed previously. The caller will clean up the 597 * attributes (including file truncations/extensions) after the fact. 598 */ 599 static void 600 undo_recreate(const char *filename, struct jstream *js, struct jattr *jattr) 601 { 602 struct jattr_data *data; 603 int fd; 604 605 if (verbose_opt > 2) 606 fprintf(stderr, "RECREATE %s (type %d)\n", filename, jattr->vtype); 607 608 remove(filename); 609 switch(jattr->vtype) { 610 case VREG: 611 if (jattr->size != -1) { 612 if ((fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600)) >= 0) { 613 if (jattr->seekpos != -1) { 614 lseek(fd, jattr->seekpos, 0); 615 for (data = &jattr->data; data; data = data->next) { 616 if (data->bytes) 617 jsreadcallback(js, write, fd, data->off, data->bytes); 618 } 619 } 620 dosetattr(filename, fd, jattr); 621 close(fd); 622 } 623 } 624 break; 625 case VDIR: 626 mkdir(filename, 0600); 627 dosetattr(filename, -1, jattr); 628 break; 629 case VBLK: 630 case VCHR: 631 if (jattr->udev) { 632 mknod(filename, S_IFBLK|0666, jattr->udev); 633 dosetattr(filename, -1, jattr); 634 } 635 break; 636 case VLNK: 637 if (jattr->symlinkdata) { 638 symlink(jattr->symlinkdata, filename); 639 dosetattr(filename, -1, jattr); 640 } 641 break; 642 default: 643 break; 644 } 645 } 646 647 static void 648 dosetattr(const char *filename, int fd, struct jattr *jattr) 649 { 650 if (fd >= 0) { 651 if (jattr->uid != (uid_t)-1 && jattr->gid != (gid_t)-1) 652 fchown(fd, jattr->uid, jattr->gid); 653 else if (jattr->uid != (uid_t)-1) 654 fchown(fd, jattr->uid, -1); 655 else if (jattr->gid != (gid_t)-1) 656 fchown(fd, -1, jattr->gid); 657 658 if (jattr->modes != (mode_t)-1) 659 fchmod(fd, jattr->modes); 660 if (jattr->fflags != -1) 661 fchflags(fd, jattr->fflags); 662 if (jattr->size != -1) 663 ftruncate(fd, jattr->size); 664 } else { 665 if (jattr->uid != (uid_t)-1 && jattr->gid != (gid_t)-1) 666 lchown(filename, jattr->uid, jattr->gid); 667 else if (jattr->uid != (uid_t)-1) 668 lchown(filename, jattr->uid, -1); 669 else if (jattr->gid != (gid_t)-1) 670 lchown(filename, -1, jattr->gid); 671 672 if (jattr->modes != (mode_t)-1) 673 lchmod(filename, jattr->modes); 674 if (jattr->fflags != -1) 675 chflags(filename, jattr->fflags); 676 if (jattr->size != -1) 677 truncate(filename, jattr->size); 678 } 679 } 680 681