1 /* $NetBSD: tape.c,v 1.49 2003/08/07 10:04:38 agc Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * (c) UNIX System Laboratories, Inc. 7 * All or some portions of this file are derived from material licensed 8 * to the University of California by American Telephone and Telegraph 9 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 10 * the permission of UNIX System Laboratories, Inc. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #include <sys/cdefs.h> 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)tape.c 8.9 (Berkeley) 5/1/95"; 41 #else 42 __RCSID("$NetBSD: tape.c,v 1.49 2003/08/07 10:04:38 agc Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/file.h> 48 #include <sys/ioctl.h> 49 #include <sys/mtio.h> 50 #include <sys/stat.h> 51 52 #include <ufs/ufs/dinode.h> 53 #include <protocols/dumprestore.h> 54 55 #include <errno.h> 56 #include <paths.h> 57 #include <setjmp.h> 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <time.h> 62 #include <unistd.h> 63 64 #include "restore.h" 65 #include "extern.h" 66 67 static u_int32_t fssize = MAXBSIZE; 68 static int mt = -1; 69 static int pipein = 0; 70 static char magtape[BUFSIZ]; 71 static int blkcnt; 72 static int numtrec; 73 static char *tapebuf; 74 static union u_spcl endoftapemark; 75 static int blksread; /* blocks read since last header */ 76 static int tpblksread = 0; /* TP_BSIZE blocks read */ 77 static int tapesread; 78 static jmp_buf restart; 79 static int gettingfile = 0; /* restart has a valid frame */ 80 #ifdef RRESTORE 81 static char *host = NULL; 82 #endif 83 84 static int ofile; 85 static char *map; 86 static char lnkbuf[MAXPATHLEN + 1]; 87 static int pathlen; 88 89 int oldinofmt; /* old inode format conversion required */ 90 int Bcvt; /* Swap Bytes (for CCI or sun) */ 91 92 #define FLUSHTAPEBUF() blkcnt = ntrec + 1 93 94 union u_ospcl { 95 char dummy[TP_BSIZE]; 96 struct s_ospcl { 97 int32_t c_type; 98 int32_t c_date; 99 int32_t c_ddate; 100 int32_t c_volume; 101 int32_t c_tapea; 102 u_int16_t c_inumber; 103 int32_t c_magic; 104 int32_t c_checksum; 105 struct odinode { 106 unsigned short odi_mode; 107 u_int16_t odi_nlink; 108 u_int16_t odi_uid; 109 u_int16_t odi_gid; 110 int32_t odi_size; 111 int32_t odi_rdev; 112 char odi_addr[36]; 113 int32_t odi_atime; 114 int32_t odi_mtime; 115 int32_t odi_ctime; 116 } c_odinode; 117 int32_t c_count; 118 char c_addr[256]; 119 } s_ospcl; 120 }; 121 122 static void accthdr __P((struct s_spcl *)); 123 static int checksum __P((int *)); 124 static void findinode __P((struct s_spcl *)); 125 static void findtapeblksize __P((void)); 126 static int gethead __P((struct s_spcl *)); 127 static void readtape __P((char *)); 128 static void setdumpnum __P((void)); 129 static void terminateinput __P((void)); 130 static void xtrfile __P((char *, long)); 131 static void xtrlnkfile __P((char *, long)); 132 static void xtrlnkskip __P((char *, long)); 133 static void xtrmap __P((char *, long)); 134 static void xtrmapskip __P((char *, long)); 135 static void xtrskip __P((char *, long)); 136 static void swap_header __P((struct s_spcl *)); 137 static void swap_old_header __P((struct s_ospcl *)); 138 139 /* 140 * Set up an input source 141 */ 142 void 143 setinput(source) 144 char *source; 145 { 146 FLUSHTAPEBUF(); 147 if (bflag) 148 newtapebuf(ntrec); 149 else 150 newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC); 151 terminal = stdin; 152 153 #ifdef RRESTORE 154 if (strchr(source, ':')) { 155 host = source; 156 source = strchr(host, ':'); 157 *source++ = '\0'; 158 if (rmthost(host) == 0) 159 exit(1); 160 } else 161 #endif 162 if (strcmp(source, "-") == 0) { 163 /* 164 * Since input is coming from a pipe we must establish 165 * our own connection to the terminal. 166 */ 167 terminal = fopen(_PATH_TTY, "r"); 168 if (terminal == NULL) { 169 (void)fprintf(stderr, "cannot open %s: %s\n", 170 _PATH_TTY, strerror(errno)); 171 terminal = fopen(_PATH_DEVNULL, "r"); 172 if (terminal == NULL) { 173 (void)fprintf(stderr, "cannot open %s: %s\n", 174 _PATH_DEVNULL, strerror(errno)); 175 exit(1); 176 } 177 } 178 pipein++; 179 } 180 (void) strcpy(magtape, source); 181 } 182 183 void 184 newtapebuf(size) 185 long size; 186 { 187 static int tapebufsize = -1; 188 189 ntrec = size; 190 if (size <= tapebufsize) 191 return; 192 if (tapebuf != NULL) 193 free(tapebuf); 194 tapebuf = malloc(size * TP_BSIZE); 195 if (tapebuf == NULL) { 196 fprintf(stderr, "Cannot allocate space for tape buffer\n"); 197 exit(1); 198 } 199 tapebufsize = size; 200 } 201 202 /* 203 * Verify that the tape drive can be accessed and 204 * that it actually is a dump tape. 205 */ 206 void 207 setup() 208 { 209 int i, j, *ip; 210 struct stat stbuf; 211 212 vprintf(stdout, "Verify tape and initialize maps\n"); 213 #ifdef RRESTORE 214 if (host) 215 mt = rmtopen(magtape, 0); 216 else 217 #endif 218 if (pipein) 219 mt = 0; 220 else 221 mt = open(magtape, O_RDONLY, 0); 222 if (mt < 0) { 223 fprintf(stderr, "%s: %s\n", magtape, strerror(errno)); 224 exit(1); 225 } 226 volno = 1; 227 setdumpnum(); 228 FLUSHTAPEBUF(); 229 if (!pipein && !bflag) 230 findtapeblksize(); 231 if (gethead(&spcl) == FAIL) { 232 blkcnt--; /* push back this block */ 233 blksread--; 234 tpblksread--; 235 cvtflag++; 236 if (gethead(&spcl) == FAIL) { 237 fprintf(stderr, "Tape is not a dump tape\n"); 238 exit(1); 239 } 240 fprintf(stderr, "Converting to new file system format.\n"); 241 } 242 if (pipein) { 243 endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : 244 FS_UFS2_MAGIC; 245 endoftapemark.s_spcl.c_type = TS_END; 246 ip = (int *)&endoftapemark; 247 j = sizeof(union u_spcl) / sizeof(int); 248 i = 0; 249 do 250 i += *ip++; 251 while (--j); 252 endoftapemark.s_spcl.c_checksum = CHECKSUM - i; 253 } 254 if (vflag || command == 't') 255 printdumpinfo(); 256 dumptime = spcl.c_ddate; 257 dumpdate = spcl.c_date; 258 if (stat(".", &stbuf) < 0) { 259 fprintf(stderr, "cannot stat .: %s\n", strerror(errno)); 260 exit(1); 261 } 262 if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE) 263 fssize = stbuf.st_blksize; 264 if (((fssize - 1) & fssize) != 0) { 265 fprintf(stderr, "bad block size %d\n", fssize); 266 exit(1); 267 } 268 if (spcl.c_volume != 1) { 269 fprintf(stderr, "Tape is not volume 1 of the dump\n"); 270 exit(1); 271 } 272 if (gethead(&spcl) == FAIL) { 273 dprintf(stdout, "header read failed at %d blocks\n", blksread); 274 panic("no header after volume mark!\n"); 275 } 276 findinode(&spcl); 277 if (spcl.c_type != TS_CLRI) { 278 fprintf(stderr, "Cannot find file removal list\n"); 279 exit(1); 280 } 281 maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1; 282 dprintf(stdout, "maxino = %d\n", maxino); 283 map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); 284 if (map == NULL) 285 panic("no memory for active inode map\n"); 286 usedinomap = map; 287 curfile.action = USING; 288 getfile(xtrmap, xtrmapskip); 289 if (spcl.c_type != TS_BITS) { 290 fprintf(stderr, "Cannot find file dump list\n"); 291 exit(1); 292 } 293 map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); 294 if (map == (char *)NULL) 295 panic("no memory for file dump list\n"); 296 dumpmap = map; 297 curfile.action = USING; 298 getfile(xtrmap, xtrmapskip); 299 /* 300 * If there may be whiteout entries on the tape, pretend that the 301 * whiteout inode exists, so that the whiteout entries can be 302 * extracted. 303 */ 304 if (oldinofmt == 0) 305 SETINO(WINO, dumpmap); 306 } 307 308 /* 309 * Prompt user to load a new dump volume. 310 * "Nextvol" is the next suggested volume to use. 311 * This suggested volume is enforced when doing full 312 * or incremental restores, but can be overrridden by 313 * the user when only extracting a subset of the files. 314 */ 315 void 316 getvol(nextvol) 317 int nextvol; 318 { 319 int newvol, savecnt, wantnext, i; 320 union u_spcl tmpspcl; 321 # define tmpbuf tmpspcl.s_spcl 322 char buf[TP_BSIZE]; 323 324 newvol = savecnt = wantnext = 0; 325 if (nextvol == 1) { 326 tapesread = 0; 327 gettingfile = 0; 328 } 329 if (pipein) { 330 if (nextvol != 1) 331 panic("Changing volumes on pipe input?\n"); 332 if (volno == 1) 333 return; 334 goto gethdr; 335 } 336 savecnt = blksread; 337 again: 338 if (pipein) 339 exit(1); /* pipes do not get a second chance */ 340 if (command == 'R' || command == 'r' || curfile.action != SKIP) { 341 newvol = nextvol; 342 wantnext = 1; 343 } else { 344 newvol = 0; 345 wantnext = 0; 346 } 347 while (newvol <= 0) { 348 if (tapesread == 0) { 349 fprintf(stderr, "%s%s%s%s%s", 350 "You have not read any tapes yet.\n", 351 "Unless you know which volume your", 352 " file(s) are on you should start\n", 353 "with the last volume and work", 354 " towards the first.\n"); 355 fprintf(stderr, 356 "(Use 1 for the first volume/tape, etc.)\n"); 357 } else { 358 fprintf(stderr, "You have read volumes"); 359 strcpy(buf, ": "); 360 for (i = 1; i < 32; i++) 361 if (tapesread & (1 << i)) { 362 fprintf(stderr, "%s%d", buf, i); 363 strcpy(buf, ", "); 364 } 365 fprintf(stderr, "\n"); 366 } 367 do { 368 fprintf(stderr, "Specify next volume #: "); 369 (void) fflush(stderr); 370 (void) fgets(buf, BUFSIZ, terminal); 371 } while (!feof(terminal) && buf[0] == '\n'); 372 if (feof(terminal)) 373 exit(1); 374 newvol = atoi(buf); 375 if (newvol <= 0) { 376 fprintf(stderr, 377 "Volume numbers are positive numerics\n"); 378 } 379 } 380 if (newvol == volno) { 381 tapesread |= 1 << volno; 382 return; 383 } 384 closemt(); 385 fprintf(stderr, "Mount tape volume %d\n", newvol); 386 fprintf(stderr, "Enter ``none'' if there are no more tapes\n"); 387 fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape); 388 (void) fflush(stderr); 389 (void) fgets(buf, BUFSIZ, terminal); 390 if (feof(terminal)) 391 exit(1); 392 if (!strcmp(buf, "none\n")) { 393 terminateinput(); 394 return; 395 } 396 if (buf[0] != '\n') { 397 (void) strcpy(magtape, buf); 398 magtape[strlen(magtape) - 1] = '\0'; 399 } 400 #ifdef RRESTORE 401 if (host) 402 mt = rmtopen(magtape, 0); 403 else 404 #endif 405 mt = open(magtape, O_RDONLY, 0); 406 407 if (mt == -1) { 408 fprintf(stderr, "Cannot open %s\n", magtape); 409 volno = -1; 410 goto again; 411 } 412 gethdr: 413 volno = newvol; 414 setdumpnum(); 415 FLUSHTAPEBUF(); 416 if (gethead(&tmpbuf) == FAIL) { 417 dprintf(stdout, "header read failed at %d blocks\n", blksread); 418 fprintf(stderr, "tape is not dump tape\n"); 419 volno = 0; 420 goto again; 421 } 422 if (tmpbuf.c_volume != volno) { 423 fprintf(stderr, 424 "Volume mismatch: expecting %d, tape header claims it is %d\n", 425 volno, tmpbuf.c_volume); 426 volno = 0; 427 goto again; 428 } 429 if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) { 430 time_t ttime = tmpbuf.c_date; 431 fprintf(stderr, "Wrong dump date\n\tgot: %s", 432 ctime(&ttime)); 433 fprintf(stderr, "\twanted: %s", ctime(&dumpdate)); 434 volno = 0; 435 goto again; 436 } 437 tapesread |= 1 << volno; 438 blksread = savecnt; 439 /* 440 * If continuing from the previous volume, skip over any 441 * blocks read already at the end of the previous volume. 442 * 443 * If coming to this volume at random, skip to the beginning 444 * of the next record. 445 */ 446 dprintf(stdout, "read %ld recs, tape starts with %ld\n", 447 (long)tpblksread, (long)tmpbuf.c_firstrec); 448 if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) { 449 if (!wantnext) { 450 tpblksread = tmpbuf.c_firstrec; 451 for (i = tmpbuf.c_count; i > 0; i--) 452 readtape(buf); 453 } else if (tmpbuf.c_firstrec > 0 && 454 tmpbuf.c_firstrec < tpblksread - 1) { 455 /* 456 * -1 since we've read the volume header 457 */ 458 i = tpblksread - tmpbuf.c_firstrec - 1; 459 dprintf(stderr, "Skipping %d duplicate record%s.\n", 460 i, i > 1 ? "s" : ""); 461 while (--i >= 0) 462 readtape(buf); 463 } 464 } 465 if (curfile.action == USING) { 466 if (volno == 1) 467 panic("active file into volume 1\n"); 468 return; 469 } 470 /* 471 * Skip up to the beginning of the next record 472 */ 473 if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) 474 for (i = tmpbuf.c_count; i > 0; i--) 475 readtape(buf); 476 (void) gethead(&spcl); 477 findinode(&spcl); 478 if (gettingfile) { 479 gettingfile = 0; 480 longjmp(restart, 1); 481 } 482 } 483 484 /* 485 * Handle unexpected EOF. 486 */ 487 static void 488 terminateinput() 489 { 490 491 if (gettingfile && curfile.action == USING) { 492 printf("Warning: %s %s\n", 493 "End-of-input encountered while extracting", curfile.name); 494 } 495 curfile.name = "<name unknown>"; 496 curfile.action = UNKNOWN; 497 curfile.mode = 0; 498 curfile.ino = maxino; 499 if (gettingfile) { 500 gettingfile = 0; 501 longjmp(restart, 1); 502 } 503 } 504 505 /* 506 * handle multiple dumps per tape by skipping forward to the 507 * appropriate one. 508 */ 509 static void 510 setdumpnum() 511 { 512 struct mtop tcom; 513 514 if (dumpnum == 1 || volno != 1) 515 return; 516 if (pipein) { 517 fprintf(stderr, "Cannot have multiple dumps on pipe input\n"); 518 exit(1); 519 } 520 tcom.mt_op = MTFSF; 521 tcom.mt_count = dumpnum - 1; 522 #ifdef RRESTORE 523 if (host) 524 rmtioctl(MTFSF, dumpnum - 1); 525 else 526 #endif 527 if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0) 528 fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno)); 529 } 530 531 void 532 printdumpinfo() 533 { 534 time_t ttime; 535 536 ttime = spcl.c_date; 537 fprintf(stdout, "Dump date: %s", ctime(&ttime)); 538 ttime = spcl.c_ddate; 539 fprintf(stdout, "Dumped from: %s", 540 (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&ttime)); 541 fprintf(stderr, "Level %d dump of %s on %s:%s\n", 542 spcl.c_level, spcl.c_filesys, 543 *spcl.c_host? spcl.c_host: "[unknown]", spcl.c_dev); 544 fprintf(stderr, "Label: %s\n", spcl.c_label); 545 } 546 547 int 548 extractfile(name) 549 char *name; 550 { 551 int flags; 552 uid_t uid; 553 gid_t gid; 554 mode_t mode; 555 struct timeval mtimep[2], ctimep[2]; 556 struct entry *ep; 557 int setbirth; 558 559 curfile.name = name; 560 curfile.action = USING; 561 mtimep[0].tv_sec = curfile.atime_sec; 562 mtimep[0].tv_usec = curfile.atime_nsec / 1000; 563 mtimep[1].tv_sec = curfile.mtime_sec; 564 mtimep[1].tv_usec = curfile.mtime_nsec / 1000; 565 566 setbirth = curfile.birthtime_sec != 0; 567 568 if (setbirth) { 569 ctimep[0].tv_sec = curfile.atime_sec; 570 ctimep[0].tv_usec = curfile.atime_nsec / 1000; 571 ctimep[1].tv_sec = curfile.birthtime_sec; 572 ctimep[1].tv_usec = curfile.birthtime_nsec / 1000; 573 } 574 uid = curfile.uid; 575 gid = curfile.gid; 576 mode = curfile.mode; 577 flags = curfile.file_flags; 578 switch (mode & IFMT) { 579 580 default: 581 fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode); 582 skipfile(); 583 return (FAIL); 584 585 case IFSOCK: 586 vprintf(stdout, "skipped socket %s\n", name); 587 skipfile(); 588 return (GOOD); 589 590 case IFDIR: 591 if (mflag) { 592 ep = lookupname(name); 593 if (ep == NULL || ep->e_flags & EXTRACT) 594 panic("unextracted directory %s\n", name); 595 skipfile(); 596 return (GOOD); 597 } 598 vprintf(stdout, "extract file %s\n", name); 599 return (genliteraldir(name, curfile.ino)); 600 601 case IFLNK: 602 lnkbuf[0] = '\0'; 603 pathlen = 0; 604 getfile(xtrlnkfile, xtrlnkskip); 605 if (pathlen == 0) { 606 vprintf(stdout, 607 "%s: zero length symbolic link (ignored)\n", name); 608 return (GOOD); 609 } 610 if (uflag) 611 (void) unlink(name); 612 if (linkit(lnkbuf, name, SYMLINK) == GOOD) { 613 if (setbirth) 614 (void) lutimes(name, ctimep); 615 (void) lutimes(name, mtimep); 616 (void) lchown(name, uid, gid); 617 (void) lchmod(name, mode); 618 (void) lchflags(name, flags); 619 return (GOOD); 620 } 621 return (FAIL); 622 623 case IFCHR: 624 case IFBLK: 625 vprintf(stdout, "extract special file %s\n", name); 626 if (Nflag) { 627 skipfile(); 628 return (GOOD); 629 } 630 if (uflag) 631 (void) unlink(name); 632 if (mknod(name, (mode & (IFCHR | IFBLK)) | 0600, 633 (int)curfile.rdev) < 0) { 634 fprintf(stderr, "%s: cannot create special file: %s\n", 635 name, strerror(errno)); 636 skipfile(); 637 return (FAIL); 638 } 639 skipfile(); 640 if (setbirth) 641 (void) utimes(name, ctimep); 642 (void) utimes(name, mtimep); 643 (void) chown(name, uid, gid); 644 (void) chmod(name, mode); 645 (void) chflags(name, flags); 646 return (GOOD); 647 648 case IFIFO: 649 vprintf(stdout, "extract fifo %s\n", name); 650 if (Nflag) { 651 skipfile(); 652 return (GOOD); 653 } 654 if (uflag) 655 (void) unlink(name); 656 if (mkfifo(name, 0600) < 0) { 657 fprintf(stderr, "%s: cannot create fifo: %s\n", 658 name, strerror(errno)); 659 skipfile(); 660 return (FAIL); 661 } 662 skipfile(); 663 if (setbirth) 664 (void) utimes(name, ctimep); 665 (void) utimes(name, mtimep); 666 (void) chown(name, uid, gid); 667 (void) chmod(name, mode); 668 (void) chflags(name, flags); 669 return (GOOD); 670 671 case IFREG: 672 vprintf(stdout, "extract file %s\n", name); 673 if (Nflag) { 674 skipfile(); 675 return (GOOD); 676 } 677 if (uflag) 678 (void) unlink(name); 679 if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 680 0600)) < 0) { 681 fprintf(stderr, "%s: cannot create file: %s\n", 682 name, strerror(errno)); 683 skipfile(); 684 return (FAIL); 685 } 686 getfile(xtrfile, xtrskip); 687 if (setbirth) 688 (void) futimes(ofile, ctimep); 689 (void) futimes(ofile, mtimep); 690 (void) fchown(ofile, uid, gid); 691 (void) fchmod(ofile, mode); 692 (void) fchflags(ofile, flags); 693 (void) close(ofile); 694 return (GOOD); 695 } 696 /* NOTREACHED */ 697 } 698 699 /* 700 * skip over bit maps on the tape 701 */ 702 void 703 skipmaps() 704 { 705 706 while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI) 707 skipfile(); 708 } 709 710 /* 711 * skip over a file on the tape 712 */ 713 void 714 skipfile() 715 { 716 717 curfile.action = SKIP; 718 getfile(xtrnull, xtrnull); 719 } 720 721 /* 722 * Extract a file from the tape. 723 * When an allocated block is found it is passed to the fill function; 724 * when an unallocated block (hole) is found, a zeroed buffer is passed 725 * to the skip function. 726 */ 727 void 728 getfile(fill, skip) 729 void (*fill) __P((char *, long)); 730 void (*skip) __P((char *, long)); 731 { 732 int i; 733 int curblk = 0; 734 quad_t size = spcl.c_size; 735 static char clearedbuf[MAXBSIZE]; 736 char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE]; 737 char junk[TP_BSIZE]; 738 739 #ifdef __GNUC__ /* XXX: to shut up gcc warnings */ 740 (void)&curblk; 741 (void)&size; 742 #endif 743 744 if (spcl.c_type == TS_END) 745 panic("ran off end of tape\n"); 746 if (spcl.c_magic != FS_UFS2_MAGIC) 747 panic("not at beginning of a file\n"); 748 if (!gettingfile && setjmp(restart) != 0) 749 return; 750 gettingfile++; 751 loop: 752 for (i = 0; i < spcl.c_count; i++) { 753 if (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI || 754 spcl.c_addr[i]) { 755 readtape(&buf[curblk++][0]); 756 if (curblk == fssize / TP_BSIZE) { 757 (*fill)((char *)buf, (long)(size > TP_BSIZE ? 758 fssize : (curblk - 1) * TP_BSIZE + size)); 759 curblk = 0; 760 } 761 } else { 762 if (curblk > 0) { 763 (*fill)((char *)buf, (long)(size > TP_BSIZE ? 764 curblk * TP_BSIZE : 765 (curblk - 1) * TP_BSIZE + size)); 766 curblk = 0; 767 } 768 (*skip)(clearedbuf, (long)(size > TP_BSIZE ? 769 TP_BSIZE : size)); 770 } 771 if ((size -= TP_BSIZE) <= 0) { 772 if (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI) { 773 /* 774 * In this case, the following expression 775 * should always be false since the size was 776 * initially set to spcl.c_size and 777 * it is initialized to spcl.c_count * TP_BSIZE 778 * in gethead(). 779 */ 780 if (!(size == 0 && i == spcl.c_count - 1)) 781 panic("inconsistent map size\n"); 782 } else { 783 for (i++; i < spcl.c_count; i++) 784 if (spcl.c_addr[i]) 785 readtape(junk); 786 } 787 break; 788 } 789 } 790 if (gethead(&spcl) == GOOD && size > 0) { 791 if (spcl.c_type == TS_ADDR) 792 goto loop; 793 dprintf(stdout, 794 "Missing address (header) block for %s at %d blocks\n", 795 curfile.name, blksread); 796 } 797 if (curblk > 0) 798 (*fill)((char *)buf, (long)((curblk * TP_BSIZE) + size)); 799 findinode(&spcl); 800 gettingfile = 0; 801 } 802 803 /* 804 * Write out the next block of a file. 805 */ 806 static void 807 xtrfile(buf, size) 808 char *buf; 809 long size; 810 { 811 812 if (Nflag) 813 return; 814 if (write(ofile, buf, (int) size) == -1) { 815 fprintf(stderr, 816 "write error extracting inode %d, name %s\nwrite: %s\n", 817 curfile.ino, curfile.name, strerror(errno)); 818 exit(1); 819 } 820 } 821 822 /* 823 * Skip over a hole in a file. 824 */ 825 /* ARGSUSED */ 826 static void 827 xtrskip(buf, size) 828 char *buf; 829 long size; 830 { 831 832 if (lseek(ofile, size, SEEK_CUR) == -1) { 833 fprintf(stderr, 834 "seek error extracting inode %d, name %s\nlseek: %s\n", 835 curfile.ino, curfile.name, strerror(errno)); 836 exit(1); 837 } 838 } 839 840 /* 841 * Collect the next block of a symbolic link. 842 */ 843 static void 844 xtrlnkfile(buf, size) 845 char *buf; 846 long size; 847 { 848 849 pathlen += size; 850 if (pathlen > MAXPATHLEN) { 851 fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n", 852 curfile.name, lnkbuf, buf, pathlen); 853 exit(1); 854 } 855 (void) strcat(lnkbuf, buf); 856 } 857 858 /* 859 * Skip over a hole in a symbolic link (should never happen). 860 */ 861 /* ARGSUSED */ 862 static void 863 xtrlnkskip(buf, size) 864 char *buf; 865 long size; 866 { 867 868 fprintf(stderr, "unallocated block in symbolic link %s\n", 869 curfile.name); 870 exit(1); 871 } 872 873 /* 874 * Collect the next block of a bit map. 875 */ 876 static void 877 xtrmap(buf, size) 878 char *buf; 879 long size; 880 { 881 882 memmove(map, buf, size); 883 map += size; 884 } 885 886 /* 887 * Skip over a hole in a bit map (should never happen). 888 */ 889 /* ARGSUSED */ 890 static void 891 xtrmapskip(buf, size) 892 char *buf; 893 long size; 894 { 895 896 panic("hole in map\n"); 897 map += size; 898 } 899 900 /* 901 * Noop, when an extraction function is not needed. 902 */ 903 /* ARGSUSED */ 904 void 905 xtrnull(buf, size) 906 char *buf; 907 long size; 908 { 909 910 return; 911 } 912 913 /* 914 * Read TP_BSIZE blocks from the input. 915 * Handle read errors, and end of media. 916 */ 917 static void 918 readtape(buf) 919 char *buf; 920 { 921 int rd, newvol, i; 922 int cnt, seek_failed; 923 924 if (blkcnt < numtrec) { 925 memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE); 926 blksread++; 927 tpblksread++; 928 return; 929 } 930 for (i = 0; i < ntrec; i++) 931 ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; 932 if (numtrec == 0) 933 numtrec = ntrec; 934 cnt = ntrec * TP_BSIZE; 935 rd = 0; 936 getmore: 937 #ifdef RRESTORE 938 if (host) 939 i = rmtread(&tapebuf[rd], cnt); 940 else 941 #endif 942 i = read(mt, &tapebuf[rd], cnt); 943 /* 944 * Check for mid-tape short read error. 945 * If found, skip rest of buffer and start with the next. 946 */ 947 if (!pipein && numtrec < ntrec && i > 0) { 948 dprintf(stdout, "mid-media short read error.\n"); 949 numtrec = ntrec; 950 } 951 /* 952 * Handle partial block read. 953 */ 954 if (pipein && i == 0 && rd > 0) 955 i = rd; 956 else if (i > 0 && i != ntrec * TP_BSIZE) { 957 if (pipein) { 958 rd += i; 959 cnt -= i; 960 if (cnt > 0) 961 goto getmore; 962 i = rd; 963 } else { 964 /* 965 * Short read. Process the blocks read. 966 */ 967 if (i % TP_BSIZE != 0) 968 vprintf(stdout, 969 "partial block read: %d should be %d\n", 970 i, ntrec * TP_BSIZE); 971 numtrec = i / TP_BSIZE; 972 } 973 } 974 /* 975 * Handle read error. 976 */ 977 if (i < 0) { 978 fprintf(stderr, "Tape read error while "); 979 switch (curfile.action) { 980 default: 981 fprintf(stderr, "trying to set up tape\n"); 982 break; 983 case UNKNOWN: 984 fprintf(stderr, "trying to resynchronize\n"); 985 break; 986 case USING: 987 fprintf(stderr, "restoring %s\n", curfile.name); 988 break; 989 case SKIP: 990 fprintf(stderr, "skipping over inode %d\n", 991 curfile.ino); 992 break; 993 } 994 if (!yflag && !reply("continue")) 995 exit(1); 996 i = ntrec * TP_BSIZE; 997 memset(tapebuf, 0, i); 998 #ifdef RRESTORE 999 if (host) 1000 seek_failed = (rmtseek(i, 1) < 0); 1001 else 1002 #endif 1003 seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1); 1004 1005 if (seek_failed) { 1006 fprintf(stderr, 1007 "continuation failed: %s\n", strerror(errno)); 1008 exit(1); 1009 } 1010 } 1011 /* 1012 * Handle end of tape. 1013 */ 1014 if (i == 0) { 1015 vprintf(stdout, "End-of-tape encountered\n"); 1016 if (!pipein) { 1017 newvol = volno + 1; 1018 volno = 0; 1019 numtrec = 0; 1020 getvol(newvol); 1021 readtape(buf); 1022 return; 1023 } 1024 if (rd % TP_BSIZE != 0) 1025 panic("partial block read: %d should be %d\n", 1026 rd, ntrec * TP_BSIZE); 1027 terminateinput(); 1028 memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE); 1029 } 1030 blkcnt = 0; 1031 memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE); 1032 blksread++; 1033 tpblksread++; 1034 } 1035 1036 static void 1037 findtapeblksize() 1038 { 1039 long i; 1040 1041 for (i = 0; i < ntrec; i++) 1042 ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; 1043 blkcnt = 0; 1044 #ifdef RRESTORE 1045 if (host) 1046 i = rmtread(tapebuf, ntrec * TP_BSIZE); 1047 else 1048 #endif 1049 i = read(mt, tapebuf, ntrec * TP_BSIZE); 1050 1051 if (i <= 0) { 1052 fprintf(stderr, "tape read error: %s\n", strerror(errno)); 1053 exit(1); 1054 } 1055 if (i % TP_BSIZE != 0) { 1056 fprintf(stderr, "Tape block size (%ld) %s (%ld)\n", 1057 (long)i, "is not a multiple of dump block size", 1058 (long)TP_BSIZE); 1059 exit(1); 1060 } 1061 ntrec = i / TP_BSIZE; 1062 numtrec = ntrec; 1063 vprintf(stdout, "Tape block size is %d\n", ntrec); 1064 } 1065 1066 void 1067 closemt() 1068 { 1069 1070 if (mt < 0) 1071 return; 1072 #ifdef RRESTORE 1073 if (host) 1074 rmtclose(); 1075 else 1076 #endif 1077 (void) close(mt); 1078 } 1079 1080 /* 1081 * Read the next block from the tape. 1082 * Check to see if it is one of several vintage headers. 1083 * If it is an old style header, convert it to a new style header. 1084 * If it is not any valid header, return an error. 1085 */ 1086 static int 1087 gethead(buf) 1088 struct s_spcl *buf; 1089 { 1090 union u_ospcl u_ospcl; 1091 1092 if (!cvtflag) { 1093 readtape((char *)buf); 1094 if (buf->c_magic != NFS_MAGIC && 1095 buf->c_magic != FS_UFS2_MAGIC) { 1096 if (bswap32(buf->c_magic) != NFS_MAGIC && 1097 bswap32(buf->c_magic) != FS_UFS2_MAGIC) 1098 return (FAIL); 1099 if (!Bcvt) { 1100 vprintf(stdout, "Note: Doing Byte swapping\n"); 1101 Bcvt = 1; 1102 } 1103 } 1104 if (checksum((int *)buf) == FAIL) 1105 return (FAIL); 1106 if (Bcvt) 1107 swap_header(buf); 1108 goto good; 1109 } 1110 1111 readtape((char *)(&u_ospcl.s_ospcl)); 1112 if (checksum((int *)(&u_ospcl.s_ospcl)) == FAIL) 1113 return (FAIL); 1114 if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC) { 1115 if (bswap32(u_ospcl.s_ospcl.c_magic) != OFS_MAGIC) 1116 return (FAIL); 1117 if (!Bcvt) { 1118 vprintf(stdout, "Note: Doing Byte swapping\n"); 1119 Bcvt = 1; 1120 } 1121 swap_old_header(&u_ospcl.s_ospcl); 1122 } 1123 1124 memset(buf, 0, (long)TP_BSIZE); 1125 buf->c_type = u_ospcl.s_ospcl.c_type; 1126 buf->c_date = u_ospcl.s_ospcl.c_date; 1127 buf->c_ddate = u_ospcl.s_ospcl.c_ddate; 1128 buf->c_volume = u_ospcl.s_ospcl.c_volume; 1129 buf->c_tapea = u_ospcl.s_ospcl.c_tapea; 1130 buf->c_inumber = u_ospcl.s_ospcl.c_inumber; 1131 buf->c_checksum = u_ospcl.s_ospcl.c_checksum; 1132 buf->c_mode = u_ospcl.s_ospcl.c_odinode.odi_mode; 1133 buf->c_uid = u_ospcl.s_ospcl.c_odinode.odi_uid; 1134 buf->c_gid = u_ospcl.s_ospcl.c_odinode.odi_gid; 1135 buf->c_size = u_ospcl.s_ospcl.c_odinode.odi_size; 1136 buf->c_rdev = u_ospcl.s_ospcl.c_odinode.odi_rdev; 1137 buf->c_atime = u_ospcl.s_ospcl.c_odinode.odi_atime; 1138 buf->c_mtime = u_ospcl.s_ospcl.c_odinode.odi_mtime; 1139 buf->c_count = u_ospcl.s_ospcl.c_count; 1140 memmove(buf->c_addr, u_ospcl.s_ospcl.c_addr, (long)256); 1141 buf->c_magic = FS_UFS2_MAGIC; 1142 good: 1143 switch (buf->c_type) { 1144 1145 case TS_CLRI: 1146 case TS_BITS: 1147 /* 1148 * Have to patch up missing information in bit map headers 1149 */ 1150 buf->c_inumber = 0; 1151 buf->c_size = buf->c_count * TP_BSIZE; 1152 break; 1153 1154 case TS_TAPE: 1155 if ((buf->c_flags & DR_NEWINODEFMT) == 0) 1156 oldinofmt = 1; 1157 /* fall through */ 1158 case TS_END: 1159 buf->c_inumber = 0; 1160 break; 1161 1162 case TS_INODE: 1163 if (buf->c_magic == NFS_MAGIC) { 1164 buf->c_tapea = buf->c_old_tapea; 1165 buf->c_firstrec = buf->c_old_firstrec; 1166 buf->c_date = buf->c_old_date; 1167 buf->c_ddate = buf->c_old_ddate; 1168 buf->c_atime = buf->c_old_atime; 1169 buf->c_mtime = buf->c_old_mtime; 1170 buf->c_birthtime = 0; 1171 buf->c_birthtimensec = 0; 1172 buf->c_atimensec = buf->c_mtimensec = 0; 1173 } 1174 1175 case TS_ADDR: 1176 break; 1177 1178 default: 1179 panic("gethead: unknown inode type %d\n", buf->c_type); 1180 break; 1181 } 1182 1183 buf->c_magic = FS_UFS2_MAGIC; 1184 1185 /* 1186 * If we are restoring a filesystem with old format inodes, 1187 * copy the uid/gid to the new location. 1188 */ 1189 if (oldinofmt) { 1190 buf->c_uid = buf->c_spare1[1]; 1191 buf->c_gid = buf->c_spare1[2]; 1192 } 1193 if (dflag) 1194 accthdr(buf); 1195 return(GOOD); 1196 } 1197 1198 /* 1199 * Check that a header is where it belongs and predict the next header 1200 */ 1201 static void 1202 accthdr(header) 1203 struct s_spcl *header; 1204 { 1205 static ino_t previno = 0x7fffffff; 1206 static int prevtype; 1207 static long predict; 1208 long blks, i; 1209 1210 if (header->c_type == TS_TAPE) { 1211 fprintf(stderr, "Volume header (%s inode format) ", 1212 oldinofmt ? "old" : "new"); 1213 if (header->c_firstrec) 1214 fprintf(stderr, "begins with record %lld", 1215 (long long)header->c_firstrec); 1216 fprintf(stderr, "\n"); 1217 previno = 0x7fffffff; 1218 return; 1219 } 1220 if (previno == 0x7fffffff) 1221 goto newcalc; 1222 switch (prevtype) { 1223 case TS_BITS: 1224 fprintf(stderr, "Dumped inodes map header"); 1225 break; 1226 case TS_CLRI: 1227 fprintf(stderr, "Used inodes map header"); 1228 break; 1229 case TS_INODE: 1230 fprintf(stderr, "File header, ino %d", previno); 1231 break; 1232 case TS_ADDR: 1233 fprintf(stderr, "File continuation header, ino %d", previno); 1234 break; 1235 case TS_END: 1236 fprintf(stderr, "End of tape header"); 1237 break; 1238 } 1239 if (predict != blksread - 1) 1240 fprintf(stderr, "; predicted %ld blocks, got %ld blocks", 1241 (long)predict, (long)(blksread - 1)); 1242 fprintf(stderr, "\n"); 1243 newcalc: 1244 blks = 0; 1245 switch (header->c_type) { 1246 case TS_END: 1247 break; 1248 case TS_CLRI: 1249 case TS_BITS: 1250 blks = header->c_count; 1251 break; 1252 default: 1253 for (i = 0; i < header->c_count; i++) 1254 if (header->c_addr[i] != 0) 1255 blks++; 1256 break; 1257 } 1258 predict = blks; 1259 blksread = 0; 1260 prevtype = header->c_type; 1261 previno = header->c_inumber; 1262 } 1263 1264 /* 1265 * Find an inode header. 1266 * Complain if had to skip, and complain is set. 1267 */ 1268 static void 1269 findinode(header) 1270 struct s_spcl *header; 1271 { 1272 static long skipcnt = 0; 1273 long i; 1274 char buf[TP_BSIZE]; 1275 1276 curfile.name = "<name unknown>"; 1277 curfile.action = UNKNOWN; 1278 curfile.mode = 0; 1279 curfile.ino = 0; 1280 top: 1281 do { 1282 if (header->c_magic != FS_UFS2_MAGIC) { 1283 skipcnt++; 1284 while (gethead(header) == FAIL || 1285 header->c_date != dumpdate) 1286 skipcnt++; 1287 } 1288 switch (header->c_type) { 1289 1290 case TS_ADDR: 1291 /* 1292 * Skip up to the beginning of the next record 1293 */ 1294 for (i = 0; i < header->c_count; i++) 1295 if (header->c_addr[i]) 1296 readtape(buf); 1297 while (gethead(header) == FAIL || 1298 header->c_date != dumpdate) 1299 skipcnt++; 1300 /* We've read a header; don't drop it. */ 1301 goto top; 1302 1303 case TS_INODE: 1304 curfile.mode = header->c_mode; 1305 curfile.uid = header->c_uid; 1306 curfile.gid = header->c_gid; 1307 curfile.file_flags = header->c_file_flags; 1308 curfile.rdev = header->c_rdev; 1309 curfile.atime_sec = header->c_atime; 1310 curfile.atime_nsec = header->c_atimensec; 1311 curfile.mtime_sec = header->c_mtime; 1312 curfile.mtime_nsec = header->c_mtimensec; 1313 curfile.birthtime_sec = header->c_birthtime; 1314 curfile.birthtime_nsec = header->c_birthtimensec; 1315 curfile.size = header->c_size; 1316 curfile.ino = header->c_inumber; 1317 break; 1318 1319 case TS_END: 1320 curfile.ino = maxino; 1321 break; 1322 1323 case TS_CLRI: 1324 curfile.name = "<file removal list>"; 1325 break; 1326 1327 case TS_BITS: 1328 curfile.name = "<file dump list>"; 1329 break; 1330 1331 case TS_TAPE: 1332 panic("unexpected tape header\n"); 1333 break; 1334 1335 default: 1336 panic("unknown tape header type %d\n", spcl.c_type); 1337 break; 1338 1339 } 1340 } while (header->c_type == TS_ADDR); 1341 if (skipcnt > 0) 1342 fprintf(stderr, "resync restore, skipped %ld blocks\n", 1343 (long)skipcnt); 1344 skipcnt = 0; 1345 } 1346 1347 static int 1348 checksum(buf) 1349 int *buf; 1350 { 1351 int i, j; 1352 1353 j = sizeof(union u_spcl) / sizeof(int); 1354 i = 0; 1355 if(!Bcvt) { 1356 do 1357 i += *buf++; 1358 while (--j); 1359 } else { 1360 do 1361 i += bswap32(*buf++); 1362 while (--j); 1363 } 1364 1365 if (i != CHECKSUM) { 1366 fprintf(stderr, "Checksum error %o, inode %d file %s\n", i, 1367 curfile.ino, curfile.name); 1368 return(FAIL); 1369 } 1370 return(GOOD); 1371 } 1372 1373 #ifdef RRESTORE 1374 #include <stdarg.h> 1375 1376 void 1377 msg(const char *fmt, ...) 1378 { 1379 va_list ap; 1380 1381 va_start(ap, fmt); 1382 (void)vfprintf(stderr, fmt, ap); 1383 va_end(ap); 1384 } 1385 #endif /* RRESTORE */ 1386 1387 static void 1388 swap_header(struct s_spcl *s) 1389 { 1390 s->c_type = bswap32(s->c_type); 1391 s->c_old_date = bswap32(s->c_old_date); 1392 s->c_old_ddate = bswap32(s->c_old_ddate); 1393 s->c_volume = bswap32(s->c_volume); 1394 s->c_old_tapea = bswap32(s->c_old_tapea); 1395 s->c_inumber = bswap32(s->c_inumber); 1396 s->c_magic = bswap32(s->c_magic); 1397 s->c_checksum = bswap32(s->c_checksum); 1398 1399 s->c_mode = bswap16(s->c_mode); 1400 s->c_size = bswap64(s->c_size); 1401 s->c_old_atime = bswap32(s->c_old_atime); 1402 s->c_atimensec = bswap32(s->c_atimensec); 1403 s->c_old_mtime = bswap32(s->c_old_mtime); 1404 s->c_mtimensec = bswap32(s->c_mtimensec); 1405 s->c_rdev = bswap32(s->c_rdev); 1406 s->c_birthtimensec = bswap32(s->c_birthtimensec); 1407 s->c_birthtime = bswap64(s->c_birthtime); 1408 s->c_atime = bswap64(s->c_atime); 1409 s->c_mtime = bswap64(s->c_mtime); 1410 s->c_file_flags = bswap32(s->c_file_flags); 1411 s->c_uid = bswap32(s->c_uid); 1412 s->c_gid = bswap32(s->c_gid); 1413 1414 s->c_count = bswap32(s->c_count); 1415 s->c_level = bswap32(s->c_level); 1416 s->c_flags = bswap32(s->c_flags); 1417 s->c_old_firstrec = bswap32(s->c_old_firstrec); 1418 1419 s->c_date = bswap64(s->c_date); 1420 s->c_ddate = bswap64(s->c_ddate); 1421 s->c_tapea = bswap64(s->c_tapea); 1422 s->c_firstrec = bswap64(s->c_firstrec); 1423 1424 /* 1425 * These are ouid and ogid. 1426 */ 1427 s->c_spare1[1] = bswap16(s->c_spare1[1]); 1428 s->c_spare1[2] = bswap16(s->c_spare1[2]); 1429 } 1430 1431 static void 1432 swap_old_header(struct s_ospcl *os) 1433 { 1434 os->c_type = bswap32(os->c_type); 1435 os->c_date = bswap32(os->c_date); 1436 os->c_ddate = bswap32(os->c_ddate); 1437 os->c_volume = bswap32(os->c_volume); 1438 os->c_tapea = bswap32(os->c_tapea); 1439 os->c_inumber = bswap16(os->c_inumber); 1440 os->c_magic = bswap32(os->c_magic); 1441 os->c_checksum = bswap32(os->c_checksum); 1442 1443 os->c_odinode.odi_mode = bswap16(os->c_odinode.odi_mode); 1444 os->c_odinode.odi_nlink = bswap16(os->c_odinode.odi_nlink); 1445 os->c_odinode.odi_uid = bswap16(os->c_odinode.odi_uid); 1446 os->c_odinode.odi_gid = bswap16(os->c_odinode.odi_gid); 1447 1448 os->c_odinode.odi_size = bswap32(os->c_odinode.odi_size); 1449 os->c_odinode.odi_rdev = bswap32(os->c_odinode.odi_rdev); 1450 os->c_odinode.odi_atime = bswap32(os->c_odinode.odi_atime); 1451 os->c_odinode.odi_mtime = bswap32(os->c_odinode.odi_mtime); 1452 os->c_odinode.odi_ctime = bswap32(os->c_odinode.odi_ctime); 1453 1454 os->c_count = bswap32(os->c_count); 1455 } 1456