1 /* $NetBSD: pass0.c,v 1.11 2002/02/04 23:41:28 perseant Exp $ */ 2 3 /* 4 * Copyright (c) 1998 Konrad E. Schroder. 5 * Copyright (c) 1980, 1986, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/time.h> 39 #include <ufs/ufs/dinode.h> 40 #include <ufs/ufs/dir.h> 41 #include <sys/mount.h> 42 #include <ufs/lfs/lfs.h> 43 #include <ufs/lfs/lfs_extern.h> 44 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 49 #include "fsck.h" 50 #include "extern.h" 51 #include "fsutil.h" 52 53 /* Flags for check_segment */ 54 #define CKSEG_VERBOSE 1 55 #define CKSEG_IGNORECLEAN 2 56 57 extern int fake_cleanseg; 58 void 59 check_segment(int, int, daddr_t, struct lfs *, int, 60 int (*)(struct lfs *, SEGSUM *, daddr_t)); 61 62 /* 63 * Pass 0. Check the LFS partial segments for valid checksums, correcting 64 * if necessary. Also check for valid offsets for inode and finfo blocks. 65 */ 66 /* 67 * XXX more could be done here---consistency between inode-held blocks and 68 * finfo blocks, for one thing. 69 */ 70 71 #define dbshift (sblock.lfs_bshift - sblock.lfs_blktodb) 72 73 void 74 pass0() 75 { 76 daddr_t daddr; 77 IFILE *ifp; 78 struct bufarea *bp; 79 ino_t ino, plastino, nextino, *visited; 80 81 /* 82 * Check the inode free list for inuse inodes, and cycles. 83 * Make sure that all free inodes are in fact on the list. 84 */ 85 visited = (ino_t *)malloc(maxino * sizeof(ino_t)); 86 memset(visited, 0, maxino * sizeof(ino_t)); 87 88 plastino = 0; 89 ino = sblock.lfs_free; 90 while (ino) { 91 if (ino >= maxino) { 92 printf("! Ino %d out of range (last was %d)\n", ino, 93 plastino); 94 break; 95 } 96 if (visited[ino]) { 97 pwarn("! Ino %d already found on the free list!\n", 98 ino); 99 if (preen || reply("FIX") == 1) { 100 /* plastino can't be zero */ 101 ifp = lfs_ientry(plastino, &bp); 102 ifp->if_nextfree = 0; 103 dirty(bp); 104 bp->b_flags &= ~B_INUSE; 105 } 106 break; 107 } 108 ++visited[ino]; 109 ifp = lfs_ientry(ino, &bp); 110 nextino = ifp->if_nextfree; 111 daddr = ifp->if_daddr; 112 bp->b_flags &= ~B_INUSE; 113 if (daddr) { 114 pwarn("! Ino %d with daddr 0x%x is on the free list!\n", 115 ino, daddr); 116 if (preen || reply("FIX") == 1) { 117 if (plastino == 0) { 118 sblock.lfs_free = nextino; 119 sbdirty(); 120 } else { 121 ifp = lfs_ientry(plastino, &bp); 122 ifp->if_nextfree = nextino; 123 dirty(bp); 124 bp->b_flags &= ~B_INUSE; 125 } 126 ino = nextino; 127 continue; 128 } 129 } 130 plastino = ino; 131 ino = nextino; 132 } 133 /* 134 * Make sure all free inodes were found on the list 135 */ 136 for (ino = ROOTINO+1; ino < maxino; ++ino) { 137 if (visited[ino]) 138 continue; 139 140 ifp = lfs_ientry(ino, &bp); 141 if (ifp->if_daddr) { 142 bp->b_flags &= ~B_INUSE; 143 continue; 144 } 145 146 pwarn("! Ino %d free, but not on the free list\n", ino); 147 if (preen || reply("FIX") == 1) { 148 ifp->if_nextfree = sblock.lfs_free; 149 sblock.lfs_free = ino; 150 sbdirty(); 151 dirty(bp); 152 } 153 bp->b_flags &= ~B_INUSE; 154 } 155 } 156 157 static void 158 dump_segsum(SEGSUM * sump, daddr_t addr) 159 { 160 printf("Dump partial summary block 0x%x\n", addr); 161 printf("\tsumsum: %x (%d)\n", sump->ss_sumsum, sump->ss_sumsum); 162 printf("\tdatasum: %x (%d)\n", sump->ss_datasum, sump->ss_datasum); 163 printf("\tnext: %x (%d)\n", sump->ss_next, sump->ss_next); 164 printf("\tcreate: %llx (%lld)\n", (long long)sump->ss_create, 165 (long long)sump->ss_create); 166 printf("\tnfinfo: %x (%d)\n", sump->ss_nfinfo, sump->ss_nfinfo); 167 printf("\tninos: %x (%d)\n", sump->ss_ninos, sump->ss_ninos); 168 printf("\tflags: %c%c\n", 169 sump->ss_flags & SS_DIROP ? 'd' : '-', 170 sump->ss_flags & SS_CONT ? 'c' : '-'); 171 } 172 173 /* XXX Don't use... broken. -JO */ 174 void 175 check_segment(int fd, int segnum, daddr_t addr, struct lfs * fs, int flags, int (*func)(struct lfs *, SEGSUM *, daddr_t)) 176 { 177 struct lfs *sbp; 178 SEGSUM *sump = NULL; 179 SEGUSE *su; 180 struct bufarea *bp = NULL; 181 int psegnum = 0, ninos = 0; 182 off_t sum_offset, db_ssize; 183 int bc, su_flags, su_nsums, su_ninos; 184 185 db_ssize = segtod(&sblock, 1); 186 187 su = lfs_gseguse(segnum, &bp); 188 su_flags = su->su_flags; 189 su_nsums = su->su_nsums; 190 su_ninos = su->su_ninos; 191 bp->b_flags &= ~B_INUSE; 192 193 /* printf("Seg at 0x%x\n",addr); */ 194 if ((flags & CKSEG_VERBOSE) && segnum * db_ssize + fs->lfs_sboffs[0] != addr) 195 pwarn("WARNING: segment begins at 0x%llx, should be 0x%llx\n", 196 (long long unsigned)addr, 197 (long long unsigned)(segnum * db_ssize + fs->lfs_sboffs[0])); 198 sum_offset = ((off_t)addr << dbshift); 199 200 /* If this segment should have a superblock, look for one */ 201 if (su_flags & SEGUSE_SUPERBLOCK) { 202 bp = getddblk(sum_offset >> dbshift, LFS_SBPAD); 203 sum_offset += LFS_SBPAD; 204 205 /* check for a superblock -- XXX this is crude */ 206 sbp = (struct lfs *)(bp->b_un.b_buf); 207 if (sbp->lfs_magic == LFS_MAGIC) { 208 #if 0 209 if (sblock.lfs_tstamp == sbp->lfs_tstamp && 210 memcmp(sbp, &sblock, sizeof(*sbp)) && 211 (flags & CKSEG_VERBOSE)) 212 pwarn("SUPERBLOCK MISMATCH SEGMENT %d\n", segnum); 213 #endif 214 } else { 215 if (flags & CKSEG_VERBOSE) 216 pwarn("SEGMENT %d SUPERBLOCK INVALID\n", segnum); 217 /* XXX allow to fix */ 218 } 219 bp->b_flags &= ~B_INUSE; 220 } 221 /* XXX need to also check whether this one *should* be dirty */ 222 if ((flags & CKSEG_IGNORECLEAN) && (su_flags & SEGUSE_DIRTY) == 0) 223 return; 224 225 while (1) { 226 if (su_nsums <= psegnum) 227 break; 228 bp = getddblk(sum_offset >> dbshift, sblock.lfs_sumsize); 229 sump = (SEGSUM *)(bp->b_un.b_buf); 230 if (sump->ss_magic != SS_MAGIC) { 231 if (flags & CKSEG_VERBOSE) 232 printf("PARTIAL SEGMENT %d SEGMENT %d BAD PARTIAL SEGMENT MAGIC (0x%x should be 0x%x)\n", 233 psegnum, segnum, sump->ss_magic, SS_MAGIC); 234 bp->b_flags &= ~B_INUSE; 235 break; 236 } 237 if (sump->ss_sumsum != cksum(&sump->ss_datasum, sblock.lfs_sumsize - sizeof(sump->ss_sumsum))) { 238 if (flags & CKSEG_VERBOSE) { 239 /* Corrupt partial segment */ 240 pwarn("CORRUPT PARTIAL SEGMENT %d/%d OF SEGMENT %d AT BLK 0x%llx", 241 psegnum, su_nsums, segnum, 242 (unsigned long long)sum_offset >> dbshift); 243 if (db_ssize < (sum_offset >> dbshift) - addr) 244 pwarn(" (+0x%llx/0x%llx)", 245 (unsigned long long)(((sum_offset >> dbshift) - addr) - 246 db_ssize), 247 (unsigned long long)db_ssize); 248 else 249 pwarn(" (-0x%llx/0x%llx)", 250 (unsigned long long)(db_ssize - 251 ((sum_offset >> dbshift) - addr)), 252 (unsigned long long)db_ssize); 253 pwarn("\n"); 254 dump_segsum(sump, sum_offset >> dbshift); 255 } 256 /* XXX fix it maybe */ 257 bp->b_flags &= ~B_INUSE; 258 break; /* XXX could be throwing away data, but if 259 * this segsum is invalid, how to know where 260 * the next summary begins? */ 261 } 262 /* 263 * Good partial segment 264 */ 265 bc = (*func)(&sblock, sump, (daddr_t)(sum_offset >> dbshift)); 266 if (bc) { 267 sum_offset += sblock.lfs_sumsize + bc; 268 ninos += (sump->ss_ninos + INOPB(&sblock) - 1) 269 / INOPB(&sblock); 270 psegnum++; 271 } else { 272 bp->b_flags &= ~B_INUSE; 273 break; 274 } 275 bp->b_flags &= ~B_INUSE; 276 } 277 if (flags & CKSEG_VERBOSE) { 278 if (ninos != su_ninos) 279 pwarn("SEGMENT %d has %d ninos, not %d\n", 280 segnum, ninos, su_ninos); 281 if (psegnum != su_nsums) 282 pwarn("SEGMENT %d has %d summaries, not %d\n", 283 segnum, psegnum, su_nsums); 284 } 285 return; 286 } 287 288 int 289 check_summary(struct lfs * fs, SEGSUM * sp, daddr_t pseg_addr) 290 { 291 FINFO *fp; 292 int bc; /* Bytes in partial segment */ 293 int nblocks; 294 daddr_t seg_addr, *dp, *idp, daddr; 295 struct bufarea *bp; 296 int i, j, k, datac, len; 297 long sn; 298 u_long *datap; 299 u_int32_t ccksum; 300 301 sn = dtosn(fs, pseg_addr); 302 seg_addr = sntod(fs, sn); 303 304 /* 305 * printf("Pseg at 0x%x, %d inos, %d 306 * finfos\n",addr>>dbshift,sp->ss_ninos,sp->ss_nfinfo); 307 */ 308 /* We've already checked the sumsum, just do the data bounds and sum */ 309 310 /* 1. Count the blocks. */ 311 nblocks = ((sp->ss_ninos + INOPB(fs) - 1) / INOPB(fs)); 312 bc = nblocks << fs->lfs_bshift; 313 314 fp = (FINFO *)(sp + 1); 315 for (i = 0; i < sp->ss_nfinfo; i++) { 316 nblocks += fp->fi_nblocks; 317 bc += fp->fi_lastlength + ((fp->fi_nblocks - 1) << fs->lfs_bshift); 318 fp = (FINFO *)(fp->fi_blocks + fp->fi_nblocks); 319 } 320 datap = (u_long *)malloc(nblocks * sizeof(*datap)); 321 datac = 0; 322 323 dp = (daddr_t *)sp; 324 dp += sblock.lfs_sumsize / sizeof(daddr_t); 325 dp--; 326 327 idp = dp; 328 daddr = pseg_addr + btofsb(&sblock, sblock.lfs_sumsize); 329 fp = (FINFO *)(sp + 1); 330 for (i = 0, j = 0; i < sp->ss_nfinfo || j < howmany(sp->ss_ninos, INOPB(fs)); i++) { 331 /* printf("*idp=%x, daddr=%x\n", *idp, daddr); */ 332 if (i >= sp->ss_nfinfo && *idp != daddr) { 333 pwarn("Not enough inode blocks in pseg at 0x%x: found %d, wanted %d\n", 334 pseg_addr, j, howmany(sp->ss_ninos, INOPB(fs))); 335 pwarn("*idp=%x, daddr=%x\n", *idp, daddr); 336 break; 337 } 338 while (j < howmany(sp->ss_ninos, INOPB(fs)) && *idp == daddr) { 339 bp = getddblk(daddr, fs->lfs_ibsize); 340 datap[datac++] = ((u_long *)(bp->b_un.b_buf))[0]; 341 bp->b_flags &= ~B_INUSE; 342 343 ++j; 344 daddr += btofsb(&sblock, fs->lfs_ibsize); 345 --idp; 346 } 347 if (i < sp->ss_nfinfo) { 348 for (k = 0; k < fp->fi_nblocks; k++) { 349 len = (k == fp->fi_nblocks - 1 ? fp->fi_lastlength 350 : fs->lfs_bsize); 351 bp = getddblk(daddr, len); 352 datap[datac++] = ((u_long *)(bp->b_un.b_buf))[0]; 353 bp->b_flags &= ~B_INUSE; 354 daddr += btofsb(&sblock, len); 355 } 356 fp = (FINFO *)(fp->fi_blocks + fp->fi_nblocks); 357 } 358 } 359 360 if (datac != nblocks) { 361 pwarn("Partial segment at 0x%x expected %d blocks counted %d\n", 362 pseg_addr, nblocks, datac); 363 } 364 ccksum = cksum(datap, nblocks * sizeof(u_long)); 365 /* Check the data checksum */ 366 if (ccksum != sp->ss_datasum) { 367 pwarn("Partial segment at 0x%x data checksum mismatch: got 0x%x, expected 0x%x\n", 368 pseg_addr, sp->ss_datasum, ccksum); 369 /* return 0; */ 370 } 371 return bc; 372 } 373