1 /* $NetBSD: utilities.c,v 1.56 2008/07/31 05:38:04 simonb 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.56 2008/07/31 05:38:04 simonb 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 int 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 int errsave = errno; 523 524 printf("returning to single-user after file system check\n"); 525 returntosingle = 1; 526 (void)signal(SIGQUIT, SIG_DFL); 527 errno = errsave; 528 } 529 530 /* 531 * Ignore a single quit signal; wait and flush just in case. 532 * Used by child processes in preen. 533 */ 534 void 535 voidquit(int sig) 536 { 537 int errsave = errno; 538 539 sleep(1); 540 (void)signal(SIGQUIT, SIG_IGN); 541 (void)signal(SIGQUIT, SIG_DFL); 542 errno = errsave; 543 } 544 545 /* 546 * determine whether an inode should be fixed. 547 */ 548 int 549 dofix(struct inodesc *idesc, const char *msg) 550 { 551 552 switch (idesc->id_fix) { 553 554 case DONTKNOW: 555 if (idesc->id_type == DATA) 556 direrror(idesc->id_number, msg); 557 else 558 pwarn("%s", msg); 559 if (preen) { 560 printf(" (SALVAGED)\n"); 561 idesc->id_fix = FIX; 562 return (ALTERED); 563 } 564 if (reply("SALVAGE") == 0) { 565 idesc->id_fix = NOFIX; 566 return (0); 567 } 568 idesc->id_fix = FIX; 569 return (ALTERED); 570 571 case FIX: 572 return (ALTERED); 573 574 case NOFIX: 575 case IGNORE: 576 return (0); 577 578 default: 579 errexit("UNKNOWN INODESC FIX MODE %d", idesc->id_fix); 580 } 581 /* NOTREACHED */ 582 return (0); 583 } 584 585 void 586 copyback_cg(struct bufarea *blk) 587 { 588 589 memcpy(blk->b_un.b_cg, cgrp, sblock->fs_cgsize); 590 if (needswap) 591 ffs_cg_swap(cgrp, blk->b_un.b_cg, sblock); 592 } 593 594 void 595 infohandler(int sig) 596 { 597 got_siginfo = 1; 598 } 599 600 /* 601 * Look up state information for an inode. 602 */ 603 struct inostat * 604 inoinfo(ino_t inum) 605 { 606 static struct inostat unallocated = { USTATE, 0, 0 }; 607 struct inostatlist *ilp; 608 int iloff; 609 610 if (inum > maxino) 611 errexit("inoinfo: inumber %llu out of range", 612 (unsigned long long)inum); 613 ilp = &inostathead[inum / sblock->fs_ipg]; 614 iloff = inum % sblock->fs_ipg; 615 if (iloff >= ilp->il_numalloced) 616 return (&unallocated); 617 return (&ilp->il_stat[iloff]); 618 } 619 620 void 621 sb_oldfscompat_read(struct fs *fs, struct fs **fssave) 622 { 623 if ((fs->fs_magic != FS_UFS1_MAGIC) || 624 (fs->fs_old_flags & FS_FLAGS_UPDATED)) 625 return; 626 627 /* Save a copy of fields that may be modified for compatibility */ 628 if (fssave) { 629 if (!*fssave) 630 *fssave = malloc(sizeof(struct fs)); 631 if (!*fssave) 632 errexit("cannot allocate space for compat store"); 633 memmove(*fssave, fs, sizeof(struct fs)); 634 635 if (debug) 636 printf("detected ufs1 superblock not yet updated for ufs2 kernels\n"); 637 638 if (doswap) { 639 uint16_t postbl[256]; 640 int i, n; 641 642 if (fs->fs_old_postblformat == FS_42POSTBLFMT) 643 n = 256; 644 else 645 n = 128; 646 647 /* extract the postbl from the unswapped superblock */ 648 if (!needswap) 649 ffs_sb_swap(*fssave, *fssave); 650 memmove(postbl, (&(*fssave)->fs_old_postbl_start), 651 n * sizeof(postbl[0])); 652 if (!needswap) 653 ffs_sb_swap(*fssave, *fssave); 654 655 /* Now swap it */ 656 for (i=0; i < n; i++) 657 postbl[i] = bswap16(postbl[i]); 658 659 /* And put it back such that it will get correctly 660 * unscrambled if it is swapped again on the way out 661 */ 662 if (needswap) 663 ffs_sb_swap(*fssave, *fssave); 664 memmove((&(*fssave)->fs_old_postbl_start), postbl, 665 n * sizeof(postbl[0])); 666 if (needswap) 667 ffs_sb_swap(*fssave, *fssave); 668 } 669 670 } 671 672 /* These fields will be overwritten by their 673 * original values in fs_oldfscompat_write, so it is harmless 674 * to modify them here. 675 */ 676 fs->fs_cstotal.cs_ndir = 677 fs->fs_old_cstotal.cs_ndir; 678 fs->fs_cstotal.cs_nbfree = 679 fs->fs_old_cstotal.cs_nbfree; 680 fs->fs_cstotal.cs_nifree = 681 fs->fs_old_cstotal.cs_nifree; 682 fs->fs_cstotal.cs_nffree = 683 fs->fs_old_cstotal.cs_nffree; 684 685 fs->fs_maxbsize = fs->fs_bsize; 686 fs->fs_time = fs->fs_old_time; 687 fs->fs_size = fs->fs_old_size; 688 fs->fs_dsize = fs->fs_old_dsize; 689 fs->fs_csaddr = fs->fs_old_csaddr; 690 fs->fs_sblockloc = SBLOCK_UFS1; 691 692 fs->fs_flags = fs->fs_old_flags; 693 694 if (fs->fs_old_postblformat == FS_42POSTBLFMT) { 695 fs->fs_old_nrpos = 8; 696 fs->fs_old_npsect = fs->fs_old_nsect; 697 fs->fs_old_interleave = 1; 698 fs->fs_old_trackskew = 0; 699 } 700 } 701 702 void 703 sb_oldfscompat_write(struct fs *fs, struct fs *fssave) 704 { 705 if ((fs->fs_magic != FS_UFS1_MAGIC) || 706 (fs->fs_old_flags & FS_FLAGS_UPDATED)) 707 return; 708 709 fs->fs_old_flags = fs->fs_flags; 710 fs->fs_old_time = fs->fs_time; 711 fs->fs_old_cstotal.cs_ndir = fs->fs_cstotal.cs_ndir; 712 fs->fs_old_cstotal.cs_nbfree = fs->fs_cstotal.cs_nbfree; 713 fs->fs_old_cstotal.cs_nifree = fs->fs_cstotal.cs_nifree; 714 fs->fs_old_cstotal.cs_nffree = fs->fs_cstotal.cs_nffree; 715 716 fs->fs_flags = fssave->fs_flags; 717 718 if (fs->fs_old_postblformat == FS_42POSTBLFMT) { 719 fs->fs_old_nrpos = fssave->fs_old_nrpos; 720 fs->fs_old_npsect = fssave->fs_old_npsect; 721 fs->fs_old_interleave = fssave->fs_old_interleave; 722 fs->fs_old_trackskew = fssave->fs_old_trackskew; 723 } 724 725 memmove(&fs->fs_old_postbl_start, &fssave->fs_old_postbl_start, 726 ((fs->fs_old_postblformat == FS_42POSTBLFMT) ? 727 512 : 256)); 728 } 729