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 675cc52631SKirk McKusick int sujrecovery = 0; 685cc52631SKirk McKusick 695cc52631SKirk McKusick static struct bufarea *allocbuf(const char *); 705cc52631SKirk 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); 745cc52631SKirk 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 */ 825cc52631SKirk McKusick static TAILQ_HEAD(bufqueue, bufarea) bufqueuehd; /* head of buffer cache LRU */ 835cc52631SKirk McKusick static LIST_HEAD(bufhash, bufarea) bufhashhd[HASHSIZE]; /* buffer hash list */ 842ec5c914SKirk McKusick static int numbufs; /* size of buffer cache */ 855cc52631SKirk McKusick static int cachelookups; /* number of cache lookups */ 865cc52631SKirk McKusick static int cachereads; /* number of cache reads */ 877703a6ffSScott Long static int flushtries; /* number of tries to reclaim memory */ 887703a6ffSScott Long 895cc52631SKirk McKusick char *buftype[BT_NUMBUFTYPES] = BT_NAMES; 905cc52631SKirk McKusick 917703a6ffSScott Long void 927703a6ffSScott Long fsutilinit(void) 937703a6ffSScott Long { 947703a6ffSScott Long diskreads = totaldiskreads = totalreads = 0; 957703a6ffSScott Long bzero(&startpass, sizeof(struct timespec)); 967703a6ffSScott Long bzero(&finishpass, sizeof(struct timespec)); 977703a6ffSScott Long bzero(&slowio_starttime, sizeof(struct timeval)); 987703a6ffSScott Long slowio_delay_usec = 10000; 997703a6ffSScott Long slowio_pollcnt = 0; 1007703a6ffSScott Long flushtries = 0; 1017703a6ffSScott Long } 1024336716bSAdrian Chadd 1034336716bSAdrian Chadd int 1041c85e6a3SKirk McKusick ftypeok(union dinode *dp) 1054336716bSAdrian Chadd { 106d8ba45e2SEd Maste switch (DIP(dp, di_mode) & IFMT) { 1074336716bSAdrian Chadd 108d8ba45e2SEd Maste case IFDIR: 109d8ba45e2SEd Maste case IFREG: 110d8ba45e2SEd Maste case IFBLK: 111d8ba45e2SEd Maste case IFCHR: 112d8ba45e2SEd Maste case IFLNK: 113d8ba45e2SEd Maste case IFSOCK: 114d8ba45e2SEd Maste case IFIFO: 1154336716bSAdrian Chadd return (1); 1164336716bSAdrian Chadd 1174336716bSAdrian Chadd default: 1184336716bSAdrian Chadd if (debug) 1191c85e6a3SKirk McKusick printf("bad file type 0%o\n", DIP(dp, di_mode)); 1204336716bSAdrian Chadd return (0); 1214336716bSAdrian Chadd } 1224336716bSAdrian Chadd } 1234336716bSAdrian Chadd 1244336716bSAdrian Chadd int 125599304a4SPoul-Henning Kamp reply(const char *question) 1264336716bSAdrian Chadd { 1274336716bSAdrian Chadd int persevere; 1284336716bSAdrian Chadd char c; 1294336716bSAdrian Chadd 1304336716bSAdrian Chadd if (preen) 1314336716bSAdrian Chadd pfatal("INTERNAL ERROR: GOT TO reply()"); 1327c332e97SKirk McKusick persevere = strcmp(question, "CONTINUE") == 0 || 1337c332e97SKirk McKusick strcmp(question, "LOOK FOR ALTERNATE SUPERBLOCKS") == 0; 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 { 1885cc52631SKirk McKusick int i; 1894336716bSAdrian Chadd 190fc56fd26SKirk McKusick if ((cgblk.b_un.b_buf = Malloc((unsigned int)sblock.fs_bsize)) == NULL) 1915cc52631SKirk McKusick errx(EEXIT, "Initial malloc(%d) failed", sblock.fs_bsize); 192ed75b5a1SKirk McKusick initbarea(&cgblk, BT_CYLGRP); 1935cc52631SKirk McKusick numbufs = cachelookups = cachereads = 0; 1945cc52631SKirk McKusick TAILQ_INIT(&bufqueuehd); 1955cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) 1965cc52631SKirk McKusick LIST_INIT(&bufhashhd[i]); 197ed75b5a1SKirk McKusick for (i = 0; i < BT_NUMBUFTYPES; i++) { 198ed75b5a1SKirk McKusick readtime[i].tv_sec = totalreadtime[i].tv_sec = 0; 199ed75b5a1SKirk McKusick readtime[i].tv_nsec = totalreadtime[i].tv_nsec = 0; 200ed75b5a1SKirk McKusick readcnt[i] = totalreadcnt[i] = 0; 201ed75b5a1SKirk McKusick } 2024336716bSAdrian Chadd } 2034336716bSAdrian Chadd 2045cc52631SKirk McKusick static struct bufarea * 2055cc52631SKirk McKusick allocbuf(const char *failreason) 2065cc52631SKirk McKusick { 2075cc52631SKirk McKusick struct bufarea *bp; 2085cc52631SKirk McKusick char *bufp; 2095cc52631SKirk McKusick 2105cc52631SKirk McKusick bp = (struct bufarea *)Malloc(sizeof(struct bufarea)); 2115cc52631SKirk McKusick bufp = Malloc((unsigned int)sblock.fs_bsize); 2125cc52631SKirk McKusick if (bp == NULL || bufp == NULL) { 2135cc52631SKirk McKusick errx(EEXIT, "%s", failreason); 2145cc52631SKirk McKusick /* NOTREACHED */ 2155cc52631SKirk McKusick } 2165cc52631SKirk McKusick numbufs++; 2175cc52631SKirk McKusick bp->b_un.b_buf = bufp; 2185cc52631SKirk McKusick TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list); 2195cc52631SKirk McKusick initbarea(bp, BT_UNKNOWN); 2205cc52631SKirk McKusick return (bp); 2215cc52631SKirk McKusick } 2225cc52631SKirk McKusick 2234336716bSAdrian Chadd /* 22481fbded2SKirk McKusick * Manage cylinder group buffers. 22585ee267aSKirk McKusick * 22685ee267aSKirk McKusick * Use getblk() here rather than cgget() because the cylinder group 22785ee267aSKirk McKusick * may be corrupted but we want it anyway so we can fix it. 22881fbded2SKirk McKusick */ 22981fbded2SKirk McKusick static struct bufarea *cgbufs; /* header for cylinder group cache */ 23081fbded2SKirk McKusick static int flushtries; /* number of tries to reclaim memory */ 23181fbded2SKirk McKusick 23281fbded2SKirk McKusick struct bufarea * 233957fc241SKirk McKusick cglookup(int cg) 23481fbded2SKirk McKusick { 23581fbded2SKirk McKusick struct bufarea *cgbp; 23681fbded2SKirk McKusick struct cg *cgp; 23781fbded2SKirk McKusick 2385cc52631SKirk McKusick if ((unsigned) cg >= sblock.fs_ncg) 2395cc52631SKirk McKusick errx(EEXIT, "cglookup: out of range cylinder group %d", cg); 24081fbded2SKirk McKusick if (cgbufs == NULL) { 241eff68496SKirk McKusick cgbufs = calloc(sblock.fs_ncg, sizeof(struct bufarea)); 24281fbded2SKirk McKusick if (cgbufs == NULL) 2435cc52631SKirk McKusick errx(EEXIT, "Cannot allocate cylinder group buffers"); 24481fbded2SKirk McKusick } 24581fbded2SKirk McKusick cgbp = &cgbufs[cg]; 24681fbded2SKirk McKusick if (cgbp->b_un.b_cg != NULL) 24781fbded2SKirk McKusick return (cgbp); 24881fbded2SKirk McKusick cgp = NULL; 24981fbded2SKirk McKusick if (flushtries == 0) 2505cc52631SKirk McKusick cgp = Malloc((unsigned int)sblock.fs_cgsize); 25181fbded2SKirk McKusick if (cgp == NULL) { 2525cc52631SKirk McKusick if (sujrecovery) 2535cc52631SKirk McKusick errx(EEXIT,"Ran out of memory during journal recovery"); 254c0bfa109SKirk McKusick flush(fswritefd, &cgblk); 25581fbded2SKirk McKusick getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); 25681fbded2SKirk McKusick return (&cgblk); 25781fbded2SKirk McKusick } 25881fbded2SKirk McKusick cgbp->b_un.b_cg = cgp; 25981fbded2SKirk McKusick initbarea(cgbp, BT_CYLGRP); 26081fbded2SKirk McKusick getblk(cgbp, cgtod(&sblock, cg), sblock.fs_cgsize); 26181fbded2SKirk McKusick return (cgbp); 26281fbded2SKirk McKusick } 26381fbded2SKirk McKusick 26481fbded2SKirk McKusick /* 2658ebae128SKirk McKusick * Mark a cylinder group buffer as dirty. 2668ebae128SKirk McKusick * Update its check-hash if they are enabled. 2678ebae128SKirk McKusick */ 2688ebae128SKirk McKusick void 2698ebae128SKirk McKusick cgdirty(struct bufarea *cgbp) 2708ebae128SKirk McKusick { 2718ebae128SKirk McKusick struct cg *cg; 2728ebae128SKirk McKusick 2738ebae128SKirk McKusick cg = cgbp->b_un.b_cg; 2748ebae128SKirk McKusick if ((sblock.fs_metackhash & CK_CYLGRP) != 0) { 2758ebae128SKirk McKusick cg->cg_ckhash = 0; 2768ebae128SKirk McKusick cg->cg_ckhash = 2778ebae128SKirk McKusick calculate_crc32c(~0L, (void *)cg, sblock.fs_cgsize); 2788ebae128SKirk McKusick } 2798ebae128SKirk McKusick dirty(cgbp); 2808ebae128SKirk McKusick } 2818ebae128SKirk McKusick 2828ebae128SKirk McKusick /* 28381fbded2SKirk McKusick * Attempt to flush a cylinder group cache entry. 28481fbded2SKirk McKusick * Return whether the flush was successful. 28581fbded2SKirk McKusick */ 28681fbded2SKirk McKusick int 28781fbded2SKirk McKusick flushentry(void) 28881fbded2SKirk McKusick { 28981fbded2SKirk McKusick struct bufarea *cgbp; 29081fbded2SKirk McKusick 2915cc52631SKirk McKusick if (sujrecovery || flushtries == sblock.fs_ncg || cgbufs == NULL) 292eff68496SKirk McKusick return (0); 29381fbded2SKirk McKusick cgbp = &cgbufs[flushtries++]; 29481fbded2SKirk McKusick if (cgbp->b_un.b_cg == NULL) 29581fbded2SKirk McKusick return (0); 29681fbded2SKirk McKusick flush(fswritefd, cgbp); 29781fbded2SKirk McKusick free(cgbp->b_un.b_buf); 29881fbded2SKirk McKusick cgbp->b_un.b_buf = NULL; 29981fbded2SKirk McKusick return (1); 30081fbded2SKirk McKusick } 30181fbded2SKirk McKusick 30281fbded2SKirk McKusick /* 3034336716bSAdrian Chadd * Manage a cache of directory blocks. 3044336716bSAdrian Chadd */ 3054336716bSAdrian Chadd struct bufarea * 306ed75b5a1SKirk McKusick getdatablk(ufs2_daddr_t blkno, long size, int type) 3074336716bSAdrian Chadd { 3083d438ad6SDavid E. O'Brien struct bufarea *bp; 3095cc52631SKirk McKusick struct bufhash *bhdp; 3104336716bSAdrian Chadd 3115cc52631SKirk McKusick cachelookups++; 3125cc52631SKirk McKusick /* If out of range, return empty buffer with b_err == -1 */ 3135cc52631SKirk McKusick if (type != BT_INODES && chkrange(blkno, size / sblock.fs_fsize)) { 3145cc52631SKirk McKusick blkno = -1; 3155cc52631SKirk McKusick type = BT_EMPTY; 3165cc52631SKirk McKusick } 3175cc52631SKirk McKusick bhdp = &bufhashhd[HASH(blkno)]; 3185cc52631SKirk McKusick LIST_FOREACH(bp, bhdp, b_hash) 3195cc52631SKirk McKusick if (bp->b_bno == fsbtodb(&sblock, blkno)) { 3205cc52631SKirk McKusick if (debug && bp->b_size != size) { 3215cc52631SKirk McKusick prtbuf("getdatablk: size mismatch", bp); 3225cc52631SKirk McKusick pfatal("getdatablk: b_size %d != size %ld\n", 3235cc52631SKirk McKusick bp->b_size, size); 3245cc52631SKirk McKusick } 3254336716bSAdrian Chadd goto foundit; 3265cc52631SKirk McKusick } 3275cc52631SKirk McKusick /* 3285cc52631SKirk McKusick * Move long-term busy buffer back to the front of the LRU so we 3295cc52631SKirk McKusick * do not endless inspect them for recycling. 3305cc52631SKirk McKusick */ 3315cc52631SKirk McKusick bp = TAILQ_LAST(&bufqueuehd, bufqueue); 3325cc52631SKirk McKusick if (bp != NULL && bp->b_refcnt != 0) { 3335cc52631SKirk McKusick TAILQ_REMOVE(&bufqueuehd, bp, b_list); 3345cc52631SKirk McKusick TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list); 3355cc52631SKirk McKusick } 3365cc52631SKirk McKusick /* 3375cc52631SKirk McKusick * Allocate up to the minimum number of buffers before 3385cc52631SKirk McKusick * considering recycling any of them. 3395cc52631SKirk McKusick */ 3405cc52631SKirk McKusick if (size > sblock.fs_bsize) 3415cc52631SKirk McKusick errx(EEXIT, "Excessive buffer size %ld > %d\n", size, 3425cc52631SKirk McKusick sblock.fs_bsize); 3435cc52631SKirk McKusick if (numbufs < MINBUFS) { 3445cc52631SKirk McKusick bp = allocbuf("cannot create minimal buffer pool"); 3455cc52631SKirk McKusick } else if (sujrecovery) { 3465cc52631SKirk McKusick /* 3475cc52631SKirk McKusick * SUJ recovery does not want anything written until it 3485cc52631SKirk McKusick * has successfully completed (so it can fail back to 3495cc52631SKirk McKusick * full fsck). Thus, we can only recycle clean buffers. 3505cc52631SKirk McKusick */ 3515cc52631SKirk McKusick TAILQ_FOREACH_REVERSE(bp, &bufqueuehd, bufqueue, b_list) 3525cc52631SKirk McKusick if ((bp->b_flags & B_DIRTY) == 0 && bp->b_refcnt == 0) 3534336716bSAdrian Chadd break; 3542ec5c914SKirk McKusick if (bp == NULL) 3555cc52631SKirk McKusick bp = allocbuf("Ran out of memory during " 3565cc52631SKirk McKusick "journal recovery"); 3575cc52631SKirk McKusick else 3585cc52631SKirk McKusick LIST_REMOVE(bp, b_hash); 3595cc52631SKirk McKusick } else { 3605cc52631SKirk McKusick /* 3615cc52631SKirk McKusick * Recycle oldest non-busy buffer. 3625cc52631SKirk McKusick */ 3635cc52631SKirk McKusick TAILQ_FOREACH_REVERSE(bp, &bufqueuehd, bufqueue, b_list) 3645cc52631SKirk McKusick if (bp->b_refcnt == 0) 3655cc52631SKirk McKusick break; 3665cc52631SKirk McKusick if (bp == NULL) 3675cc52631SKirk McKusick bp = allocbuf("Ran out of memory for buffers"); 3685cc52631SKirk McKusick else 3695cc52631SKirk McKusick LIST_REMOVE(bp, b_hash); 3705cc52631SKirk McKusick } 3715cc52631SKirk McKusick flush(fswritefd, bp); 372ed75b5a1SKirk McKusick bp->b_type = type; 3735cc52631SKirk McKusick LIST_INSERT_HEAD(bhdp, bp, b_hash); 3744336716bSAdrian Chadd getblk(bp, blkno, size); 3755cc52631SKirk McKusick cachereads++; 3764336716bSAdrian Chadd /* fall through */ 3774336716bSAdrian Chadd foundit: 3785cc52631SKirk McKusick if (debug && bp->b_type != type) { 3795cc52631SKirk McKusick printf("getdatablk: buffer type changed to %s", 3805cc52631SKirk McKusick BT_BUFTYPE(type)); 3815cc52631SKirk McKusick prtbuf("", bp); 3825cc52631SKirk McKusick } 3835cc52631SKirk McKusick TAILQ_REMOVE(&bufqueuehd, bp, b_list); 3845cc52631SKirk McKusick TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list); 3855cc52631SKirk McKusick if (bp->b_errs == 0) 3865cc52631SKirk McKusick bp->b_refcnt++; 3874336716bSAdrian Chadd return (bp); 3884336716bSAdrian Chadd } 3894336716bSAdrian Chadd 3904336716bSAdrian Chadd void 3911c85e6a3SKirk McKusick getblk(struct bufarea *bp, ufs2_daddr_t blk, long size) 3924336716bSAdrian Chadd { 3931c85e6a3SKirk McKusick ufs2_daddr_t dblk; 394ed75b5a1SKirk McKusick struct timespec start, finish; 3954336716bSAdrian Chadd 3964336716bSAdrian Chadd dblk = fsbtodb(&sblock, blk); 397ed75b5a1SKirk McKusick if (bp->b_bno == dblk) { 398ed75b5a1SKirk McKusick totalreads++; 399ed75b5a1SKirk McKusick } else { 400ed75b5a1SKirk McKusick if (debug) { 401ed75b5a1SKirk McKusick readcnt[bp->b_type]++; 402ed75b5a1SKirk McKusick clock_gettime(CLOCK_REALTIME_PRECISE, &start); 403ed75b5a1SKirk McKusick } 4045cc52631SKirk McKusick if (bp->b_type != BT_EMPTY) 4055cc52631SKirk McKusick bp->b_errs = 4065cc52631SKirk McKusick blread(fsreadfd, bp->b_un.b_buf, dblk, size); 4075cc52631SKirk McKusick else 4085cc52631SKirk McKusick bp->b_errs = -1; 409ed75b5a1SKirk McKusick if (debug) { 410ed75b5a1SKirk McKusick clock_gettime(CLOCK_REALTIME_PRECISE, &finish); 4116040822cSAlan Somers timespecsub(&finish, &start, &finish); 4126040822cSAlan Somers timespecadd(&readtime[bp->b_type], &finish, 4136040822cSAlan Somers &readtime[bp->b_type]); 414ed75b5a1SKirk McKusick } 4154336716bSAdrian Chadd bp->b_bno = dblk; 4164336716bSAdrian Chadd bp->b_size = size; 4174336716bSAdrian Chadd } 4184336716bSAdrian Chadd } 4194336716bSAdrian Chadd 4204336716bSAdrian Chadd void 4215cc52631SKirk McKusick brelse(struct bufarea *bp) 4224336716bSAdrian Chadd { 4234336716bSAdrian Chadd 4245cc52631SKirk McKusick if (bp->b_refcnt <= 0) 4255cc52631SKirk McKusick prtbuf("brelse: buffer with negative reference count", bp); 4265cc52631SKirk McKusick bp->b_refcnt--; 4275cc52631SKirk McKusick } 4285cc52631SKirk McKusick 4295cc52631SKirk McKusick void 4305cc52631SKirk McKusick flush(int fd, struct bufarea *bp) 4315cc52631SKirk McKusick { 4325cc52631SKirk McKusick struct inode ip; 4335cc52631SKirk McKusick 4345cc52631SKirk McKusick if ((bp->b_flags & B_DIRTY) == 0) 4354336716bSAdrian Chadd return; 4365cc52631SKirk McKusick bp->b_flags &= ~B_DIRTY; 4377578c6abSKirk McKusick if (fswritefd < 0) { 4387578c6abSKirk McKusick pfatal("WRITING IN READ_ONLY MODE.\n"); 4397578c6abSKirk McKusick return; 4407578c6abSKirk McKusick } 4414336716bSAdrian Chadd if (bp->b_errs != 0) 442599304a4SPoul-Henning Kamp pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n", 4434336716bSAdrian Chadd (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 444599304a4SPoul-Henning Kamp (long long)bp->b_bno); 4454336716bSAdrian Chadd bp->b_errs = 0; 446a6bbdf81SKirk McKusick /* 447a6bbdf81SKirk McKusick * Write using the appropriate function. 448a6bbdf81SKirk McKusick */ 449a6bbdf81SKirk McKusick switch (bp->b_type) { 450a6bbdf81SKirk McKusick case BT_SUPERBLK: 4514336716bSAdrian Chadd if (bp != &sblk) 452af89fcf7SLi-Wen Hsu pfatal("BUFFER %p DOES NOT MATCH SBLK %p\n", 453af89fcf7SLi-Wen Hsu bp, &sblk); 4548ebae128SKirk McKusick if (sbput(fd, bp->b_un.b_fs, 0) == 0) 455dffce215SKirk McKusick fsmodified = 1; 456a6bbdf81SKirk McKusick break; 457a6bbdf81SKirk McKusick case BT_CYLGRP: 4585cc52631SKirk McKusick if (sujrecovery) 4595cc52631SKirk McKusick cg_write(bp); 46085ee267aSKirk McKusick if (cgput(fswritefd, &sblock, bp->b_un.b_cg) == 0) 461a6bbdf81SKirk McKusick fsmodified = 1; 462a6bbdf81SKirk McKusick break; 4635cc52631SKirk McKusick case BT_INODES: 4645cc52631SKirk McKusick if (debug && sblock.fs_magic == FS_UFS2_MAGIC) { 4655cc52631SKirk McKusick struct ufs2_dinode *dp = bp->b_un.b_dinode2; 4665cc52631SKirk McKusick int i; 4675cc52631SKirk McKusick 4685cc52631SKirk McKusick for (i = 0; i < INOPB(&sblock); dp++, i++) { 4695cc52631SKirk McKusick if (ffs_verify_dinode_ckhash(&sblock, dp) == 0) 4705cc52631SKirk McKusick continue; 4715cc52631SKirk McKusick pwarn("flush: INODE CHECK-HASH FAILED"); 4725cc52631SKirk McKusick ip.i_bp = bp; 4735cc52631SKirk McKusick ip.i_dp = (union dinode *)dp; 4745cc52631SKirk McKusick ip.i_number = bp->b_index + i; 4755cc52631SKirk McKusick prtinode(&ip); 4765cc52631SKirk McKusick if (preen || reply("FIX") != 0) { 4775cc52631SKirk McKusick if (preen) 4785cc52631SKirk McKusick printf(" (FIXED)\n"); 4795cc52631SKirk McKusick ffs_update_dinode_ckhash(&sblock, dp); 4805cc52631SKirk McKusick inodirty(&ip); 4815cc52631SKirk McKusick } 4825cc52631SKirk McKusick } 4835cc52631SKirk McKusick } 4845cc52631SKirk McKusick /* FALLTHROUGH */ 485a6bbdf81SKirk McKusick default: 486a6bbdf81SKirk McKusick blwrite(fd, bp->b_un.b_buf, bp->b_bno, bp->b_size); 487a6bbdf81SKirk McKusick break; 488a6bbdf81SKirk McKusick } 4894336716bSAdrian Chadd } 4904336716bSAdrian Chadd 4915cc52631SKirk McKusick /* 4925cc52631SKirk McKusick * Journaled soft updates does not maintain cylinder group summary 4935cc52631SKirk McKusick * information during cleanup, so this routine recalculates the summary 4945cc52631SKirk McKusick * information and updates the superblock summary in preparation for 4955cc52631SKirk McKusick * writing out the cylinder group. 4965cc52631SKirk McKusick */ 4975cc52631SKirk McKusick static void 4985cc52631SKirk McKusick cg_write(struct bufarea *bp) 4995cc52631SKirk McKusick { 5005cc52631SKirk McKusick ufs1_daddr_t fragno, cgbno, maxbno; 5015cc52631SKirk McKusick u_int8_t *blksfree; 5025cc52631SKirk McKusick struct cg *cgp; 5035cc52631SKirk McKusick int blk; 5045cc52631SKirk McKusick int i; 5055cc52631SKirk McKusick 5065cc52631SKirk McKusick /* 5075cc52631SKirk McKusick * Fix the frag and cluster summary. 5085cc52631SKirk McKusick */ 5095cc52631SKirk McKusick cgp = bp->b_un.b_cg; 5105cc52631SKirk McKusick cgp->cg_cs.cs_nbfree = 0; 5115cc52631SKirk McKusick cgp->cg_cs.cs_nffree = 0; 5125cc52631SKirk McKusick bzero(&cgp->cg_frsum, sizeof(cgp->cg_frsum)); 5135cc52631SKirk McKusick maxbno = fragstoblks(&sblock, sblock.fs_fpg); 5145cc52631SKirk McKusick if (sblock.fs_contigsumsize > 0) { 5155cc52631SKirk McKusick for (i = 1; i <= sblock.fs_contigsumsize; i++) 5165cc52631SKirk McKusick cg_clustersum(cgp)[i] = 0; 5175cc52631SKirk McKusick bzero(cg_clustersfree(cgp), howmany(maxbno, CHAR_BIT)); 5185cc52631SKirk McKusick } 5195cc52631SKirk McKusick blksfree = cg_blksfree(cgp); 5205cc52631SKirk McKusick for (cgbno = 0; cgbno < maxbno; cgbno++) { 5215cc52631SKirk McKusick if (ffs_isfreeblock(&sblock, blksfree, cgbno)) 5225cc52631SKirk McKusick continue; 5235cc52631SKirk McKusick if (ffs_isblock(&sblock, blksfree, cgbno)) { 5245cc52631SKirk McKusick ffs_clusteracct(&sblock, cgp, cgbno, 1); 5255cc52631SKirk McKusick cgp->cg_cs.cs_nbfree++; 5265cc52631SKirk McKusick continue; 5275cc52631SKirk McKusick } 5285cc52631SKirk McKusick fragno = blkstofrags(&sblock, cgbno); 5295cc52631SKirk McKusick blk = blkmap(&sblock, blksfree, fragno); 5305cc52631SKirk McKusick ffs_fragacct(&sblock, blk, cgp->cg_frsum, 1); 5315cc52631SKirk McKusick for (i = 0; i < sblock.fs_frag; i++) 5325cc52631SKirk McKusick if (isset(blksfree, fragno + i)) 5335cc52631SKirk McKusick cgp->cg_cs.cs_nffree++; 5345cc52631SKirk McKusick } 5355cc52631SKirk McKusick /* 5365cc52631SKirk McKusick * Update the superblock cg summary from our now correct values 5375cc52631SKirk McKusick * before writing the block. 5385cc52631SKirk McKusick */ 5395cc52631SKirk McKusick sblock.fs_cs(&sblock, cgp->cg_cgx) = cgp->cg_cs; 5405cc52631SKirk McKusick } 5415cc52631SKirk McKusick 5427578c6abSKirk McKusick void 543599304a4SPoul-Henning Kamp rwerror(const char *mesg, ufs2_daddr_t blk) 5444336716bSAdrian Chadd { 5454336716bSAdrian Chadd 54615fca934SKirk McKusick if (bkgrdcheck) 54715fca934SKirk McKusick exit(EEXIT); 5484336716bSAdrian Chadd if (preen == 0) 5494336716bSAdrian Chadd printf("\n"); 550bf58d635SIan Dowse pfatal("CANNOT %s: %ld", mesg, (long)blk); 5514336716bSAdrian Chadd if (reply("CONTINUE") == 0) 5524336716bSAdrian Chadd exit(EEXIT); 5534336716bSAdrian Chadd } 5544336716bSAdrian Chadd 5554336716bSAdrian Chadd void 556b70cd7eeSWarner Losh ckfini(int markclean) 5574336716bSAdrian Chadd { 5583d438ad6SDavid E. O'Brien struct bufarea *bp, *nbp; 559fc56fd26SKirk McKusick struct inoinfo *inp, *ninp; 560fc56fd26SKirk McKusick int ofsmodified, cnt, cg, i; 5614336716bSAdrian Chadd 5627578c6abSKirk McKusick if (bkgrdflag) { 5637578c6abSKirk McKusick unlink(snapname); 5647578c6abSKirk McKusick if ((!(sblock.fs_flags & FS_UNCLEAN)) != markclean) { 5657578c6abSKirk McKusick cmd.value = FS_UNCLEAN; 5667578c6abSKirk McKusick cmd.size = markclean ? -1 : 1; 5677578c6abSKirk McKusick if (sysctlbyname("vfs.ffs.setflags", 0, 0, 5687578c6abSKirk McKusick &cmd, sizeof cmd) == -1) 569c0bfa109SKirk McKusick pwarn("CANNOT SET FILE SYSTEM DIRTY FLAG\n"); 5707578c6abSKirk McKusick if (!preen) { 5717578c6abSKirk McKusick printf("\n***** FILE SYSTEM MARKED %s *****\n", 5727578c6abSKirk McKusick markclean ? "CLEAN" : "DIRTY"); 5737578c6abSKirk McKusick if (!markclean) 5747578c6abSKirk McKusick rerun = 1; 5757578c6abSKirk McKusick } 5767578c6abSKirk McKusick } else if (!preen && !markclean) { 5777578c6abSKirk McKusick printf("\n***** FILE SYSTEM STILL DIRTY *****\n"); 5787578c6abSKirk McKusick rerun = 1; 5797578c6abSKirk McKusick } 580c0bfa109SKirk McKusick bkgrdflag = 0; 5817578c6abSKirk McKusick } 58220123b25SRobert Wing if (debug && cachelookups > 0) 5835cc52631SKirk McKusick printf("cache with %d buffers missed %d of %d (%d%%)\n", 5845cc52631SKirk McKusick numbufs, cachereads, cachelookups, 5855cc52631SKirk McKusick (int)(cachereads * 100 / cachelookups)); 5864336716bSAdrian Chadd if (fswritefd < 0) { 5874336716bSAdrian Chadd (void)close(fsreadfd); 5884336716bSAdrian Chadd return; 5894336716bSAdrian Chadd } 5905cc52631SKirk McKusick /* 5915cc52631SKirk McKusick * To remain idempotent with partial truncations the buffers 5925cc52631SKirk McKusick * must be flushed in this order: 5935cc52631SKirk McKusick * 1) cylinder groups (bitmaps) 5945cc52631SKirk McKusick * 2) indirect, directory, external attribute, and data blocks 5955cc52631SKirk McKusick * 3) inode blocks 5965cc52631SKirk McKusick * 4) superblock 5975cc52631SKirk McKusick * This ordering preserves access to the modified pointers 5985cc52631SKirk McKusick * until they are freed. 5995cc52631SKirk McKusick */ 6005cc52631SKirk McKusick /* Step 1: cylinder groups */ 6015cc52631SKirk McKusick if (debug) 6025cc52631SKirk McKusick printf("Flush Cylinder groups\n"); 6035cc52631SKirk McKusick if (cgbufs != NULL) { 6045cc52631SKirk McKusick for (cnt = 0; cnt < sblock.fs_ncg; cnt++) { 6055cc52631SKirk McKusick if (cgbufs[cnt].b_un.b_cg == NULL) 6065cc52631SKirk McKusick continue; 6075cc52631SKirk McKusick flush(fswritefd, &cgbufs[cnt]); 6085cc52631SKirk McKusick free(cgbufs[cnt].b_un.b_cg); 6095cc52631SKirk McKusick } 6105cc52631SKirk McKusick free(cgbufs); 611fc56fd26SKirk McKusick cgbufs = NULL; 6125cc52631SKirk McKusick } 6135cc52631SKirk McKusick flush(fswritefd, &cgblk); 6145cc52631SKirk McKusick free(cgblk.b_un.b_buf); 615fc56fd26SKirk McKusick cgblk.b_un.b_buf = NULL; 6165cc52631SKirk McKusick cnt = 0; 6175cc52631SKirk McKusick /* Step 2: indirect, directory, external attribute, and data blocks */ 6185cc52631SKirk McKusick if (debug) 6195cc52631SKirk McKusick printf("Flush indirect, directory, external attribute, " 6205cc52631SKirk McKusick "and data blocks\n"); 621fc56fd26SKirk McKusick if (pdirbp != NULL) { 6225cc52631SKirk McKusick brelse(pdirbp); 623fc56fd26SKirk McKusick pdirbp = NULL; 624fc56fd26SKirk McKusick } 6255cc52631SKirk McKusick TAILQ_FOREACH_REVERSE_SAFE(bp, &bufqueuehd, bufqueue, b_list, nbp) { 6265cc52631SKirk McKusick switch (bp->b_type) { 6275cc52631SKirk McKusick /* These should not be in the buffer cache list */ 6285cc52631SKirk McKusick case BT_UNKNOWN: 6295cc52631SKirk McKusick case BT_SUPERBLK: 6305cc52631SKirk McKusick case BT_CYLGRP: 6315cc52631SKirk McKusick default: 6325cc52631SKirk McKusick prtbuf("ckfini: improper buffer type on cache list",bp); 6335cc52631SKirk McKusick continue; 6345cc52631SKirk McKusick /* These are the ones to flush in this step */ 6355cc52631SKirk McKusick case BT_EMPTY: 6365cc52631SKirk McKusick if (bp->b_bno >= 0) 6375cc52631SKirk McKusick pfatal("Unused BT_EMPTY buffer for block %jd\n", 6385cc52631SKirk McKusick (intmax_t)bp->b_bno); 6395cc52631SKirk McKusick /* FALLTHROUGH */ 6405cc52631SKirk McKusick case BT_LEVEL1: 6415cc52631SKirk McKusick case BT_LEVEL2: 6425cc52631SKirk McKusick case BT_LEVEL3: 6435cc52631SKirk McKusick case BT_EXTATTR: 6445cc52631SKirk McKusick case BT_DIRDATA: 6455cc52631SKirk McKusick case BT_DATA: 6465cc52631SKirk McKusick break; 6475cc52631SKirk McKusick /* These are the ones to flush in the next step */ 6485cc52631SKirk McKusick case BT_INODES: 6495cc52631SKirk McKusick continue; 6505cc52631SKirk McKusick } 6515cc52631SKirk McKusick if (debug && bp->b_refcnt != 0) { 6525cc52631SKirk McKusick prtbuf("ckfini: clearing in-use buffer", bp); 6535cc52631SKirk McKusick pfatal("ckfini: clearing in-use buffer\n"); 6545cc52631SKirk McKusick } 6555cc52631SKirk McKusick TAILQ_REMOVE(&bufqueuehd, bp, b_list); 6565cc52631SKirk McKusick cnt++; 6575cc52631SKirk McKusick flush(fswritefd, bp); 6585cc52631SKirk McKusick free(bp->b_un.b_buf); 6595cc52631SKirk McKusick free((char *)bp); 6605cc52631SKirk McKusick } 6615cc52631SKirk McKusick /* Step 3: inode blocks */ 6625cc52631SKirk McKusick if (debug) 6635cc52631SKirk McKusick printf("Flush inode blocks\n"); 664fc56fd26SKirk McKusick if (icachebp != NULL) { 6655cc52631SKirk McKusick brelse(icachebp); 666fc56fd26SKirk McKusick icachebp = NULL; 667fc56fd26SKirk McKusick } 6685cc52631SKirk McKusick TAILQ_FOREACH_REVERSE_SAFE(bp, &bufqueuehd, bufqueue, b_list, nbp) { 6695cc52631SKirk McKusick if (debug && bp->b_refcnt != 0) { 6705cc52631SKirk McKusick prtbuf("ckfini: clearing in-use buffer", bp); 6715cc52631SKirk McKusick pfatal("ckfini: clearing in-use buffer\n"); 6725cc52631SKirk McKusick } 6735cc52631SKirk McKusick TAILQ_REMOVE(&bufqueuehd, bp, b_list); 6745cc52631SKirk McKusick cnt++; 6755cc52631SKirk McKusick flush(fswritefd, bp); 6765cc52631SKirk McKusick free(bp->b_un.b_buf); 6775cc52631SKirk McKusick free((char *)bp); 6785cc52631SKirk McKusick } 6795cc52631SKirk McKusick if (numbufs != cnt) 6805cc52631SKirk McKusick errx(EEXIT, "panic: lost %d buffers", numbufs - cnt); 6815cc52631SKirk McKusick /* Step 4: superblock */ 6825cc52631SKirk McKusick if (debug) 6835cc52631SKirk McKusick printf("Flush the superblock\n"); 6844336716bSAdrian Chadd flush(fswritefd, &sblk); 685be639cc8SKirk McKusick if (havesb && cursnapshot == 0 && 686be639cc8SKirk McKusick sblk.b_bno != sblock.fs_sblockloc / dev_bsize) { 687be639cc8SKirk McKusick if (preen || reply("UPDATE STANDARD SUPERBLOCK")) { 688be639cc8SKirk McKusick /* Change write destination to standard superblock */ 689dffce215SKirk McKusick sblock.fs_sblockactualloc = sblock.fs_sblockloc; 690ada981b2SKirk McKusick sblk.b_bno = sblock.fs_sblockloc / dev_bsize; 6914336716bSAdrian Chadd sbdirty(); 6924336716bSAdrian Chadd flush(fswritefd, &sblk); 693be639cc8SKirk McKusick } else { 694be639cc8SKirk McKusick markclean = 0; 695be639cc8SKirk McKusick } 6964336716bSAdrian Chadd } 6977578c6abSKirk McKusick if (cursnapshot == 0 && sblock.fs_clean != markclean) { 69868aff084SKirk McKusick if ((sblock.fs_clean = markclean) != 0) { 69938375c40SKirk McKusick sblock.fs_flags &= ~(FS_UNCLEAN | FS_NEEDSFSCK); 70068aff084SKirk McKusick sblock.fs_pendingblocks = 0; 70168aff084SKirk McKusick sblock.fs_pendinginodes = 0; 70268aff084SKirk McKusick } 7034336716bSAdrian Chadd sbdirty(); 7044336716bSAdrian Chadd ofsmodified = fsmodified; 7054336716bSAdrian Chadd flush(fswritefd, &sblk); 7064336716bSAdrian Chadd fsmodified = ofsmodified; 7074336716bSAdrian Chadd if (!preen) { 7084336716bSAdrian Chadd printf("\n***** FILE SYSTEM MARKED %s *****\n", 7094336716bSAdrian Chadd markclean ? "CLEAN" : "DIRTY"); 7104336716bSAdrian Chadd if (!markclean) 7114336716bSAdrian Chadd rerun = 1; 7124336716bSAdrian Chadd } 713910b491eSKirk McKusick } else if (!preen) { 714910b491eSKirk McKusick if (markclean) { 715910b491eSKirk McKusick printf("\n***** FILE SYSTEM IS CLEAN *****\n"); 716910b491eSKirk McKusick } else { 7174336716bSAdrian Chadd printf("\n***** FILE SYSTEM STILL DIRTY *****\n"); 7184336716bSAdrian Chadd rerun = 1; 7194336716bSAdrian Chadd } 720910b491eSKirk McKusick } 721fc56fd26SKirk McKusick /* 722fc56fd26SKirk McKusick * Free allocated tracking structures. 723fc56fd26SKirk McKusick */ 724fc56fd26SKirk McKusick if (blockmap != NULL) 725fc56fd26SKirk McKusick free(blockmap); 726fc56fd26SKirk McKusick blockmap = NULL; 727fc56fd26SKirk McKusick if (inostathead != NULL) { 728fc56fd26SKirk McKusick for (cg = 0; cg < sblock.fs_ncg; cg++) 729fc56fd26SKirk McKusick if (inostathead[cg].il_stat != NULL) 730fc56fd26SKirk McKusick free((char *)inostathead[cg].il_stat); 731fc56fd26SKirk McKusick free(inostathead); 732fc56fd26SKirk McKusick } 733fc56fd26SKirk McKusick inostathead = NULL; 734fc56fd26SKirk McKusick if (inpsort != NULL) 735fc56fd26SKirk McKusick free(inpsort); 736fc56fd26SKirk McKusick inpsort = NULL; 737fc56fd26SKirk McKusick if (inphead != NULL) { 738fc56fd26SKirk McKusick for (i = 0; i < dirhash; i++) { 739fc56fd26SKirk McKusick for (inp = inphead[i]; inp != NULL; inp = ninp) { 740fc56fd26SKirk McKusick ninp = inp->i_nexthash; 741fc56fd26SKirk McKusick free(inp); 742fc56fd26SKirk McKusick } 743fc56fd26SKirk McKusick } 744fc56fd26SKirk McKusick free(inphead); 745fc56fd26SKirk McKusick } 746fc56fd26SKirk McKusick inphead = NULL; 7475cc52631SKirk McKusick finalIOstats(); 7484336716bSAdrian Chadd (void)close(fsreadfd); 7494336716bSAdrian Chadd (void)close(fswritefd); 7504336716bSAdrian Chadd } 7514336716bSAdrian Chadd 752ed75b5a1SKirk McKusick /* 753ed75b5a1SKirk McKusick * Print out I/O statistics. 754ed75b5a1SKirk McKusick */ 755ed75b5a1SKirk McKusick void 756ed75b5a1SKirk McKusick IOstats(char *what) 757ed75b5a1SKirk McKusick { 758ed75b5a1SKirk McKusick int i; 759ed75b5a1SKirk McKusick 760ed75b5a1SKirk McKusick if (debug == 0) 761ed75b5a1SKirk McKusick return; 762ed75b5a1SKirk McKusick if (diskreads == 0) { 763ed75b5a1SKirk McKusick printf("%s: no I/O\n\n", what); 764ed75b5a1SKirk McKusick return; 765ed75b5a1SKirk McKusick } 766ed75b5a1SKirk McKusick if (startpass.tv_sec == 0) 767ed75b5a1SKirk McKusick startpass = startprog; 768ed75b5a1SKirk McKusick printf("%s: I/O statistics\n", what); 769ed75b5a1SKirk McKusick printIOstats(); 770ed75b5a1SKirk McKusick totaldiskreads += diskreads; 771ed75b5a1SKirk McKusick diskreads = 0; 772ed75b5a1SKirk McKusick for (i = 0; i < BT_NUMBUFTYPES; i++) { 7736040822cSAlan Somers timespecadd(&totalreadtime[i], &readtime[i], &totalreadtime[i]); 774ed75b5a1SKirk McKusick totalreadcnt[i] += readcnt[i]; 775ed75b5a1SKirk McKusick readtime[i].tv_sec = readtime[i].tv_nsec = 0; 776ed75b5a1SKirk McKusick readcnt[i] = 0; 777ed75b5a1SKirk McKusick } 778ed75b5a1SKirk McKusick clock_gettime(CLOCK_REALTIME_PRECISE, &startpass); 779ed75b5a1SKirk McKusick } 780ed75b5a1SKirk McKusick 781ed75b5a1SKirk McKusick void 782ed75b5a1SKirk McKusick finalIOstats(void) 783ed75b5a1SKirk McKusick { 784ed75b5a1SKirk McKusick int i; 785ed75b5a1SKirk McKusick 786ed75b5a1SKirk McKusick if (debug == 0) 787ed75b5a1SKirk McKusick return; 788ed75b5a1SKirk McKusick printf("Final I/O statistics\n"); 789ed75b5a1SKirk McKusick totaldiskreads += diskreads; 790ed75b5a1SKirk McKusick diskreads = totaldiskreads; 791ed75b5a1SKirk McKusick startpass = startprog; 792ed75b5a1SKirk McKusick for (i = 0; i < BT_NUMBUFTYPES; i++) { 7936040822cSAlan Somers timespecadd(&totalreadtime[i], &readtime[i], &totalreadtime[i]); 794ed75b5a1SKirk McKusick totalreadcnt[i] += readcnt[i]; 795ed75b5a1SKirk McKusick readtime[i] = totalreadtime[i]; 796ed75b5a1SKirk McKusick readcnt[i] = totalreadcnt[i]; 797ed75b5a1SKirk McKusick } 798ed75b5a1SKirk McKusick printIOstats(); 799ed75b5a1SKirk McKusick } 800ed75b5a1SKirk McKusick 801ed75b5a1SKirk McKusick static void printIOstats(void) 802ed75b5a1SKirk McKusick { 803ed75b5a1SKirk McKusick long long msec, totalmsec; 804ed75b5a1SKirk McKusick int i; 805ed75b5a1SKirk McKusick 806ed75b5a1SKirk McKusick clock_gettime(CLOCK_REALTIME_PRECISE, &finishpass); 8076040822cSAlan Somers timespecsub(&finishpass, &startpass, &finishpass); 808061ea59dSKirk McKusick printf("Running time: %jd.%03ld sec\n", 8094b3bbe04SSean Bruno (intmax_t)finishpass.tv_sec, finishpass.tv_nsec / 1000000); 810ed75b5a1SKirk McKusick printf("buffer reads by type:\n"); 811ed75b5a1SKirk McKusick for (totalmsec = 0, i = 0; i < BT_NUMBUFTYPES; i++) 812ed75b5a1SKirk McKusick totalmsec += readtime[i].tv_sec * 1000 + 813ed75b5a1SKirk McKusick readtime[i].tv_nsec / 1000000; 814ed75b5a1SKirk McKusick if (totalmsec == 0) 815ed75b5a1SKirk McKusick totalmsec = 1; 816ed75b5a1SKirk McKusick for (i = 0; i < BT_NUMBUFTYPES; i++) { 817ed75b5a1SKirk McKusick if (readcnt[i] == 0) 818ed75b5a1SKirk McKusick continue; 819061ea59dSKirk McKusick msec = 820061ea59dSKirk McKusick readtime[i].tv_sec * 1000 + readtime[i].tv_nsec / 1000000; 8214b3bbe04SSean Bruno printf("%21s:%8ld %2ld.%ld%% %4jd.%03ld sec %2lld.%lld%%\n", 822ed75b5a1SKirk McKusick buftype[i], readcnt[i], readcnt[i] * 100 / diskreads, 82381fbded2SKirk McKusick (readcnt[i] * 1000 / diskreads) % 10, 8244b3bbe04SSean Bruno (intmax_t)readtime[i].tv_sec, readtime[i].tv_nsec / 1000000, 825ed75b5a1SKirk McKusick msec * 100 / totalmsec, (msec * 1000 / totalmsec) % 10); 826ed75b5a1SKirk McKusick } 827ed75b5a1SKirk McKusick printf("\n"); 828ed75b5a1SKirk McKusick } 829ed75b5a1SKirk McKusick 8304336716bSAdrian Chadd int 831aef8d244SPawel Jakub Dawidek blread(int fd, char *buf, ufs2_daddr_t blk, long size) 8324336716bSAdrian Chadd { 8334336716bSAdrian Chadd char *cp; 8344336716bSAdrian Chadd int i, errs; 8354336716bSAdrian Chadd off_t offset; 8364336716bSAdrian Chadd 8374336716bSAdrian Chadd offset = blk; 8384336716bSAdrian Chadd offset *= dev_bsize; 8399d580d7cSIan Dowse if (bkgrdflag) 8409d580d7cSIan Dowse slowio_start(); 841ed75b5a1SKirk McKusick totalreads++; 842ed75b5a1SKirk McKusick diskreads++; 84304e5c6f1SEdward Tomasz Napierala if (pread(fd, buf, (int)size, offset) == size) { 8449d580d7cSIan Dowse if (bkgrdflag) 8459d580d7cSIan Dowse slowio_end(); 8464336716bSAdrian Chadd return (0); 8479d580d7cSIan Dowse } 848ce779f37SScott Long 849ce779f37SScott Long /* 850ce779f37SScott Long * This is handled specially here instead of in rwerror because 851ce779f37SScott Long * rwerror is used for all sorts of errors, not just true read/write 852ce779f37SScott Long * errors. It should be refactored and fixed. 853ce779f37SScott Long */ 854ce779f37SScott Long if (surrender) { 855ce779f37SScott Long pfatal("CANNOT READ_BLK: %ld", (long)blk); 856ce779f37SScott Long errx(EEXIT, "ABORTING DUE TO READ ERRORS"); 857ce779f37SScott Long } else 8587578c6abSKirk McKusick rwerror("READ BLK", blk); 859ce779f37SScott Long 8604336716bSAdrian Chadd errs = 0; 8614336716bSAdrian Chadd memset(buf, 0, (size_t)size); 8624336716bSAdrian Chadd printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 8634336716bSAdrian Chadd for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 86404e5c6f1SEdward Tomasz Napierala if (pread(fd, cp, (int)secsize, offset + i) != secsize) { 8654336716bSAdrian Chadd if (secsize != dev_bsize && dev_bsize != 1) 86684fc0d7eSMaxime Henrion printf(" %jd (%jd),", 86784fc0d7eSMaxime Henrion (intmax_t)(blk * dev_bsize + i) / secsize, 86884fc0d7eSMaxime Henrion (intmax_t)blk + i / dev_bsize); 8694336716bSAdrian Chadd else 87084fc0d7eSMaxime Henrion printf(" %jd,", (intmax_t)blk + i / dev_bsize); 8714336716bSAdrian Chadd errs++; 8724336716bSAdrian Chadd } 8734336716bSAdrian Chadd } 8744336716bSAdrian Chadd printf("\n"); 8754336716bSAdrian Chadd if (errs) 8764336716bSAdrian Chadd resolved = 0; 8774336716bSAdrian Chadd return (errs); 8784336716bSAdrian Chadd } 8794336716bSAdrian Chadd 8804336716bSAdrian Chadd void 8814a835375SDavid E. O'Brien blwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size) 8824336716bSAdrian Chadd { 8834336716bSAdrian Chadd int i; 8844336716bSAdrian Chadd char *cp; 8854336716bSAdrian Chadd off_t offset; 8864336716bSAdrian Chadd 8874336716bSAdrian Chadd if (fd < 0) 8884336716bSAdrian Chadd return; 8894336716bSAdrian Chadd offset = blk; 8904336716bSAdrian Chadd offset *= dev_bsize; 89104e5c6f1SEdward Tomasz Napierala if (pwrite(fd, buf, size, offset) == size) { 8924336716bSAdrian Chadd fsmodified = 1; 8934336716bSAdrian Chadd return; 8944336716bSAdrian Chadd } 8954336716bSAdrian Chadd resolved = 0; 8967578c6abSKirk McKusick rwerror("WRITE BLK", blk); 8974336716bSAdrian Chadd printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 8984336716bSAdrian Chadd for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 89904e5c6f1SEdward Tomasz Napierala if (pwrite(fd, cp, dev_bsize, offset + i) != dev_bsize) 90084fc0d7eSMaxime Henrion printf(" %jd,", (intmax_t)blk + i / dev_bsize); 9014336716bSAdrian Chadd printf("\n"); 9024336716bSAdrian Chadd return; 9034336716bSAdrian Chadd } 9044336716bSAdrian Chadd 9058d3dfc26SDag-Erling Smørgrav void 9068d3dfc26SDag-Erling Smørgrav blerase(int fd, ufs2_daddr_t blk, long size) 9078d3dfc26SDag-Erling Smørgrav { 9088d3dfc26SDag-Erling Smørgrav off_t ioarg[2]; 9098d3dfc26SDag-Erling Smørgrav 9108d3dfc26SDag-Erling Smørgrav if (fd < 0) 9118d3dfc26SDag-Erling Smørgrav return; 9128d3dfc26SDag-Erling Smørgrav ioarg[0] = blk * dev_bsize; 9138d3dfc26SDag-Erling Smørgrav ioarg[1] = size; 9148d3dfc26SDag-Erling Smørgrav ioctl(fd, DIOCGDELETE, ioarg); 9158d3dfc26SDag-Erling Smørgrav /* we don't really care if we succeed or not */ 9168d3dfc26SDag-Erling Smørgrav return; 9178d3dfc26SDag-Erling Smørgrav } 9188d3dfc26SDag-Erling Smørgrav 9198ce80d4bSDag-Erling Smørgrav /* 9208ce80d4bSDag-Erling Smørgrav * Fill a contiguous region with all-zeroes. Note ZEROBUFSIZE is by 9218ce80d4bSDag-Erling Smørgrav * definition a multiple of dev_bsize. 9228ce80d4bSDag-Erling Smørgrav */ 9232b5373deSDag-Erling Smørgrav void 9242b5373deSDag-Erling Smørgrav blzero(int fd, ufs2_daddr_t blk, long size) 9252b5373deSDag-Erling Smørgrav { 9262b5373deSDag-Erling Smørgrav static char *zero; 9272b5373deSDag-Erling Smørgrav off_t offset, len; 9282b5373deSDag-Erling Smørgrav 9292b5373deSDag-Erling Smørgrav if (fd < 0) 9302b5373deSDag-Erling Smørgrav return; 9312b5373deSDag-Erling Smørgrav if (zero == NULL) { 9328ce80d4bSDag-Erling Smørgrav zero = calloc(ZEROBUFSIZE, 1); 9332b5373deSDag-Erling Smørgrav if (zero == NULL) 9342b5373deSDag-Erling Smørgrav errx(EEXIT, "cannot allocate buffer pool"); 9352b5373deSDag-Erling Smørgrav } 9362b5373deSDag-Erling Smørgrav offset = blk * dev_bsize; 9372b5373deSDag-Erling Smørgrav if (lseek(fd, offset, 0) < 0) 9382b5373deSDag-Erling Smørgrav rwerror("SEEK BLK", blk); 9392b5373deSDag-Erling Smørgrav while (size > 0) { 9401120faabSMarcelo Araujo len = MIN(ZEROBUFSIZE, size); 9412b5373deSDag-Erling Smørgrav if (write(fd, zero, len) != len) 9422b5373deSDag-Erling Smørgrav rwerror("WRITE BLK", blk); 9432b5373deSDag-Erling Smørgrav blk += len / dev_bsize; 9442b5373deSDag-Erling Smørgrav size -= len; 9452b5373deSDag-Erling Smørgrav } 9462b5373deSDag-Erling Smørgrav } 9472b5373deSDag-Erling Smørgrav 9484336716bSAdrian Chadd /* 949910b491eSKirk McKusick * Verify cylinder group's magic number and other parameters. If the 950910b491eSKirk McKusick * test fails, offer an option to rebuild the whole cylinder group. 95114320f1eSXin LI */ 952495b1baaSKirk McKusick #undef CHK 953495b1baaSKirk McKusick #define CHK(lhs, op, rhs, fmt) \ 954495b1baaSKirk McKusick if (lhs op rhs) { \ 955*27c6009eSKirk McKusick pwarn("UFS%d cylinder group %d failed: " \ 956*27c6009eSKirk McKusick "%s (" #fmt ") %s %s (" #fmt ")\n", \ 957*27c6009eSKirk McKusick sblock.fs_magic == FS_UFS1_MAGIC ? 1 : 2, cg, \ 958*27c6009eSKirk McKusick #lhs, (intmax_t)lhs, #op, #rhs, (intmax_t)rhs); \ 959495b1baaSKirk McKusick error = 1; \ 960495b1baaSKirk McKusick } 961910b491eSKirk McKusick int 9625cc52631SKirk McKusick check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild) 96314320f1eSXin LI { 96481fbded2SKirk McKusick struct cg *cgp = cgbp->b_un.b_cg; 9655cc52631SKirk McKusick uint32_t cghash, calchash; 966689724cbSKirk McKusick static int prevfailcg = -1; 967495b1baaSKirk McKusick int error; 96814320f1eSXin LI 969910b491eSKirk McKusick /* 970910b491eSKirk McKusick * Extended cylinder group checks. 971910b491eSKirk McKusick */ 9725cc52631SKirk McKusick calchash = cgp->cg_ckhash; 9736385cabdSKirk McKusick if ((sblock.fs_metackhash & CK_CYLGRP) != 0 && 9746385cabdSKirk McKusick (ckhashadd & CK_CYLGRP) == 0) { 9755cc52631SKirk McKusick cghash = cgp->cg_ckhash; 9765cc52631SKirk McKusick cgp->cg_ckhash = 0; 9775cc52631SKirk McKusick calchash = calculate_crc32c(~0L, (void *)cgp, sblock.fs_cgsize); 9785cc52631SKirk McKusick cgp->cg_ckhash = cghash; 9795cc52631SKirk McKusick } 980495b1baaSKirk McKusick error = 0; 981495b1baaSKirk McKusick CHK(cgp->cg_ckhash, !=, calchash, "%jd"); 982495b1baaSKirk McKusick CHK(cg_chkmagic(cgp), ==, 0, "%jd"); 983495b1baaSKirk McKusick CHK(cgp->cg_cgx, !=, cg, "%jd"); 984495b1baaSKirk McKusick CHK(cgp->cg_ndblk, >, sblock.fs_fpg, "%jd"); 985495b1baaSKirk McKusick if (sblock.fs_magic == FS_UFS1_MAGIC) { 986495b1baaSKirk McKusick CHK(cgp->cg_old_niblk, !=, sblock.fs_ipg, "%jd"); 987495b1baaSKirk McKusick CHK(cgp->cg_old_ncyl, >, sblock.fs_old_cpg, "%jd"); 988495b1baaSKirk McKusick } else if (sblock.fs_magic == FS_UFS2_MAGIC) { 989495b1baaSKirk McKusick CHK(cgp->cg_niblk, !=, sblock.fs_ipg, "%jd"); 990495b1baaSKirk McKusick CHK(cgp->cg_initediblk, >, sblock.fs_ipg, "%jd"); 991910b491eSKirk McKusick } 992495b1baaSKirk McKusick if (error == 0) 993495b1baaSKirk McKusick return (1); 994689724cbSKirk McKusick if (prevfailcg == cg) 9955cc52631SKirk McKusick return (0); 996689724cbSKirk McKusick prevfailcg = cg; 997689724cbSKirk McKusick pfatal("CYLINDER GROUP %d: INTEGRITY CHECK FAILED", cg); 998689724cbSKirk McKusick if (!request_rebuild) { 999689724cbSKirk McKusick printf("\n"); 1000689724cbSKirk McKusick return (0); 1001689724cbSKirk McKusick } 1002910b491eSKirk McKusick if (!reply("REBUILD CYLINDER GROUP")) { 1003910b491eSKirk McKusick printf("YOU WILL NEED TO RERUN FSCK.\n"); 1004910b491eSKirk McKusick rerun = 1; 1005910b491eSKirk McKusick return (1); 1006910b491eSKirk McKusick } 1007910b491eSKirk McKusick /* 1008910b491eSKirk McKusick * Zero out the cylinder group and then initialize critical fields. 1009910b491eSKirk McKusick * Bit maps and summaries will be recalculated by later passes. 1010910b491eSKirk McKusick */ 101114320f1eSXin LI memset(cgp, 0, (size_t)sblock.fs_cgsize); 1012910b491eSKirk McKusick cgp->cg_magic = CG_MAGIC; 101314320f1eSXin LI cgp->cg_cgx = cg; 101414320f1eSXin LI cgp->cg_niblk = sblock.fs_ipg; 10151120faabSMarcelo Araujo cgp->cg_initediblk = MIN(sblock.fs_ipg, 2 * INOPB(&sblock)); 1016910b491eSKirk McKusick if (cgbase(&sblock, cg) + sblock.fs_fpg < sblock.fs_size) 1017910b491eSKirk McKusick cgp->cg_ndblk = sblock.fs_fpg; 1018910b491eSKirk McKusick else 101914320f1eSXin LI cgp->cg_ndblk = sblock.fs_size - cgbase(&sblock, cg); 1020910b491eSKirk McKusick cgp->cg_iusedoff = &cgp->cg_space[0] - (u_char *)(&cgp->cg_firstfield); 1021910b491eSKirk McKusick if (sblock.fs_magic == FS_UFS1_MAGIC) { 1022910b491eSKirk McKusick cgp->cg_niblk = 0; 1023910b491eSKirk McKusick cgp->cg_initediblk = 0; 1024910b491eSKirk McKusick cgp->cg_old_ncyl = sblock.fs_old_cpg; 1025910b491eSKirk McKusick cgp->cg_old_niblk = sblock.fs_ipg; 1026910b491eSKirk McKusick cgp->cg_old_btotoff = cgp->cg_iusedoff; 1027910b491eSKirk McKusick cgp->cg_old_boff = cgp->cg_old_btotoff + 1028910b491eSKirk McKusick sblock.fs_old_cpg * sizeof(int32_t); 1029910b491eSKirk McKusick cgp->cg_iusedoff = cgp->cg_old_boff + 1030910b491eSKirk McKusick sblock.fs_old_cpg * sizeof(u_int16_t); 1031910b491eSKirk McKusick } 1032910b491eSKirk McKusick cgp->cg_freeoff = cgp->cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT); 1033910b491eSKirk McKusick cgp->cg_nextfreeoff = cgp->cg_freeoff + howmany(sblock.fs_fpg,CHAR_BIT); 1034910b491eSKirk McKusick if (sblock.fs_contigsumsize > 0) { 1035910b491eSKirk McKusick cgp->cg_nclusterblks = cgp->cg_ndblk / sblock.fs_frag; 1036910b491eSKirk McKusick cgp->cg_clustersumoff = 1037910b491eSKirk McKusick roundup(cgp->cg_nextfreeoff, sizeof(u_int32_t)); 1038910b491eSKirk McKusick cgp->cg_clustersumoff -= sizeof(u_int32_t); 1039910b491eSKirk McKusick cgp->cg_clusteroff = cgp->cg_clustersumoff + 1040910b491eSKirk McKusick (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t); 1041910b491eSKirk McKusick cgp->cg_nextfreeoff = cgp->cg_clusteroff + 1042910b491eSKirk McKusick howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT); 1043910b491eSKirk McKusick } 1044495b1baaSKirk McKusick cgp->cg_ckhash = calculate_crc32c(~0L, (void *)cgp, sblock.fs_cgsize); 10458ebae128SKirk McKusick cgdirty(cgbp); 1046910b491eSKirk McKusick return (0); 104714320f1eSXin LI } 104814320f1eSXin LI 104914320f1eSXin LI /* 10504336716bSAdrian Chadd * allocate a data block with the specified number of fragments 10514336716bSAdrian Chadd */ 10521c85e6a3SKirk McKusick ufs2_daddr_t 1053b70cd7eeSWarner Losh allocblk(long frags) 10544336716bSAdrian Chadd { 10554336716bSAdrian Chadd int i, j, k, cg, baseblk; 105681fbded2SKirk McKusick struct bufarea *cgbp; 105781fbded2SKirk McKusick struct cg *cgp; 10584336716bSAdrian Chadd 10594336716bSAdrian Chadd if (frags <= 0 || frags > sblock.fs_frag) 10604336716bSAdrian Chadd return (0); 10614336716bSAdrian Chadd for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 10624336716bSAdrian Chadd for (j = 0; j <= sblock.fs_frag - frags; j++) { 10634336716bSAdrian Chadd if (testbmap(i + j)) 10644336716bSAdrian Chadd continue; 10654336716bSAdrian Chadd for (k = 1; k < frags; k++) 10664336716bSAdrian Chadd if (testbmap(i + j + k)) 10674336716bSAdrian Chadd break; 10684336716bSAdrian Chadd if (k < frags) { 10694336716bSAdrian Chadd j += k; 10704336716bSAdrian Chadd continue; 10714336716bSAdrian Chadd } 10724336716bSAdrian Chadd cg = dtog(&sblock, i + j); 1073957fc241SKirk McKusick cgbp = cglookup(cg); 107481fbded2SKirk McKusick cgp = cgbp->b_un.b_cg; 107584a0e3f9SKirk McKusick if (!check_cgmagic(cg, cgbp, 0)) { 107684a0e3f9SKirk McKusick i = (cg + 1) * sblock.fs_fpg - sblock.fs_frag; 107784a0e3f9SKirk McKusick continue; 107884a0e3f9SKirk McKusick } 10794336716bSAdrian Chadd baseblk = dtogd(&sblock, i + j); 10804336716bSAdrian Chadd for (k = 0; k < frags; k++) { 10814336716bSAdrian Chadd setbmap(i + j + k); 10824336716bSAdrian Chadd clrbit(cg_blksfree(cgp), baseblk + k); 10834336716bSAdrian Chadd } 10844336716bSAdrian Chadd n_blks += frags; 10854336716bSAdrian Chadd if (frags == sblock.fs_frag) 10864336716bSAdrian Chadd cgp->cg_cs.cs_nbfree--; 10874336716bSAdrian Chadd else 10884336716bSAdrian Chadd cgp->cg_cs.cs_nffree -= frags; 10898ebae128SKirk McKusick cgdirty(cgbp); 10904336716bSAdrian Chadd return (i + j); 10914336716bSAdrian Chadd } 10924336716bSAdrian Chadd } 10934336716bSAdrian Chadd return (0); 10944336716bSAdrian Chadd } 10954336716bSAdrian Chadd 10964336716bSAdrian Chadd /* 10977180f1abSKirk McKusick * Slow down IO so as to leave some disk bandwidth for other processes 10984336716bSAdrian Chadd */ 10994336716bSAdrian Chadd void 11009d580d7cSIan Dowse slowio_start() 11019d580d7cSIan Dowse { 11029d580d7cSIan Dowse 110308983aeeSScott Long /* Delay one in every 8 operations */ 11049d580d7cSIan Dowse slowio_pollcnt = (slowio_pollcnt + 1) & 7; 11059d580d7cSIan Dowse if (slowio_pollcnt == 0) { 11069d580d7cSIan Dowse gettimeofday(&slowio_starttime, NULL); 11079d580d7cSIan Dowse } 11089d580d7cSIan Dowse } 11099d580d7cSIan Dowse 11109d580d7cSIan Dowse void 11119d580d7cSIan Dowse slowio_end() 11129d580d7cSIan Dowse { 11139d580d7cSIan Dowse struct timeval tv; 11149d580d7cSIan Dowse int delay_usec; 11159d580d7cSIan Dowse 11169d580d7cSIan Dowse if (slowio_pollcnt != 0) 11179d580d7cSIan Dowse return; 11189d580d7cSIan Dowse 11199d580d7cSIan Dowse /* Update the slowdown interval. */ 11209d580d7cSIan Dowse gettimeofday(&tv, NULL); 11219d580d7cSIan Dowse delay_usec = (tv.tv_sec - slowio_starttime.tv_sec) * 1000000 + 11229d580d7cSIan Dowse (tv.tv_usec - slowio_starttime.tv_usec); 11239d580d7cSIan Dowse if (delay_usec < 64) 11249d580d7cSIan Dowse delay_usec = 64; 112508983aeeSScott Long if (delay_usec > 2500000) 112608983aeeSScott Long delay_usec = 2500000; 11279d580d7cSIan Dowse slowio_delay_usec = (slowio_delay_usec * 63 + delay_usec) >> 6; 112808983aeeSScott Long /* delay by 8 times the average IO delay */ 112908983aeeSScott Long if (slowio_delay_usec > 64) 113008983aeeSScott Long usleep(slowio_delay_usec * 8); 11319d580d7cSIan Dowse } 11329d580d7cSIan Dowse 11334336716bSAdrian Chadd /* 11344336716bSAdrian Chadd * Find a pathname 11354336716bSAdrian Chadd */ 11364336716bSAdrian Chadd void 1137b70cd7eeSWarner Losh getpathname(char *namebuf, ino_t curdir, ino_t ino) 11384336716bSAdrian Chadd { 11394336716bSAdrian Chadd int len; 11403d438ad6SDavid E. O'Brien char *cp; 11415cc52631SKirk McKusick struct inode ip; 11424336716bSAdrian Chadd struct inodesc idesc; 11434336716bSAdrian Chadd static int busy = 0; 11444336716bSAdrian Chadd 11451dc349abSEd Maste if (curdir == ino && ino == UFS_ROOTINO) { 11464336716bSAdrian Chadd (void)strcpy(namebuf, "/"); 11474336716bSAdrian Chadd return; 11484336716bSAdrian Chadd } 1149af6726e6SDon Lewis if (busy || !INO_IS_DVALID(curdir)) { 11504336716bSAdrian Chadd (void)strcpy(namebuf, "?"); 11514336716bSAdrian Chadd return; 11524336716bSAdrian Chadd } 11534336716bSAdrian Chadd busy = 1; 11544336716bSAdrian Chadd memset(&idesc, 0, sizeof(struct inodesc)); 11554336716bSAdrian Chadd idesc.id_type = DATA; 11564336716bSAdrian Chadd idesc.id_fix = IGNORE; 11574336716bSAdrian Chadd cp = &namebuf[MAXPATHLEN - 1]; 11584336716bSAdrian Chadd *cp = '\0'; 11594336716bSAdrian Chadd if (curdir != ino) { 11604336716bSAdrian Chadd idesc.id_parent = curdir; 11614336716bSAdrian Chadd goto namelookup; 11624336716bSAdrian Chadd } 11631dc349abSEd Maste while (ino != UFS_ROOTINO) { 11644336716bSAdrian Chadd idesc.id_number = ino; 11654336716bSAdrian Chadd idesc.id_func = findino; 1166599304a4SPoul-Henning Kamp idesc.id_name = strdup(".."); 11675cc52631SKirk McKusick ginode(ino, &ip); 11685cc52631SKirk McKusick if ((ckinode(ip.i_dp, &idesc) & FOUND) == 0) { 11695cc52631SKirk McKusick irelse(&ip); 11704336716bSAdrian Chadd break; 11715cc52631SKirk McKusick } 11725cc52631SKirk McKusick irelse(&ip); 11734336716bSAdrian Chadd namelookup: 11744336716bSAdrian Chadd idesc.id_number = idesc.id_parent; 11754336716bSAdrian Chadd idesc.id_parent = ino; 11764336716bSAdrian Chadd idesc.id_func = findname; 11774336716bSAdrian Chadd idesc.id_name = namebuf; 11785cc52631SKirk McKusick ginode(idesc.id_number, &ip); 11795cc52631SKirk McKusick if ((ckinode(ip.i_dp, &idesc) & FOUND) == 0) { 11805cc52631SKirk McKusick irelse(&ip); 11814336716bSAdrian Chadd break; 11825cc52631SKirk McKusick } 11835cc52631SKirk McKusick irelse(&ip); 11844336716bSAdrian Chadd len = strlen(namebuf); 11854336716bSAdrian Chadd cp -= len; 11864336716bSAdrian Chadd memmove(cp, namebuf, (size_t)len); 11874336716bSAdrian Chadd *--cp = '/'; 11880ecf59f6SConrad Meyer if (cp < &namebuf[UFS_MAXNAMLEN]) 11894336716bSAdrian Chadd break; 11904336716bSAdrian Chadd ino = idesc.id_number; 11914336716bSAdrian Chadd } 11924336716bSAdrian Chadd busy = 0; 11931dc349abSEd Maste if (ino != UFS_ROOTINO) 11944336716bSAdrian Chadd *--cp = '?'; 11954336716bSAdrian Chadd memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); 11964336716bSAdrian Chadd } 11974336716bSAdrian Chadd 11984336716bSAdrian Chadd void 1199599304a4SPoul-Henning Kamp catch(int sig __unused) 12004336716bSAdrian Chadd { 1201381ee4c2SPoul-Henning Kamp 12024336716bSAdrian Chadd ckfini(0); 12034336716bSAdrian Chadd exit(12); 12044336716bSAdrian Chadd } 12054336716bSAdrian Chadd 12064336716bSAdrian Chadd /* 12074336716bSAdrian Chadd * When preening, allow a single quit to signal 12084336716bSAdrian Chadd * a special exit after file system checks complete 12094336716bSAdrian Chadd * so that reboot sequence may be interrupted. 12104336716bSAdrian Chadd */ 12114336716bSAdrian Chadd void 1212599304a4SPoul-Henning Kamp catchquit(int sig __unused) 12134336716bSAdrian Chadd { 12144336716bSAdrian Chadd printf("returning to single-user after file system check\n"); 12154336716bSAdrian Chadd returntosingle = 1; 12164336716bSAdrian Chadd (void)signal(SIGQUIT, SIG_DFL); 12174336716bSAdrian Chadd } 12184336716bSAdrian Chadd 12194336716bSAdrian Chadd /* 12204336716bSAdrian Chadd * determine whether an inode should be fixed. 12214336716bSAdrian Chadd */ 12224336716bSAdrian Chadd int 1223599304a4SPoul-Henning Kamp dofix(struct inodesc *idesc, const char *msg) 12244336716bSAdrian Chadd { 12254336716bSAdrian Chadd 12264336716bSAdrian Chadd switch (idesc->id_fix) { 12274336716bSAdrian Chadd 12284336716bSAdrian Chadd case DONTKNOW: 12294336716bSAdrian Chadd if (idesc->id_type == DATA) 12304336716bSAdrian Chadd direrror(idesc->id_number, msg); 12314336716bSAdrian Chadd else 12325979df34SKris Kennaway pwarn("%s", msg); 12334336716bSAdrian Chadd if (preen) { 12344336716bSAdrian Chadd printf(" (SALVAGED)\n"); 12354336716bSAdrian Chadd idesc->id_fix = FIX; 12364336716bSAdrian Chadd return (ALTERED); 12374336716bSAdrian Chadd } 12384336716bSAdrian Chadd if (reply("SALVAGE") == 0) { 12394336716bSAdrian Chadd idesc->id_fix = NOFIX; 12404336716bSAdrian Chadd return (0); 12414336716bSAdrian Chadd } 12424336716bSAdrian Chadd idesc->id_fix = FIX; 12434336716bSAdrian Chadd return (ALTERED); 12444336716bSAdrian Chadd 12454336716bSAdrian Chadd case FIX: 12464336716bSAdrian Chadd return (ALTERED); 12474336716bSAdrian Chadd 12484336716bSAdrian Chadd case NOFIX: 12494336716bSAdrian Chadd case IGNORE: 12504336716bSAdrian Chadd return (0); 12514336716bSAdrian Chadd 12524336716bSAdrian Chadd default: 12534336716bSAdrian Chadd errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix); 12544336716bSAdrian Chadd } 12554336716bSAdrian Chadd /* NOTREACHED */ 12564336716bSAdrian Chadd return (0); 12574336716bSAdrian Chadd } 12584336716bSAdrian Chadd 12594336716bSAdrian Chadd #include <stdarg.h> 12604336716bSAdrian Chadd 12614336716bSAdrian Chadd /* 12625cc52631SKirk McKusick * Print details about a buffer. 12635cc52631SKirk McKusick */ 12645cc52631SKirk McKusick static void 12655cc52631SKirk McKusick prtbuf(const char *msg, struct bufarea *bp) 12665cc52631SKirk McKusick { 12675cc52631SKirk McKusick 1268c6951facSCy Schubert printf("%s: bp %p, type %s, bno %jd, size %d, refcnt %d, flags %s, " 1269c6951facSCy Schubert "index %jd\n", msg, bp, BT_BUFTYPE(bp->b_type), (intmax_t) bp->b_bno, 12705cc52631SKirk McKusick bp->b_size, bp->b_refcnt, bp->b_flags & B_DIRTY ? "dirty" : "clean", 1271c6951facSCy Schubert (intmax_t) bp->b_index); 12725cc52631SKirk McKusick } 12735cc52631SKirk McKusick 12745cc52631SKirk McKusick /* 12754b85a12fSUlrich Spörlein * An unexpected inconsistency occurred. 12764336716bSAdrian Chadd * Die if preening or file system is running with soft dependency protocol, 12774336716bSAdrian Chadd * otherwise just print message and continue. 12784336716bSAdrian Chadd */ 12794336716bSAdrian Chadd void 12804336716bSAdrian Chadd pfatal(const char *fmt, ...) 12814336716bSAdrian Chadd { 12824336716bSAdrian Chadd va_list ap; 12834336716bSAdrian Chadd va_start(ap, fmt); 12844336716bSAdrian Chadd if (!preen) { 128515fca934SKirk McKusick (void)vfprintf(stdout, fmt, ap); 12864336716bSAdrian Chadd va_end(ap); 12874336716bSAdrian Chadd if (usedsoftdep) 128815fca934SKirk McKusick (void)fprintf(stdout, 12894336716bSAdrian Chadd "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n"); 129038375c40SKirk McKusick /* 129138375c40SKirk McKusick * Force foreground fsck to clean up inconsistency. 129238375c40SKirk McKusick */ 129338375c40SKirk McKusick if (bkgrdflag) { 129438375c40SKirk McKusick cmd.value = FS_NEEDSFSCK; 129538375c40SKirk McKusick cmd.size = 1; 129638375c40SKirk McKusick if (sysctlbyname("vfs.ffs.setflags", 0, 0, 129738375c40SKirk McKusick &cmd, sizeof cmd) == -1) 129838375c40SKirk McKusick pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n"); 129915fca934SKirk McKusick fprintf(stdout, "CANNOT RUN IN BACKGROUND\n"); 130038375c40SKirk McKusick ckfini(0); 130138375c40SKirk McKusick exit(EEXIT); 130238375c40SKirk McKusick } 13034336716bSAdrian Chadd return; 13044336716bSAdrian Chadd } 13054336716bSAdrian Chadd if (cdevname == NULL) 1306599304a4SPoul-Henning Kamp cdevname = strdup("fsck"); 130715fca934SKirk McKusick (void)fprintf(stdout, "%s: ", cdevname); 130815fca934SKirk McKusick (void)vfprintf(stdout, fmt, ap); 130915fca934SKirk McKusick (void)fprintf(stdout, 13104336716bSAdrian Chadd "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n", 13114336716bSAdrian Chadd cdevname, usedsoftdep ? " SOFT UPDATE " : " "); 131238375c40SKirk McKusick /* 131338375c40SKirk McKusick * Force foreground fsck to clean up inconsistency. 131438375c40SKirk McKusick */ 131538375c40SKirk McKusick if (bkgrdflag) { 131638375c40SKirk McKusick cmd.value = FS_NEEDSFSCK; 131738375c40SKirk McKusick cmd.size = 1; 131838375c40SKirk McKusick if (sysctlbyname("vfs.ffs.setflags", 0, 0, 131938375c40SKirk McKusick &cmd, sizeof cmd) == -1) 132038375c40SKirk McKusick pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n"); 132138375c40SKirk McKusick } 13224336716bSAdrian Chadd ckfini(0); 13234336716bSAdrian Chadd exit(EEXIT); 13244336716bSAdrian Chadd } 13254336716bSAdrian Chadd 13264336716bSAdrian Chadd /* 13274336716bSAdrian Chadd * Pwarn just prints a message when not preening or running soft dependency 13284336716bSAdrian Chadd * protocol, or a warning (preceded by filename) when preening. 13294336716bSAdrian Chadd */ 13304336716bSAdrian Chadd void 13314336716bSAdrian Chadd pwarn(const char *fmt, ...) 13324336716bSAdrian Chadd { 13334336716bSAdrian Chadd va_list ap; 13344336716bSAdrian Chadd va_start(ap, fmt); 13354336716bSAdrian Chadd if (preen) 133615fca934SKirk McKusick (void)fprintf(stdout, "%s: ", cdevname); 133715fca934SKirk McKusick (void)vfprintf(stdout, fmt, ap); 13384336716bSAdrian Chadd va_end(ap); 13394336716bSAdrian Chadd } 13404336716bSAdrian Chadd 13414336716bSAdrian Chadd /* 13424336716bSAdrian Chadd * Stub for routines from kernel. 13434336716bSAdrian Chadd */ 13444336716bSAdrian Chadd void 13454336716bSAdrian Chadd panic(const char *fmt, ...) 13464336716bSAdrian Chadd { 13474336716bSAdrian Chadd va_list ap; 13484336716bSAdrian Chadd va_start(ap, fmt); 13494336716bSAdrian Chadd pfatal("INTERNAL INCONSISTENCY:"); 135015fca934SKirk McKusick (void)vfprintf(stdout, fmt, ap); 13514336716bSAdrian Chadd va_end(ap); 13524336716bSAdrian Chadd exit(EEXIT); 13534336716bSAdrian Chadd } 1354