1 /* $NetBSD: utilities.c,v 1.58 2010/01/07 01:39:56 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1986, 1993 5 * The Regents of the University of California. All rights reserved. 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 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95"; 36 #else 37 __RCSID("$NetBSD: utilities.c,v 1.58 2010/01/07 01:39:56 christos Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/param.h> 42 #include <sys/time.h> 43 44 #include <ufs/ufs/dinode.h> 45 #include <ufs/ufs/dir.h> 46 #include <ufs/ffs/fs.h> 47 #include <ufs/ffs/ffs_extern.h> 48 #include <ufs/ufs/ufs_bswap.h> 49 50 #include <ctype.h> 51 #include <err.h> 52 #include <errno.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <unistd.h> 57 #include <signal.h> 58 59 #include "fsutil.h" 60 #include "fsck.h" 61 #include "extern.h" 62 #include "exitvalues.h" 63 64 long diskreads, totalreads; /* Disk cache statistics */ 65 66 static void rwerror(const char *, daddr_t); 67 68 extern volatile sig_atomic_t returntosingle; 69 70 int 71 ftypeok(union dinode *dp) 72 { 73 switch (iswap16(DIP(dp, mode)) & IFMT) { 74 75 case IFDIR: 76 case IFREG: 77 case IFBLK: 78 case IFCHR: 79 case IFLNK: 80 case IFSOCK: 81 case IFIFO: 82 return (1); 83 84 default: 85 if (debug) 86 printf("bad file type 0%o\n", iswap16(DIP(dp, mode))); 87 return (0); 88 } 89 } 90 91 int 92 reply(const char *question) 93 { 94 int persevere; 95 char c; 96 97 if (preen) 98 pfatal("INTERNAL ERROR: GOT TO reply()"); 99 persevere = !strcmp(question, "CONTINUE"); 100 printf("\n"); 101 if (!persevere && (nflag || fswritefd < 0)) { 102 printf("%s? no\n\n", question); 103 resolved = 0; 104 return (0); 105 } 106 if (yflag || (persevere && nflag)) { 107 printf("%s? yes\n\n", question); 108 return (1); 109 } 110 do { 111 printf("%s? [yn] ", question); 112 (void) fflush(stdout); 113 c = getc(stdin); 114 while (c != '\n' && getc(stdin) != '\n') { 115 if (feof(stdin)) { 116 resolved = 0; 117 return (0); 118 } 119 } 120 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 121 printf("\n"); 122 if (c == 'y' || c == 'Y') 123 return (1); 124 resolved = 0; 125 return (0); 126 } 127 128 /* 129 * Malloc buffers and set up cache. 130 */ 131 void 132 bufinit(void) 133 { 134 struct bufarea *bp; 135 long bufcnt, i; 136 char *bufp; 137 138 pbp = pdirbp = (struct bufarea *)0; 139 bufp = malloc((unsigned int)sblock->fs_bsize); 140 if (bufp == 0) 141 errexit("cannot allocate buffer pool"); 142 cgblk.b_un.b_buf = bufp; 143 initbarea(&cgblk); 144 bufp = malloc((unsigned int)APPLEUFS_LABEL_SIZE); 145 if (bufp == 0) 146 errexit("cannot allocate buffer pool"); 147 appleufsblk.b_un.b_buf = bufp; 148 initbarea(&appleufsblk); 149 bufhead.b_next = bufhead.b_prev = &bufhead; 150 bufcnt = MAXBUFSPACE / sblock->fs_bsize; 151 if (bufcnt < MINBUFS) 152 bufcnt = MINBUFS; 153 for (i = 0; i < bufcnt; i++) { 154 bp = malloc(sizeof(struct bufarea)); 155 bufp = malloc((unsigned int)sblock->fs_bsize); 156 if (bp == NULL || bufp == NULL) { 157 if (i >= MINBUFS) { 158 if (bp) 159 free(bp); 160 if (bufp) 161 free(bufp); 162 break; 163 } 164 errexit("cannot allocate buffer pool"); 165 } 166 bp->b_un.b_buf = bufp; 167 bp->b_prev = &bufhead; 168 bp->b_next = bufhead.b_next; 169 bufhead.b_next->b_prev = bp; 170 bufhead.b_next = bp; 171 initbarea(bp); 172 } 173 bufhead.b_size = i; /* save number of buffers */ 174 } 175 176 /* 177 * Manage a cache of directory blocks. 178 */ 179 struct bufarea * 180 getdatablk(daddr_t blkno, long size) 181 { 182 struct bufarea *bp; 183 184 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 185 if (bp->b_bno == fsbtodb(sblock, blkno)) 186 goto foundit; 187 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 188 if ((bp->b_flags & B_INUSE) == 0) 189 break; 190 if (bp == &bufhead) 191 errexit("deadlocked buffer pool"); 192 /* fall through */ 193 foundit: 194 getblk(bp, blkno, size); 195 bp->b_prev->b_next = bp->b_next; 196 bp->b_next->b_prev = bp->b_prev; 197 bp->b_prev = &bufhead; 198 bp->b_next = bufhead.b_next; 199 bufhead.b_next->b_prev = bp; 200 bufhead.b_next = bp; 201 bp->b_flags |= B_INUSE; 202 return (bp); 203 } 204 205 void 206 getblk(struct bufarea *bp, daddr_t blk, long size) 207 { 208 daddr_t dblk; 209 210 dblk = fsbtodb(sblock, blk); 211 totalreads++; 212 if (bp->b_bno != dblk) { 213 flush(fswritefd, bp); 214 diskreads++; 215 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); 216 bp->b_bno = dblk; 217 bp->b_size = size; 218 } 219 } 220 221 void 222 flush(int fd, struct bufarea *bp) 223 { 224 int i, j; 225 struct csum *ccsp; 226 227 if (!bp->b_dirty) 228 return; 229 if (bp->b_errs != 0) 230 pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n", 231 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 232 (long long)bp->b_bno); 233 bp->b_dirty = 0; 234 bp->b_errs = 0; 235 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 236 if (bp != &sblk) 237 return; 238 for (i = 0, j = 0; i < sblock->fs_cssize; i += sblock->fs_bsize, j++) { 239 int size = sblock->fs_cssize - i < sblock->fs_bsize ? 240 sblock->fs_cssize - i : sblock->fs_bsize; 241 ccsp = (struct csum *)((char *)sblock->fs_csp + i); 242 if (needswap) 243 ffs_csum_swap(ccsp, ccsp, size); 244 bwrite(fswritefd, (char *)ccsp, 245 fsbtodb(sblock, sblock->fs_csaddr + j * sblock->fs_frag), 246 size); 247 if (needswap) 248 ffs_csum_swap(ccsp, ccsp, size); 249 } 250 } 251 252 static void 253 rwerror(const char *mesg, daddr_t blk) 254 { 255 256 if (preen == 0) 257 printf("\n"); 258 pfatal("CANNOT %s: BLK %lld", mesg, (long long)blk); 259 if (reply("CONTINUE") == 0) 260 exit(FSCK_EXIT_CHECK_FAILED); 261 } 262 263 void 264 ckfini(void) 265 { 266 struct bufarea *bp, *nbp; 267 int ofsmodified, cnt = 0; 268 269 if (fswritefd < 0) { 270 (void)close(fsreadfd); 271 return; 272 } 273 flush(fswritefd, &sblk); 274 if (havesb && bflag != 0 && 275 (preen || reply("UPDATE STANDARD SUPERBLOCK"))) { 276 if (preen) 277 pwarn("UPDATING STANDARD SUPERBLOCK\n"); 278 if (!is_ufs2 && (sblock->fs_old_flags & FS_FLAGS_UPDATED) == 0) 279 sblk.b_bno = SBLOCK_UFS1 / dev_bsize; 280 else 281 sblk.b_bno = sblock->fs_sblockloc / dev_bsize; 282 sbdirty(); 283 flush(fswritefd, &sblk); 284 } 285 flush(fswritefd, &appleufsblk); 286 free(appleufsblk.b_un.b_buf); 287 flush(fswritefd, &cgblk); 288 free(cgblk.b_un.b_buf); 289 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { 290 cnt++; 291 flush(fswritefd, bp); 292 nbp = bp->b_prev; 293 free(bp->b_un.b_buf); 294 free((char *)bp); 295 } 296 if (bufhead.b_size != cnt) 297 errexit("Panic: lost %d buffers", bufhead.b_size - cnt); 298 pbp = pdirbp = (struct bufarea *)0; 299 if (markclean && (sblock->fs_clean & FS_ISCLEAN) == 0) { 300 /* 301 * Mark the file system as clean, and sync the superblock. 302 */ 303 if (preen) 304 pwarn("MARKING FILE SYSTEM CLEAN\n"); 305 else if (!reply("MARK FILE SYSTEM CLEAN")) 306 markclean = 0; 307 if (markclean) { 308 sblock->fs_clean = FS_ISCLEAN; 309 sblock->fs_pendingblocks = 0; 310 sblock->fs_pendinginodes = 0; 311 sbdirty(); 312 ofsmodified = fsmodified; 313 flush(fswritefd, &sblk); 314 #if LITE2BORKEN 315 fsmodified = ofsmodified; 316 #endif 317 if (!preen) 318 printf( 319 "\n***** FILE SYSTEM MARKED CLEAN *****\n"); 320 } 321 } 322 if (debug) 323 printf("cache missed %ld of %ld (%d%%)\n", diskreads, 324 totalreads, (int)(diskreads * 100 / totalreads)); 325 cleanup_wapbl(); 326 (void)close(fsreadfd); 327 (void)close(fswritefd); 328 } 329 330 int 331 bread(int fd, char *buf, daddr_t blk, long size) 332 { 333 char *cp; 334 int i, errs; 335 off_t offset; 336 337 offset = blk; 338 offset *= dev_bsize; 339 if ((pread(fd, buf, (int)size, offset) == size) && 340 read_wapbl(buf, size, blk) == 0) 341 return (0); 342 rwerror("READ", blk); 343 errs = 0; 344 memset(buf, 0, (size_t)size); 345 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 346 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 347 if (pread(fd, cp, (int)secsize, offset + i) != secsize) { 348 if (secsize != dev_bsize && dev_bsize != 1) 349 printf(" %lld (%lld),", 350 (long long)((blk*dev_bsize + i) / secsize), 351 (long long)(blk + i / dev_bsize)); 352 else 353 printf(" %lld,", 354 (long long)(blk + i / dev_bsize)); 355 errs++; 356 } 357 } 358 printf("\n"); 359 return (errs); 360 } 361 362 void 363 bwrite(int fd, char *buf, daddr_t blk, long size) 364 { 365 int i; 366 char *cp; 367 off_t offset; 368 369 if (fd < 0) 370 return; 371 offset = blk; 372 offset *= dev_bsize; 373 if (pwrite(fd, buf, (int)size, offset) == size) { 374 fsmodified = 1; 375 return; 376 } 377 rwerror("WRITE", blk); 378 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 379 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 380 if (pwrite(fd, cp, (int)dev_bsize, offset + i) != dev_bsize) 381 printf(" %lld,", (long long)(blk + i / dev_bsize)); 382 printf("\n"); 383 return; 384 } 385 386 /* 387 * allocate a data block with the specified number of fragments 388 */ 389 daddr_t 390 allocblk(long frags) 391 { 392 int i, j, k, cg, baseblk; 393 struct cg *cgp = cgrp; 394 395 if (frags <= 0 || frags > sblock->fs_frag) 396 return (0); 397 for (i = 0; i < maxfsblock - sblock->fs_frag; i += sblock->fs_frag) { 398 for (j = 0; j <= sblock->fs_frag - frags; j++) { 399 if (testbmap(i + j)) 400 continue; 401 for (k = 1; k < frags; k++) 402 if (testbmap(i + j + k)) 403 break; 404 if (k < frags) { 405 j += k; 406 continue; 407 } 408 cg = dtog(sblock, i + j); 409 getblk(&cgblk, cgtod(sblock, cg), sblock->fs_cgsize); 410 memcpy(cgp, cgblk.b_un.b_cg, sblock->fs_cgsize); 411 if ((doswap && !needswap) || (!doswap && needswap)) 412 ffs_cg_swap(cgblk.b_un.b_cg, cgp, sblock); 413 if (!cg_chkmagic(cgp, 0)) 414 pfatal("CG %d: ALLOCBLK: BAD MAGIC NUMBER\n", 415 cg); 416 baseblk = dtogd(sblock, i + j); 417 for (k = 0; k < frags; k++) { 418 setbmap(i + j + k); 419 clrbit(cg_blksfree(cgp, 0), baseblk + k); 420 } 421 n_blks += frags; 422 if (frags == sblock->fs_frag) 423 cgp->cg_cs.cs_nbfree--; 424 else 425 cgp->cg_cs.cs_nffree -= frags; 426 cgdirty(); 427 return (i + j); 428 } 429 } 430 return (0); 431 } 432 433 /* 434 * Free a previously allocated block 435 */ 436 void 437 freeblk(daddr_t blkno, long frags) 438 { 439 struct inodesc idesc; 440 441 idesc.id_blkno = blkno; 442 idesc.id_numfrags = frags; 443 (void)pass4check(&idesc); 444 } 445 446 /* 447 * Find a pathname 448 */ 449 void 450 getpathname(char *namebuf, size_t namebuflen, ino_t curdir, ino_t ino) 451 { 452 int len; 453 char *cp; 454 struct inodesc idesc; 455 static int busy = 0; 456 struct inostat *info; 457 458 if (curdir == ino && ino == ROOTINO) { 459 (void)strlcpy(namebuf, "/", namebuflen); 460 return; 461 } 462 info = inoinfo(curdir); 463 if (busy || (info->ino_state != DSTATE && info->ino_state != DFOUND)) { 464 (void)strlcpy(namebuf, "?", namebuflen); 465 return; 466 } 467 busy = 1; 468 memset(&idesc, 0, sizeof(struct inodesc)); 469 idesc.id_type = DATA; 470 idesc.id_fix = IGNORE; 471 cp = &namebuf[MAXPATHLEN - 1]; 472 *cp = '\0'; 473 if (curdir != ino) { 474 idesc.id_parent = curdir; 475 goto namelookup; 476 } 477 while (ino != ROOTINO) { 478 idesc.id_number = ino; 479 idesc.id_func = findino; 480 idesc.id_name = ".."; 481 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 482 break; 483 namelookup: 484 idesc.id_number = idesc.id_parent; 485 idesc.id_parent = ino; 486 idesc.id_func = findname; 487 idesc.id_name = namebuf; 488 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) 489 break; 490 len = strlen(namebuf); 491 cp -= len; 492 memmove(cp, namebuf, (size_t)len); 493 *--cp = '/'; 494 if (cp < &namebuf[FFS_MAXNAMLEN]) 495 break; 496 ino = idesc.id_number; 497 } 498 busy = 0; 499 if (ino != ROOTINO) 500 *--cp = '?'; 501 memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); 502 } 503 504 void 505 catch(int sig) 506 { 507 if (!doinglevel2) { 508 markclean = 0; 509 ckfini(); 510 } 511 _exit(FSCK_EXIT_SIGNALLED); 512 } 513 514 /* 515 * When preening, allow a single quit to signal 516 * a special exit after filesystem checks complete 517 * so that reboot sequence may be interrupted. 518 */ 519 void 520 catchquit(int sig) 521 { 522 static const char msg[] = 523 "returning to single-user after file system check\n"; 524 int serrno = errno; 525 526 (void)write(STDOUT_FILENO, msg, sizeof(msg) - 1); 527 returntosingle = 1; 528 (void)signal(SIGQUIT, SIG_DFL); 529 errno = serrno; 530 } 531 532 /* 533 * Ignore a single quit signal; wait and flush just in case. 534 * Used by child processes in preen. 535 */ 536 void 537 voidquit(int sig) 538 { 539 int serrno = errno; 540 541 sleep(1); 542 (void)signal(SIGQUIT, SIG_IGN); 543 (void)signal(SIGQUIT, SIG_DFL); 544 errno = serrno; 545 } 546 547 /* 548 * determine whether an inode should be fixed. 549 */ 550 int 551 dofix(struct inodesc *idesc, const char *msg) 552 { 553 554 switch (idesc->id_fix) { 555 556 case DONTKNOW: 557 if (idesc->id_type == DATA) 558 direrror(idesc->id_number, msg); 559 else 560 pwarn("%s", msg); 561 if (preen) { 562 printf(" (SALVAGED)\n"); 563 idesc->id_fix = FIX; 564 return (ALTERED); 565 } 566 if (reply("SALVAGE") == 0) { 567 idesc->id_fix = NOFIX; 568 return (0); 569 } 570 idesc->id_fix = FIX; 571 return (ALTERED); 572 573 case FIX: 574 return (ALTERED); 575 576 case NOFIX: 577 case IGNORE: 578 return (0); 579 580 default: 581 errexit("UNKNOWN INODESC FIX MODE %d", idesc->id_fix); 582 } 583 /* NOTREACHED */ 584 return (0); 585 } 586 587 void 588 copyback_cg(struct bufarea *blk) 589 { 590 591 memcpy(blk->b_un.b_cg, cgrp, sblock->fs_cgsize); 592 if (needswap) 593 ffs_cg_swap(cgrp, blk->b_un.b_cg, sblock); 594 } 595 596 void 597 infohandler(int sig) 598 { 599 got_siginfo = 1; 600 } 601 602 /* 603 * Look up state information for an inode. 604 */ 605 struct inostat * 606 inoinfo(ino_t inum) 607 { 608 static struct inostat unallocated = { USTATE, 0, 0 }; 609 struct inostatlist *ilp; 610 int iloff; 611 612 if (inum > maxino) 613 errexit("inoinfo: inumber %llu out of range", 614 (unsigned long long)inum); 615 ilp = &inostathead[inum / sblock->fs_ipg]; 616 iloff = inum % sblock->fs_ipg; 617 if (iloff >= ilp->il_numalloced) 618 return (&unallocated); 619 return (&ilp->il_stat[iloff]); 620 } 621 622 void 623 sb_oldfscompat_read(struct fs *fs, struct fs **fssave) 624 { 625 if ((fs->fs_magic != FS_UFS1_MAGIC) || 626 (fs->fs_old_flags & FS_FLAGS_UPDATED)) 627 return; 628 629 /* Save a copy of fields that may be modified for compatibility */ 630 if (fssave) { 631 if (!*fssave) 632 *fssave = malloc(sizeof(struct fs)); 633 if (!*fssave) 634 errexit("cannot allocate space for compat store"); 635 memmove(*fssave, fs, sizeof(struct fs)); 636 637 if (debug) 638 printf("detected ufs1 superblock not yet updated for ufs2 kernels\n"); 639 640 if (doswap) { 641 uint16_t postbl[256]; 642 int i, n; 643 644 if (fs->fs_old_postblformat == FS_42POSTBLFMT) 645 n = 256; 646 else 647 n = 128; 648 649 /* extract the postbl from the unswapped superblock */ 650 if (!needswap) 651 ffs_sb_swap(*fssave, *fssave); 652 memmove(postbl, (&(*fssave)->fs_old_postbl_start), 653 n * sizeof(postbl[0])); 654 if (!needswap) 655 ffs_sb_swap(*fssave, *fssave); 656 657 /* Now swap it */ 658 for (i=0; i < n; i++) 659 postbl[i] = bswap16(postbl[i]); 660 661 /* And put it back such that it will get correctly 662 * unscrambled if it is swapped again on the way out 663 */ 664 if (needswap) 665 ffs_sb_swap(*fssave, *fssave); 666 memmove((&(*fssave)->fs_old_postbl_start), postbl, 667 n * sizeof(postbl[0])); 668 if (needswap) 669 ffs_sb_swap(*fssave, *fssave); 670 } 671 672 } 673 674 /* These fields will be overwritten by their 675 * original values in fs_oldfscompat_write, so it is harmless 676 * to modify them here. 677 */ 678 fs->fs_cstotal.cs_ndir = 679 fs->fs_old_cstotal.cs_ndir; 680 fs->fs_cstotal.cs_nbfree = 681 fs->fs_old_cstotal.cs_nbfree; 682 fs->fs_cstotal.cs_nifree = 683 fs->fs_old_cstotal.cs_nifree; 684 fs->fs_cstotal.cs_nffree = 685 fs->fs_old_cstotal.cs_nffree; 686 687 fs->fs_maxbsize = fs->fs_bsize; 688 fs->fs_time = fs->fs_old_time; 689 fs->fs_size = fs->fs_old_size; 690 fs->fs_dsize = fs->fs_old_dsize; 691 fs->fs_csaddr = fs->fs_old_csaddr; 692 fs->fs_sblockloc = SBLOCK_UFS1; 693 694 fs->fs_flags = fs->fs_old_flags; 695 696 if (fs->fs_old_postblformat == FS_42POSTBLFMT) { 697 fs->fs_old_nrpos = 8; 698 fs->fs_old_npsect = fs->fs_old_nsect; 699 fs->fs_old_interleave = 1; 700 fs->fs_old_trackskew = 0; 701 } 702 } 703 704 void 705 sb_oldfscompat_write(struct fs *fs, struct fs *fssave) 706 { 707 if ((fs->fs_magic != FS_UFS1_MAGIC) || 708 (fs->fs_old_flags & FS_FLAGS_UPDATED)) 709 return; 710 711 fs->fs_old_flags = fs->fs_flags; 712 fs->fs_old_time = fs->fs_time; 713 fs->fs_old_cstotal.cs_ndir = fs->fs_cstotal.cs_ndir; 714 fs->fs_old_cstotal.cs_nbfree = fs->fs_cstotal.cs_nbfree; 715 fs->fs_old_cstotal.cs_nifree = fs->fs_cstotal.cs_nifree; 716 fs->fs_old_cstotal.cs_nffree = fs->fs_cstotal.cs_nffree; 717 718 fs->fs_flags = fssave->fs_flags; 719 720 if (fs->fs_old_postblformat == FS_42POSTBLFMT) { 721 fs->fs_old_nrpos = fssave->fs_old_nrpos; 722 fs->fs_old_npsect = fssave->fs_old_npsect; 723 fs->fs_old_interleave = fssave->fs_old_interleave; 724 fs->fs_old_trackskew = fssave->fs_old_trackskew; 725 } 726 727 memmove(&fs->fs_old_postbl_start, &fssave->fs_old_postbl_start, 728 ((fs->fs_old_postblformat == FS_42POSTBLFMT) ? 729 512 : 256)); 730 } 731