17c478bd9Sstevel@tonic-gate /* 2*ce37393aSowenr * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 37c478bd9Sstevel@tonic-gate * Use is subject to license terms. 47c478bd9Sstevel@tonic-gate */ 57c478bd9Sstevel@tonic-gate 67c478bd9Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 77c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 87c478bd9Sstevel@tonic-gate 97c478bd9Sstevel@tonic-gate /* 107c478bd9Sstevel@tonic-gate * Copyright (c) 1980, 1986, 1990 The Regents of the University of California. 117c478bd9Sstevel@tonic-gate * All rights reserved. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms are permitted 147c478bd9Sstevel@tonic-gate * provided that: (1) source distributions retain this entire copyright 157c478bd9Sstevel@tonic-gate * notice and comment, and (2) distributions including binaries display 167c478bd9Sstevel@tonic-gate * the following acknowledgement: ``This product includes software 177c478bd9Sstevel@tonic-gate * developed by the University of California, Berkeley and its contributors'' 187c478bd9Sstevel@tonic-gate * in the documentation or other materials provided with the distribution 197c478bd9Sstevel@tonic-gate * and in all advertising materials mentioning features or use of this 207c478bd9Sstevel@tonic-gate * software. Neither the name of the University nor the names of its 217c478bd9Sstevel@tonic-gate * contributors may be used to endorse or promote products derived 227c478bd9Sstevel@tonic-gate * from this software without specific prior written permission. 237c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 247c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 257c478bd9Sstevel@tonic-gate * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 267c478bd9Sstevel@tonic-gate */ 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 297c478bd9Sstevel@tonic-gate 30355d6bb5Sswilcox #include <stdio.h> 31355d6bb5Sswilcox #include <stdlib.h> 32355d6bb5Sswilcox #include <unistd.h> 33355d6bb5Sswilcox #include <string.h> 347c478bd9Sstevel@tonic-gate #include <sys/param.h> 357c478bd9Sstevel@tonic-gate #include <sys/types.h> 367c478bd9Sstevel@tonic-gate #include <sys/mntent.h> 377c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h> 387c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 39355d6bb5Sswilcox #define _KERNEL 40355d6bb5Sswilcox #include <sys/fs/ufs_fsdir.h> 41355d6bb5Sswilcox #undef _KERNEL 427c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h> 437c478bd9Sstevel@tonic-gate #include "fsck.h" 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate /* 46355d6bb5Sswilcox * for each large file (size > MAXOFF_T), the global largefile_count 47355d6bb5Sswilcox * gets incremented during this pass. 487c478bd9Sstevel@tonic-gate */ 497c478bd9Sstevel@tonic-gate 50355d6bb5Sswilcox static uint32_t badblk; /* number seen for the current inode */ 51355d6bb5Sswilcox static uint32_t dupblk; /* number seen for the current inode */ 527c478bd9Sstevel@tonic-gate 53355d6bb5Sswilcox static void clear_attr_acl(fsck_ino_t, fsck_ino_t, char *); 54355d6bb5Sswilcox static void verify_inode(fsck_ino_t, struct inodesc *, fsck_ino_t); 55355d6bb5Sswilcox static void check_dirholes(fsck_ino_t, struct inodesc *); 56355d6bb5Sswilcox static void collapse_dirhole(fsck_ino_t, struct inodesc *); 57355d6bb5Sswilcox static void note_used(daddr32_t); 587c478bd9Sstevel@tonic-gate 59355d6bb5Sswilcox void 60355d6bb5Sswilcox pass1(void) 617c478bd9Sstevel@tonic-gate { 62355d6bb5Sswilcox uint_t c, i; 63355d6bb5Sswilcox daddr32_t cgd; 647c478bd9Sstevel@tonic-gate struct inodesc idesc; 65355d6bb5Sswilcox fsck_ino_t inumber; 66355d6bb5Sswilcox fsck_ino_t maxinumber; 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate /* 697c478bd9Sstevel@tonic-gate * Set file system reserved blocks in used block map. 707c478bd9Sstevel@tonic-gate */ 717c478bd9Sstevel@tonic-gate for (c = 0; c < sblock.fs_ncg; c++) { 727c478bd9Sstevel@tonic-gate cgd = cgdmin(&sblock, c); 737c478bd9Sstevel@tonic-gate if (c == 0) { 74355d6bb5Sswilcox /* 75355d6bb5Sswilcox * Doing the first cylinder group, account for 76355d6bb5Sswilcox * the cg summaries as well. 77355d6bb5Sswilcox */ 787c478bd9Sstevel@tonic-gate i = cgbase(&sblock, c); 797c478bd9Sstevel@tonic-gate cgd += howmany(sblock.fs_cssize, sblock.fs_fsize); 80355d6bb5Sswilcox } else { 817c478bd9Sstevel@tonic-gate i = cgsblock(&sblock, c); 82355d6bb5Sswilcox } 83355d6bb5Sswilcox for (; i < cgd; i++) { 84355d6bb5Sswilcox note_used(i); 85355d6bb5Sswilcox } 867c478bd9Sstevel@tonic-gate } 877c478bd9Sstevel@tonic-gate /* 88355d6bb5Sswilcox * Note blocks being used by the log, so we don't declare 89355d6bb5Sswilcox * them as available and some time in the future we get a 90355d6bb5Sswilcox * freeing free block panic. 917c478bd9Sstevel@tonic-gate */ 92355d6bb5Sswilcox if (islog && islogok && sblock.fs_logbno) 9339542a18Sabalfour examinelog(¬e_used); 947c478bd9Sstevel@tonic-gate 957c478bd9Sstevel@tonic-gate /* 96355d6bb5Sswilcox * Find all allocated blocks. This must be completed before 97355d6bb5Sswilcox * we read the contents of any directories, as dirscan() et al 98355d6bb5Sswilcox * don't want to know about block allocation holes. So, part 99355d6bb5Sswilcox * of this pass is to truncate any directories with holes to 100355d6bb5Sswilcox * just before those holes, so dirscan() can remain blissfully 101355d6bb5Sswilcox * ignorant. 1027c478bd9Sstevel@tonic-gate */ 1037c478bd9Sstevel@tonic-gate inumber = 0; 1047c478bd9Sstevel@tonic-gate n_files = n_blks = 0; 1057c478bd9Sstevel@tonic-gate resetinodebuf(); 1067c478bd9Sstevel@tonic-gate maxinumber = sblock.fs_ncg * sblock.fs_ipg; 1077c478bd9Sstevel@tonic-gate for (c = 0; c < sblock.fs_ncg; c++) { 1087c478bd9Sstevel@tonic-gate for (i = 0; i < sblock.fs_ipg; i++, inumber++) { 1097c478bd9Sstevel@tonic-gate if (inumber < UFSROOTINO) 1107c478bd9Sstevel@tonic-gate continue; 111355d6bb5Sswilcox init_inodesc(&idesc); 112355d6bb5Sswilcox idesc.id_type = ADDR; 113355d6bb5Sswilcox idesc.id_func = pass1check; 114355d6bb5Sswilcox verify_inode(inumber, &idesc, maxinumber); 115355d6bb5Sswilcox } 116355d6bb5Sswilcox } 117355d6bb5Sswilcox freeinodebuf(); 118355d6bb5Sswilcox } 119355d6bb5Sswilcox 120355d6bb5Sswilcox /* 121355d6bb5Sswilcox * Perform checks on an inode and setup/track the state of the inode 122355d6bb5Sswilcox * in maps (statemap[], lncntp[]) for future reference and validation. 123355d6bb5Sswilcox * Initiate the calls to ckinode and in turn pass1check() to handle 124355d6bb5Sswilcox * further validation. 125355d6bb5Sswilcox */ 126355d6bb5Sswilcox static void 127355d6bb5Sswilcox verify_inode(fsck_ino_t inumber, struct inodesc *idesc, fsck_ino_t maxinumber) 128355d6bb5Sswilcox { 129355d6bb5Sswilcox int j, clear, flags; 130355d6bb5Sswilcox int isdir; 131355d6bb5Sswilcox char *err; 132355d6bb5Sswilcox fsck_ino_t shadow, attrinode; 133355d6bb5Sswilcox daddr32_t ndb; 134355d6bb5Sswilcox struct dinode *dp; 135355d6bb5Sswilcox struct inoinfo *iip; 136355d6bb5Sswilcox 1377c478bd9Sstevel@tonic-gate dp = getnextinode(inumber); 1387c478bd9Sstevel@tonic-gate if ((dp->di_mode & IFMT) == 0) { 1397c478bd9Sstevel@tonic-gate /* mode and type of file is not set */ 140355d6bb5Sswilcox if ((memcmp((void *)dp->di_db, (void *)zino.di_db, 141355d6bb5Sswilcox NDADDR * sizeof (daddr32_t)) != 0) || 142355d6bb5Sswilcox (memcmp((void *)dp->di_ib, (void *)zino.di_ib, 143355d6bb5Sswilcox NIADDR * sizeof (daddr32_t)) != 0) || 144355d6bb5Sswilcox (dp->di_mode != 0) || (dp->di_size != 0)) { 145355d6bb5Sswilcox pfatal("PARTIALLY ALLOCATED INODE I=%u", inumber); 1467c478bd9Sstevel@tonic-gate if (reply("CLEAR") == 1) { 1477c478bd9Sstevel@tonic-gate dp = ginode(inumber); 1487c478bd9Sstevel@tonic-gate clearinode(dp); 1497c478bd9Sstevel@tonic-gate inodirty(); 150355d6bb5Sswilcox } else { 151355d6bb5Sswilcox iscorrupt = 1; 1527c478bd9Sstevel@tonic-gate } 1537c478bd9Sstevel@tonic-gate } 1547c478bd9Sstevel@tonic-gate statemap[inumber] = USTATE; 155355d6bb5Sswilcox return; 1567c478bd9Sstevel@tonic-gate } 1577c478bd9Sstevel@tonic-gate 158355d6bb5Sswilcox isdir = ((dp->di_mode & IFMT) == IFDIR) || 159355d6bb5Sswilcox ((dp->di_mode & IFMT) == IFATTRDIR); 160355d6bb5Sswilcox 161355d6bb5Sswilcox lastino = inumber; 162355d6bb5Sswilcox if (dp->di_size > (u_offset_t)UFS_MAXOFFSET_T) { 163355d6bb5Sswilcox pfatal("NEGATIVE SIZE %lld I=%d", 164355d6bb5Sswilcox (longlong_t)dp->di_size, inumber); 165355d6bb5Sswilcox goto bogus; 1667c478bd9Sstevel@tonic-gate } 167355d6bb5Sswilcox 168355d6bb5Sswilcox /* 169355d6bb5Sswilcox * A more precise test of the type is done later on. Just get 170355d6bb5Sswilcox * rid of the blatantly-wrong ones before we do any 171355d6bb5Sswilcox * significant work. 172355d6bb5Sswilcox */ 173355d6bb5Sswilcox if ((dp->di_mode & IFMT) == IFMT) { 174355d6bb5Sswilcox pfatal("BAD MODE 0%o I=%d", 175355d6bb5Sswilcox dp->di_mode & IFMT, inumber); 176355d6bb5Sswilcox if (reply("BAD MODE: MAKE IT A FILE") == 1) { 177355d6bb5Sswilcox statemap[inumber] = FSTATE; 178355d6bb5Sswilcox dp = ginode(inumber); 179355d6bb5Sswilcox dp->di_mode = IFREG | 0600; 180355d6bb5Sswilcox inodirty(); 181355d6bb5Sswilcox truncino(inumber, sblock.fs_fsize, TI_NOPARENT); 182355d6bb5Sswilcox dp = getnextrefresh(); 183355d6bb5Sswilcox } else { 184355d6bb5Sswilcox iscorrupt = 1; 185355d6bb5Sswilcox } 186355d6bb5Sswilcox } 187355d6bb5Sswilcox 188355d6bb5Sswilcox ndb = howmany(dp->di_size, (u_offset_t)sblock.fs_bsize); 189355d6bb5Sswilcox if (ndb < 0) { 190355d6bb5Sswilcox /* extra space to distinguish from previous pfatal() */ 191355d6bb5Sswilcox pfatal("NEGATIVE SIZE %lld I=%d", 192355d6bb5Sswilcox (longlong_t)dp->di_size, inumber); 193355d6bb5Sswilcox goto bogus; 194355d6bb5Sswilcox } 195355d6bb5Sswilcox 1967c478bd9Sstevel@tonic-gate if ((dp->di_mode & IFMT) == IFBLK || 1977c478bd9Sstevel@tonic-gate (dp->di_mode & IFMT) == IFCHR) { 198355d6bb5Sswilcox if (dp->di_size != 0) { 199355d6bb5Sswilcox pfatal("SPECIAL FILE WITH NON-ZERO LENGTH %lld I=%d", 200355d6bb5Sswilcox (longlong_t)dp->di_size, inumber); 201355d6bb5Sswilcox goto bogus; 202355d6bb5Sswilcox } 203355d6bb5Sswilcox 2047c478bd9Sstevel@tonic-gate for (j = 0; j < NDADDR; j++) { 205355d6bb5Sswilcox /* 206355d6bb5Sswilcox * It's a device, so all the block pointers 207355d6bb5Sswilcox * should be zero except for di_ordev. 208355d6bb5Sswilcox * di_ordev is overlayed on the block array, 209355d6bb5Sswilcox * but where varies between big and little 210355d6bb5Sswilcox * endian, so make sure that the only non-zero 211355d6bb5Sswilcox * element is the correct one. There can be 212355d6bb5Sswilcox * a device whose ordev is zero, so we can't 213355d6bb5Sswilcox * check for the reverse. 214355d6bb5Sswilcox */ 2157c478bd9Sstevel@tonic-gate if (dp->di_db[j] != 0 && 2167c478bd9Sstevel@tonic-gate &dp->di_db[j] != &dp->di_ordev) { 2177c478bd9Sstevel@tonic-gate if (debug) { 218355d6bb5Sswilcox (void) printf( 219355d6bb5Sswilcox "spec file di_db[%d] has %d\n", 220355d6bb5Sswilcox j, dp->di_db[j]); 2217c478bd9Sstevel@tonic-gate } 222355d6bb5Sswilcox pfatal( 223355d6bb5Sswilcox "SPECIAL FILE WITH NON-ZERO FRAGMENT LIST I=%d", 224355d6bb5Sswilcox inumber); 225355d6bb5Sswilcox goto bogus; 226355d6bb5Sswilcox } 227355d6bb5Sswilcox } 228355d6bb5Sswilcox 229355d6bb5Sswilcox for (j = 0; j < NIADDR; j++) { 230355d6bb5Sswilcox if (dp->di_ib[j] != 0) { 231355d6bb5Sswilcox if (debug) 232355d6bb5Sswilcox (void) printf( 233355d6bb5Sswilcox "special has %d at ib[%d]\n", 234355d6bb5Sswilcox dp->di_ib[j], j); 235355d6bb5Sswilcox pfatal( 236355d6bb5Sswilcox "SPECIAL FILE WITH NON-ZERO FRAGMENT LIST I=%d", 237355d6bb5Sswilcox inumber); 238355d6bb5Sswilcox goto bogus; 2397c478bd9Sstevel@tonic-gate } 2407c478bd9Sstevel@tonic-gate } 2417c478bd9Sstevel@tonic-gate } else { 242355d6bb5Sswilcox /* 243355d6bb5Sswilcox * This assignment is mostly here to appease lint, but 244355d6bb5Sswilcox * doesn't hurt. 245355d6bb5Sswilcox */ 246355d6bb5Sswilcox err = "Internal error: unexpected variant of having " 247355d6bb5Sswilcox "blocks past end of file I=%d"; 248355d6bb5Sswilcox 249355d6bb5Sswilcox clear = 0; 250355d6bb5Sswilcox 251355d6bb5Sswilcox /* 252355d6bb5Sswilcox * If it's not a device, it has to follow the 253355d6bb5Sswilcox * rules for files. In particular, no blocks after 254355d6bb5Sswilcox * the last one that di_size says is in use. 255355d6bb5Sswilcox */ 256355d6bb5Sswilcox for (j = ndb; j < NDADDR; j++) { 2577c478bd9Sstevel@tonic-gate if (dp->di_db[j] != 0) { 2587c478bd9Sstevel@tonic-gate if (debug) { 259355d6bb5Sswilcox (void) printf("bad file direct " 260355d6bb5Sswilcox "addr[%d]: block 0x%x " 261355d6bb5Sswilcox "format: 0%o\n", 262355d6bb5Sswilcox j, dp->di_db[j], 263355d6bb5Sswilcox dp->di_mode & IFMT); 2647c478bd9Sstevel@tonic-gate } 265355d6bb5Sswilcox err = "FILE WITH FRAGMENTS PAST END I=%d"; 266355d6bb5Sswilcox clear = 1; 267355d6bb5Sswilcox break; 2687c478bd9Sstevel@tonic-gate } 2697c478bd9Sstevel@tonic-gate } 270355d6bb5Sswilcox 271355d6bb5Sswilcox /* 272355d6bb5Sswilcox * Find last indirect pointer that should be in use, 273355d6bb5Sswilcox * and make sure any after it are clear. 274355d6bb5Sswilcox */ 275355d6bb5Sswilcox if (!clear) { 276355d6bb5Sswilcox for (j = 0, ndb -= NDADDR; ndb > 0; j++) { 2777c478bd9Sstevel@tonic-gate ndb /= NINDIR(&sblock); 278355d6bb5Sswilcox } 279355d6bb5Sswilcox for (; j < NIADDR; j++) { 2807c478bd9Sstevel@tonic-gate if (dp->di_ib[j] != 0) { 2817c478bd9Sstevel@tonic-gate if (debug) { 282355d6bb5Sswilcox (void) printf("bad file " 283355d6bb5Sswilcox "indirect addr: block %d\n", 2847c478bd9Sstevel@tonic-gate dp->di_ib[j]); 2857c478bd9Sstevel@tonic-gate } 286355d6bb5Sswilcox err = 287355d6bb5Sswilcox "FILE WITH FRAGMENTS PAST END I=%d"; 288355d6bb5Sswilcox clear = 2; 289355d6bb5Sswilcox break; 2907c478bd9Sstevel@tonic-gate } 291355d6bb5Sswilcox } 292355d6bb5Sswilcox } 293355d6bb5Sswilcox 294355d6bb5Sswilcox if (clear) { 295355d6bb5Sswilcox /* 296355d6bb5Sswilcox * The discarded blocks will be garbage- 297355d6bb5Sswilcox * collected in pass5. If we're told not to 298355d6bb5Sswilcox * discard them, it's just lost blocks, which 299355d6bb5Sswilcox * isn't worth setting iscorrupt for. 300355d6bb5Sswilcox */ 301355d6bb5Sswilcox pwarn(err, inumber); 302355d6bb5Sswilcox if (preen || reply("DISCARD EXCESS FRAGMENTS") == 1) { 303355d6bb5Sswilcox dp = ginode(inumber); 304355d6bb5Sswilcox if (clear == 1) { 305355d6bb5Sswilcox for (; j < NDADDR; j++) 306355d6bb5Sswilcox dp->di_db[j] = 0; 307355d6bb5Sswilcox j = 0; 308355d6bb5Sswilcox } 309355d6bb5Sswilcox for (; j < NIADDR; j++) 310355d6bb5Sswilcox dp->di_ib[j] = 0; 311355d6bb5Sswilcox inodirty(); 312355d6bb5Sswilcox dp = getnextrefresh(); 313355d6bb5Sswilcox if (preen) 314355d6bb5Sswilcox (void) printf(" (TRUNCATED)"); 315355d6bb5Sswilcox } 316355d6bb5Sswilcox } 317355d6bb5Sswilcox } 318355d6bb5Sswilcox 3197c478bd9Sstevel@tonic-gate if (ftypeok(dp) == 0) { 320355d6bb5Sswilcox pfatal("UNKNOWN FILE TYPE 0%o I=%d", dp->di_mode, inumber); 321355d6bb5Sswilcox goto bogus; 3227c478bd9Sstevel@tonic-gate } 3237c478bd9Sstevel@tonic-gate n_files++; 324355d6bb5Sswilcox TRACK_LNCNTP(inumber, lncntp[inumber] = dp->di_nlink); 325355d6bb5Sswilcox 3267c478bd9Sstevel@tonic-gate /* 327355d6bb5Sswilcox * We can't do anything about it right now, so note that its 328355d6bb5Sswilcox * processing is being delayed. Otherwise, we'd be changing 329355d6bb5Sswilcox * the block allocations out from under ourselves, which causes 330355d6bb5Sswilcox * no end of confusion. 331355d6bb5Sswilcox */ 332355d6bb5Sswilcox flags = statemap[inumber] & INDELAYD; 333355d6bb5Sswilcox 334355d6bb5Sswilcox /* 335355d6bb5Sswilcox * if errorlocked or logging, then open deleted files will 3367c478bd9Sstevel@tonic-gate * manifest as di_nlink <= 0 and di_mode != 0 3377c478bd9Sstevel@tonic-gate * so skip them; they're ok. 338*ce37393aSowenr * Also skip anything already marked to be cleared. 3397c478bd9Sstevel@tonic-gate */ 3407c478bd9Sstevel@tonic-gate if (dp->di_nlink <= 0 && 341*ce37393aSowenr !((errorlocked || islog) && dp->di_mode == 0) && 342*ce37393aSowenr !(flags & INCLEAR)) { 343355d6bb5Sswilcox flags |= INZLINK; 344355d6bb5Sswilcox if (debug) 345355d6bb5Sswilcox (void) printf( 346355d6bb5Sswilcox "marking i=%d INZLINK; nlink %d, mode 0%o, islog %d\n", 347355d6bb5Sswilcox inumber, dp->di_nlink, dp->di_mode, islog); 3487c478bd9Sstevel@tonic-gate } 3497c478bd9Sstevel@tonic-gate 350355d6bb5Sswilcox switch (dp->di_mode & IFMT) { 351355d6bb5Sswilcox case IFDIR: 352355d6bb5Sswilcox case IFATTRDIR: 353355d6bb5Sswilcox if (dp->di_size == 0) { 3547c478bd9Sstevel@tonic-gate /* 355355d6bb5Sswilcox * INCLEAR means it will be ignored by passes 2 & 3. 3567c478bd9Sstevel@tonic-gate */ 357355d6bb5Sswilcox if ((dp->di_mode & IFMT) == IFDIR) 358355d6bb5Sswilcox (void) printf("ZERO-LENGTH DIR I=%d\n", 3597c478bd9Sstevel@tonic-gate inumber); 3607c478bd9Sstevel@tonic-gate else 361355d6bb5Sswilcox (void) printf("ZERO-LENGTH ATTRDIR I=%d\n", 362355d6bb5Sswilcox inumber); 363355d6bb5Sswilcox add_orphan_dir(inumber); 364355d6bb5Sswilcox flags |= INCLEAR; 365*ce37393aSowenr flags &= ~INZLINK; /* It will be cleared anyway */ 366355d6bb5Sswilcox } 367355d6bb5Sswilcox statemap[inumber] = DSTATE | flags; 3687c478bd9Sstevel@tonic-gate cacheino(dp, inumber); 369355d6bb5Sswilcox countdirs++; 370355d6bb5Sswilcox break; 371355d6bb5Sswilcox 372355d6bb5Sswilcox case IFSHAD: 373355d6bb5Sswilcox if (dp->di_size == 0) { 374355d6bb5Sswilcox (void) printf("ZERO-LENGTH SHADOW I=%d\n", inumber); 375355d6bb5Sswilcox flags |= INCLEAR; 376*ce37393aSowenr flags &= ~INZLINK; /* It will be cleared anyway */ 377355d6bb5Sswilcox } 378355d6bb5Sswilcox statemap[inumber] = SSTATE | flags; 3797c478bd9Sstevel@tonic-gate cacheacl(dp, inumber); 380355d6bb5Sswilcox break; 381355d6bb5Sswilcox 382355d6bb5Sswilcox default: 383355d6bb5Sswilcox statemap[inumber] = FSTATE | flags; 384355d6bb5Sswilcox } 385355d6bb5Sswilcox 386355d6bb5Sswilcox badblk = 0; 387355d6bb5Sswilcox dupblk = 0; 388355d6bb5Sswilcox idesc->id_number = inumber; 389355d6bb5Sswilcox idesc->id_fix = DONTKNOW; 3907c478bd9Sstevel@tonic-gate if (dp->di_size > (u_offset_t)MAXOFF_T) { 3917c478bd9Sstevel@tonic-gate largefile_count++; 3927c478bd9Sstevel@tonic-gate } 3937c478bd9Sstevel@tonic-gate 394355d6bb5Sswilcox (void) ckinode(dp, idesc, CKI_TRAVERSE); 395355d6bb5Sswilcox if (isdir && (idesc->id_firsthole >= 0)) 396355d6bb5Sswilcox check_dirholes(inumber, idesc); 397355d6bb5Sswilcox 398355d6bb5Sswilcox if (dp->di_blocks != idesc->id_entryno) { 3997c478bd9Sstevel@tonic-gate /* 400355d6bb5Sswilcox * The kernel releases any blocks it finds in the lists, 401355d6bb5Sswilcox * ignoring the block count itself. So, a bad count is 402355d6bb5Sswilcox * not grounds for setting iscorrupt. 4037c478bd9Sstevel@tonic-gate */ 404355d6bb5Sswilcox pwarn("INCORRECT DISK BLOCK COUNT I=%u (%d should be %d)", 405355d6bb5Sswilcox inumber, (uint32_t)dp->di_blocks, idesc->id_entryno); 406355d6bb5Sswilcox if (!preen && (reply("CORRECT") == 0)) 407355d6bb5Sswilcox return; 4087c478bd9Sstevel@tonic-gate dp = ginode(inumber); 409355d6bb5Sswilcox dp->di_blocks = idesc->id_entryno; 410355d6bb5Sswilcox iip = getinoinfo(inumber); 411355d6bb5Sswilcox if (iip != NULL) 412355d6bb5Sswilcox iip->i_isize = dp->di_size; 4137c478bd9Sstevel@tonic-gate inodirty(); 414355d6bb5Sswilcox if (preen) 415355d6bb5Sswilcox (void) printf(" (CORRECTED)\n"); 416355d6bb5Sswilcox } 417355d6bb5Sswilcox if (isdir && (dp->di_blocks == 0)) { 4187c478bd9Sstevel@tonic-gate /* 419355d6bb5Sswilcox * INCLEAR will cause passes 2 and 3 to skip it. 4207c478bd9Sstevel@tonic-gate */ 421355d6bb5Sswilcox (void) printf("DIR WITH ZERO BLOCKS I=%d\n", inumber); 4227c478bd9Sstevel@tonic-gate statemap[inumber] = DCLEAR; 423355d6bb5Sswilcox add_orphan_dir(inumber); 424355d6bb5Sswilcox } 425355d6bb5Sswilcox 4267c478bd9Sstevel@tonic-gate /* 4277c478bd9Sstevel@tonic-gate * Check that the ACL is on a valid file type 4287c478bd9Sstevel@tonic-gate */ 4297c478bd9Sstevel@tonic-gate shadow = dp->di_shadow; 4307c478bd9Sstevel@tonic-gate if (shadow != 0) { 4317c478bd9Sstevel@tonic-gate if (acltypeok(dp) == 0) { 432355d6bb5Sswilcox clear_attr_acl(inumber, -1, 433355d6bb5Sswilcox "NON-ZERO ACL REFERENCE, I=%d\n"); 4347c478bd9Sstevel@tonic-gate } else if ((shadow <= UFSROOTINO) || 4357c478bd9Sstevel@tonic-gate (shadow > maxinumber)) { 436355d6bb5Sswilcox clear_attr_acl(inumber, -1, 437355d6bb5Sswilcox "BAD ACL REFERENCE I=%d\n"); 4387c478bd9Sstevel@tonic-gate } else { 4397c478bd9Sstevel@tonic-gate registershadowclient(shadow, 4407c478bd9Sstevel@tonic-gate inumber, &shadowclientinfo); 4417c478bd9Sstevel@tonic-gate } 4427c478bd9Sstevel@tonic-gate } 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate attrinode = dp->di_oeftflag; 4457c478bd9Sstevel@tonic-gate if (attrinode != 0) { 4467c478bd9Sstevel@tonic-gate if ((attrinode <= UFSROOTINO) || 4477c478bd9Sstevel@tonic-gate (attrinode > maxinumber)) { 448355d6bb5Sswilcox clear_attr_acl(attrinode, inumber, 449355d6bb5Sswilcox "BAD ATTRIBUTE REFERENCE TO I=%d FROM I=%d\n"); 4507c478bd9Sstevel@tonic-gate } else { 4517c478bd9Sstevel@tonic-gate dp = ginode(attrinode); 4527c478bd9Sstevel@tonic-gate if ((dp->di_mode & IFMT) != IFATTRDIR) { 453355d6bb5Sswilcox clear_attr_acl(attrinode, inumber, 454355d6bb5Sswilcox "BAD ATTRIBUTE DIR REF TO I=%d FROM I=%d\n"); 455355d6bb5Sswilcox } else if (dp->di_size == 0) { 456355d6bb5Sswilcox clear_attr_acl(attrinode, inumber, 457355d6bb5Sswilcox "REFERENCE TO ZERO-LENGTH ATTRIBUTE DIR I=%d from I=%d\n"); 4587c478bd9Sstevel@tonic-gate } else { 459355d6bb5Sswilcox registershadowclient(attrinode, inumber, 4607c478bd9Sstevel@tonic-gate &attrclientinfo); 4617c478bd9Sstevel@tonic-gate } 4627c478bd9Sstevel@tonic-gate } 4637c478bd9Sstevel@tonic-gate } 464355d6bb5Sswilcox return; 465355d6bb5Sswilcox 466355d6bb5Sswilcox /* 467355d6bb5Sswilcox * If we got here, we've not had the chance to see if a 468355d6bb5Sswilcox * directory has holes, but we know the directory's bad, 469355d6bb5Sswilcox * so it's safe to always return false (no holes found). 470355d6bb5Sswilcox * 471355d6bb5Sswilcox * Also, a pfatal() is always done before jumping here, so 472355d6bb5Sswilcox * we know we're not in preen mode. 473355d6bb5Sswilcox */ 474355d6bb5Sswilcox bogus: 475355d6bb5Sswilcox if (isdir) { 476355d6bb5Sswilcox /* 477355d6bb5Sswilcox * INCLEAR makes passes 2 & 3 skip it. 478355d6bb5Sswilcox */ 4797c478bd9Sstevel@tonic-gate statemap[inumber] = DCLEAR; 480355d6bb5Sswilcox add_orphan_dir(inumber); 4817c478bd9Sstevel@tonic-gate cacheino(dp, inumber); 482355d6bb5Sswilcox } else { 4837c478bd9Sstevel@tonic-gate statemap[inumber] = FCLEAR; 484355d6bb5Sswilcox } 4857c478bd9Sstevel@tonic-gate if (reply("CLEAR") == 1) { 486355d6bb5Sswilcox (void) tdelete((void *)inumber, &limbo_dirs, ino_t_cmp); 487355d6bb5Sswilcox freeino(inumber, TI_PARENT); 4887c478bd9Sstevel@tonic-gate inodirty(); 489355d6bb5Sswilcox } else { 490355d6bb5Sswilcox iscorrupt = 1; 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate } 4937c478bd9Sstevel@tonic-gate 494355d6bb5Sswilcox /* 495355d6bb5Sswilcox * Do fixup for bad acl/attr references. If PARENT is -1, then 496355d6bb5Sswilcox * we assume we're working on a shadow, otherwise an extended attribute. 497355d6bb5Sswilcox * FMT must be a printf format string, with one %d directive for 498355d6bb5Sswilcox * the inode number. 499355d6bb5Sswilcox */ 500355d6bb5Sswilcox static void 501355d6bb5Sswilcox clear_attr_acl(fsck_ino_t inumber, fsck_ino_t parent, char *fmt) 502355d6bb5Sswilcox { 503355d6bb5Sswilcox fsck_ino_t victim = inumber; 504355d6bb5Sswilcox struct dinode *dp; 505355d6bb5Sswilcox 506355d6bb5Sswilcox if (parent != -1) 507355d6bb5Sswilcox victim = parent; 508355d6bb5Sswilcox 509355d6bb5Sswilcox if (fmt != NULL) { 510355d6bb5Sswilcox if (parent == -1) 511355d6bb5Sswilcox pwarn(fmt, (int)inumber); 512355d6bb5Sswilcox else 513355d6bb5Sswilcox pwarn(fmt, (int)inumber, (int)parent); 514355d6bb5Sswilcox } 515355d6bb5Sswilcox 516355d6bb5Sswilcox if (debug) 517355d6bb5Sswilcox (void) printf("parent file/dir I=%d\nvictim I=%d", 518355d6bb5Sswilcox (int)parent, (int)victim); 519355d6bb5Sswilcox 520355d6bb5Sswilcox if (!preen && (reply("REMOVE REFERENCE") == 0)) { 521355d6bb5Sswilcox iscorrupt = 1; 522355d6bb5Sswilcox return; 523355d6bb5Sswilcox } 524355d6bb5Sswilcox 525355d6bb5Sswilcox dp = ginode(victim); 526355d6bb5Sswilcox if (parent == -1) { 527355d6bb5Sswilcox /* 528355d6bb5Sswilcox * The file had a bad shadow/acl, so lock it down 529355d6bb5Sswilcox * until someone can protect it the way they need it 530355d6bb5Sswilcox * to be (i.e., be conservatively paranoid). 531355d6bb5Sswilcox */ 532355d6bb5Sswilcox dp->di_shadow = 0; 533355d6bb5Sswilcox dp->di_mode &= IFMT; 534355d6bb5Sswilcox } else { 535355d6bb5Sswilcox dp->di_oeftflag = 0; 536355d6bb5Sswilcox } 537355d6bb5Sswilcox 538355d6bb5Sswilcox inodirty(); 539355d6bb5Sswilcox if (preen) 540355d6bb5Sswilcox (void) printf(" (CORRECTED)\n"); 541355d6bb5Sswilcox } 542355d6bb5Sswilcox 543355d6bb5Sswilcox /* 544355d6bb5Sswilcox * Check if we have holes in the directory's indirect 545355d6bb5Sswilcox * blocks. If there are, get rid of everything after 546355d6bb5Sswilcox * the first hole. 547355d6bb5Sswilcox */ 548355d6bb5Sswilcox static void 549355d6bb5Sswilcox check_dirholes(fsck_ino_t inumber, struct inodesc *idesc) 550355d6bb5Sswilcox { 551355d6bb5Sswilcox char pathbuf[MAXPATHLEN + 1]; 552355d6bb5Sswilcox 553355d6bb5Sswilcox getpathname(pathbuf, idesc->id_number, idesc->id_number); 554355d6bb5Sswilcox pfatal("I=%d DIRECTORY %s: CONTAINS EMPTY BLOCKS", 555355d6bb5Sswilcox idesc->id_number, pathbuf); 556355d6bb5Sswilcox if (reply("TRUNCATE AT FIRST EMPTY BLOCK") == 1) { 557355d6bb5Sswilcox /* 558355d6bb5Sswilcox * We found a hole, so get rid of it. 559355d6bb5Sswilcox */ 560355d6bb5Sswilcox collapse_dirhole(inumber, idesc); 561355d6bb5Sswilcox 562355d6bb5Sswilcox if (preen) 563355d6bb5Sswilcox (void) printf(" (TRUNCATED)\n"); 564355d6bb5Sswilcox } else { 565355d6bb5Sswilcox iscorrupt = 1; 566355d6bb5Sswilcox } 567355d6bb5Sswilcox } 568355d6bb5Sswilcox 569355d6bb5Sswilcox /* 570355d6bb5Sswilcox * Truncate a directory to its first hole. If there are non-holes 571355d6bb5Sswilcox * in the direct blocks after the problem block, move them down so 572355d6bb5Sswilcox * that there's somewhat less lossage. Doing this for indirect blocks 573355d6bb5Sswilcox * is left as an exercise for the reader. 574355d6bb5Sswilcox */ 575355d6bb5Sswilcox static void 576355d6bb5Sswilcox collapse_dirhole(fsck_ino_t inumber, struct inodesc *idesc) 577355d6bb5Sswilcox { 578355d6bb5Sswilcox offset_t new_size; 579355d6bb5Sswilcox int blocks; 580355d6bb5Sswilcox 581355d6bb5Sswilcox if (idesc->id_firsthole < 0) { 582355d6bb5Sswilcox return; 583355d6bb5Sswilcox } 584355d6bb5Sswilcox 585355d6bb5Sswilcox /* 586355d6bb5Sswilcox * Since truncino() adjusts the size, we don't need to do that here, 587355d6bb5Sswilcox * but we have to tell it what final size we want. 588355d6bb5Sswilcox * 589355d6bb5Sswilcox * We need to count from block zero up through the last block 590355d6bb5Sswilcox * before the hole. If the hole is in the indirect blocks, chop at 591355d6bb5Sswilcox * the start of the nearest level of indirection. Orphans will 592355d6bb5Sswilcox * get reconnected, so we're not actually losing anything by doing 593355d6bb5Sswilcox * it this way, and we're simplifying truncation significantly. 594355d6bb5Sswilcox */ 595355d6bb5Sswilcox new_size = idesc->id_firsthole * (offset_t)sblock.fs_bsize; 596355d6bb5Sswilcox blocks = howmany(new_size, sblock.fs_bsize); 597355d6bb5Sswilcox if (blocks > NDADDR) { 598355d6bb5Sswilcox if (blocks < (NDADDR + NINDIR(&sblock))) 599355d6bb5Sswilcox blocks = NDADDR; 600355d6bb5Sswilcox else if (blocks < (NDADDR + NINDIR(&sblock) + 601355d6bb5Sswilcox (NINDIR(&sblock) * NINDIR(&sblock)))) 602355d6bb5Sswilcox blocks = NDADDR + NINDIR(&sblock); 603355d6bb5Sswilcox else 604355d6bb5Sswilcox blocks = NDADDR + NINDIR(&sblock) + 605355d6bb5Sswilcox (NINDIR(&sblock) * NINDIR(&sblock)); 606355d6bb5Sswilcox new_size = blocks * sblock.fs_bsize; 607355d6bb5Sswilcox if (debug) 608355d6bb5Sswilcox (void) printf("to %lld (blocks %d)\n", 609355d6bb5Sswilcox (longlong_t)new_size, blocks); 610355d6bb5Sswilcox } 611355d6bb5Sswilcox truncino(inumber, new_size, TI_NOPARENT); 612355d6bb5Sswilcox 613355d6bb5Sswilcox /* 614355d6bb5Sswilcox * Technically, there are still the original number of fragments 615355d6bb5Sswilcox * associated with the object. However, that number is not used 616355d6bb5Sswilcox * to control anything, so we can do the in-memory truncation of 617355d6bb5Sswilcox * it without bad things happening. 618355d6bb5Sswilcox */ 619355d6bb5Sswilcox idesc->id_entryno = btodb(new_size); 620355d6bb5Sswilcox } 621355d6bb5Sswilcox 622355d6bb5Sswilcox int 623355d6bb5Sswilcox pass1check(struct inodesc *idesc) 6247c478bd9Sstevel@tonic-gate { 6257c478bd9Sstevel@tonic-gate int res = KEEPON; 6267c478bd9Sstevel@tonic-gate int anyout; 6277c478bd9Sstevel@tonic-gate int nfrags; 628355d6bb5Sswilcox daddr32_t lbn; 629355d6bb5Sswilcox daddr32_t fragno = idesc->id_blkno; 630355d6bb5Sswilcox struct dinode *dp; 6317c478bd9Sstevel@tonic-gate 632303bf60bSsdebnath /* 633303bf60bSsdebnath * If this is a fallocate'd file, block numbers may be stored 634303bf60bSsdebnath * as negative. In that case negate the negative numbers. 635303bf60bSsdebnath */ 636303bf60bSsdebnath dp = ginode(idesc->id_number); 637303bf60bSsdebnath if (dp->di_cflags & IFALLOCATE && fragno < 0) 638303bf60bSsdebnath fragno = -fragno; 639303bf60bSsdebnath 640355d6bb5Sswilcox if ((anyout = chkrange(fragno, idesc->id_numfrags)) != 0) { 641355d6bb5Sswilcox /* 642355d6bb5Sswilcox * Note that blkerror() exits when preening. 643355d6bb5Sswilcox */ 644355d6bb5Sswilcox blkerror(idesc->id_number, "OUT OF RANGE", 645355d6bb5Sswilcox fragno, idesc->id_lbn * sblock.fs_frag); 646355d6bb5Sswilcox 647355d6bb5Sswilcox dp = ginode(idesc->id_number); 648355d6bb5Sswilcox if ((((dp->di_mode & IFMT) == IFDIR) || 649355d6bb5Sswilcox ((dp->di_mode & IFMT) == IFATTRDIR)) && 650355d6bb5Sswilcox (idesc->id_firsthole < 0)) { 651355d6bb5Sswilcox idesc->id_firsthole = idesc->id_lbn; 652355d6bb5Sswilcox } 653355d6bb5Sswilcox 6547c478bd9Sstevel@tonic-gate if (++badblk >= MAXBAD) { 655355d6bb5Sswilcox pwarn("EXCESSIVE BAD FRAGMENTS I=%u", 6567c478bd9Sstevel@tonic-gate idesc->id_number); 6577c478bd9Sstevel@tonic-gate if (reply("CONTINUE") == 0) 658355d6bb5Sswilcox errexit("Program terminated."); 659355d6bb5Sswilcox /* 660355d6bb5Sswilcox * See discussion below as to why we don't 661355d6bb5Sswilcox * want to short-circuit the processing of 662355d6bb5Sswilcox * this inode. However, we know that this 663355d6bb5Sswilcox * particular block is bad, so we don't need 664355d6bb5Sswilcox * to go through the dup check loop. 665355d6bb5Sswilcox */ 666355d6bb5Sswilcox return (SKIP | STOP); 6677c478bd9Sstevel@tonic-gate } 668355d6bb5Sswilcox } 669355d6bb5Sswilcox 670355d6bb5Sswilcox /* 671355d6bb5Sswilcox * For each fragment, verify that it is a legal one (either 672355d6bb5Sswilcox * by having already found the entire run to be legal, or by 673355d6bb5Sswilcox * individual inspection), and if it is legal, see if we've 674355d6bb5Sswilcox * seen it before or not. If we haven't, note that we've seen 675355d6bb5Sswilcox * it and continue on. If we have (our in-core bitmap shows 676355d6bb5Sswilcox * it as already being busy), then this must be a duplicate 677355d6bb5Sswilcox * allocation. Whine and moan accordingly. 678355d6bb5Sswilcox * 679355d6bb5Sswilcox * Note that for full-block allocations, this will produce 680355d6bb5Sswilcox * a complaint for each fragment making up the block (i.e., 681355d6bb5Sswilcox * fs_frags' worth). Among other things, this could be 682355d6bb5Sswilcox * considered artificially inflating the dup-block count. 683355d6bb5Sswilcox * However, since it is possible that one file has a full 684355d6bb5Sswilcox * fs block allocated, but another is only claiming a frag 685355d6bb5Sswilcox * or two out of the middle, we'll just live it. 686355d6bb5Sswilcox */ 687355d6bb5Sswilcox for (nfrags = 0; nfrags < idesc->id_numfrags; fragno++, nfrags++) { 688355d6bb5Sswilcox if (anyout && chkrange(fragno, 1)) { 689355d6bb5Sswilcox /* bad fragment number */ 690355d6bb5Sswilcox res = SKIP; 691355d6bb5Sswilcox } else if (!testbmap(fragno)) { 692355d6bb5Sswilcox /* no other claims seen as yet */ 693355d6bb5Sswilcox note_used(fragno); 6947c478bd9Sstevel@tonic-gate } else { 695355d6bb5Sswilcox /* 696355d6bb5Sswilcox * We have a duplicate claim for the same fragment. 697355d6bb5Sswilcox * 698355d6bb5Sswilcox * blkerror() exits when preening. 699355d6bb5Sswilcox * 700355d6bb5Sswilcox * We want to report all the dups up until 701355d6bb5Sswilcox * hitting MAXDUP. Fortunately, blkerror()'s 702355d6bb5Sswilcox * side-effects on statemap[] are idempotent, 703355d6bb5Sswilcox * so the ``extra'' calls are harmless. 704355d6bb5Sswilcox */ 705355d6bb5Sswilcox lbn = idesc->id_lbn * sblock.fs_frag + nfrags; 706355d6bb5Sswilcox if (dupblk < MAXDUP) 707355d6bb5Sswilcox blkerror(idesc->id_number, "DUP", fragno, lbn); 708355d6bb5Sswilcox 709355d6bb5Sswilcox /* 710355d6bb5Sswilcox * Use ==, so we only complain once, no matter 711355d6bb5Sswilcox * how far over the limit we end up going. 712355d6bb5Sswilcox */ 713355d6bb5Sswilcox if (++dupblk == MAXDUP) { 714355d6bb5Sswilcox pwarn("EXCESSIVE DUPLICATE FRAGMENTS I=%u", 715355d6bb5Sswilcox idesc->id_number); 716355d6bb5Sswilcox if (reply("CONTINUE") == 0) 717355d6bb5Sswilcox errexit("Program terminated."); 718355d6bb5Sswilcox 719355d6bb5Sswilcox /* 720355d6bb5Sswilcox * If we stop the traversal here, then 721355d6bb5Sswilcox * there may be more dups in the 722355d6bb5Sswilcox * inode's block list that don't get 723355d6bb5Sswilcox * flagged. Later, if we're told to 724355d6bb5Sswilcox * clear one of the files claiming 725355d6bb5Sswilcox * these blocks, but not the other, we 726355d6bb5Sswilcox * will release blocks that are 727355d6bb5Sswilcox * actually still in use. An additional 728355d6bb5Sswilcox * fsck run would be necessary to undo 729355d6bb5Sswilcox * the damage. So, instead of the 730355d6bb5Sswilcox * traditional return (STOP) when told 731355d6bb5Sswilcox * to continue, we really do just continue. 732355d6bb5Sswilcox */ 7337c478bd9Sstevel@tonic-gate } 734355d6bb5Sswilcox (void) find_dup_ref(fragno, idesc->id_number, lbn, 735355d6bb5Sswilcox DB_CREATE | DB_INCR); 7367c478bd9Sstevel@tonic-gate } 7377c478bd9Sstevel@tonic-gate /* 738355d6bb5Sswilcox * id_entryno counts the number of disk blocks found. 7397c478bd9Sstevel@tonic-gate */ 740355d6bb5Sswilcox idesc->id_entryno += btodb(sblock.fs_fsize); 7417c478bd9Sstevel@tonic-gate } 7427c478bd9Sstevel@tonic-gate return (res); 7437c478bd9Sstevel@tonic-gate } 744355d6bb5Sswilcox 745355d6bb5Sswilcox static void 746355d6bb5Sswilcox note_used(daddr32_t frag) 747355d6bb5Sswilcox { 748355d6bb5Sswilcox n_blks++; 749355d6bb5Sswilcox setbmap(frag); 750355d6bb5Sswilcox } 751