18a16b7a1SPedro F. Giffuni /*- 28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 38a16b7a1SPedro F. Giffuni * 44336716bSAdrian Chadd * Copyright (c) 1980, 1986, 1993 54336716bSAdrian Chadd * The Regents of the University of California. All rights reserved. 64336716bSAdrian Chadd * 74336716bSAdrian Chadd * Redistribution and use in source and binary forms, with or without 84336716bSAdrian Chadd * modification, are permitted provided that the following conditions 94336716bSAdrian Chadd * are met: 104336716bSAdrian Chadd * 1. Redistributions of source code must retain the above copyright 114336716bSAdrian Chadd * notice, this list of conditions and the following disclaimer. 124336716bSAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 134336716bSAdrian Chadd * notice, this list of conditions and the following disclaimer in the 144336716bSAdrian Chadd * documentation and/or other materials provided with the distribution. 15fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 164336716bSAdrian Chadd * may be used to endorse or promote products derived from this software 174336716bSAdrian Chadd * without specific prior written permission. 184336716bSAdrian Chadd * 194336716bSAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 204336716bSAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 214336716bSAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 224336716bSAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 234336716bSAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 244336716bSAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 254336716bSAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 264336716bSAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 274336716bSAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 284336716bSAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 294336716bSAdrian Chadd * SUCH DAMAGE. 304336716bSAdrian Chadd */ 314336716bSAdrian Chadd 324336716bSAdrian Chadd #if 0 33c69284caSDavid E. O'Brien #ifndef lint 344336716bSAdrian Chadd static const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95"; 354336716bSAdrian Chadd #endif /* not lint */ 36c69284caSDavid E. O'Brien #endif 37c69284caSDavid E. O'Brien #include <sys/cdefs.h> 38c69284caSDavid E. O'Brien __FBSDID("$FreeBSD$"); 394336716bSAdrian Chadd 404336716bSAdrian Chadd #include <sys/param.h> 419d580d7cSIan Dowse #include <sys/time.h> 424336716bSAdrian Chadd #include <sys/types.h> 43bf58d635SIan Dowse #include <sys/sysctl.h> 448d3dfc26SDag-Erling Smørgrav #include <sys/disk.h> 451c85e6a3SKirk McKusick #include <sys/disklabel.h> 468d3dfc26SDag-Erling Smørgrav #include <sys/ioctl.h> 474336716bSAdrian Chadd #include <sys/stat.h> 484336716bSAdrian Chadd 494336716bSAdrian Chadd #include <ufs/ufs/dinode.h> 504336716bSAdrian Chadd #include <ufs/ufs/dir.h> 514336716bSAdrian Chadd #include <ufs/ffs/fs.h> 524336716bSAdrian Chadd 534336716bSAdrian Chadd #include <err.h> 544336716bSAdrian Chadd #include <errno.h> 554336716bSAdrian Chadd #include <string.h> 564336716bSAdrian Chadd #include <ctype.h> 574336716bSAdrian Chadd #include <fstab.h> 5884fc0d7eSMaxime Henrion #include <stdint.h> 594336716bSAdrian Chadd #include <stdio.h> 604336716bSAdrian Chadd #include <stdlib.h> 61ed75b5a1SKirk McKusick #include <time.h> 624336716bSAdrian Chadd #include <unistd.h> 6375e3597aSKirk McKusick #include <libufs.h> 644336716bSAdrian Chadd 654336716bSAdrian Chadd #include "fsck.h" 664336716bSAdrian Chadd 67*5cc52631SKirk McKusick int sujrecovery = 0; 68*5cc52631SKirk McKusick 69*5cc52631SKirk McKusick static struct bufarea *allocbuf(const char *); 70*5cc52631SKirk McKusick static void cg_write(struct bufarea *); 719d580d7cSIan Dowse static void slowio_start(void); 729d580d7cSIan Dowse static void slowio_end(void); 73ed75b5a1SKirk McKusick static void printIOstats(void); 74*5cc52631SKirk McKusick static void prtbuf(const char *, struct bufarea *); 759d580d7cSIan Dowse 76ed75b5a1SKirk McKusick static long diskreads, totaldiskreads, totalreads; /* Disk cache statistics */ 77ed75b5a1SKirk McKusick static struct timespec startpass, finishpass; 789d580d7cSIan Dowse struct timeval slowio_starttime; 799d580d7cSIan Dowse int slowio_delay_usec = 10000; /* Initial IO delay for background fsck */ 809d580d7cSIan Dowse int slowio_pollcnt; 8181fbded2SKirk McKusick static struct bufarea cgblk; /* backup buffer for cylinder group blocks */ 82*5cc52631SKirk McKusick static TAILQ_HEAD(bufqueue, bufarea) bufqueuehd; /* head of buffer cache LRU */ 83*5cc52631SKirk McKusick static LIST_HEAD(bufhash, bufarea) bufhashhd[HASHSIZE]; /* buffer hash list */ 842ec5c914SKirk McKusick static int numbufs; /* size of buffer cache */ 85*5cc52631SKirk McKusick static int cachelookups; /* number of cache lookups */ 86*5cc52631SKirk McKusick static int cachereads; /* number of cache reads */ 877703a6ffSScott Long static struct bufarea *cgbufs; /* header for cylinder group cache */ 887703a6ffSScott Long static int flushtries; /* number of tries to reclaim memory */ 897703a6ffSScott Long 90*5cc52631SKirk McKusick char *buftype[BT_NUMBUFTYPES] = BT_NAMES; 91*5cc52631SKirk McKusick 927703a6ffSScott Long void 937703a6ffSScott Long fsutilinit(void) 947703a6ffSScott Long { 957703a6ffSScott Long diskreads = totaldiskreads = totalreads = 0; 967703a6ffSScott Long bzero(&startpass, sizeof(struct timespec)); 977703a6ffSScott Long bzero(&finishpass, sizeof(struct timespec)); 987703a6ffSScott Long bzero(&slowio_starttime, sizeof(struct timeval)); 997703a6ffSScott Long slowio_delay_usec = 10000; 1007703a6ffSScott Long slowio_pollcnt = 0; 1017703a6ffSScott Long flushtries = 0; 1027703a6ffSScott Long } 1034336716bSAdrian Chadd 1044336716bSAdrian Chadd int 1051c85e6a3SKirk McKusick ftypeok(union dinode *dp) 1064336716bSAdrian Chadd { 107d8ba45e2SEd Maste switch (DIP(dp, di_mode) & IFMT) { 1084336716bSAdrian Chadd 109d8ba45e2SEd Maste case IFDIR: 110d8ba45e2SEd Maste case IFREG: 111d8ba45e2SEd Maste case IFBLK: 112d8ba45e2SEd Maste case IFCHR: 113d8ba45e2SEd Maste case IFLNK: 114d8ba45e2SEd Maste case IFSOCK: 115d8ba45e2SEd Maste case IFIFO: 1164336716bSAdrian Chadd return (1); 1174336716bSAdrian Chadd 1184336716bSAdrian Chadd default: 1194336716bSAdrian Chadd if (debug) 1201c85e6a3SKirk McKusick printf("bad file type 0%o\n", DIP(dp, di_mode)); 1214336716bSAdrian Chadd return (0); 1224336716bSAdrian Chadd } 1234336716bSAdrian Chadd } 1244336716bSAdrian Chadd 1254336716bSAdrian Chadd int 126599304a4SPoul-Henning Kamp reply(const char *question) 1274336716bSAdrian Chadd { 1284336716bSAdrian Chadd int persevere; 1294336716bSAdrian Chadd char c; 1304336716bSAdrian Chadd 1314336716bSAdrian Chadd if (preen) 1324336716bSAdrian Chadd pfatal("INTERNAL ERROR: GOT TO reply()"); 1334336716bSAdrian Chadd persevere = !strcmp(question, "CONTINUE"); 1344336716bSAdrian Chadd printf("\n"); 1357578c6abSKirk McKusick if (!persevere && (nflag || (fswritefd < 0 && bkgrdflag == 0))) { 1364336716bSAdrian Chadd printf("%s? no\n\n", question); 1374336716bSAdrian Chadd resolved = 0; 1384336716bSAdrian Chadd return (0); 1394336716bSAdrian Chadd } 1404336716bSAdrian Chadd if (yflag || (persevere && nflag)) { 1414336716bSAdrian Chadd printf("%s? yes\n\n", question); 1424336716bSAdrian Chadd return (1); 1434336716bSAdrian Chadd } 1444336716bSAdrian Chadd do { 1454336716bSAdrian Chadd printf("%s? [yn] ", question); 1464336716bSAdrian Chadd (void) fflush(stdout); 1474336716bSAdrian Chadd c = getc(stdin); 1484336716bSAdrian Chadd while (c != '\n' && getc(stdin) != '\n') { 1494336716bSAdrian Chadd if (feof(stdin)) { 1504336716bSAdrian Chadd resolved = 0; 1514336716bSAdrian Chadd return (0); 1524336716bSAdrian Chadd } 1534336716bSAdrian Chadd } 1544336716bSAdrian Chadd } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 1554336716bSAdrian Chadd printf("\n"); 1564336716bSAdrian Chadd if (c == 'y' || c == 'Y') 1574336716bSAdrian Chadd return (1); 1584336716bSAdrian Chadd resolved = 0; 1594336716bSAdrian Chadd return (0); 1604336716bSAdrian Chadd } 1614336716bSAdrian Chadd 1624336716bSAdrian Chadd /* 1634336716bSAdrian Chadd * Look up state information for an inode. 1644336716bSAdrian Chadd */ 1654336716bSAdrian Chadd struct inostat * 166b70cd7eeSWarner Losh inoinfo(ino_t inum) 1674336716bSAdrian Chadd { 1684336716bSAdrian Chadd static struct inostat unallocated = { USTATE, 0, 0 }; 1694336716bSAdrian Chadd struct inostatlist *ilp; 1704336716bSAdrian Chadd int iloff; 1714336716bSAdrian Chadd 1724336716bSAdrian Chadd if (inum > maxino) 173623d7cb6SMatthew D Fleming errx(EEXIT, "inoinfo: inumber %ju out of range", 174623d7cb6SMatthew D Fleming (uintmax_t)inum); 1754336716bSAdrian Chadd ilp = &inostathead[inum / sblock.fs_ipg]; 1764336716bSAdrian Chadd iloff = inum % sblock.fs_ipg; 1774336716bSAdrian Chadd if (iloff >= ilp->il_numalloced) 1784336716bSAdrian Chadd return (&unallocated); 1794336716bSAdrian Chadd return (&ilp->il_stat[iloff]); 1804336716bSAdrian Chadd } 1814336716bSAdrian Chadd 1824336716bSAdrian Chadd /* 1834336716bSAdrian Chadd * Malloc buffers and set up cache. 1844336716bSAdrian Chadd */ 1854336716bSAdrian Chadd void 186b70cd7eeSWarner Losh bufinit(void) 1874336716bSAdrian Chadd { 188*5cc52631SKirk McKusick int i; 1894336716bSAdrian Chadd 190*5cc52631SKirk McKusick pdirbp = (struct bufarea *)0; 191*5cc52631SKirk McKusick bzero(&cgblk, sizeof(struct bufarea)); 192*5cc52631SKirk McKusick cgblk.b_un.b_buf = Malloc((unsigned int)sblock.fs_bsize); 193*5cc52631SKirk McKusick if (cgblk.b_un.b_buf == NULL) 194*5cc52631SKirk McKusick errx(EEXIT, "Initial malloc(%d) failed", sblock.fs_bsize); 195ed75b5a1SKirk McKusick initbarea(&cgblk, BT_CYLGRP); 196*5cc52631SKirk McKusick cgbufs = NULL; 197*5cc52631SKirk McKusick numbufs = cachelookups = cachereads = 0; 198*5cc52631SKirk McKusick TAILQ_INIT(&bufqueuehd); 199*5cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) 200*5cc52631SKirk McKusick LIST_INIT(&bufhashhd[i]); 201ed75b5a1SKirk McKusick for (i = 0; i < BT_NUMBUFTYPES; i++) { 202ed75b5a1SKirk McKusick readtime[i].tv_sec = totalreadtime[i].tv_sec = 0; 203ed75b5a1SKirk McKusick readtime[i].tv_nsec = totalreadtime[i].tv_nsec = 0; 204ed75b5a1SKirk McKusick readcnt[i] = totalreadcnt[i] = 0; 205ed75b5a1SKirk McKusick } 2064336716bSAdrian Chadd } 2074336716bSAdrian Chadd 208*5cc52631SKirk McKusick static struct bufarea * 209*5cc52631SKirk McKusick allocbuf(const char *failreason) 210*5cc52631SKirk McKusick { 211*5cc52631SKirk McKusick struct bufarea *bp; 212*5cc52631SKirk McKusick char *bufp; 213*5cc52631SKirk McKusick 214*5cc52631SKirk McKusick bp = (struct bufarea *)Malloc(sizeof(struct bufarea)); 215*5cc52631SKirk McKusick bufp = Malloc((unsigned int)sblock.fs_bsize); 216*5cc52631SKirk McKusick if (bp == NULL || bufp == NULL) { 217*5cc52631SKirk McKusick errx(EEXIT, "%s", failreason); 218*5cc52631SKirk McKusick /* NOTREACHED */ 219*5cc52631SKirk McKusick } 220*5cc52631SKirk McKusick numbufs++; 221*5cc52631SKirk McKusick bp->b_un.b_buf = bufp; 222*5cc52631SKirk McKusick TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list); 223*5cc52631SKirk McKusick initbarea(bp, BT_UNKNOWN); 224*5cc52631SKirk McKusick return (bp); 225*5cc52631SKirk McKusick } 226*5cc52631SKirk McKusick 2274336716bSAdrian Chadd /* 22881fbded2SKirk McKusick * Manage cylinder group buffers. 22985ee267aSKirk McKusick * 23085ee267aSKirk McKusick * Use getblk() here rather than cgget() because the cylinder group 23185ee267aSKirk McKusick * may be corrupted but we want it anyway so we can fix it. 23281fbded2SKirk McKusick */ 23381fbded2SKirk McKusick static struct bufarea *cgbufs; /* header for cylinder group cache */ 23481fbded2SKirk McKusick static int flushtries; /* number of tries to reclaim memory */ 23581fbded2SKirk McKusick 23681fbded2SKirk McKusick struct bufarea * 237957fc241SKirk McKusick cglookup(int cg) 23881fbded2SKirk McKusick { 23981fbded2SKirk McKusick struct bufarea *cgbp; 24081fbded2SKirk McKusick struct cg *cgp; 24181fbded2SKirk McKusick 242*5cc52631SKirk McKusick if ((unsigned) cg >= sblock.fs_ncg) 243*5cc52631SKirk McKusick errx(EEXIT, "cglookup: out of range cylinder group %d", cg); 24481fbded2SKirk McKusick if (cgbufs == NULL) { 245eff68496SKirk McKusick cgbufs = calloc(sblock.fs_ncg, sizeof(struct bufarea)); 24681fbded2SKirk McKusick if (cgbufs == NULL) 247*5cc52631SKirk McKusick errx(EEXIT, "Cannot allocate cylinder group buffers"); 24881fbded2SKirk McKusick } 24981fbded2SKirk McKusick cgbp = &cgbufs[cg]; 25081fbded2SKirk McKusick if (cgbp->b_un.b_cg != NULL) 25181fbded2SKirk McKusick return (cgbp); 25281fbded2SKirk McKusick cgp = NULL; 25381fbded2SKirk McKusick if (flushtries == 0) 254*5cc52631SKirk McKusick cgp = Malloc((unsigned int)sblock.fs_cgsize); 25581fbded2SKirk McKusick if (cgp == NULL) { 256*5cc52631SKirk McKusick if (sujrecovery) 257*5cc52631SKirk McKusick errx(EEXIT,"Ran out of memory during journal recovery"); 25881fbded2SKirk McKusick getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); 25981fbded2SKirk McKusick return (&cgblk); 26081fbded2SKirk McKusick } 26181fbded2SKirk McKusick cgbp->b_un.b_cg = cgp; 26281fbded2SKirk McKusick initbarea(cgbp, BT_CYLGRP); 26381fbded2SKirk McKusick getblk(cgbp, cgtod(&sblock, cg), sblock.fs_cgsize); 26481fbded2SKirk McKusick return (cgbp); 26581fbded2SKirk McKusick } 26681fbded2SKirk McKusick 26781fbded2SKirk McKusick /* 2688ebae128SKirk McKusick * Mark a cylinder group buffer as dirty. 2698ebae128SKirk McKusick * Update its check-hash if they are enabled. 2708ebae128SKirk McKusick */ 2718ebae128SKirk McKusick void 2728ebae128SKirk McKusick cgdirty(struct bufarea *cgbp) 2738ebae128SKirk McKusick { 2748ebae128SKirk McKusick struct cg *cg; 2758ebae128SKirk McKusick 2768ebae128SKirk McKusick cg = cgbp->b_un.b_cg; 2778ebae128SKirk McKusick if ((sblock.fs_metackhash & CK_CYLGRP) != 0) { 2788ebae128SKirk McKusick cg->cg_ckhash = 0; 2798ebae128SKirk McKusick cg->cg_ckhash = 2808ebae128SKirk McKusick calculate_crc32c(~0L, (void *)cg, sblock.fs_cgsize); 2818ebae128SKirk McKusick } 2828ebae128SKirk McKusick dirty(cgbp); 2838ebae128SKirk McKusick } 2848ebae128SKirk McKusick 2858ebae128SKirk McKusick /* 28681fbded2SKirk McKusick * Attempt to flush a cylinder group cache entry. 28781fbded2SKirk McKusick * Return whether the flush was successful. 28881fbded2SKirk McKusick */ 28981fbded2SKirk McKusick int 29081fbded2SKirk McKusick flushentry(void) 29181fbded2SKirk McKusick { 29281fbded2SKirk McKusick struct bufarea *cgbp; 29381fbded2SKirk McKusick 294*5cc52631SKirk McKusick if (sujrecovery || flushtries == sblock.fs_ncg || cgbufs == NULL) 295eff68496SKirk McKusick return (0); 29681fbded2SKirk McKusick cgbp = &cgbufs[flushtries++]; 29781fbded2SKirk McKusick if (cgbp->b_un.b_cg == NULL) 29881fbded2SKirk McKusick return (0); 29981fbded2SKirk McKusick flush(fswritefd, cgbp); 30081fbded2SKirk McKusick free(cgbp->b_un.b_buf); 30181fbded2SKirk McKusick cgbp->b_un.b_buf = NULL; 30281fbded2SKirk McKusick return (1); 30381fbded2SKirk McKusick } 30481fbded2SKirk McKusick 30581fbded2SKirk McKusick /* 3064336716bSAdrian Chadd * Manage a cache of directory blocks. 3074336716bSAdrian Chadd */ 3084336716bSAdrian Chadd struct bufarea * 309ed75b5a1SKirk McKusick getdatablk(ufs2_daddr_t blkno, long size, int type) 3104336716bSAdrian Chadd { 3113d438ad6SDavid E. O'Brien struct bufarea *bp; 312*5cc52631SKirk McKusick struct bufhash *bhdp; 3134336716bSAdrian Chadd 314*5cc52631SKirk McKusick cachelookups++; 315*5cc52631SKirk McKusick /* If out of range, return empty buffer with b_err == -1 */ 316*5cc52631SKirk McKusick if (type != BT_INODES && chkrange(blkno, size / sblock.fs_fsize)) { 317*5cc52631SKirk McKusick blkno = -1; 318*5cc52631SKirk McKusick type = BT_EMPTY; 319*5cc52631SKirk McKusick } 320*5cc52631SKirk McKusick bhdp = &bufhashhd[HASH(blkno)]; 321*5cc52631SKirk McKusick LIST_FOREACH(bp, bhdp, b_hash) 322*5cc52631SKirk McKusick if (bp->b_bno == fsbtodb(&sblock, blkno)) { 323*5cc52631SKirk McKusick if (debug && bp->b_size != size) { 324*5cc52631SKirk McKusick prtbuf("getdatablk: size mismatch", bp); 325*5cc52631SKirk McKusick pfatal("getdatablk: b_size %d != size %ld\n", 326*5cc52631SKirk McKusick bp->b_size, size); 327*5cc52631SKirk McKusick } 3284336716bSAdrian Chadd goto foundit; 329*5cc52631SKirk McKusick } 330*5cc52631SKirk McKusick /* 331*5cc52631SKirk McKusick * Move long-term busy buffer back to the front of the LRU so we 332*5cc52631SKirk McKusick * do not endless inspect them for recycling. 333*5cc52631SKirk McKusick */ 334*5cc52631SKirk McKusick bp = TAILQ_LAST(&bufqueuehd, bufqueue); 335*5cc52631SKirk McKusick if (bp != NULL && bp->b_refcnt != 0) { 336*5cc52631SKirk McKusick TAILQ_REMOVE(&bufqueuehd, bp, b_list); 337*5cc52631SKirk McKusick TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list); 338*5cc52631SKirk McKusick } 339*5cc52631SKirk McKusick /* 340*5cc52631SKirk McKusick * Allocate up to the minimum number of buffers before 341*5cc52631SKirk McKusick * considering recycling any of them. 342*5cc52631SKirk McKusick */ 343*5cc52631SKirk McKusick if (size > sblock.fs_bsize) 344*5cc52631SKirk McKusick errx(EEXIT, "Excessive buffer size %ld > %d\n", size, 345*5cc52631SKirk McKusick sblock.fs_bsize); 346*5cc52631SKirk McKusick if (numbufs < MINBUFS) { 347*5cc52631SKirk McKusick bp = allocbuf("cannot create minimal buffer pool"); 348*5cc52631SKirk McKusick } else if (sujrecovery) { 349*5cc52631SKirk McKusick /* 350*5cc52631SKirk McKusick * SUJ recovery does not want anything written until it 351*5cc52631SKirk McKusick * has successfully completed (so it can fail back to 352*5cc52631SKirk McKusick * full fsck). Thus, we can only recycle clean buffers. 353*5cc52631SKirk McKusick */ 354*5cc52631SKirk McKusick TAILQ_FOREACH_REVERSE(bp, &bufqueuehd, bufqueue, b_list) 355*5cc52631SKirk McKusick if ((bp->b_flags & B_DIRTY) == 0 && bp->b_refcnt == 0) 3564336716bSAdrian Chadd break; 3572ec5c914SKirk McKusick if (bp == NULL) 358*5cc52631SKirk McKusick bp = allocbuf("Ran out of memory during " 359*5cc52631SKirk McKusick "journal recovery"); 360*5cc52631SKirk McKusick else 361*5cc52631SKirk McKusick LIST_REMOVE(bp, b_hash); 362*5cc52631SKirk McKusick } else { 363*5cc52631SKirk McKusick /* 364*5cc52631SKirk McKusick * Recycle oldest non-busy buffer. 365*5cc52631SKirk McKusick */ 366*5cc52631SKirk McKusick TAILQ_FOREACH_REVERSE(bp, &bufqueuehd, bufqueue, b_list) 367*5cc52631SKirk McKusick if (bp->b_refcnt == 0) 368*5cc52631SKirk McKusick break; 369*5cc52631SKirk McKusick if (bp == NULL) 370*5cc52631SKirk McKusick bp = allocbuf("Ran out of memory for buffers"); 371*5cc52631SKirk McKusick else 372*5cc52631SKirk McKusick LIST_REMOVE(bp, b_hash); 373*5cc52631SKirk McKusick } 374*5cc52631SKirk McKusick flush(fswritefd, bp); 375ed75b5a1SKirk McKusick bp->b_type = type; 376*5cc52631SKirk McKusick LIST_INSERT_HEAD(bhdp, bp, b_hash); 3774336716bSAdrian Chadd getblk(bp, blkno, size); 378*5cc52631SKirk McKusick cachereads++; 3794336716bSAdrian Chadd /* fall through */ 3804336716bSAdrian Chadd foundit: 381*5cc52631SKirk McKusick if (debug && bp->b_type != type) { 382*5cc52631SKirk McKusick printf("getdatablk: buffer type changed to %s", 383*5cc52631SKirk McKusick BT_BUFTYPE(type)); 384*5cc52631SKirk McKusick prtbuf("", bp); 385*5cc52631SKirk McKusick } 386*5cc52631SKirk McKusick TAILQ_REMOVE(&bufqueuehd, bp, b_list); 387*5cc52631SKirk McKusick TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list); 388*5cc52631SKirk McKusick if (bp->b_errs == 0) 389*5cc52631SKirk McKusick bp->b_refcnt++; 3904336716bSAdrian Chadd return (bp); 3914336716bSAdrian Chadd } 3924336716bSAdrian Chadd 3934336716bSAdrian Chadd void 3941c85e6a3SKirk McKusick getblk(struct bufarea *bp, ufs2_daddr_t blk, long size) 3954336716bSAdrian Chadd { 3961c85e6a3SKirk McKusick ufs2_daddr_t dblk; 397ed75b5a1SKirk McKusick struct timespec start, finish; 3984336716bSAdrian Chadd 3994336716bSAdrian Chadd dblk = fsbtodb(&sblock, blk); 400ed75b5a1SKirk McKusick if (bp->b_bno == dblk) { 401ed75b5a1SKirk McKusick totalreads++; 402ed75b5a1SKirk McKusick } else { 403ed75b5a1SKirk McKusick if (debug) { 404ed75b5a1SKirk McKusick readcnt[bp->b_type]++; 405ed75b5a1SKirk McKusick clock_gettime(CLOCK_REALTIME_PRECISE, &start); 406ed75b5a1SKirk McKusick } 407*5cc52631SKirk McKusick if (bp->b_type != BT_EMPTY) 408*5cc52631SKirk McKusick bp->b_errs = 409*5cc52631SKirk McKusick blread(fsreadfd, bp->b_un.b_buf, dblk, size); 410*5cc52631SKirk McKusick else 411*5cc52631SKirk McKusick bp->b_errs = -1; 412ed75b5a1SKirk McKusick if (debug) { 413ed75b5a1SKirk McKusick clock_gettime(CLOCK_REALTIME_PRECISE, &finish); 4146040822cSAlan Somers timespecsub(&finish, &start, &finish); 4156040822cSAlan Somers timespecadd(&readtime[bp->b_type], &finish, 4166040822cSAlan Somers &readtime[bp->b_type]); 417ed75b5a1SKirk McKusick } 4184336716bSAdrian Chadd bp->b_bno = dblk; 4194336716bSAdrian Chadd bp->b_size = size; 4204336716bSAdrian Chadd } 4214336716bSAdrian Chadd } 4224336716bSAdrian Chadd 4234336716bSAdrian Chadd void 424*5cc52631SKirk McKusick brelse(struct bufarea *bp) 4254336716bSAdrian Chadd { 4264336716bSAdrian Chadd 427*5cc52631SKirk McKusick if (bp->b_refcnt <= 0) 428*5cc52631SKirk McKusick prtbuf("brelse: buffer with negative reference count", bp); 429*5cc52631SKirk McKusick bp->b_refcnt--; 430*5cc52631SKirk McKusick } 431*5cc52631SKirk McKusick 432*5cc52631SKirk McKusick void 433*5cc52631SKirk McKusick flush(int fd, struct bufarea *bp) 434*5cc52631SKirk McKusick { 435*5cc52631SKirk McKusick struct inode ip; 436*5cc52631SKirk McKusick 437*5cc52631SKirk McKusick if ((bp->b_flags & B_DIRTY) == 0) 4384336716bSAdrian Chadd return; 439*5cc52631SKirk McKusick bp->b_flags &= ~B_DIRTY; 4407578c6abSKirk McKusick if (fswritefd < 0) { 4417578c6abSKirk McKusick pfatal("WRITING IN READ_ONLY MODE.\n"); 4427578c6abSKirk McKusick return; 4437578c6abSKirk McKusick } 4444336716bSAdrian Chadd if (bp->b_errs != 0) 445599304a4SPoul-Henning Kamp pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n", 4464336716bSAdrian Chadd (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 447599304a4SPoul-Henning Kamp (long long)bp->b_bno); 4484336716bSAdrian Chadd bp->b_errs = 0; 449a6bbdf81SKirk McKusick /* 450a6bbdf81SKirk McKusick * Write using the appropriate function. 451a6bbdf81SKirk McKusick */ 452a6bbdf81SKirk McKusick switch (bp->b_type) { 453a6bbdf81SKirk McKusick case BT_SUPERBLK: 4544336716bSAdrian Chadd if (bp != &sblk) 455af89fcf7SLi-Wen Hsu pfatal("BUFFER %p DOES NOT MATCH SBLK %p\n", 456af89fcf7SLi-Wen Hsu bp, &sblk); 4578ebae128SKirk McKusick if (sbput(fd, bp->b_un.b_fs, 0) == 0) 458dffce215SKirk McKusick fsmodified = 1; 459a6bbdf81SKirk McKusick break; 460a6bbdf81SKirk McKusick case BT_CYLGRP: 461*5cc52631SKirk McKusick if (sujrecovery) 462*5cc52631SKirk McKusick cg_write(bp); 46385ee267aSKirk McKusick if (cgput(fswritefd, &sblock, bp->b_un.b_cg) == 0) 464a6bbdf81SKirk McKusick fsmodified = 1; 465a6bbdf81SKirk McKusick break; 466*5cc52631SKirk McKusick case BT_INODES: 467*5cc52631SKirk McKusick if (debug && sblock.fs_magic == FS_UFS2_MAGIC) { 468*5cc52631SKirk McKusick struct ufs2_dinode *dp = bp->b_un.b_dinode2; 469*5cc52631SKirk McKusick int i; 470*5cc52631SKirk McKusick 471*5cc52631SKirk McKusick for (i = 0; i < INOPB(&sblock); dp++, i++) { 472*5cc52631SKirk McKusick if (ffs_verify_dinode_ckhash(&sblock, dp) == 0) 473*5cc52631SKirk McKusick continue; 474*5cc52631SKirk McKusick pwarn("flush: INODE CHECK-HASH FAILED"); 475*5cc52631SKirk McKusick ip.i_bp = bp; 476*5cc52631SKirk McKusick ip.i_dp = (union dinode *)dp; 477*5cc52631SKirk McKusick ip.i_number = bp->b_index + i; 478*5cc52631SKirk McKusick prtinode(&ip); 479*5cc52631SKirk McKusick if (preen || reply("FIX") != 0) { 480*5cc52631SKirk McKusick if (preen) 481*5cc52631SKirk McKusick printf(" (FIXED)\n"); 482*5cc52631SKirk McKusick ffs_update_dinode_ckhash(&sblock, dp); 483*5cc52631SKirk McKusick inodirty(&ip); 484*5cc52631SKirk McKusick } 485*5cc52631SKirk McKusick } 486*5cc52631SKirk McKusick } 487*5cc52631SKirk McKusick /* FALLTHROUGH */ 488a6bbdf81SKirk McKusick default: 489a6bbdf81SKirk McKusick blwrite(fd, bp->b_un.b_buf, bp->b_bno, bp->b_size); 490a6bbdf81SKirk McKusick break; 491a6bbdf81SKirk McKusick } 4924336716bSAdrian Chadd } 4934336716bSAdrian Chadd 494*5cc52631SKirk McKusick /* 495*5cc52631SKirk McKusick * Journaled soft updates does not maintain cylinder group summary 496*5cc52631SKirk McKusick * information during cleanup, so this routine recalculates the summary 497*5cc52631SKirk McKusick * information and updates the superblock summary in preparation for 498*5cc52631SKirk McKusick * writing out the cylinder group. 499*5cc52631SKirk McKusick */ 500*5cc52631SKirk McKusick static void 501*5cc52631SKirk McKusick cg_write(struct bufarea *bp) 502*5cc52631SKirk McKusick { 503*5cc52631SKirk McKusick ufs1_daddr_t fragno, cgbno, maxbno; 504*5cc52631SKirk McKusick u_int8_t *blksfree; 505*5cc52631SKirk McKusick struct cg *cgp; 506*5cc52631SKirk McKusick int blk; 507*5cc52631SKirk McKusick int i; 508*5cc52631SKirk McKusick 509*5cc52631SKirk McKusick /* 510*5cc52631SKirk McKusick * Fix the frag and cluster summary. 511*5cc52631SKirk McKusick */ 512*5cc52631SKirk McKusick cgp = bp->b_un.b_cg; 513*5cc52631SKirk McKusick cgp->cg_cs.cs_nbfree = 0; 514*5cc52631SKirk McKusick cgp->cg_cs.cs_nffree = 0; 515*5cc52631SKirk McKusick bzero(&cgp->cg_frsum, sizeof(cgp->cg_frsum)); 516*5cc52631SKirk McKusick maxbno = fragstoblks(&sblock, sblock.fs_fpg); 517*5cc52631SKirk McKusick if (sblock.fs_contigsumsize > 0) { 518*5cc52631SKirk McKusick for (i = 1; i <= sblock.fs_contigsumsize; i++) 519*5cc52631SKirk McKusick cg_clustersum(cgp)[i] = 0; 520*5cc52631SKirk McKusick bzero(cg_clustersfree(cgp), howmany(maxbno, CHAR_BIT)); 521*5cc52631SKirk McKusick } 522*5cc52631SKirk McKusick blksfree = cg_blksfree(cgp); 523*5cc52631SKirk McKusick for (cgbno = 0; cgbno < maxbno; cgbno++) { 524*5cc52631SKirk McKusick if (ffs_isfreeblock(&sblock, blksfree, cgbno)) 525*5cc52631SKirk McKusick continue; 526*5cc52631SKirk McKusick if (ffs_isblock(&sblock, blksfree, cgbno)) { 527*5cc52631SKirk McKusick ffs_clusteracct(&sblock, cgp, cgbno, 1); 528*5cc52631SKirk McKusick cgp->cg_cs.cs_nbfree++; 529*5cc52631SKirk McKusick continue; 530*5cc52631SKirk McKusick } 531*5cc52631SKirk McKusick fragno = blkstofrags(&sblock, cgbno); 532*5cc52631SKirk McKusick blk = blkmap(&sblock, blksfree, fragno); 533*5cc52631SKirk McKusick ffs_fragacct(&sblock, blk, cgp->cg_frsum, 1); 534*5cc52631SKirk McKusick for (i = 0; i < sblock.fs_frag; i++) 535*5cc52631SKirk McKusick if (isset(blksfree, fragno + i)) 536*5cc52631SKirk McKusick cgp->cg_cs.cs_nffree++; 537*5cc52631SKirk McKusick } 538*5cc52631SKirk McKusick /* 539*5cc52631SKirk McKusick * Update the superblock cg summary from our now correct values 540*5cc52631SKirk McKusick * before writing the block. 541*5cc52631SKirk McKusick */ 542*5cc52631SKirk McKusick sblock.fs_cs(&sblock, cgp->cg_cgx) = cgp->cg_cs; 543*5cc52631SKirk McKusick } 544*5cc52631SKirk McKusick 5457578c6abSKirk McKusick void 546599304a4SPoul-Henning Kamp rwerror(const char *mesg, ufs2_daddr_t blk) 5474336716bSAdrian Chadd { 5484336716bSAdrian Chadd 54915fca934SKirk McKusick if (bkgrdcheck) 55015fca934SKirk McKusick exit(EEXIT); 5514336716bSAdrian Chadd if (preen == 0) 5524336716bSAdrian Chadd printf("\n"); 553bf58d635SIan Dowse pfatal("CANNOT %s: %ld", mesg, (long)blk); 5544336716bSAdrian Chadd if (reply("CONTINUE") == 0) 5554336716bSAdrian Chadd exit(EEXIT); 5564336716bSAdrian Chadd } 5574336716bSAdrian Chadd 5584336716bSAdrian Chadd void 559b70cd7eeSWarner Losh ckfini(int markclean) 5604336716bSAdrian Chadd { 5613d438ad6SDavid E. O'Brien struct bufarea *bp, *nbp; 5622ec5c914SKirk McKusick int ofsmodified, cnt; 5634336716bSAdrian Chadd 5647578c6abSKirk McKusick if (bkgrdflag) { 5657578c6abSKirk McKusick unlink(snapname); 5667578c6abSKirk McKusick if ((!(sblock.fs_flags & FS_UNCLEAN)) != markclean) { 5677578c6abSKirk McKusick cmd.value = FS_UNCLEAN; 5687578c6abSKirk McKusick cmd.size = markclean ? -1 : 1; 5697578c6abSKirk McKusick if (sysctlbyname("vfs.ffs.setflags", 0, 0, 5707578c6abSKirk McKusick &cmd, sizeof cmd) == -1) 5717578c6abSKirk McKusick rwerror("SET FILE SYSTEM FLAGS", FS_UNCLEAN); 5727578c6abSKirk McKusick if (!preen) { 5737578c6abSKirk McKusick printf("\n***** FILE SYSTEM MARKED %s *****\n", 5747578c6abSKirk McKusick markclean ? "CLEAN" : "DIRTY"); 5757578c6abSKirk McKusick if (!markclean) 5767578c6abSKirk McKusick rerun = 1; 5777578c6abSKirk McKusick } 5787578c6abSKirk McKusick } else if (!preen && !markclean) { 5797578c6abSKirk McKusick printf("\n***** FILE SYSTEM STILL DIRTY *****\n"); 5807578c6abSKirk McKusick rerun = 1; 5817578c6abSKirk McKusick } 5827578c6abSKirk McKusick } 5832ec5c914SKirk McKusick if (debug && totalreads > 0) 584*5cc52631SKirk McKusick printf("cache with %d buffers missed %d of %d (%d%%)\n", 585*5cc52631SKirk McKusick numbufs, cachereads, cachelookups, 586*5cc52631SKirk McKusick (int)(cachereads * 100 / cachelookups)); 5874336716bSAdrian Chadd if (fswritefd < 0) { 5884336716bSAdrian Chadd (void)close(fsreadfd); 5894336716bSAdrian Chadd return; 5904336716bSAdrian Chadd } 591*5cc52631SKirk McKusick /* 592*5cc52631SKirk McKusick * To remain idempotent with partial truncations the buffers 593*5cc52631SKirk McKusick * must be flushed in this order: 594*5cc52631SKirk McKusick * 1) cylinder groups (bitmaps) 595*5cc52631SKirk McKusick * 2) indirect, directory, external attribute, and data blocks 596*5cc52631SKirk McKusick * 3) inode blocks 597*5cc52631SKirk McKusick * 4) superblock 598*5cc52631SKirk McKusick * This ordering preserves access to the modified pointers 599*5cc52631SKirk McKusick * until they are freed. 600*5cc52631SKirk McKusick */ 601*5cc52631SKirk McKusick /* Step 1: cylinder groups */ 602*5cc52631SKirk McKusick if (debug) 603*5cc52631SKirk McKusick printf("Flush Cylinder groups\n"); 604*5cc52631SKirk McKusick if (cgbufs != NULL) { 605*5cc52631SKirk McKusick for (cnt = 0; cnt < sblock.fs_ncg; cnt++) { 606*5cc52631SKirk McKusick if (cgbufs[cnt].b_un.b_cg == NULL) 607*5cc52631SKirk McKusick continue; 608*5cc52631SKirk McKusick flush(fswritefd, &cgbufs[cnt]); 609*5cc52631SKirk McKusick free(cgbufs[cnt].b_un.b_cg); 610*5cc52631SKirk McKusick } 611*5cc52631SKirk McKusick free(cgbufs); 612*5cc52631SKirk McKusick } 613*5cc52631SKirk McKusick flush(fswritefd, &cgblk); 614*5cc52631SKirk McKusick free(cgblk.b_un.b_buf); 615*5cc52631SKirk McKusick cnt = 0; 616*5cc52631SKirk McKusick /* Step 2: indirect, directory, external attribute, and data blocks */ 617*5cc52631SKirk McKusick if (debug) 618*5cc52631SKirk McKusick printf("Flush indirect, directory, external attribute, " 619*5cc52631SKirk McKusick "and data blocks\n"); 620*5cc52631SKirk McKusick if (pdirbp != NULL) 621*5cc52631SKirk McKusick brelse(pdirbp); 622*5cc52631SKirk McKusick TAILQ_FOREACH_REVERSE_SAFE(bp, &bufqueuehd, bufqueue, b_list, nbp) { 623*5cc52631SKirk McKusick switch (bp->b_type) { 624*5cc52631SKirk McKusick /* These should not be in the buffer cache list */ 625*5cc52631SKirk McKusick case BT_UNKNOWN: 626*5cc52631SKirk McKusick case BT_SUPERBLK: 627*5cc52631SKirk McKusick case BT_CYLGRP: 628*5cc52631SKirk McKusick default: 629*5cc52631SKirk McKusick prtbuf("ckfini: improper buffer type on cache list",bp); 630*5cc52631SKirk McKusick continue; 631*5cc52631SKirk McKusick /* These are the ones to flush in this step */ 632*5cc52631SKirk McKusick case BT_EMPTY: 633*5cc52631SKirk McKusick if (bp->b_bno >= 0) 634*5cc52631SKirk McKusick pfatal("Unused BT_EMPTY buffer for block %jd\n", 635*5cc52631SKirk McKusick (intmax_t)bp->b_bno); 636*5cc52631SKirk McKusick /* FALLTHROUGH */ 637*5cc52631SKirk McKusick case BT_LEVEL1: 638*5cc52631SKirk McKusick case BT_LEVEL2: 639*5cc52631SKirk McKusick case BT_LEVEL3: 640*5cc52631SKirk McKusick case BT_EXTATTR: 641*5cc52631SKirk McKusick case BT_DIRDATA: 642*5cc52631SKirk McKusick case BT_DATA: 643*5cc52631SKirk McKusick break; 644*5cc52631SKirk McKusick /* These are the ones to flush in the next step */ 645*5cc52631SKirk McKusick case BT_INODES: 646*5cc52631SKirk McKusick continue; 647*5cc52631SKirk McKusick } 648*5cc52631SKirk McKusick if (debug && bp->b_refcnt != 0) { 649*5cc52631SKirk McKusick prtbuf("ckfini: clearing in-use buffer", bp); 650*5cc52631SKirk McKusick pfatal("ckfini: clearing in-use buffer\n"); 651*5cc52631SKirk McKusick } 652*5cc52631SKirk McKusick TAILQ_REMOVE(&bufqueuehd, bp, b_list); 653*5cc52631SKirk McKusick cnt++; 654*5cc52631SKirk McKusick flush(fswritefd, bp); 655*5cc52631SKirk McKusick free(bp->b_un.b_buf); 656*5cc52631SKirk McKusick free((char *)bp); 657*5cc52631SKirk McKusick } 658*5cc52631SKirk McKusick /* Step 3: inode blocks */ 659*5cc52631SKirk McKusick if (debug) 660*5cc52631SKirk McKusick printf("Flush inode blocks\n"); 661*5cc52631SKirk McKusick if (icachebp != NULL) 662*5cc52631SKirk McKusick brelse(icachebp); 663*5cc52631SKirk McKusick TAILQ_FOREACH_REVERSE_SAFE(bp, &bufqueuehd, bufqueue, b_list, nbp) { 664*5cc52631SKirk McKusick if (debug && bp->b_refcnt != 0) { 665*5cc52631SKirk McKusick prtbuf("ckfini: clearing in-use buffer", bp); 666*5cc52631SKirk McKusick pfatal("ckfini: clearing in-use buffer\n"); 667*5cc52631SKirk McKusick } 668*5cc52631SKirk McKusick TAILQ_REMOVE(&bufqueuehd, bp, b_list); 669*5cc52631SKirk McKusick cnt++; 670*5cc52631SKirk McKusick flush(fswritefd, bp); 671*5cc52631SKirk McKusick free(bp->b_un.b_buf); 672*5cc52631SKirk McKusick free((char *)bp); 673*5cc52631SKirk McKusick } 674*5cc52631SKirk McKusick if (numbufs != cnt) 675*5cc52631SKirk McKusick errx(EEXIT, "panic: lost %d buffers", numbufs - cnt); 676*5cc52631SKirk McKusick /* Step 4: superblock */ 677*5cc52631SKirk McKusick if (debug) 678*5cc52631SKirk McKusick printf("Flush the superblock\n"); 6794336716bSAdrian Chadd flush(fswritefd, &sblk); 6801c85e6a3SKirk McKusick if (havesb && cursnapshot == 0 && sblock.fs_magic == FS_UFS2_MAGIC && 681ada981b2SKirk McKusick sblk.b_bno != sblock.fs_sblockloc / dev_bsize && 6824336716bSAdrian Chadd !preen && reply("UPDATE STANDARD SUPERBLOCK")) { 683dffce215SKirk McKusick /* Change the write destination to standard superblock */ 684dffce215SKirk McKusick sblock.fs_sblockactualloc = sblock.fs_sblockloc; 685ada981b2SKirk McKusick sblk.b_bno = sblock.fs_sblockloc / dev_bsize; 6864336716bSAdrian Chadd sbdirty(); 6874336716bSAdrian Chadd flush(fswritefd, &sblk); 6884336716bSAdrian Chadd } 689*5cc52631SKirk McKusick pdirbp = (struct bufarea *)0; 6907578c6abSKirk McKusick if (cursnapshot == 0 && sblock.fs_clean != markclean) { 69168aff084SKirk McKusick if ((sblock.fs_clean = markclean) != 0) { 69238375c40SKirk McKusick sblock.fs_flags &= ~(FS_UNCLEAN | FS_NEEDSFSCK); 69368aff084SKirk McKusick sblock.fs_pendingblocks = 0; 69468aff084SKirk McKusick sblock.fs_pendinginodes = 0; 69568aff084SKirk McKusick } 6964336716bSAdrian Chadd sbdirty(); 6974336716bSAdrian Chadd ofsmodified = fsmodified; 6984336716bSAdrian Chadd flush(fswritefd, &sblk); 6994336716bSAdrian Chadd fsmodified = ofsmodified; 7004336716bSAdrian Chadd if (!preen) { 7014336716bSAdrian Chadd printf("\n***** FILE SYSTEM MARKED %s *****\n", 7024336716bSAdrian Chadd markclean ? "CLEAN" : "DIRTY"); 7034336716bSAdrian Chadd if (!markclean) 7044336716bSAdrian Chadd rerun = 1; 7054336716bSAdrian Chadd } 706910b491eSKirk McKusick } else if (!preen) { 707910b491eSKirk McKusick if (markclean) { 708910b491eSKirk McKusick printf("\n***** FILE SYSTEM IS CLEAN *****\n"); 709910b491eSKirk McKusick } else { 7104336716bSAdrian Chadd printf("\n***** FILE SYSTEM STILL DIRTY *****\n"); 7114336716bSAdrian Chadd rerun = 1; 7124336716bSAdrian Chadd } 713910b491eSKirk McKusick } 714*5cc52631SKirk McKusick finalIOstats(); 7154336716bSAdrian Chadd (void)close(fsreadfd); 7164336716bSAdrian Chadd (void)close(fswritefd); 7174336716bSAdrian Chadd } 7184336716bSAdrian Chadd 719ed75b5a1SKirk McKusick /* 720ed75b5a1SKirk McKusick * Print out I/O statistics. 721ed75b5a1SKirk McKusick */ 722ed75b5a1SKirk McKusick void 723ed75b5a1SKirk McKusick IOstats(char *what) 724ed75b5a1SKirk McKusick { 725ed75b5a1SKirk McKusick int i; 726ed75b5a1SKirk McKusick 727ed75b5a1SKirk McKusick if (debug == 0) 728ed75b5a1SKirk McKusick return; 729ed75b5a1SKirk McKusick if (diskreads == 0) { 730ed75b5a1SKirk McKusick printf("%s: no I/O\n\n", what); 731ed75b5a1SKirk McKusick return; 732ed75b5a1SKirk McKusick } 733ed75b5a1SKirk McKusick if (startpass.tv_sec == 0) 734ed75b5a1SKirk McKusick startpass = startprog; 735ed75b5a1SKirk McKusick printf("%s: I/O statistics\n", what); 736ed75b5a1SKirk McKusick printIOstats(); 737ed75b5a1SKirk McKusick totaldiskreads += diskreads; 738ed75b5a1SKirk McKusick diskreads = 0; 739ed75b5a1SKirk McKusick for (i = 0; i < BT_NUMBUFTYPES; i++) { 7406040822cSAlan Somers timespecadd(&totalreadtime[i], &readtime[i], &totalreadtime[i]); 741ed75b5a1SKirk McKusick totalreadcnt[i] += readcnt[i]; 742ed75b5a1SKirk McKusick readtime[i].tv_sec = readtime[i].tv_nsec = 0; 743ed75b5a1SKirk McKusick readcnt[i] = 0; 744ed75b5a1SKirk McKusick } 745ed75b5a1SKirk McKusick clock_gettime(CLOCK_REALTIME_PRECISE, &startpass); 746ed75b5a1SKirk McKusick } 747ed75b5a1SKirk McKusick 748ed75b5a1SKirk McKusick void 749ed75b5a1SKirk McKusick finalIOstats(void) 750ed75b5a1SKirk McKusick { 751ed75b5a1SKirk McKusick int i; 752ed75b5a1SKirk McKusick 753ed75b5a1SKirk McKusick if (debug == 0) 754ed75b5a1SKirk McKusick return; 755ed75b5a1SKirk McKusick printf("Final I/O statistics\n"); 756ed75b5a1SKirk McKusick totaldiskreads += diskreads; 757ed75b5a1SKirk McKusick diskreads = totaldiskreads; 758ed75b5a1SKirk McKusick startpass = startprog; 759ed75b5a1SKirk McKusick for (i = 0; i < BT_NUMBUFTYPES; i++) { 7606040822cSAlan Somers timespecadd(&totalreadtime[i], &readtime[i], &totalreadtime[i]); 761ed75b5a1SKirk McKusick totalreadcnt[i] += readcnt[i]; 762ed75b5a1SKirk McKusick readtime[i] = totalreadtime[i]; 763ed75b5a1SKirk McKusick readcnt[i] = totalreadcnt[i]; 764ed75b5a1SKirk McKusick } 765ed75b5a1SKirk McKusick printIOstats(); 766ed75b5a1SKirk McKusick } 767ed75b5a1SKirk McKusick 768ed75b5a1SKirk McKusick static void printIOstats(void) 769ed75b5a1SKirk McKusick { 770ed75b5a1SKirk McKusick long long msec, totalmsec; 771ed75b5a1SKirk McKusick int i; 772ed75b5a1SKirk McKusick 773ed75b5a1SKirk McKusick clock_gettime(CLOCK_REALTIME_PRECISE, &finishpass); 7746040822cSAlan Somers timespecsub(&finishpass, &startpass, &finishpass); 775061ea59dSKirk McKusick printf("Running time: %jd.%03ld sec\n", 7764b3bbe04SSean Bruno (intmax_t)finishpass.tv_sec, finishpass.tv_nsec / 1000000); 777ed75b5a1SKirk McKusick printf("buffer reads by type:\n"); 778ed75b5a1SKirk McKusick for (totalmsec = 0, i = 0; i < BT_NUMBUFTYPES; i++) 779ed75b5a1SKirk McKusick totalmsec += readtime[i].tv_sec * 1000 + 780ed75b5a1SKirk McKusick readtime[i].tv_nsec / 1000000; 781ed75b5a1SKirk McKusick if (totalmsec == 0) 782ed75b5a1SKirk McKusick totalmsec = 1; 783ed75b5a1SKirk McKusick for (i = 0; i < BT_NUMBUFTYPES; i++) { 784ed75b5a1SKirk McKusick if (readcnt[i] == 0) 785ed75b5a1SKirk McKusick continue; 786061ea59dSKirk McKusick msec = 787061ea59dSKirk McKusick readtime[i].tv_sec * 1000 + readtime[i].tv_nsec / 1000000; 7884b3bbe04SSean Bruno printf("%21s:%8ld %2ld.%ld%% %4jd.%03ld sec %2lld.%lld%%\n", 789ed75b5a1SKirk McKusick buftype[i], readcnt[i], readcnt[i] * 100 / diskreads, 79081fbded2SKirk McKusick (readcnt[i] * 1000 / diskreads) % 10, 7914b3bbe04SSean Bruno (intmax_t)readtime[i].tv_sec, readtime[i].tv_nsec / 1000000, 792ed75b5a1SKirk McKusick msec * 100 / totalmsec, (msec * 1000 / totalmsec) % 10); 793ed75b5a1SKirk McKusick } 794ed75b5a1SKirk McKusick printf("\n"); 795ed75b5a1SKirk McKusick } 796ed75b5a1SKirk McKusick 7974336716bSAdrian Chadd int 798aef8d244SPawel Jakub Dawidek blread(int fd, char *buf, ufs2_daddr_t blk, long size) 7994336716bSAdrian Chadd { 8004336716bSAdrian Chadd char *cp; 8014336716bSAdrian Chadd int i, errs; 8024336716bSAdrian Chadd off_t offset; 8034336716bSAdrian Chadd 8044336716bSAdrian Chadd offset = blk; 8054336716bSAdrian Chadd offset *= dev_bsize; 8069d580d7cSIan Dowse if (bkgrdflag) 8079d580d7cSIan Dowse slowio_start(); 808ed75b5a1SKirk McKusick totalreads++; 809ed75b5a1SKirk McKusick diskreads++; 81004e5c6f1SEdward Tomasz Napierala if (pread(fd, buf, (int)size, offset) == size) { 8119d580d7cSIan Dowse if (bkgrdflag) 8129d580d7cSIan Dowse slowio_end(); 8134336716bSAdrian Chadd return (0); 8149d580d7cSIan Dowse } 815ce779f37SScott Long 816ce779f37SScott Long /* 817ce779f37SScott Long * This is handled specially here instead of in rwerror because 818ce779f37SScott Long * rwerror is used for all sorts of errors, not just true read/write 819ce779f37SScott Long * errors. It should be refactored and fixed. 820ce779f37SScott Long */ 821ce779f37SScott Long if (surrender) { 822ce779f37SScott Long pfatal("CANNOT READ_BLK: %ld", (long)blk); 823ce779f37SScott Long errx(EEXIT, "ABORTING DUE TO READ ERRORS"); 824ce779f37SScott Long } else 8257578c6abSKirk McKusick rwerror("READ BLK", blk); 826ce779f37SScott Long 8274336716bSAdrian Chadd errs = 0; 8284336716bSAdrian Chadd memset(buf, 0, (size_t)size); 8294336716bSAdrian Chadd printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 8304336716bSAdrian Chadd for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 83104e5c6f1SEdward Tomasz Napierala if (pread(fd, cp, (int)secsize, offset + i) != secsize) { 8324336716bSAdrian Chadd if (secsize != dev_bsize && dev_bsize != 1) 83384fc0d7eSMaxime Henrion printf(" %jd (%jd),", 83484fc0d7eSMaxime Henrion (intmax_t)(blk * dev_bsize + i) / secsize, 83584fc0d7eSMaxime Henrion (intmax_t)blk + i / dev_bsize); 8364336716bSAdrian Chadd else 83784fc0d7eSMaxime Henrion printf(" %jd,", (intmax_t)blk + i / dev_bsize); 8384336716bSAdrian Chadd errs++; 8394336716bSAdrian Chadd } 8404336716bSAdrian Chadd } 8414336716bSAdrian Chadd printf("\n"); 8424336716bSAdrian Chadd if (errs) 8434336716bSAdrian Chadd resolved = 0; 8444336716bSAdrian Chadd return (errs); 8454336716bSAdrian Chadd } 8464336716bSAdrian Chadd 8474336716bSAdrian Chadd void 8484a835375SDavid E. O'Brien blwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size) 8494336716bSAdrian Chadd { 8504336716bSAdrian Chadd int i; 8514336716bSAdrian Chadd char *cp; 8524336716bSAdrian Chadd off_t offset; 8534336716bSAdrian Chadd 8544336716bSAdrian Chadd if (fd < 0) 8554336716bSAdrian Chadd return; 8564336716bSAdrian Chadd offset = blk; 8574336716bSAdrian Chadd offset *= dev_bsize; 85804e5c6f1SEdward Tomasz Napierala if (pwrite(fd, buf, size, offset) == size) { 8594336716bSAdrian Chadd fsmodified = 1; 8604336716bSAdrian Chadd return; 8614336716bSAdrian Chadd } 8624336716bSAdrian Chadd resolved = 0; 8637578c6abSKirk McKusick rwerror("WRITE BLK", blk); 8644336716bSAdrian Chadd printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 8654336716bSAdrian Chadd for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 86604e5c6f1SEdward Tomasz Napierala if (pwrite(fd, cp, dev_bsize, offset + i) != dev_bsize) 86784fc0d7eSMaxime Henrion printf(" %jd,", (intmax_t)blk + i / dev_bsize); 8684336716bSAdrian Chadd printf("\n"); 8694336716bSAdrian Chadd return; 8704336716bSAdrian Chadd } 8714336716bSAdrian Chadd 8728d3dfc26SDag-Erling Smørgrav void 8738d3dfc26SDag-Erling Smørgrav blerase(int fd, ufs2_daddr_t blk, long size) 8748d3dfc26SDag-Erling Smørgrav { 8758d3dfc26SDag-Erling Smørgrav off_t ioarg[2]; 8768d3dfc26SDag-Erling Smørgrav 8778d3dfc26SDag-Erling Smørgrav if (fd < 0) 8788d3dfc26SDag-Erling Smørgrav return; 8798d3dfc26SDag-Erling Smørgrav ioarg[0] = blk * dev_bsize; 8808d3dfc26SDag-Erling Smørgrav ioarg[1] = size; 8818d3dfc26SDag-Erling Smørgrav ioctl(fd, DIOCGDELETE, ioarg); 8828d3dfc26SDag-Erling Smørgrav /* we don't really care if we succeed or not */ 8838d3dfc26SDag-Erling Smørgrav return; 8848d3dfc26SDag-Erling Smørgrav } 8858d3dfc26SDag-Erling Smørgrav 8868ce80d4bSDag-Erling Smørgrav /* 8878ce80d4bSDag-Erling Smørgrav * Fill a contiguous region with all-zeroes. Note ZEROBUFSIZE is by 8888ce80d4bSDag-Erling Smørgrav * definition a multiple of dev_bsize. 8898ce80d4bSDag-Erling Smørgrav */ 8902b5373deSDag-Erling Smørgrav void 8912b5373deSDag-Erling Smørgrav blzero(int fd, ufs2_daddr_t blk, long size) 8922b5373deSDag-Erling Smørgrav { 8932b5373deSDag-Erling Smørgrav static char *zero; 8942b5373deSDag-Erling Smørgrav off_t offset, len; 8952b5373deSDag-Erling Smørgrav 8962b5373deSDag-Erling Smørgrav if (fd < 0) 8972b5373deSDag-Erling Smørgrav return; 8982b5373deSDag-Erling Smørgrav if (zero == NULL) { 8998ce80d4bSDag-Erling Smørgrav zero = calloc(ZEROBUFSIZE, 1); 9002b5373deSDag-Erling Smørgrav if (zero == NULL) 9012b5373deSDag-Erling Smørgrav errx(EEXIT, "cannot allocate buffer pool"); 9022b5373deSDag-Erling Smørgrav } 9032b5373deSDag-Erling Smørgrav offset = blk * dev_bsize; 9042b5373deSDag-Erling Smørgrav if (lseek(fd, offset, 0) < 0) 9052b5373deSDag-Erling Smørgrav rwerror("SEEK BLK", blk); 9062b5373deSDag-Erling Smørgrav while (size > 0) { 9071120faabSMarcelo Araujo len = MIN(ZEROBUFSIZE, size); 9082b5373deSDag-Erling Smørgrav if (write(fd, zero, len) != len) 9092b5373deSDag-Erling Smørgrav rwerror("WRITE BLK", blk); 9102b5373deSDag-Erling Smørgrav blk += len / dev_bsize; 9112b5373deSDag-Erling Smørgrav size -= len; 9122b5373deSDag-Erling Smørgrav } 9132b5373deSDag-Erling Smørgrav } 9142b5373deSDag-Erling Smørgrav 9154336716bSAdrian Chadd /* 916910b491eSKirk McKusick * Verify cylinder group's magic number and other parameters. If the 917910b491eSKirk McKusick * test fails, offer an option to rebuild the whole cylinder group. 91814320f1eSXin LI */ 919910b491eSKirk McKusick int 920*5cc52631SKirk McKusick check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild) 92114320f1eSXin LI { 92281fbded2SKirk McKusick struct cg *cgp = cgbp->b_un.b_cg; 923*5cc52631SKirk McKusick uint32_t cghash, calchash; 92414320f1eSXin LI 925910b491eSKirk McKusick /* 926910b491eSKirk McKusick * Extended cylinder group checks. 927910b491eSKirk McKusick */ 928*5cc52631SKirk McKusick calchash = cgp->cg_ckhash; 929*5cc52631SKirk McKusick if ((sblock.fs_metackhash & CK_CYLGRP) != 0) { 930*5cc52631SKirk McKusick cghash = cgp->cg_ckhash; 931*5cc52631SKirk McKusick cgp->cg_ckhash = 0; 932*5cc52631SKirk McKusick calchash = calculate_crc32c(~0L, (void *)cgp, sblock.fs_cgsize); 933*5cc52631SKirk McKusick cgp->cg_ckhash = cghash; 934*5cc52631SKirk McKusick } 935*5cc52631SKirk McKusick if (cgp->cg_ckhash == calchash && 936*5cc52631SKirk McKusick cg_chkmagic(cgp) && 937*5cc52631SKirk McKusick cgp->cg_cgx == cg && 938910b491eSKirk McKusick ((sblock.fs_magic == FS_UFS1_MAGIC && 939910b491eSKirk McKusick cgp->cg_old_niblk == sblock.fs_ipg && 940910b491eSKirk McKusick cgp->cg_ndblk <= sblock.fs_fpg && 94136ef6b65SKirk McKusick cgp->cg_old_ncyl <= sblock.fs_old_cpg) || 942910b491eSKirk McKusick (sblock.fs_magic == FS_UFS2_MAGIC && 943910b491eSKirk McKusick cgp->cg_niblk == sblock.fs_ipg && 944910b491eSKirk McKusick cgp->cg_ndblk <= sblock.fs_fpg && 945910b491eSKirk McKusick cgp->cg_initediblk <= sblock.fs_ipg))) { 946910b491eSKirk McKusick return (1); 947910b491eSKirk McKusick } 948*5cc52631SKirk McKusick pfatal("CYLINDER GROUP %d: INTEGRITY CHECK FAILED", cg); 949*5cc52631SKirk McKusick if (!request_rebuild) 950*5cc52631SKirk McKusick return (0); 951910b491eSKirk McKusick if (!reply("REBUILD CYLINDER GROUP")) { 952910b491eSKirk McKusick printf("YOU WILL NEED TO RERUN FSCK.\n"); 953910b491eSKirk McKusick rerun = 1; 954910b491eSKirk McKusick return (1); 955910b491eSKirk McKusick } 956910b491eSKirk McKusick /* 957910b491eSKirk McKusick * Zero out the cylinder group and then initialize critical fields. 958910b491eSKirk McKusick * Bit maps and summaries will be recalculated by later passes. 959910b491eSKirk McKusick */ 96014320f1eSXin LI memset(cgp, 0, (size_t)sblock.fs_cgsize); 961910b491eSKirk McKusick cgp->cg_magic = CG_MAGIC; 96214320f1eSXin LI cgp->cg_cgx = cg; 96314320f1eSXin LI cgp->cg_niblk = sblock.fs_ipg; 9641120faabSMarcelo Araujo cgp->cg_initediblk = MIN(sblock.fs_ipg, 2 * INOPB(&sblock)); 965910b491eSKirk McKusick if (cgbase(&sblock, cg) + sblock.fs_fpg < sblock.fs_size) 966910b491eSKirk McKusick cgp->cg_ndblk = sblock.fs_fpg; 967910b491eSKirk McKusick else 96814320f1eSXin LI cgp->cg_ndblk = sblock.fs_size - cgbase(&sblock, cg); 969910b491eSKirk McKusick cgp->cg_iusedoff = &cgp->cg_space[0] - (u_char *)(&cgp->cg_firstfield); 970910b491eSKirk McKusick if (sblock.fs_magic == FS_UFS1_MAGIC) { 971910b491eSKirk McKusick cgp->cg_niblk = 0; 972910b491eSKirk McKusick cgp->cg_initediblk = 0; 973910b491eSKirk McKusick cgp->cg_old_ncyl = sblock.fs_old_cpg; 974910b491eSKirk McKusick cgp->cg_old_niblk = sblock.fs_ipg; 975910b491eSKirk McKusick cgp->cg_old_btotoff = cgp->cg_iusedoff; 976910b491eSKirk McKusick cgp->cg_old_boff = cgp->cg_old_btotoff + 977910b491eSKirk McKusick sblock.fs_old_cpg * sizeof(int32_t); 978910b491eSKirk McKusick cgp->cg_iusedoff = cgp->cg_old_boff + 979910b491eSKirk McKusick sblock.fs_old_cpg * sizeof(u_int16_t); 980910b491eSKirk McKusick } 981910b491eSKirk McKusick cgp->cg_freeoff = cgp->cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT); 982910b491eSKirk McKusick cgp->cg_nextfreeoff = cgp->cg_freeoff + howmany(sblock.fs_fpg,CHAR_BIT); 983910b491eSKirk McKusick if (sblock.fs_contigsumsize > 0) { 984910b491eSKirk McKusick cgp->cg_nclusterblks = cgp->cg_ndblk / sblock.fs_frag; 985910b491eSKirk McKusick cgp->cg_clustersumoff = 986910b491eSKirk McKusick roundup(cgp->cg_nextfreeoff, sizeof(u_int32_t)); 987910b491eSKirk McKusick cgp->cg_clustersumoff -= sizeof(u_int32_t); 988910b491eSKirk McKusick cgp->cg_clusteroff = cgp->cg_clustersumoff + 989910b491eSKirk McKusick (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t); 990910b491eSKirk McKusick cgp->cg_nextfreeoff = cgp->cg_clusteroff + 991910b491eSKirk McKusick howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT); 992910b491eSKirk McKusick } 9938ebae128SKirk McKusick cgdirty(cgbp); 994910b491eSKirk McKusick return (0); 99514320f1eSXin LI } 99614320f1eSXin LI 99714320f1eSXin LI /* 9984336716bSAdrian Chadd * allocate a data block with the specified number of fragments 9994336716bSAdrian Chadd */ 10001c85e6a3SKirk McKusick ufs2_daddr_t 1001b70cd7eeSWarner Losh allocblk(long frags) 10024336716bSAdrian Chadd { 10034336716bSAdrian Chadd int i, j, k, cg, baseblk; 100481fbded2SKirk McKusick struct bufarea *cgbp; 100581fbded2SKirk McKusick struct cg *cgp; 10064336716bSAdrian Chadd 10074336716bSAdrian Chadd if (frags <= 0 || frags > sblock.fs_frag) 10084336716bSAdrian Chadd return (0); 10094336716bSAdrian Chadd for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 10104336716bSAdrian Chadd for (j = 0; j <= sblock.fs_frag - frags; j++) { 10114336716bSAdrian Chadd if (testbmap(i + j)) 10124336716bSAdrian Chadd continue; 10134336716bSAdrian Chadd for (k = 1; k < frags; k++) 10144336716bSAdrian Chadd if (testbmap(i + j + k)) 10154336716bSAdrian Chadd break; 10164336716bSAdrian Chadd if (k < frags) { 10174336716bSAdrian Chadd j += k; 10184336716bSAdrian Chadd continue; 10194336716bSAdrian Chadd } 10204336716bSAdrian Chadd cg = dtog(&sblock, i + j); 1021957fc241SKirk McKusick cgbp = cglookup(cg); 102281fbded2SKirk McKusick cgp = cgbp->b_un.b_cg; 1023*5cc52631SKirk McKusick if (!check_cgmagic(cg, cgbp, 0)) 1024910b491eSKirk McKusick return (0); 10254336716bSAdrian Chadd baseblk = dtogd(&sblock, i + j); 10264336716bSAdrian Chadd for (k = 0; k < frags; k++) { 10274336716bSAdrian Chadd setbmap(i + j + k); 10284336716bSAdrian Chadd clrbit(cg_blksfree(cgp), baseblk + k); 10294336716bSAdrian Chadd } 10304336716bSAdrian Chadd n_blks += frags; 10314336716bSAdrian Chadd if (frags == sblock.fs_frag) 10324336716bSAdrian Chadd cgp->cg_cs.cs_nbfree--; 10334336716bSAdrian Chadd else 10344336716bSAdrian Chadd cgp->cg_cs.cs_nffree -= frags; 10358ebae128SKirk McKusick cgdirty(cgbp); 10364336716bSAdrian Chadd return (i + j); 10374336716bSAdrian Chadd } 10384336716bSAdrian Chadd } 10394336716bSAdrian Chadd return (0); 10404336716bSAdrian Chadd } 10414336716bSAdrian Chadd 10424336716bSAdrian Chadd /* 10437180f1abSKirk McKusick * Slow down IO so as to leave some disk bandwidth for other processes 10444336716bSAdrian Chadd */ 10454336716bSAdrian Chadd void 10469d580d7cSIan Dowse slowio_start() 10479d580d7cSIan Dowse { 10489d580d7cSIan Dowse 104908983aeeSScott Long /* Delay one in every 8 operations */ 10509d580d7cSIan Dowse slowio_pollcnt = (slowio_pollcnt + 1) & 7; 10519d580d7cSIan Dowse if (slowio_pollcnt == 0) { 10529d580d7cSIan Dowse gettimeofday(&slowio_starttime, NULL); 10539d580d7cSIan Dowse } 10549d580d7cSIan Dowse } 10559d580d7cSIan Dowse 10569d580d7cSIan Dowse void 10579d580d7cSIan Dowse slowio_end() 10589d580d7cSIan Dowse { 10599d580d7cSIan Dowse struct timeval tv; 10609d580d7cSIan Dowse int delay_usec; 10619d580d7cSIan Dowse 10629d580d7cSIan Dowse if (slowio_pollcnt != 0) 10639d580d7cSIan Dowse return; 10649d580d7cSIan Dowse 10659d580d7cSIan Dowse /* Update the slowdown interval. */ 10669d580d7cSIan Dowse gettimeofday(&tv, NULL); 10679d580d7cSIan Dowse delay_usec = (tv.tv_sec - slowio_starttime.tv_sec) * 1000000 + 10689d580d7cSIan Dowse (tv.tv_usec - slowio_starttime.tv_usec); 10699d580d7cSIan Dowse if (delay_usec < 64) 10709d580d7cSIan Dowse delay_usec = 64; 107108983aeeSScott Long if (delay_usec > 2500000) 107208983aeeSScott Long delay_usec = 2500000; 10739d580d7cSIan Dowse slowio_delay_usec = (slowio_delay_usec * 63 + delay_usec) >> 6; 107408983aeeSScott Long /* delay by 8 times the average IO delay */ 107508983aeeSScott Long if (slowio_delay_usec > 64) 107608983aeeSScott Long usleep(slowio_delay_usec * 8); 10779d580d7cSIan Dowse } 10789d580d7cSIan Dowse 10794336716bSAdrian Chadd /* 10804336716bSAdrian Chadd * Find a pathname 10814336716bSAdrian Chadd */ 10824336716bSAdrian Chadd void 1083b70cd7eeSWarner Losh getpathname(char *namebuf, ino_t curdir, ino_t ino) 10844336716bSAdrian Chadd { 10854336716bSAdrian Chadd int len; 10863d438ad6SDavid E. O'Brien char *cp; 1087*5cc52631SKirk McKusick struct inode ip; 10884336716bSAdrian Chadd struct inodesc idesc; 10894336716bSAdrian Chadd static int busy = 0; 10904336716bSAdrian Chadd 10911dc349abSEd Maste if (curdir == ino && ino == UFS_ROOTINO) { 10924336716bSAdrian Chadd (void)strcpy(namebuf, "/"); 10934336716bSAdrian Chadd return; 10944336716bSAdrian Chadd } 1095af6726e6SDon Lewis if (busy || !INO_IS_DVALID(curdir)) { 10964336716bSAdrian Chadd (void)strcpy(namebuf, "?"); 10974336716bSAdrian Chadd return; 10984336716bSAdrian Chadd } 10994336716bSAdrian Chadd busy = 1; 11004336716bSAdrian Chadd memset(&idesc, 0, sizeof(struct inodesc)); 11014336716bSAdrian Chadd idesc.id_type = DATA; 11024336716bSAdrian Chadd idesc.id_fix = IGNORE; 11034336716bSAdrian Chadd cp = &namebuf[MAXPATHLEN - 1]; 11044336716bSAdrian Chadd *cp = '\0'; 11054336716bSAdrian Chadd if (curdir != ino) { 11064336716bSAdrian Chadd idesc.id_parent = curdir; 11074336716bSAdrian Chadd goto namelookup; 11084336716bSAdrian Chadd } 11091dc349abSEd Maste while (ino != UFS_ROOTINO) { 11104336716bSAdrian Chadd idesc.id_number = ino; 11114336716bSAdrian Chadd idesc.id_func = findino; 1112599304a4SPoul-Henning Kamp idesc.id_name = strdup(".."); 1113*5cc52631SKirk McKusick ginode(ino, &ip); 1114*5cc52631SKirk McKusick if ((ckinode(ip.i_dp, &idesc) & FOUND) == 0) { 1115*5cc52631SKirk McKusick irelse(&ip); 11164336716bSAdrian Chadd break; 1117*5cc52631SKirk McKusick } 1118*5cc52631SKirk McKusick irelse(&ip); 11194336716bSAdrian Chadd namelookup: 11204336716bSAdrian Chadd idesc.id_number = idesc.id_parent; 11214336716bSAdrian Chadd idesc.id_parent = ino; 11224336716bSAdrian Chadd idesc.id_func = findname; 11234336716bSAdrian Chadd idesc.id_name = namebuf; 1124*5cc52631SKirk McKusick ginode(idesc.id_number, &ip); 1125*5cc52631SKirk McKusick if ((ckinode(ip.i_dp, &idesc) & FOUND) == 0) { 1126*5cc52631SKirk McKusick irelse(&ip); 11274336716bSAdrian Chadd break; 1128*5cc52631SKirk McKusick } 1129*5cc52631SKirk McKusick irelse(&ip); 11304336716bSAdrian Chadd len = strlen(namebuf); 11314336716bSAdrian Chadd cp -= len; 11324336716bSAdrian Chadd memmove(cp, namebuf, (size_t)len); 11334336716bSAdrian Chadd *--cp = '/'; 11340ecf59f6SConrad Meyer if (cp < &namebuf[UFS_MAXNAMLEN]) 11354336716bSAdrian Chadd break; 11364336716bSAdrian Chadd ino = idesc.id_number; 11374336716bSAdrian Chadd } 11384336716bSAdrian Chadd busy = 0; 11391dc349abSEd Maste if (ino != UFS_ROOTINO) 11404336716bSAdrian Chadd *--cp = '?'; 11414336716bSAdrian Chadd memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); 11424336716bSAdrian Chadd } 11434336716bSAdrian Chadd 11444336716bSAdrian Chadd void 1145599304a4SPoul-Henning Kamp catch(int sig __unused) 11464336716bSAdrian Chadd { 1147381ee4c2SPoul-Henning Kamp 11484336716bSAdrian Chadd ckfini(0); 11494336716bSAdrian Chadd exit(12); 11504336716bSAdrian Chadd } 11514336716bSAdrian Chadd 11524336716bSAdrian Chadd /* 11534336716bSAdrian Chadd * When preening, allow a single quit to signal 11544336716bSAdrian Chadd * a special exit after file system checks complete 11554336716bSAdrian Chadd * so that reboot sequence may be interrupted. 11564336716bSAdrian Chadd */ 11574336716bSAdrian Chadd void 1158599304a4SPoul-Henning Kamp catchquit(int sig __unused) 11594336716bSAdrian Chadd { 11604336716bSAdrian Chadd printf("returning to single-user after file system check\n"); 11614336716bSAdrian Chadd returntosingle = 1; 11624336716bSAdrian Chadd (void)signal(SIGQUIT, SIG_DFL); 11634336716bSAdrian Chadd } 11644336716bSAdrian Chadd 11654336716bSAdrian Chadd /* 11664336716bSAdrian Chadd * determine whether an inode should be fixed. 11674336716bSAdrian Chadd */ 11684336716bSAdrian Chadd int 1169599304a4SPoul-Henning Kamp dofix(struct inodesc *idesc, const char *msg) 11704336716bSAdrian Chadd { 11714336716bSAdrian Chadd 11724336716bSAdrian Chadd switch (idesc->id_fix) { 11734336716bSAdrian Chadd 11744336716bSAdrian Chadd case DONTKNOW: 11754336716bSAdrian Chadd if (idesc->id_type == DATA) 11764336716bSAdrian Chadd direrror(idesc->id_number, msg); 11774336716bSAdrian Chadd else 11785979df34SKris Kennaway pwarn("%s", msg); 11794336716bSAdrian Chadd if (preen) { 11804336716bSAdrian Chadd printf(" (SALVAGED)\n"); 11814336716bSAdrian Chadd idesc->id_fix = FIX; 11824336716bSAdrian Chadd return (ALTERED); 11834336716bSAdrian Chadd } 11844336716bSAdrian Chadd if (reply("SALVAGE") == 0) { 11854336716bSAdrian Chadd idesc->id_fix = NOFIX; 11864336716bSAdrian Chadd return (0); 11874336716bSAdrian Chadd } 11884336716bSAdrian Chadd idesc->id_fix = FIX; 11894336716bSAdrian Chadd return (ALTERED); 11904336716bSAdrian Chadd 11914336716bSAdrian Chadd case FIX: 11924336716bSAdrian Chadd return (ALTERED); 11934336716bSAdrian Chadd 11944336716bSAdrian Chadd case NOFIX: 11954336716bSAdrian Chadd case IGNORE: 11964336716bSAdrian Chadd return (0); 11974336716bSAdrian Chadd 11984336716bSAdrian Chadd default: 11994336716bSAdrian Chadd errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix); 12004336716bSAdrian Chadd } 12014336716bSAdrian Chadd /* NOTREACHED */ 12024336716bSAdrian Chadd return (0); 12034336716bSAdrian Chadd } 12044336716bSAdrian Chadd 12054336716bSAdrian Chadd #include <stdarg.h> 12064336716bSAdrian Chadd 12074336716bSAdrian Chadd /* 1208*5cc52631SKirk McKusick * Print details about a buffer. 1209*5cc52631SKirk McKusick */ 1210*5cc52631SKirk McKusick static void 1211*5cc52631SKirk McKusick prtbuf(const char *msg, struct bufarea *bp) 1212*5cc52631SKirk McKusick { 1213*5cc52631SKirk McKusick 1214*5cc52631SKirk McKusick printf("%s: bp %p, type %s, bno %ld, size %d, refcnt %d, flags %s, " 1215*5cc52631SKirk McKusick "index %d\n", msg, bp, BT_BUFTYPE(bp->b_type), bp->b_bno, 1216*5cc52631SKirk McKusick bp->b_size, bp->b_refcnt, bp->b_flags & B_DIRTY ? "dirty" : "clean", 1217*5cc52631SKirk McKusick bp->b_index); 1218*5cc52631SKirk McKusick } 1219*5cc52631SKirk McKusick 1220*5cc52631SKirk McKusick /* 12214b85a12fSUlrich Spörlein * An unexpected inconsistency occurred. 12224336716bSAdrian Chadd * Die if preening or file system is running with soft dependency protocol, 12234336716bSAdrian Chadd * otherwise just print message and continue. 12244336716bSAdrian Chadd */ 12254336716bSAdrian Chadd void 12264336716bSAdrian Chadd pfatal(const char *fmt, ...) 12274336716bSAdrian Chadd { 12284336716bSAdrian Chadd va_list ap; 12294336716bSAdrian Chadd va_start(ap, fmt); 12304336716bSAdrian Chadd if (!preen) { 123115fca934SKirk McKusick (void)vfprintf(stdout, fmt, ap); 12324336716bSAdrian Chadd va_end(ap); 12334336716bSAdrian Chadd if (usedsoftdep) 123415fca934SKirk McKusick (void)fprintf(stdout, 12354336716bSAdrian Chadd "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n"); 123638375c40SKirk McKusick /* 123738375c40SKirk McKusick * Force foreground fsck to clean up inconsistency. 123838375c40SKirk McKusick */ 123938375c40SKirk McKusick if (bkgrdflag) { 124038375c40SKirk McKusick cmd.value = FS_NEEDSFSCK; 124138375c40SKirk McKusick cmd.size = 1; 124238375c40SKirk McKusick if (sysctlbyname("vfs.ffs.setflags", 0, 0, 124338375c40SKirk McKusick &cmd, sizeof cmd) == -1) 124438375c40SKirk McKusick pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n"); 124515fca934SKirk McKusick fprintf(stdout, "CANNOT RUN IN BACKGROUND\n"); 124638375c40SKirk McKusick ckfini(0); 124738375c40SKirk McKusick exit(EEXIT); 124838375c40SKirk McKusick } 12494336716bSAdrian Chadd return; 12504336716bSAdrian Chadd } 12514336716bSAdrian Chadd if (cdevname == NULL) 1252599304a4SPoul-Henning Kamp cdevname = strdup("fsck"); 125315fca934SKirk McKusick (void)fprintf(stdout, "%s: ", cdevname); 125415fca934SKirk McKusick (void)vfprintf(stdout, fmt, ap); 125515fca934SKirk McKusick (void)fprintf(stdout, 12564336716bSAdrian Chadd "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n", 12574336716bSAdrian Chadd cdevname, usedsoftdep ? " SOFT UPDATE " : " "); 125838375c40SKirk McKusick /* 125938375c40SKirk McKusick * Force foreground fsck to clean up inconsistency. 126038375c40SKirk McKusick */ 126138375c40SKirk McKusick if (bkgrdflag) { 126238375c40SKirk McKusick cmd.value = FS_NEEDSFSCK; 126338375c40SKirk McKusick cmd.size = 1; 126438375c40SKirk McKusick if (sysctlbyname("vfs.ffs.setflags", 0, 0, 126538375c40SKirk McKusick &cmd, sizeof cmd) == -1) 126638375c40SKirk McKusick pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n"); 126738375c40SKirk McKusick } 12684336716bSAdrian Chadd ckfini(0); 12694336716bSAdrian Chadd exit(EEXIT); 12704336716bSAdrian Chadd } 12714336716bSAdrian Chadd 12724336716bSAdrian Chadd /* 12734336716bSAdrian Chadd * Pwarn just prints a message when not preening or running soft dependency 12744336716bSAdrian Chadd * protocol, or a warning (preceded by filename) when preening. 12754336716bSAdrian Chadd */ 12764336716bSAdrian Chadd void 12774336716bSAdrian Chadd pwarn(const char *fmt, ...) 12784336716bSAdrian Chadd { 12794336716bSAdrian Chadd va_list ap; 12804336716bSAdrian Chadd va_start(ap, fmt); 12814336716bSAdrian Chadd if (preen) 128215fca934SKirk McKusick (void)fprintf(stdout, "%s: ", cdevname); 128315fca934SKirk McKusick (void)vfprintf(stdout, fmt, ap); 12844336716bSAdrian Chadd va_end(ap); 12854336716bSAdrian Chadd } 12864336716bSAdrian Chadd 12874336716bSAdrian Chadd /* 12884336716bSAdrian Chadd * Stub for routines from kernel. 12894336716bSAdrian Chadd */ 12904336716bSAdrian Chadd void 12914336716bSAdrian Chadd panic(const char *fmt, ...) 12924336716bSAdrian Chadd { 12934336716bSAdrian Chadd va_list ap; 12944336716bSAdrian Chadd va_start(ap, fmt); 12954336716bSAdrian Chadd pfatal("INTERNAL INCONSISTENCY:"); 129615fca934SKirk McKusick (void)vfprintf(stdout, fmt, ap); 12974336716bSAdrian Chadd va_end(ap); 12984336716bSAdrian Chadd exit(EEXIT); 12994336716bSAdrian Chadd } 1300