1*f6e4162fSjsg /* $OpenBSD: pass1.c,v 1.50 2024/09/15 07:14:58 jsg Exp $ */ 287304b87Stholo /* $NetBSD: pass1.c,v 1.16 1996/09/27 22:45:15 christos Exp $ */ 3df930be7Sderaadt 4df930be7Sderaadt /* 5df930be7Sderaadt * Copyright (c) 1980, 1986, 1993 6df930be7Sderaadt * The Regents of the University of California. All rights reserved. 7df930be7Sderaadt * 8df930be7Sderaadt * Redistribution and use in source and binary forms, with or without 9df930be7Sderaadt * modification, are permitted provided that the following conditions 10df930be7Sderaadt * are met: 11df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright 12df930be7Sderaadt * notice, this list of conditions and the following disclaimer. 13df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 14df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the 15df930be7Sderaadt * documentation and/or other materials provided with the distribution. 161ef0d710Smillert * 3. Neither the name of the University nor the names of its contributors 17df930be7Sderaadt * may be used to endorse or promote products derived from this software 18df930be7Sderaadt * without specific prior written permission. 19df930be7Sderaadt * 20df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30df930be7Sderaadt * SUCH DAMAGE. 31df930be7Sderaadt */ 32df930be7Sderaadt 3378eb0b7eSderaadt #include <sys/param.h> /* MIN setbit btodb isset */ 34df930be7Sderaadt #include <sys/time.h> 35df930be7Sderaadt #include <ufs/ufs/dinode.h> 36df930be7Sderaadt #include <ufs/ufs/dir.h> 37df930be7Sderaadt #include <ufs/ffs/fs.h> 38df930be7Sderaadt 39df930be7Sderaadt #include <stdio.h> 40df930be7Sderaadt #include <stdlib.h> 41df930be7Sderaadt #include <string.h> 429ce41040Schl #include <unistd.h> 43b9fc9a72Sderaadt #include <limits.h> 44df930be7Sderaadt 45df930be7Sderaadt #include "fsck.h" 46df930be7Sderaadt #include "extern.h" 4787304b87Stholo #include "fsutil.h" 48df930be7Sderaadt 491abdbfdeSderaadt static daddr_t badblk; 501abdbfdeSderaadt static daddr_t dupblk; 51c72b5b24Smillert static void checkinode(ino_t, struct inodesc *); 52df930be7Sderaadt 53af9e537cSd static ino_t info_inumber; 54af9e537cSd 55af9e537cSd static int 56379d1969Sderaadt pass1_info(char *buf, size_t buflen) 57af9e537cSd { 583b92bd08Sderaadt return (snprintf(buf, buflen, "phase 1, inode %llu/%llu", 593b92bd08Sderaadt (unsigned long long)info_inumber, 603b92bd08Sderaadt (unsigned long long)sblock.fs_ipg * sblock.fs_ncg) > 0); 61af9e537cSd } 62af9e537cSd 63df930be7Sderaadt void 6460d6b16fSgluk pass1(void) 65df930be7Sderaadt { 664d91232bSotto ino_t inumber, inosused, ninosused; 674d91232bSotto size_t inospace; 684d91232bSotto struct inostat *info; 692062fcb1Sotto struct bufarea *cgbp; 702062fcb1Sotto struct cg *cgp; 71abbb3558Sotto u_int c; 724d91232bSotto struct inodesc idesc; 731abdbfdeSderaadt daddr_t i, cgd; 74df930be7Sderaadt 75df930be7Sderaadt /* 76df930be7Sderaadt * Set file system reserved blocks in used block map. 77df930be7Sderaadt */ 78df930be7Sderaadt for (c = 0; c < sblock.fs_ncg; c++) { 79df930be7Sderaadt cgd = cgdmin(&sblock, c); 80e7bce8e3Sderaadt if (c == 0) 81df930be7Sderaadt i = cgbase(&sblock, c); 82e7bce8e3Sderaadt else 83df930be7Sderaadt i = cgsblock(&sblock, c); 84df930be7Sderaadt for (; i < cgd; i++) 85df930be7Sderaadt setbmap(i); 86df930be7Sderaadt } 872fffe0e0Smillert i = sblock.fs_csaddr; 88e7bce8e3Sderaadt cgd = i + howmany(sblock.fs_cssize, sblock.fs_fsize); 89e7bce8e3Sderaadt for (; i < cgd; i++) 90e7bce8e3Sderaadt setbmap(i); 91df930be7Sderaadt /* 92df930be7Sderaadt * Find all allocated blocks. 93df930be7Sderaadt */ 94df930be7Sderaadt memset(&idesc, 0, sizeof(struct inodesc)); 95df930be7Sderaadt idesc.id_type = ADDR; 96df930be7Sderaadt idesc.id_func = pass1check; 97df930be7Sderaadt n_files = n_blks = 0; 98af9e537cSd info_inumber = 0; 99af9e537cSd info_fn = pass1_info; 100df930be7Sderaadt for (c = 0; c < sblock.fs_ncg; c++) { 1012fffe0e0Smillert inumber = c * sblock.fs_ipg; 1022fffe0e0Smillert setinodebuf(inumber); 1032062fcb1Sotto cgbp = cglookup(c); 1042062fcb1Sotto cgp = cgbp->b_un.b_cg; 105f88b7ac5Sotto if (sblock.fs_magic == FS_UFS2_MAGIC) { 1062062fcb1Sotto inosused = cgp->cg_initediblk; 107f88b7ac5Sotto if (inosused > sblock.fs_ipg) 108f88b7ac5Sotto inosused = sblock.fs_ipg; 109f88b7ac5Sotto } else 1102fffe0e0Smillert inosused = sblock.fs_ipg; 1114d91232bSotto 1124d91232bSotto /* 1134d91232bSotto * Allocate inoinfo structures for the allocated inodes. 1144d91232bSotto */ 1154d91232bSotto inostathead[c].il_numalloced = inosused; 1164d91232bSotto if (inosused == 0) { 1174d91232bSotto inostathead[c].il_stat = 0; 1184d91232bSotto continue; 1194d91232bSotto } 1202062fcb1Sotto info = Calloc((unsigned)inosused, sizeof(struct inostat)); 1214d91232bSotto inospace = (unsigned)inosused * sizeof(struct inostat); 1224d91232bSotto if (info == NULL) 1232062fcb1Sotto errexit("cannot alloc %zu bytes for inoinfo\n", inospace); 1244d91232bSotto inostathead[c].il_stat = info; 1254d91232bSotto /* 1264d91232bSotto * Scan the allocated inodes. 1274d91232bSotto */ 1282fffe0e0Smillert for (i = 0; i < inosused; i++, inumber++) { 129af9e537cSd info_inumber = inumber; 1304d91232bSotto if (inumber < ROOTINO) { 1314d91232bSotto (void)getnextinode(inumber); 132df930be7Sderaadt continue; 1334d91232bSotto } 134df930be7Sderaadt checkinode(inumber, &idesc); 135df930be7Sderaadt } 1364d91232bSotto lastino += 1; 1374d91232bSotto if (inosused < sblock.fs_ipg || inumber == lastino) 1384d91232bSotto continue; 1394d91232bSotto /* 1404d91232bSotto * If we were not able to determine in advance which inodes 1414d91232bSotto * were in use, then reduce the size of the inoinfo structure 1424d91232bSotto * to the size necessary to describe the inodes that we 1434d91232bSotto * really found. 1444d91232bSotto */ 1454d91232bSotto if (lastino < (c * sblock.fs_ipg)) 1464d91232bSotto ninosused = 0; 1474d91232bSotto else 1484d91232bSotto ninosused = lastino - (c * sblock.fs_ipg); 1494d91232bSotto inostathead[c].il_numalloced = ninosused; 1504d91232bSotto if (ninosused == 0) { 1514d91232bSotto free(inostathead[c].il_stat); 1524d91232bSotto inostathead[c].il_stat = 0; 1534d91232bSotto continue; 1544d91232bSotto } 1554d91232bSotto if (ninosused != inosused) { 1564d91232bSotto struct inostat *ninfo; 157527c1077Sderaadt size_t ninospace; 1586a0422b3Sguenther 1592062fcb1Sotto ninfo = Reallocarray(info, ninosused, sizeof(*ninfo)); 160527c1077Sderaadt if (ninfo == NULL) { 161527c1077Sderaadt pfatal("too many inodes %llu, or out of memory\n", 1624d91232bSotto (unsigned long long)ninosused); 1634d91232bSotto exit(8); 1644d91232bSotto } 165527c1077Sderaadt ninospace = ninosused * sizeof(*ninfo); 1664d91232bSotto if (ninosused > inosused) 167527c1077Sderaadt memset(&ninfo[inosused], 0, ninospace - inospace); 1684d91232bSotto inostathead[c].il_stat = ninfo; 1694d91232bSotto } 170df930be7Sderaadt } 171af9e537cSd info_fn = NULL; 172df930be7Sderaadt freeinodebuf(); 173df930be7Sderaadt } 174df930be7Sderaadt 17587304b87Stholo static void 17660d6b16fSgluk checkinode(ino_t inumber, struct inodesc *idesc) 177df930be7Sderaadt { 1782fffe0e0Smillert union dinode *dp; 17916f07c23Sotto off_t kernmaxfilesize; 180df930be7Sderaadt struct zlncnt *zlnp; 181df930be7Sderaadt int ndb, j; 182df930be7Sderaadt mode_t mode; 183ac7b90deSotto u_int64_t lndb; 184df930be7Sderaadt 185df930be7Sderaadt dp = getnextinode(inumber); 1862fffe0e0Smillert mode = DIP(dp, di_mode) & IFMT; 187df930be7Sderaadt if (mode == 0) { 1882fffe0e0Smillert if ((sblock.fs_magic == FS_UFS1_MAGIC && 1892fffe0e0Smillert (memcmp(dp->dp1.di_db, ufs1_zino.di_db, 190c29211cfSpedro NDADDR * sizeof(int32_t)) || 1912fffe0e0Smillert memcmp(dp->dp1.di_ib, ufs1_zino.di_ib, 192c29211cfSpedro NIADDR * sizeof(int32_t)) || 1932fffe0e0Smillert dp->dp1.di_mode || dp->dp1.di_size)) || 1942fffe0e0Smillert (sblock.fs_magic == FS_UFS2_MAGIC && 1952fffe0e0Smillert (memcmp(dp->dp2.di_db, ufs2_zino.di_db, 1961abdbfdeSderaadt NDADDR * sizeof(daddr_t)) || 1972fffe0e0Smillert memcmp(dp->dp2.di_ib, ufs2_zino.di_ib, 1981abdbfdeSderaadt NIADDR * sizeof(daddr_t)) || 1992fffe0e0Smillert dp->dp2.di_mode || dp->dp2.di_size))) { 2003b92bd08Sderaadt pfatal("PARTIALLY ALLOCATED INODE I=%llu", 2013b92bd08Sderaadt (unsigned long long)inumber); 202df930be7Sderaadt if (reply("CLEAR") == 1) { 203df930be7Sderaadt dp = ginode(inumber); 204df930be7Sderaadt clearinode(dp); 205df930be7Sderaadt inodirty(); 206df930be7Sderaadt } 207df930be7Sderaadt } 2084aab0ea5Sotto SET_ISTATE(inumber, USTATE); 209df930be7Sderaadt return; 210df930be7Sderaadt } 211df930be7Sderaadt lastino = inumber; 21216f07c23Sotto /* This should match the file size limit in ffs_mountfs(). */ 213e4b46811Sderaadt kernmaxfilesize = FS_KERNMAXFILESIZE(getpagesize(), &sblock); 21416f07c23Sotto if (DIP(dp, di_size) > kernmaxfilesize || 21516f07c23Sotto DIP(dp, di_size) > sblock.fs_maxfilesize || 21616f07c23Sotto (mode == IFDIR && DIP(dp, di_size) > MAXDIRSIZE)) { 217df930be7Sderaadt if (debug) 2182fffe0e0Smillert printf("bad size %llu:", 2192fffe0e0Smillert (unsigned long long)DIP(dp, di_size)); 220df930be7Sderaadt goto unknown; 221df930be7Sderaadt } 222df930be7Sderaadt if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) { 223df930be7Sderaadt dp = ginode(inumber); 2242fffe0e0Smillert DIP_SET(dp, di_size, sblock.fs_fsize); 2252fffe0e0Smillert DIP_SET(dp, di_mode, IFREG|0600); 226df930be7Sderaadt inodirty(); 227df930be7Sderaadt } 2282fffe0e0Smillert lndb = howmany(DIP(dp, di_size), sblock.fs_bsize); 229ac7b90deSotto ndb = lndb > (u_int64_t)INT_MAX ? -1 : (int)lndb; 230df930be7Sderaadt if (ndb < 0) { 231df930be7Sderaadt if (debug) 232a4df0321Sderaadt printf("bad size %llu ndb %d:", 2332fffe0e0Smillert (unsigned long long)DIP(dp, di_size), ndb); 234df930be7Sderaadt goto unknown; 235df930be7Sderaadt } 236df930be7Sderaadt if (mode == IFBLK || mode == IFCHR) 237df930be7Sderaadt ndb++; 238df930be7Sderaadt if (mode == IFLNK) { 239df930be7Sderaadt /* 240df930be7Sderaadt * Fake ndb value so direct/indirect block checks below 241df930be7Sderaadt * will detect any garbage after symlink string. 242df930be7Sderaadt */ 243da5362d5Sguenther if (DIP(dp, di_size) < sblock.fs_maxsymlinklen) { 2442fffe0e0Smillert if (sblock.fs_magic == FS_UFS1_MAGIC) 2452fffe0e0Smillert ndb = howmany(DIP(dp, di_size), 246c29211cfSpedro sizeof(int32_t)); 2472fffe0e0Smillert else 2482fffe0e0Smillert ndb = howmany(DIP(dp, di_size), 249c29211cfSpedro sizeof(int64_t)); 250df930be7Sderaadt if (ndb > NDADDR) { 251df930be7Sderaadt j = ndb - NDADDR; 252df930be7Sderaadt for (ndb = 1; j > 1; j--) 253df930be7Sderaadt ndb *= NINDIR(&sblock); 254df930be7Sderaadt ndb += NDADDR; 255df930be7Sderaadt } 256df930be7Sderaadt } 257df930be7Sderaadt } 258df930be7Sderaadt for (j = ndb; j < NDADDR; j++) 2592fffe0e0Smillert if (DIP(dp, di_db[j]) != 0) { 260df930be7Sderaadt if (debug) 2616a0422b3Sguenther printf("bad direct addr: %lld\n", 2626a0422b3Sguenther (long long)DIP(dp, di_db[j])); 263df930be7Sderaadt goto unknown; 264df930be7Sderaadt } 265df930be7Sderaadt for (j = 0, ndb -= NDADDR; ndb > 0; j++) 266df930be7Sderaadt ndb /= NINDIR(&sblock); 267df930be7Sderaadt for (; j < NIADDR; j++) 2682fffe0e0Smillert if (DIP(dp, di_ib[j]) != 0) { 269df930be7Sderaadt if (debug) 2706a0422b3Sguenther printf("bad indirect addr: %lld\n", 2716a0422b3Sguenther (long long)DIP(dp, di_ib[j])); 272df930be7Sderaadt goto unknown; 273df930be7Sderaadt } 274df930be7Sderaadt if (ftypeok(dp) == 0) 275df930be7Sderaadt goto unknown; 276df930be7Sderaadt n_files++; 2774d91232bSotto ILNCOUNT(inumber) = DIP(dp, di_nlink); 2782fffe0e0Smillert if (DIP(dp, di_nlink) <= 0) { 2792062fcb1Sotto zlnp = Malloc(sizeof *zlnp); 280df930be7Sderaadt if (zlnp == NULL) { 281df930be7Sderaadt pfatal("LINK COUNT TABLE OVERFLOW"); 282767b3369Sart if (reply("CONTINUE") == 0) { 283767b3369Sart ckfini(0); 28487304b87Stholo errexit("%s", ""); 285767b3369Sart } 286df930be7Sderaadt } else { 287df930be7Sderaadt zlnp->zlncnt = inumber; 288df930be7Sderaadt zlnp->next = zlnhead; 289df930be7Sderaadt zlnhead = zlnp; 290df930be7Sderaadt } 291df930be7Sderaadt } 292df930be7Sderaadt if (mode == IFDIR) { 2932fffe0e0Smillert if (DIP(dp, di_size) == 0) 2944aab0ea5Sotto SET_ISTATE(inumber, DCLEAR); 295df930be7Sderaadt else 2964aab0ea5Sotto SET_ISTATE(inumber, DSTATE); 297df930be7Sderaadt cacheino(dp, inumber); 298df930be7Sderaadt } else 2994aab0ea5Sotto SET_ISTATE(inumber, FSTATE); 3004aab0ea5Sotto SET_ITYPE(inumber, IFTODT(mode)); 301df930be7Sderaadt badblk = dupblk = 0; 302df930be7Sderaadt idesc->id_number = inumber; 303df930be7Sderaadt (void)ckinode(dp, idesc); 304df930be7Sderaadt idesc->id_entryno *= btodb(sblock.fs_fsize); 3052fffe0e0Smillert if (DIP(dp, di_blocks) != idesc->id_entryno) { 3066a0422b3Sguenther pwarn("INCORRECT BLOCK COUNT I=%llu (%lld should be %lld)", 3076a0422b3Sguenther (unsigned long long)inumber, (long long)DIP(dp, di_blocks), 3086a0422b3Sguenther (long long)idesc->id_entryno); 309df930be7Sderaadt if (preen) 310df930be7Sderaadt printf(" (CORRECTED)\n"); 311df930be7Sderaadt else if (reply("CORRECT") == 0) 312df930be7Sderaadt return; 313df930be7Sderaadt dp = ginode(inumber); 3142fffe0e0Smillert DIP_SET(dp, di_blocks, idesc->id_entryno); 315df930be7Sderaadt inodirty(); 316df930be7Sderaadt } 317df930be7Sderaadt return; 318df930be7Sderaadt unknown: 3193b92bd08Sderaadt pfatal("UNKNOWN FILE TYPE I=%llu", (unsigned long long)inumber); 3204aab0ea5Sotto SET_ISTATE(inumber, FCLEAR); 321df930be7Sderaadt if (reply("CLEAR") == 1) { 3224aab0ea5Sotto SET_ISTATE(inumber, USTATE); 323df930be7Sderaadt dp = ginode(inumber); 324df930be7Sderaadt clearinode(dp); 325df930be7Sderaadt inodirty(); 326df930be7Sderaadt } 327df930be7Sderaadt } 328df930be7Sderaadt 329df930be7Sderaadt int 33060d6b16fSgluk pass1check(struct inodesc *idesc) 331df930be7Sderaadt { 332df930be7Sderaadt int res = KEEPON; 333df930be7Sderaadt int anyout, nfrags; 3341abdbfdeSderaadt daddr_t blkno = idesc->id_blkno; 335e073c79dSmpech struct dups *dlp; 336df930be7Sderaadt struct dups *new; 337df930be7Sderaadt 338df930be7Sderaadt if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) { 339df930be7Sderaadt blkerror(idesc->id_number, "BAD", blkno); 340df930be7Sderaadt if (badblk++ >= MAXBAD) { 3413b92bd08Sderaadt pwarn("EXCESSIVE BAD BLKS I=%llu", 3423b92bd08Sderaadt (unsigned long long)idesc->id_number); 343df930be7Sderaadt if (preen) 344df930be7Sderaadt printf(" (SKIPPING)\n"); 345767b3369Sart else if (reply("CONTINUE") == 0) { 346767b3369Sart ckfini(0); 34787304b87Stholo errexit("%s", ""); 348767b3369Sart } 349df930be7Sderaadt return (STOP); 350df930be7Sderaadt } 351df930be7Sderaadt } 352df930be7Sderaadt for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { 353df930be7Sderaadt if (anyout && chkrange(blkno, 1)) { 354df930be7Sderaadt res = SKIP; 355df930be7Sderaadt } else if (!testbmap(blkno)) { 356df930be7Sderaadt n_blks++; 357df930be7Sderaadt setbmap(blkno); 358df930be7Sderaadt } else { 359df930be7Sderaadt blkerror(idesc->id_number, "DUP", blkno); 360df930be7Sderaadt if (dupblk++ >= MAXDUP) { 3613b92bd08Sderaadt pwarn("EXCESSIVE DUP BLKS I=%llu", 3623b92bd08Sderaadt (unsigned long long)idesc->id_number); 363df930be7Sderaadt if (preen) 364df930be7Sderaadt printf(" (SKIPPING)\n"); 365767b3369Sart else if (reply("CONTINUE") == 0) { 366767b3369Sart ckfini(0); 36787304b87Stholo errexit("%s", ""); 368767b3369Sart } 369df930be7Sderaadt return (STOP); 370df930be7Sderaadt } 3712062fcb1Sotto new = Malloc(sizeof(struct dups)); 372df930be7Sderaadt if (new == NULL) { 373df930be7Sderaadt pfatal("DUP TABLE OVERFLOW."); 374767b3369Sart if (reply("CONTINUE") == 0) { 375767b3369Sart ckfini(0); 37687304b87Stholo errexit("%s", ""); 377767b3369Sart } 378df930be7Sderaadt return (STOP); 379df930be7Sderaadt } 380df930be7Sderaadt new->dup = blkno; 381df930be7Sderaadt if (muldup == 0) { 382df930be7Sderaadt duplist = muldup = new; 383df930be7Sderaadt new->next = 0; 384df930be7Sderaadt } else { 385df930be7Sderaadt new->next = muldup->next; 386df930be7Sderaadt muldup->next = new; 387df930be7Sderaadt } 388df930be7Sderaadt for (dlp = duplist; dlp != muldup; dlp = dlp->next) 389df930be7Sderaadt if (dlp->dup == blkno) 390df930be7Sderaadt break; 391df930be7Sderaadt if (dlp == muldup && dlp->dup != blkno) 392df930be7Sderaadt muldup = new; 393df930be7Sderaadt } 394df930be7Sderaadt /* 395df930be7Sderaadt * count the number of blocks found in id_entryno 396df930be7Sderaadt */ 397df930be7Sderaadt idesc->id_entryno++; 398df930be7Sderaadt } 399df930be7Sderaadt return (res); 400df930be7Sderaadt } 401