1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 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 #if 0 33 #ifndef lint 34 static const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95"; 35 #endif /* not lint */ 36 #endif 37 #include <sys/cdefs.h> 38 __FBSDID("$FreeBSD$"); 39 40 #include <sys/param.h> 41 #include <sys/time.h> 42 #include <sys/types.h> 43 #include <sys/sysctl.h> 44 #include <sys/disk.h> 45 #include <sys/disklabel.h> 46 #include <sys/ioctl.h> 47 #include <sys/stat.h> 48 49 #include <ufs/ufs/dinode.h> 50 #include <ufs/ufs/dir.h> 51 #include <ufs/ffs/fs.h> 52 53 #include <err.h> 54 #include <errno.h> 55 #include <string.h> 56 #include <ctype.h> 57 #include <fstab.h> 58 #include <stdint.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <time.h> 62 #include <unistd.h> 63 #include <libufs.h> 64 65 #include "fsck.h" 66 67 int sujrecovery = 0; 68 69 static struct bufarea *allocbuf(const char *); 70 static void cg_write(struct bufarea *); 71 static void slowio_start(void); 72 static void slowio_end(void); 73 static void printIOstats(void); 74 static void prtbuf(const char *, struct bufarea *); 75 76 static long diskreads, totaldiskreads, totalreads; /* Disk cache statistics */ 77 static struct timespec startpass, finishpass; 78 struct timeval slowio_starttime; 79 int slowio_delay_usec = 10000; /* Initial IO delay for background fsck */ 80 int slowio_pollcnt; 81 static struct bufarea cgblk; /* backup buffer for cylinder group blocks */ 82 static TAILQ_HEAD(bufqueue, bufarea) bufqueuehd; /* head of buffer cache LRU */ 83 static LIST_HEAD(bufhash, bufarea) bufhashhd[HASHSIZE]; /* buffer hash list */ 84 static int numbufs; /* size of buffer cache */ 85 static int cachelookups; /* number of cache lookups */ 86 static int cachereads; /* number of cache reads */ 87 static int flushtries; /* number of tries to reclaim memory */ 88 89 char *buftype[BT_NUMBUFTYPES] = BT_NAMES; 90 91 void 92 fsutilinit(void) 93 { 94 diskreads = totaldiskreads = totalreads = 0; 95 bzero(&startpass, sizeof(struct timespec)); 96 bzero(&finishpass, sizeof(struct timespec)); 97 bzero(&slowio_starttime, sizeof(struct timeval)); 98 slowio_delay_usec = 10000; 99 slowio_pollcnt = 0; 100 flushtries = 0; 101 } 102 103 int 104 ftypeok(union dinode *dp) 105 { 106 switch (DIP(dp, di_mode) & IFMT) { 107 108 case IFDIR: 109 case IFREG: 110 case IFBLK: 111 case IFCHR: 112 case IFLNK: 113 case IFSOCK: 114 case IFIFO: 115 return (1); 116 117 default: 118 if (debug) 119 printf("bad file type 0%o\n", DIP(dp, di_mode)); 120 return (0); 121 } 122 } 123 124 int 125 reply(const char *question) 126 { 127 int persevere; 128 char c; 129 130 if (preen) 131 pfatal("INTERNAL ERROR: GOT TO reply()"); 132 persevere = strcmp(question, "CONTINUE") == 0 || 133 strcmp(question, "LOOK FOR ALTERNATE SUPERBLOCKS") == 0; 134 printf("\n"); 135 if (!persevere && (nflag || (fswritefd < 0 && bkgrdflag == 0))) { 136 printf("%s? no\n\n", question); 137 resolved = 0; 138 return (0); 139 } 140 if (yflag || (persevere && nflag)) { 141 printf("%s? yes\n\n", question); 142 return (1); 143 } 144 do { 145 printf("%s? [yn] ", question); 146 (void) fflush(stdout); 147 c = getc(stdin); 148 while (c != '\n' && getc(stdin) != '\n') { 149 if (feof(stdin)) { 150 resolved = 0; 151 return (0); 152 } 153 } 154 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 155 printf("\n"); 156 if (c == 'y' || c == 'Y') 157 return (1); 158 resolved = 0; 159 return (0); 160 } 161 162 /* 163 * Look up state information for an inode. 164 */ 165 struct inostat * 166 inoinfo(ino_t inum) 167 { 168 static struct inostat unallocated = { USTATE, 0, 0 }; 169 struct inostatlist *ilp; 170 int iloff; 171 172 if (inum > maxino) 173 errx(EEXIT, "inoinfo: inumber %ju out of range", 174 (uintmax_t)inum); 175 ilp = &inostathead[inum / sblock.fs_ipg]; 176 iloff = inum % sblock.fs_ipg; 177 if (iloff >= ilp->il_numalloced) 178 return (&unallocated); 179 return (&ilp->il_stat[iloff]); 180 } 181 182 /* 183 * Malloc buffers and set up cache. 184 */ 185 void 186 bufinit(void) 187 { 188 int i; 189 190 if ((cgblk.b_un.b_buf = Malloc((unsigned int)sblock.fs_bsize)) == NULL) 191 errx(EEXIT, "Initial malloc(%d) failed", sblock.fs_bsize); 192 initbarea(&cgblk, BT_CYLGRP); 193 numbufs = cachelookups = cachereads = 0; 194 TAILQ_INIT(&bufqueuehd); 195 for (i = 0; i < HASHSIZE; i++) 196 LIST_INIT(&bufhashhd[i]); 197 for (i = 0; i < BT_NUMBUFTYPES; i++) { 198 readtime[i].tv_sec = totalreadtime[i].tv_sec = 0; 199 readtime[i].tv_nsec = totalreadtime[i].tv_nsec = 0; 200 readcnt[i] = totalreadcnt[i] = 0; 201 } 202 } 203 204 static struct bufarea * 205 allocbuf(const char *failreason) 206 { 207 struct bufarea *bp; 208 char *bufp; 209 210 bp = (struct bufarea *)Malloc(sizeof(struct bufarea)); 211 bufp = Malloc((unsigned int)sblock.fs_bsize); 212 if (bp == NULL || bufp == NULL) { 213 errx(EEXIT, "%s", failreason); 214 /* NOTREACHED */ 215 } 216 numbufs++; 217 bp->b_un.b_buf = bufp; 218 TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list); 219 initbarea(bp, BT_UNKNOWN); 220 return (bp); 221 } 222 223 /* 224 * Manage cylinder group buffers. 225 * 226 * Use getblk() here rather than cgget() because the cylinder group 227 * may be corrupted but we want it anyway so we can fix it. 228 */ 229 static struct bufarea *cgbufs; /* header for cylinder group cache */ 230 static int flushtries; /* number of tries to reclaim memory */ 231 232 struct bufarea * 233 cglookup(int cg) 234 { 235 struct bufarea *cgbp; 236 struct cg *cgp; 237 238 if ((unsigned) cg >= sblock.fs_ncg) 239 errx(EEXIT, "cglookup: out of range cylinder group %d", cg); 240 if (cgbufs == NULL) { 241 cgbufs = calloc(sblock.fs_ncg, sizeof(struct bufarea)); 242 if (cgbufs == NULL) 243 errx(EEXIT, "Cannot allocate cylinder group buffers"); 244 } 245 cgbp = &cgbufs[cg]; 246 if (cgbp->b_un.b_cg != NULL) 247 return (cgbp); 248 cgp = NULL; 249 if (flushtries == 0) 250 cgp = Malloc((unsigned int)sblock.fs_cgsize); 251 if (cgp == NULL) { 252 if (sujrecovery) 253 errx(EEXIT,"Ran out of memory during journal recovery"); 254 flush(fswritefd, &cgblk); 255 getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); 256 return (&cgblk); 257 } 258 cgbp->b_un.b_cg = cgp; 259 initbarea(cgbp, BT_CYLGRP); 260 getblk(cgbp, cgtod(&sblock, cg), sblock.fs_cgsize); 261 return (cgbp); 262 } 263 264 /* 265 * Mark a cylinder group buffer as dirty. 266 * Update its check-hash if they are enabled. 267 */ 268 void 269 cgdirty(struct bufarea *cgbp) 270 { 271 struct cg *cg; 272 273 cg = cgbp->b_un.b_cg; 274 if ((sblock.fs_metackhash & CK_CYLGRP) != 0) { 275 cg->cg_ckhash = 0; 276 cg->cg_ckhash = 277 calculate_crc32c(~0L, (void *)cg, sblock.fs_cgsize); 278 } 279 dirty(cgbp); 280 } 281 282 /* 283 * Attempt to flush a cylinder group cache entry. 284 * Return whether the flush was successful. 285 */ 286 int 287 flushentry(void) 288 { 289 struct bufarea *cgbp; 290 291 if (sujrecovery || flushtries == sblock.fs_ncg || cgbufs == NULL) 292 return (0); 293 cgbp = &cgbufs[flushtries++]; 294 if (cgbp->b_un.b_cg == NULL) 295 return (0); 296 flush(fswritefd, cgbp); 297 free(cgbp->b_un.b_buf); 298 cgbp->b_un.b_buf = NULL; 299 return (1); 300 } 301 302 /* 303 * Manage a cache of directory blocks. 304 */ 305 struct bufarea * 306 getdatablk(ufs2_daddr_t blkno, long size, int type) 307 { 308 struct bufarea *bp; 309 struct bufhash *bhdp; 310 311 cachelookups++; 312 /* If out of range, return empty buffer with b_err == -1 */ 313 if (type != BT_INODES && chkrange(blkno, size / sblock.fs_fsize)) { 314 blkno = -1; 315 type = BT_EMPTY; 316 } 317 bhdp = &bufhashhd[HASH(blkno)]; 318 LIST_FOREACH(bp, bhdp, b_hash) 319 if (bp->b_bno == fsbtodb(&sblock, blkno)) { 320 if (debug && bp->b_size != size) { 321 prtbuf("getdatablk: size mismatch", bp); 322 pfatal("getdatablk: b_size %d != size %ld\n", 323 bp->b_size, size); 324 } 325 goto foundit; 326 } 327 /* 328 * Move long-term busy buffer back to the front of the LRU so we 329 * do not endless inspect them for recycling. 330 */ 331 bp = TAILQ_LAST(&bufqueuehd, bufqueue); 332 if (bp != NULL && bp->b_refcnt != 0) { 333 TAILQ_REMOVE(&bufqueuehd, bp, b_list); 334 TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list); 335 } 336 /* 337 * Allocate up to the minimum number of buffers before 338 * considering recycling any of them. 339 */ 340 if (size > sblock.fs_bsize) 341 errx(EEXIT, "Excessive buffer size %ld > %d\n", size, 342 sblock.fs_bsize); 343 if (numbufs < MINBUFS) { 344 bp = allocbuf("cannot create minimal buffer pool"); 345 } else if (sujrecovery) { 346 /* 347 * SUJ recovery does not want anything written until it 348 * has successfully completed (so it can fail back to 349 * full fsck). Thus, we can only recycle clean buffers. 350 */ 351 TAILQ_FOREACH_REVERSE(bp, &bufqueuehd, bufqueue, b_list) 352 if ((bp->b_flags & B_DIRTY) == 0 && bp->b_refcnt == 0) 353 break; 354 if (bp == NULL) 355 bp = allocbuf("Ran out of memory during " 356 "journal recovery"); 357 else 358 LIST_REMOVE(bp, b_hash); 359 } else { 360 /* 361 * Recycle oldest non-busy buffer. 362 */ 363 TAILQ_FOREACH_REVERSE(bp, &bufqueuehd, bufqueue, b_list) 364 if (bp->b_refcnt == 0) 365 break; 366 if (bp == NULL) 367 bp = allocbuf("Ran out of memory for buffers"); 368 else 369 LIST_REMOVE(bp, b_hash); 370 } 371 flush(fswritefd, bp); 372 bp->b_type = type; 373 LIST_INSERT_HEAD(bhdp, bp, b_hash); 374 getblk(bp, blkno, size); 375 cachereads++; 376 /* fall through */ 377 foundit: 378 if (debug && bp->b_type != type) { 379 printf("getdatablk: buffer type changed to %s", 380 BT_BUFTYPE(type)); 381 prtbuf("", bp); 382 } 383 TAILQ_REMOVE(&bufqueuehd, bp, b_list); 384 TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list); 385 if (bp->b_errs == 0) 386 bp->b_refcnt++; 387 return (bp); 388 } 389 390 void 391 getblk(struct bufarea *bp, ufs2_daddr_t blk, long size) 392 { 393 ufs2_daddr_t dblk; 394 struct timespec start, finish; 395 396 dblk = fsbtodb(&sblock, blk); 397 if (bp->b_bno == dblk) { 398 totalreads++; 399 } else { 400 if (debug) { 401 readcnt[bp->b_type]++; 402 clock_gettime(CLOCK_REALTIME_PRECISE, &start); 403 } 404 if (bp->b_type != BT_EMPTY) 405 bp->b_errs = 406 blread(fsreadfd, bp->b_un.b_buf, dblk, size); 407 else 408 bp->b_errs = -1; 409 if (debug) { 410 clock_gettime(CLOCK_REALTIME_PRECISE, &finish); 411 timespecsub(&finish, &start, &finish); 412 timespecadd(&readtime[bp->b_type], &finish, 413 &readtime[bp->b_type]); 414 } 415 bp->b_bno = dblk; 416 bp->b_size = size; 417 } 418 } 419 420 void 421 brelse(struct bufarea *bp) 422 { 423 424 if (bp->b_refcnt <= 0) 425 prtbuf("brelse: buffer with negative reference count", bp); 426 bp->b_refcnt--; 427 } 428 429 void 430 flush(int fd, struct bufarea *bp) 431 { 432 struct inode ip; 433 434 if ((bp->b_flags & B_DIRTY) == 0) 435 return; 436 bp->b_flags &= ~B_DIRTY; 437 if (fswritefd < 0) { 438 pfatal("WRITING IN READ_ONLY MODE.\n"); 439 return; 440 } 441 if (bp->b_errs != 0) 442 pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n", 443 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 444 (long long)bp->b_bno); 445 bp->b_errs = 0; 446 /* 447 * Write using the appropriate function. 448 */ 449 switch (bp->b_type) { 450 case BT_SUPERBLK: 451 if (bp != &sblk) 452 pfatal("BUFFER %p DOES NOT MATCH SBLK %p\n", 453 bp, &sblk); 454 if (sbput(fd, bp->b_un.b_fs, 0) == 0) 455 fsmodified = 1; 456 break; 457 case BT_CYLGRP: 458 if (sujrecovery) 459 cg_write(bp); 460 if (cgput(fswritefd, &sblock, bp->b_un.b_cg) == 0) 461 fsmodified = 1; 462 break; 463 case BT_INODES: 464 if (debug && sblock.fs_magic == FS_UFS2_MAGIC) { 465 struct ufs2_dinode *dp = bp->b_un.b_dinode2; 466 int i; 467 468 for (i = 0; i < bp->b_size; dp++, i += sizeof(*dp)) { 469 if (ffs_verify_dinode_ckhash(&sblock, dp) == 0) 470 continue; 471 pwarn("flush: INODE CHECK-HASH FAILED"); 472 ip.i_bp = bp; 473 ip.i_dp = (union dinode *)dp; 474 ip.i_number = bp->b_index + i; 475 prtinode(&ip); 476 if (preen || reply("FIX") != 0) { 477 if (preen) 478 printf(" (FIXED)\n"); 479 ffs_update_dinode_ckhash(&sblock, dp); 480 inodirty(&ip); 481 } 482 } 483 } 484 /* FALLTHROUGH */ 485 default: 486 blwrite(fd, bp->b_un.b_buf, bp->b_bno, bp->b_size); 487 break; 488 } 489 } 490 491 /* 492 * Journaled soft updates does not maintain cylinder group summary 493 * information during cleanup, so this routine recalculates the summary 494 * information and updates the superblock summary in preparation for 495 * writing out the cylinder group. 496 */ 497 static void 498 cg_write(struct bufarea *bp) 499 { 500 ufs1_daddr_t fragno, cgbno, maxbno; 501 u_int8_t *blksfree; 502 struct cg *cgp; 503 int blk; 504 int i; 505 506 /* 507 * Fix the frag and cluster summary. 508 */ 509 cgp = bp->b_un.b_cg; 510 cgp->cg_cs.cs_nbfree = 0; 511 cgp->cg_cs.cs_nffree = 0; 512 bzero(&cgp->cg_frsum, sizeof(cgp->cg_frsum)); 513 maxbno = fragstoblks(&sblock, sblock.fs_fpg); 514 if (sblock.fs_contigsumsize > 0) { 515 for (i = 1; i <= sblock.fs_contigsumsize; i++) 516 cg_clustersum(cgp)[i] = 0; 517 bzero(cg_clustersfree(cgp), howmany(maxbno, CHAR_BIT)); 518 } 519 blksfree = cg_blksfree(cgp); 520 for (cgbno = 0; cgbno < maxbno; cgbno++) { 521 if (ffs_isfreeblock(&sblock, blksfree, cgbno)) 522 continue; 523 if (ffs_isblock(&sblock, blksfree, cgbno)) { 524 ffs_clusteracct(&sblock, cgp, cgbno, 1); 525 cgp->cg_cs.cs_nbfree++; 526 continue; 527 } 528 fragno = blkstofrags(&sblock, cgbno); 529 blk = blkmap(&sblock, blksfree, fragno); 530 ffs_fragacct(&sblock, blk, cgp->cg_frsum, 1); 531 for (i = 0; i < sblock.fs_frag; i++) 532 if (isset(blksfree, fragno + i)) 533 cgp->cg_cs.cs_nffree++; 534 } 535 /* 536 * Update the superblock cg summary from our now correct values 537 * before writing the block. 538 */ 539 sblock.fs_cs(&sblock, cgp->cg_cgx) = cgp->cg_cs; 540 } 541 542 void 543 rwerror(const char *mesg, ufs2_daddr_t blk) 544 { 545 546 if (bkgrdcheck) 547 exit(EEXIT); 548 if (preen == 0) 549 printf("\n"); 550 pfatal("CANNOT %s: %ld", mesg, (long)blk); 551 if (reply("CONTINUE") == 0) 552 exit(EEXIT); 553 } 554 555 void 556 ckfini(int markclean) 557 { 558 struct bufarea *bp, *nbp; 559 struct inoinfo *inp, *ninp; 560 int ofsmodified, cnt, cg, i; 561 562 if (bkgrdflag) { 563 unlink(snapname); 564 if ((!(sblock.fs_flags & FS_UNCLEAN)) != markclean) { 565 cmd.value = FS_UNCLEAN; 566 cmd.size = markclean ? -1 : 1; 567 if (sysctlbyname("vfs.ffs.setflags", 0, 0, 568 &cmd, sizeof cmd) == -1) 569 pwarn("CANNOT SET FILE SYSTEM DIRTY FLAG\n"); 570 if (!preen) { 571 printf("\n***** FILE SYSTEM MARKED %s *****\n", 572 markclean ? "CLEAN" : "DIRTY"); 573 if (!markclean) 574 rerun = 1; 575 } 576 } else if (!preen && !markclean) { 577 printf("\n***** FILE SYSTEM STILL DIRTY *****\n"); 578 rerun = 1; 579 } 580 bkgrdflag = 0; 581 } 582 if (debug && cachelookups > 0) 583 printf("cache with %d buffers missed %d of %d (%d%%)\n", 584 numbufs, cachereads, cachelookups, 585 (int)(cachereads * 100 / cachelookups)); 586 if (fswritefd < 0) { 587 (void)close(fsreadfd); 588 return; 589 } 590 /* 591 * To remain idempotent with partial truncations the buffers 592 * must be flushed in this order: 593 * 1) cylinder groups (bitmaps) 594 * 2) indirect, directory, external attribute, and data blocks 595 * 3) inode blocks 596 * 4) superblock 597 * This ordering preserves access to the modified pointers 598 * until they are freed. 599 */ 600 /* Step 1: cylinder groups */ 601 if (debug) 602 printf("Flush Cylinder groups\n"); 603 if (cgbufs != NULL) { 604 for (cnt = 0; cnt < sblock.fs_ncg; cnt++) { 605 if (cgbufs[cnt].b_un.b_cg == NULL) 606 continue; 607 flush(fswritefd, &cgbufs[cnt]); 608 free(cgbufs[cnt].b_un.b_cg); 609 } 610 free(cgbufs); 611 cgbufs = NULL; 612 } 613 flush(fswritefd, &cgblk); 614 free(cgblk.b_un.b_buf); 615 cgblk.b_un.b_buf = NULL; 616 cnt = 0; 617 /* Step 2: indirect, directory, external attribute, and data blocks */ 618 if (debug) 619 printf("Flush indirect, directory, external attribute, " 620 "and data blocks\n"); 621 if (pdirbp != NULL) { 622 brelse(pdirbp); 623 pdirbp = NULL; 624 } 625 TAILQ_FOREACH_REVERSE_SAFE(bp, &bufqueuehd, bufqueue, b_list, nbp) { 626 switch (bp->b_type) { 627 /* These should not be in the buffer cache list */ 628 case BT_UNKNOWN: 629 case BT_SUPERBLK: 630 case BT_CYLGRP: 631 default: 632 prtbuf("ckfini: improper buffer type on cache list",bp); 633 continue; 634 /* These are the ones to flush in this step */ 635 case BT_EMPTY: 636 if (bp->b_bno >= 0) 637 pfatal("Unused BT_EMPTY buffer for block %jd\n", 638 (intmax_t)bp->b_bno); 639 /* FALLTHROUGH */ 640 case BT_LEVEL1: 641 case BT_LEVEL2: 642 case BT_LEVEL3: 643 case BT_EXTATTR: 644 case BT_DIRDATA: 645 case BT_DATA: 646 break; 647 /* These are the ones to flush in the next step */ 648 case BT_INODES: 649 continue; 650 } 651 if (debug && bp->b_refcnt != 0) { 652 prtbuf("ckfini: clearing in-use buffer", bp); 653 pfatal("ckfini: clearing in-use buffer\n"); 654 } 655 TAILQ_REMOVE(&bufqueuehd, bp, b_list); 656 cnt++; 657 flush(fswritefd, bp); 658 free(bp->b_un.b_buf); 659 free((char *)bp); 660 } 661 /* Step 3: inode blocks */ 662 if (debug) 663 printf("Flush inode blocks\n"); 664 if (icachebp != NULL) { 665 brelse(icachebp); 666 icachebp = NULL; 667 } 668 TAILQ_FOREACH_REVERSE_SAFE(bp, &bufqueuehd, bufqueue, b_list, nbp) { 669 if (debug && bp->b_refcnt != 0) { 670 prtbuf("ckfini: clearing in-use buffer", bp); 671 pfatal("ckfini: clearing in-use buffer\n"); 672 } 673 TAILQ_REMOVE(&bufqueuehd, bp, b_list); 674 cnt++; 675 flush(fswritefd, bp); 676 free(bp->b_un.b_buf); 677 free((char *)bp); 678 } 679 if (numbufs != cnt) 680 errx(EEXIT, "panic: lost %d buffers", numbufs - cnt); 681 /* Step 4: superblock */ 682 if (debug) 683 printf("Flush the superblock\n"); 684 flush(fswritefd, &sblk); 685 if (havesb && cursnapshot == 0 && 686 sblk.b_bno != sblock.fs_sblockloc / dev_bsize) { 687 if (preen || reply("UPDATE STANDARD SUPERBLOCK")) { 688 /* Change write destination to standard superblock */ 689 sblock.fs_sblockactualloc = sblock.fs_sblockloc; 690 sblk.b_bno = sblock.fs_sblockloc / dev_bsize; 691 sbdirty(); 692 flush(fswritefd, &sblk); 693 } else { 694 markclean = 0; 695 } 696 } 697 if (cursnapshot == 0 && sblock.fs_clean != markclean) { 698 if ((sblock.fs_clean = markclean) != 0) { 699 sblock.fs_flags &= ~(FS_UNCLEAN | FS_NEEDSFSCK); 700 sblock.fs_pendingblocks = 0; 701 sblock.fs_pendinginodes = 0; 702 } 703 sbdirty(); 704 ofsmodified = fsmodified; 705 flush(fswritefd, &sblk); 706 fsmodified = ofsmodified; 707 if (!preen) { 708 printf("\n***** FILE SYSTEM MARKED %s *****\n", 709 markclean ? "CLEAN" : "DIRTY"); 710 if (!markclean) 711 rerun = 1; 712 } 713 } else if (!preen) { 714 if (markclean) { 715 printf("\n***** FILE SYSTEM IS CLEAN *****\n"); 716 } else { 717 printf("\n***** FILE SYSTEM STILL DIRTY *****\n"); 718 rerun = 1; 719 } 720 } 721 /* 722 * Free allocated tracking structures. 723 */ 724 if (blockmap != NULL) 725 free(blockmap); 726 blockmap = NULL; 727 if (inostathead != NULL) { 728 for (cg = 0; cg < sblock.fs_ncg; cg++) 729 if (inostathead[cg].il_stat != NULL) 730 free((char *)inostathead[cg].il_stat); 731 free(inostathead); 732 } 733 inostathead = NULL; 734 if (inpsort != NULL) 735 free(inpsort); 736 inpsort = NULL; 737 if (inphead != NULL) { 738 for (i = 0; i < dirhash; i++) { 739 for (inp = inphead[i]; inp != NULL; inp = ninp) { 740 ninp = inp->i_nexthash; 741 free(inp); 742 } 743 } 744 free(inphead); 745 } 746 inphead = NULL; 747 finalIOstats(); 748 (void)close(fsreadfd); 749 (void)close(fswritefd); 750 } 751 752 /* 753 * Print out I/O statistics. 754 */ 755 void 756 IOstats(char *what) 757 { 758 int i; 759 760 if (debug == 0) 761 return; 762 if (diskreads == 0) { 763 printf("%s: no I/O\n\n", what); 764 return; 765 } 766 if (startpass.tv_sec == 0) 767 startpass = startprog; 768 printf("%s: I/O statistics\n", what); 769 printIOstats(); 770 totaldiskreads += diskreads; 771 diskreads = 0; 772 for (i = 0; i < BT_NUMBUFTYPES; i++) { 773 timespecadd(&totalreadtime[i], &readtime[i], &totalreadtime[i]); 774 totalreadcnt[i] += readcnt[i]; 775 readtime[i].tv_sec = readtime[i].tv_nsec = 0; 776 readcnt[i] = 0; 777 } 778 clock_gettime(CLOCK_REALTIME_PRECISE, &startpass); 779 } 780 781 void 782 finalIOstats(void) 783 { 784 int i; 785 786 if (debug == 0) 787 return; 788 printf("Final I/O statistics\n"); 789 totaldiskreads += diskreads; 790 diskreads = totaldiskreads; 791 startpass = startprog; 792 for (i = 0; i < BT_NUMBUFTYPES; i++) { 793 timespecadd(&totalreadtime[i], &readtime[i], &totalreadtime[i]); 794 totalreadcnt[i] += readcnt[i]; 795 readtime[i] = totalreadtime[i]; 796 readcnt[i] = totalreadcnt[i]; 797 } 798 printIOstats(); 799 } 800 801 static void printIOstats(void) 802 { 803 long long msec, totalmsec; 804 int i; 805 806 clock_gettime(CLOCK_REALTIME_PRECISE, &finishpass); 807 timespecsub(&finishpass, &startpass, &finishpass); 808 printf("Running time: %jd.%03ld sec\n", 809 (intmax_t)finishpass.tv_sec, finishpass.tv_nsec / 1000000); 810 printf("buffer reads by type:\n"); 811 for (totalmsec = 0, i = 0; i < BT_NUMBUFTYPES; i++) 812 totalmsec += readtime[i].tv_sec * 1000 + 813 readtime[i].tv_nsec / 1000000; 814 if (totalmsec == 0) 815 totalmsec = 1; 816 for (i = 0; i < BT_NUMBUFTYPES; i++) { 817 if (readcnt[i] == 0) 818 continue; 819 msec = 820 readtime[i].tv_sec * 1000 + readtime[i].tv_nsec / 1000000; 821 printf("%21s:%8ld %2ld.%ld%% %4jd.%03ld sec %2lld.%lld%%\n", 822 buftype[i], readcnt[i], readcnt[i] * 100 / diskreads, 823 (readcnt[i] * 1000 / diskreads) % 10, 824 (intmax_t)readtime[i].tv_sec, readtime[i].tv_nsec / 1000000, 825 msec * 100 / totalmsec, (msec * 1000 / totalmsec) % 10); 826 } 827 printf("\n"); 828 } 829 830 int 831 blread(int fd, char *buf, ufs2_daddr_t blk, long size) 832 { 833 char *cp; 834 int i, errs; 835 off_t offset; 836 837 offset = blk; 838 offset *= dev_bsize; 839 if (bkgrdflag) 840 slowio_start(); 841 totalreads++; 842 diskreads++; 843 if (pread(fd, buf, (int)size, offset) == size) { 844 if (bkgrdflag) 845 slowio_end(); 846 return (0); 847 } 848 849 /* 850 * This is handled specially here instead of in rwerror because 851 * rwerror is used for all sorts of errors, not just true read/write 852 * errors. It should be refactored and fixed. 853 */ 854 if (surrender) { 855 pfatal("CANNOT READ_BLK: %ld", (long)blk); 856 errx(EEXIT, "ABORTING DUE TO READ ERRORS"); 857 } else 858 rwerror("READ BLK", blk); 859 860 errs = 0; 861 memset(buf, 0, (size_t)size); 862 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 863 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 864 if (pread(fd, cp, (int)secsize, offset + i) != secsize) { 865 if (secsize != dev_bsize && dev_bsize != 1) 866 printf(" %jd (%jd),", 867 (intmax_t)(blk * dev_bsize + i) / secsize, 868 (intmax_t)blk + i / dev_bsize); 869 else 870 printf(" %jd,", (intmax_t)blk + i / dev_bsize); 871 errs++; 872 } 873 } 874 printf("\n"); 875 if (errs) 876 resolved = 0; 877 return (errs); 878 } 879 880 void 881 blwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size) 882 { 883 int i; 884 char *cp; 885 off_t offset; 886 887 if (fd < 0) 888 return; 889 offset = blk; 890 offset *= dev_bsize; 891 if (pwrite(fd, buf, size, offset) == size) { 892 fsmodified = 1; 893 return; 894 } 895 resolved = 0; 896 rwerror("WRITE BLK", blk); 897 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 898 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 899 if (pwrite(fd, cp, dev_bsize, offset + i) != dev_bsize) 900 printf(" %jd,", (intmax_t)blk + i / dev_bsize); 901 printf("\n"); 902 return; 903 } 904 905 void 906 blerase(int fd, ufs2_daddr_t blk, long size) 907 { 908 off_t ioarg[2]; 909 910 if (fd < 0) 911 return; 912 ioarg[0] = blk * dev_bsize; 913 ioarg[1] = size; 914 ioctl(fd, DIOCGDELETE, ioarg); 915 /* we don't really care if we succeed or not */ 916 return; 917 } 918 919 /* 920 * Fill a contiguous region with all-zeroes. Note ZEROBUFSIZE is by 921 * definition a multiple of dev_bsize. 922 */ 923 void 924 blzero(int fd, ufs2_daddr_t blk, long size) 925 { 926 static char *zero; 927 off_t offset, len; 928 929 if (fd < 0) 930 return; 931 if (zero == NULL) { 932 zero = calloc(ZEROBUFSIZE, 1); 933 if (zero == NULL) 934 errx(EEXIT, "cannot allocate buffer pool"); 935 } 936 offset = blk * dev_bsize; 937 if (lseek(fd, offset, 0) < 0) 938 rwerror("SEEK BLK", blk); 939 while (size > 0) { 940 len = MIN(ZEROBUFSIZE, size); 941 if (write(fd, zero, len) != len) 942 rwerror("WRITE BLK", blk); 943 blk += len / dev_bsize; 944 size -= len; 945 } 946 } 947 948 /* 949 * Verify cylinder group's magic number and other parameters. If the 950 * test fails, offer an option to rebuild the whole cylinder group. 951 */ 952 #undef CHK 953 #define CHK(lhs, op, rhs, fmt) \ 954 if (lhs op rhs) { \ 955 pwarn("UFS%d cylinder group %d failed: " \ 956 "%s (" #fmt ") %s %s (" #fmt ")\n", \ 957 sblock.fs_magic == FS_UFS1_MAGIC ? 1 : 2, cg, \ 958 #lhs, (intmax_t)lhs, #op, #rhs, (intmax_t)rhs); \ 959 error = 1; \ 960 } 961 int 962 check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild) 963 { 964 struct cg *cgp = cgbp->b_un.b_cg; 965 uint32_t cghash, calchash; 966 static int prevfailcg = -1; 967 int error; 968 969 /* 970 * Extended cylinder group checks. 971 */ 972 calchash = cgp->cg_ckhash; 973 if ((sblock.fs_metackhash & CK_CYLGRP) != 0 && 974 (ckhashadd & CK_CYLGRP) == 0) { 975 cghash = cgp->cg_ckhash; 976 cgp->cg_ckhash = 0; 977 calchash = calculate_crc32c(~0L, (void *)cgp, sblock.fs_cgsize); 978 cgp->cg_ckhash = cghash; 979 } 980 error = 0; 981 CHK(cgp->cg_ckhash, !=, calchash, "%jd"); 982 CHK(cg_chkmagic(cgp), ==, 0, "%jd"); 983 CHK(cgp->cg_cgx, !=, cg, "%jd"); 984 CHK(cgp->cg_ndblk, >, sblock.fs_fpg, "%jd"); 985 if (sblock.fs_magic == FS_UFS1_MAGIC) { 986 CHK(cgp->cg_old_niblk, !=, sblock.fs_ipg, "%jd"); 987 CHK(cgp->cg_old_ncyl, >, sblock.fs_old_cpg, "%jd"); 988 } else if (sblock.fs_magic == FS_UFS2_MAGIC) { 989 CHK(cgp->cg_niblk, !=, sblock.fs_ipg, "%jd"); 990 CHK(cgp->cg_initediblk, >, sblock.fs_ipg, "%jd"); 991 } 992 if (error == 0) 993 return (1); 994 if (prevfailcg == cg) 995 return (0); 996 prevfailcg = cg; 997 pfatal("CYLINDER GROUP %d: INTEGRITY CHECK FAILED", cg); 998 if (!request_rebuild) { 999 printf("\n"); 1000 return (0); 1001 } 1002 if (!reply("REBUILD CYLINDER GROUP")) { 1003 printf("YOU WILL NEED TO RERUN FSCK.\n"); 1004 rerun = 1; 1005 return (1); 1006 } 1007 /* 1008 * Zero out the cylinder group and then initialize critical fields. 1009 * Bit maps and summaries will be recalculated by later passes. 1010 */ 1011 memset(cgp, 0, (size_t)sblock.fs_cgsize); 1012 cgp->cg_magic = CG_MAGIC; 1013 cgp->cg_cgx = cg; 1014 cgp->cg_niblk = sblock.fs_ipg; 1015 cgp->cg_initediblk = MIN(sblock.fs_ipg, 2 * INOPB(&sblock)); 1016 if (cgbase(&sblock, cg) + sblock.fs_fpg < sblock.fs_size) 1017 cgp->cg_ndblk = sblock.fs_fpg; 1018 else 1019 cgp->cg_ndblk = sblock.fs_size - cgbase(&sblock, cg); 1020 cgp->cg_iusedoff = &cgp->cg_space[0] - (u_char *)(&cgp->cg_firstfield); 1021 if (sblock.fs_magic == FS_UFS1_MAGIC) { 1022 cgp->cg_niblk = 0; 1023 cgp->cg_initediblk = 0; 1024 cgp->cg_old_ncyl = sblock.fs_old_cpg; 1025 cgp->cg_old_niblk = sblock.fs_ipg; 1026 cgp->cg_old_btotoff = cgp->cg_iusedoff; 1027 cgp->cg_old_boff = cgp->cg_old_btotoff + 1028 sblock.fs_old_cpg * sizeof(int32_t); 1029 cgp->cg_iusedoff = cgp->cg_old_boff + 1030 sblock.fs_old_cpg * sizeof(u_int16_t); 1031 } 1032 cgp->cg_freeoff = cgp->cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT); 1033 cgp->cg_nextfreeoff = cgp->cg_freeoff + howmany(sblock.fs_fpg,CHAR_BIT); 1034 if (sblock.fs_contigsumsize > 0) { 1035 cgp->cg_nclusterblks = cgp->cg_ndblk / sblock.fs_frag; 1036 cgp->cg_clustersumoff = 1037 roundup(cgp->cg_nextfreeoff, sizeof(u_int32_t)); 1038 cgp->cg_clustersumoff -= sizeof(u_int32_t); 1039 cgp->cg_clusteroff = cgp->cg_clustersumoff + 1040 (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t); 1041 cgp->cg_nextfreeoff = cgp->cg_clusteroff + 1042 howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT); 1043 } 1044 cgp->cg_ckhash = calculate_crc32c(~0L, (void *)cgp, sblock.fs_cgsize); 1045 cgdirty(cgbp); 1046 return (0); 1047 } 1048 1049 /* 1050 * allocate a data block with the specified number of fragments 1051 */ 1052 ufs2_daddr_t 1053 allocblk(long frags) 1054 { 1055 int i, j, k, cg, baseblk; 1056 struct bufarea *cgbp; 1057 struct cg *cgp; 1058 1059 if (frags <= 0 || frags > sblock.fs_frag) 1060 return (0); 1061 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 1062 for (j = 0; j <= sblock.fs_frag - frags; j++) { 1063 if (testbmap(i + j)) 1064 continue; 1065 for (k = 1; k < frags; k++) 1066 if (testbmap(i + j + k)) 1067 break; 1068 if (k < frags) { 1069 j += k; 1070 continue; 1071 } 1072 cg = dtog(&sblock, i + j); 1073 cgbp = cglookup(cg); 1074 cgp = cgbp->b_un.b_cg; 1075 if (!check_cgmagic(cg, cgbp, 0)) { 1076 i = (cg + 1) * sblock.fs_fpg - sblock.fs_frag; 1077 continue; 1078 } 1079 baseblk = dtogd(&sblock, i + j); 1080 for (k = 0; k < frags; k++) { 1081 setbmap(i + j + k); 1082 clrbit(cg_blksfree(cgp), baseblk + k); 1083 } 1084 n_blks += frags; 1085 if (frags == sblock.fs_frag) 1086 cgp->cg_cs.cs_nbfree--; 1087 else 1088 cgp->cg_cs.cs_nffree -= frags; 1089 cgdirty(cgbp); 1090 return (i + j); 1091 } 1092 } 1093 return (0); 1094 } 1095 1096 /* 1097 * Slow down IO so as to leave some disk bandwidth for other processes 1098 */ 1099 void 1100 slowio_start() 1101 { 1102 1103 /* Delay one in every 8 operations */ 1104 slowio_pollcnt = (slowio_pollcnt + 1) & 7; 1105 if (slowio_pollcnt == 0) { 1106 gettimeofday(&slowio_starttime, NULL); 1107 } 1108 } 1109 1110 void 1111 slowio_end() 1112 { 1113 struct timeval tv; 1114 int delay_usec; 1115 1116 if (slowio_pollcnt != 0) 1117 return; 1118 1119 /* Update the slowdown interval. */ 1120 gettimeofday(&tv, NULL); 1121 delay_usec = (tv.tv_sec - slowio_starttime.tv_sec) * 1000000 + 1122 (tv.tv_usec - slowio_starttime.tv_usec); 1123 if (delay_usec < 64) 1124 delay_usec = 64; 1125 if (delay_usec > 2500000) 1126 delay_usec = 2500000; 1127 slowio_delay_usec = (slowio_delay_usec * 63 + delay_usec) >> 6; 1128 /* delay by 8 times the average IO delay */ 1129 if (slowio_delay_usec > 64) 1130 usleep(slowio_delay_usec * 8); 1131 } 1132 1133 /* 1134 * Find a pathname 1135 */ 1136 void 1137 getpathname(char *namebuf, ino_t curdir, ino_t ino) 1138 { 1139 int len; 1140 char *cp; 1141 struct inode ip; 1142 struct inodesc idesc; 1143 static int busy = 0; 1144 1145 if (curdir == ino && ino == UFS_ROOTINO) { 1146 (void)strcpy(namebuf, "/"); 1147 return; 1148 } 1149 if (busy || !INO_IS_DVALID(curdir)) { 1150 (void)strcpy(namebuf, "?"); 1151 return; 1152 } 1153 busy = 1; 1154 memset(&idesc, 0, sizeof(struct inodesc)); 1155 idesc.id_type = DATA; 1156 idesc.id_fix = IGNORE; 1157 cp = &namebuf[MAXPATHLEN - 1]; 1158 *cp = '\0'; 1159 if (curdir != ino) { 1160 idesc.id_parent = curdir; 1161 goto namelookup; 1162 } 1163 while (ino != UFS_ROOTINO) { 1164 idesc.id_number = ino; 1165 idesc.id_func = findino; 1166 idesc.id_name = strdup(".."); 1167 ginode(ino, &ip); 1168 if ((ckinode(ip.i_dp, &idesc) & FOUND) == 0) { 1169 irelse(&ip); 1170 break; 1171 } 1172 irelse(&ip); 1173 namelookup: 1174 idesc.id_number = idesc.id_parent; 1175 idesc.id_parent = ino; 1176 idesc.id_func = findname; 1177 idesc.id_name = namebuf; 1178 ginode(idesc.id_number, &ip); 1179 if ((ckinode(ip.i_dp, &idesc) & FOUND) == 0) { 1180 irelse(&ip); 1181 break; 1182 } 1183 irelse(&ip); 1184 len = strlen(namebuf); 1185 cp -= len; 1186 memmove(cp, namebuf, (size_t)len); 1187 *--cp = '/'; 1188 if (cp < &namebuf[UFS_MAXNAMLEN]) 1189 break; 1190 ino = idesc.id_number; 1191 } 1192 busy = 0; 1193 if (ino != UFS_ROOTINO) 1194 *--cp = '?'; 1195 memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); 1196 } 1197 1198 void 1199 catch(int sig __unused) 1200 { 1201 1202 ckfini(0); 1203 exit(12); 1204 } 1205 1206 /* 1207 * When preening, allow a single quit to signal 1208 * a special exit after file system checks complete 1209 * so that reboot sequence may be interrupted. 1210 */ 1211 void 1212 catchquit(int sig __unused) 1213 { 1214 printf("returning to single-user after file system check\n"); 1215 returntosingle = 1; 1216 (void)signal(SIGQUIT, SIG_DFL); 1217 } 1218 1219 /* 1220 * determine whether an inode should be fixed. 1221 */ 1222 int 1223 dofix(struct inodesc *idesc, const char *msg) 1224 { 1225 1226 switch (idesc->id_fix) { 1227 1228 case DONTKNOW: 1229 if (idesc->id_type == DATA) 1230 direrror(idesc->id_number, msg); 1231 else 1232 pwarn("%s", msg); 1233 if (preen) { 1234 printf(" (SALVAGED)\n"); 1235 idesc->id_fix = FIX; 1236 return (ALTERED); 1237 } 1238 if (reply("SALVAGE") == 0) { 1239 idesc->id_fix = NOFIX; 1240 return (0); 1241 } 1242 idesc->id_fix = FIX; 1243 return (ALTERED); 1244 1245 case FIX: 1246 return (ALTERED); 1247 1248 case NOFIX: 1249 case IGNORE: 1250 return (0); 1251 1252 default: 1253 errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix); 1254 } 1255 /* NOTREACHED */ 1256 return (0); 1257 } 1258 1259 #include <stdarg.h> 1260 1261 /* 1262 * Print details about a buffer. 1263 */ 1264 static void 1265 prtbuf(const char *msg, struct bufarea *bp) 1266 { 1267 1268 printf("%s: bp %p, type %s, bno %jd, size %d, refcnt %d, flags %s, " 1269 "index %jd\n", msg, bp, BT_BUFTYPE(bp->b_type), (intmax_t) bp->b_bno, 1270 bp->b_size, bp->b_refcnt, bp->b_flags & B_DIRTY ? "dirty" : "clean", 1271 (intmax_t) bp->b_index); 1272 } 1273 1274 /* 1275 * An unexpected inconsistency occurred. 1276 * Die if preening or file system is running with soft dependency protocol, 1277 * otherwise just print message and continue. 1278 */ 1279 void 1280 pfatal(const char *fmt, ...) 1281 { 1282 va_list ap; 1283 va_start(ap, fmt); 1284 if (!preen) { 1285 (void)vfprintf(stdout, fmt, ap); 1286 va_end(ap); 1287 if (usedsoftdep) 1288 (void)fprintf(stdout, 1289 "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n"); 1290 /* 1291 * Force foreground fsck to clean up inconsistency. 1292 */ 1293 if (bkgrdflag) { 1294 cmd.value = FS_NEEDSFSCK; 1295 cmd.size = 1; 1296 if (sysctlbyname("vfs.ffs.setflags", 0, 0, 1297 &cmd, sizeof cmd) == -1) 1298 pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n"); 1299 fprintf(stdout, "CANNOT RUN IN BACKGROUND\n"); 1300 ckfini(0); 1301 exit(EEXIT); 1302 } 1303 return; 1304 } 1305 if (cdevname == NULL) 1306 cdevname = strdup("fsck"); 1307 (void)fprintf(stdout, "%s: ", cdevname); 1308 (void)vfprintf(stdout, fmt, ap); 1309 (void)fprintf(stdout, 1310 "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n", 1311 cdevname, usedsoftdep ? " SOFT UPDATE " : " "); 1312 /* 1313 * Force foreground fsck to clean up inconsistency. 1314 */ 1315 if (bkgrdflag) { 1316 cmd.value = FS_NEEDSFSCK; 1317 cmd.size = 1; 1318 if (sysctlbyname("vfs.ffs.setflags", 0, 0, 1319 &cmd, sizeof cmd) == -1) 1320 pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n"); 1321 } 1322 ckfini(0); 1323 exit(EEXIT); 1324 } 1325 1326 /* 1327 * Pwarn just prints a message when not preening or running soft dependency 1328 * protocol, or a warning (preceded by filename) when preening. 1329 */ 1330 void 1331 pwarn(const char *fmt, ...) 1332 { 1333 va_list ap; 1334 va_start(ap, fmt); 1335 if (preen) 1336 (void)fprintf(stdout, "%s: ", cdevname); 1337 (void)vfprintf(stdout, fmt, ap); 1338 va_end(ap); 1339 } 1340 1341 /* 1342 * Stub for routines from kernel. 1343 */ 1344 void 1345 panic(const char *fmt, ...) 1346 { 1347 va_list ap; 1348 va_start(ap, fmt); 1349 pfatal("INTERNAL INCONSISTENCY:"); 1350 (void)vfprintf(stdout, fmt, ap); 1351 va_end(ap); 1352 exit(EEXIT); 1353 } 1354