xref: /onnv-gate/usr/src/cmd/fs.d/ufs/fsck/pass1.c (revision 6106:ab0a40818b83)
10Sstevel@tonic-gate /*
2*6106Sowenr  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
70Sstevel@tonic-gate /*	  All Rights Reserved  	*/
80Sstevel@tonic-gate 
90Sstevel@tonic-gate /*
100Sstevel@tonic-gate  * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
110Sstevel@tonic-gate  * All rights reserved.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * Redistribution and use in source and binary forms are permitted
140Sstevel@tonic-gate  * provided that: (1) source distributions retain this entire copyright
150Sstevel@tonic-gate  * notice and comment, and (2) distributions including binaries display
160Sstevel@tonic-gate  * the following acknowledgement:  ``This product includes software
170Sstevel@tonic-gate  * developed by the University of California, Berkeley and its contributors''
180Sstevel@tonic-gate  * in the documentation or other materials provided with the distribution
190Sstevel@tonic-gate  * and in all advertising materials mentioning features or use of this
200Sstevel@tonic-gate  * software. Neither the name of the University nor the names of its
210Sstevel@tonic-gate  * contributors may be used to endorse or promote products derived
220Sstevel@tonic-gate  * from this software without specific prior written permission.
230Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
240Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
250Sstevel@tonic-gate  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
290Sstevel@tonic-gate 
30392Sswilcox #include <stdio.h>
31392Sswilcox #include <stdlib.h>
32392Sswilcox #include <unistd.h>
33392Sswilcox #include <string.h>
340Sstevel@tonic-gate #include <sys/param.h>
350Sstevel@tonic-gate #include <sys/types.h>
360Sstevel@tonic-gate #include <sys/mntent.h>
370Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
380Sstevel@tonic-gate #include <sys/vnode.h>
39392Sswilcox #define	_KERNEL
40392Sswilcox #include <sys/fs/ufs_fsdir.h>
41392Sswilcox #undef _KERNEL
420Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
430Sstevel@tonic-gate #include "fsck.h"
440Sstevel@tonic-gate 
450Sstevel@tonic-gate /*
46392Sswilcox  * for each large file (size > MAXOFF_T), the global largefile_count
47392Sswilcox  * gets incremented during this pass.
480Sstevel@tonic-gate  */
490Sstevel@tonic-gate 
50392Sswilcox static uint32_t badblk;		/* number seen for the current inode */
51392Sswilcox static uint32_t dupblk;		/* number seen for the current inode */
520Sstevel@tonic-gate 
53392Sswilcox static void clear_attr_acl(fsck_ino_t, fsck_ino_t, char *);
54392Sswilcox static void verify_inode(fsck_ino_t, struct inodesc *, fsck_ino_t);
55392Sswilcox static void check_dirholes(fsck_ino_t, struct inodesc *);
56392Sswilcox static void collapse_dirhole(fsck_ino_t, struct inodesc *);
57392Sswilcox static void note_used(daddr32_t);
580Sstevel@tonic-gate 
59392Sswilcox void
pass1(void)60392Sswilcox pass1(void)
610Sstevel@tonic-gate {
62392Sswilcox 	uint_t c, i;
63392Sswilcox 	daddr32_t cgd;
640Sstevel@tonic-gate 	struct inodesc idesc;
65392Sswilcox 	fsck_ino_t inumber;
66392Sswilcox 	fsck_ino_t maxinumber;
670Sstevel@tonic-gate 
680Sstevel@tonic-gate 	/*
690Sstevel@tonic-gate 	 * Set file system reserved blocks in used block map.
700Sstevel@tonic-gate 	 */
710Sstevel@tonic-gate 	for (c = 0; c < sblock.fs_ncg; c++) {
720Sstevel@tonic-gate 		cgd = cgdmin(&sblock, c);
730Sstevel@tonic-gate 		if (c == 0) {
74392Sswilcox 			/*
75392Sswilcox 			 * Doing the first cylinder group, account for
76392Sswilcox 			 * the cg summaries as well.
77392Sswilcox 			 */
780Sstevel@tonic-gate 			i = cgbase(&sblock, c);
790Sstevel@tonic-gate 			cgd += howmany(sblock.fs_cssize, sblock.fs_fsize);
80392Sswilcox 		} else {
810Sstevel@tonic-gate 			i = cgsblock(&sblock, c);
82392Sswilcox 		}
83392Sswilcox 		for (; i < cgd; i++) {
84392Sswilcox 			note_used(i);
85392Sswilcox 		}
860Sstevel@tonic-gate 	}
870Sstevel@tonic-gate 	/*
88392Sswilcox 	 * Note blocks being used by the log, so we don't declare
89392Sswilcox 	 * them as available and some time in the future we get a
90392Sswilcox 	 * freeing free block panic.
910Sstevel@tonic-gate 	 */
92392Sswilcox 	if (islog && islogok && sblock.fs_logbno)
934132Sabalfour 		examinelog(&note_used);
940Sstevel@tonic-gate 
950Sstevel@tonic-gate 	/*
96392Sswilcox 	 * Find all allocated blocks.  This must be completed before
97392Sswilcox 	 * we read the contents of any directories, as dirscan() et al
98392Sswilcox 	 * don't want to know about block allocation holes.  So, part
99392Sswilcox 	 * of this pass is to truncate any directories with holes to
100392Sswilcox 	 * just before those holes, so dirscan() can remain blissfully
101392Sswilcox 	 * ignorant.
1020Sstevel@tonic-gate 	 */
1030Sstevel@tonic-gate 	inumber = 0;
1040Sstevel@tonic-gate 	n_files = n_blks = 0;
1050Sstevel@tonic-gate 	resetinodebuf();
1060Sstevel@tonic-gate 	maxinumber = sblock.fs_ncg * sblock.fs_ipg;
1070Sstevel@tonic-gate 	for (c = 0; c < sblock.fs_ncg; c++) {
1080Sstevel@tonic-gate 		for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
1090Sstevel@tonic-gate 			if (inumber < UFSROOTINO)
1100Sstevel@tonic-gate 				continue;
111392Sswilcox 			init_inodesc(&idesc);
112392Sswilcox 			idesc.id_type = ADDR;
113392Sswilcox 			idesc.id_func = pass1check;
114392Sswilcox 			verify_inode(inumber, &idesc, maxinumber);
1150Sstevel@tonic-gate 		}
1160Sstevel@tonic-gate 	}
1170Sstevel@tonic-gate 	freeinodebuf();
1180Sstevel@tonic-gate }
1190Sstevel@tonic-gate 
120392Sswilcox /*
121392Sswilcox  * Perform checks on an inode and setup/track the state of the inode
122392Sswilcox  * in maps (statemap[], lncntp[]) for future reference and validation.
123392Sswilcox  * Initiate the calls to ckinode and in turn pass1check() to handle
124392Sswilcox  * further validation.
125392Sswilcox  */
126392Sswilcox static void
verify_inode(fsck_ino_t inumber,struct inodesc * idesc,fsck_ino_t maxinumber)127392Sswilcox verify_inode(fsck_ino_t inumber, struct inodesc *idesc, fsck_ino_t maxinumber)
128392Sswilcox {
129392Sswilcox 	int j, clear, flags;
130392Sswilcox 	int isdir;
131392Sswilcox 	char *err;
132392Sswilcox 	fsck_ino_t shadow, attrinode;
133392Sswilcox 	daddr32_t ndb;
134392Sswilcox 	struct dinode *dp;
135392Sswilcox 	struct inoinfo *iip;
136392Sswilcox 
137392Sswilcox 	dp = getnextinode(inumber);
138392Sswilcox 	if ((dp->di_mode & IFMT) == 0) {
139392Sswilcox 		/* mode and type of file is not set */
140392Sswilcox 		if ((memcmp((void *)dp->di_db, (void *)zino.di_db,
141*6106Sowenr 		    NDADDR * sizeof (daddr32_t)) != 0) ||
142392Sswilcox 		    (memcmp((void *)dp->di_ib, (void *)zino.di_ib,
143*6106Sowenr 		    NIADDR * sizeof (daddr32_t)) != 0) ||
144392Sswilcox 		    (dp->di_mode != 0) || (dp->di_size != 0)) {
145392Sswilcox 			pfatal("PARTIALLY ALLOCATED INODE I=%u", inumber);
146392Sswilcox 			if (reply("CLEAR") == 1) {
147392Sswilcox 				dp = ginode(inumber);
148392Sswilcox 				clearinode(dp);
149392Sswilcox 				inodirty();
150392Sswilcox 			} else {
151392Sswilcox 				iscorrupt = 1;
152392Sswilcox 			}
153392Sswilcox 		}
154392Sswilcox 		statemap[inumber] = USTATE;
155392Sswilcox 		return;
156392Sswilcox 	}
157392Sswilcox 
158392Sswilcox 	isdir = ((dp->di_mode & IFMT) == IFDIR) ||
159*6106Sowenr 	    ((dp->di_mode & IFMT) == IFATTRDIR);
160392Sswilcox 
161392Sswilcox 	lastino = inumber;
162392Sswilcox 	if (dp->di_size > (u_offset_t)UFS_MAXOFFSET_T) {
163392Sswilcox 		pfatal("NEGATIVE SIZE %lld I=%d",
164392Sswilcox 		    (longlong_t)dp->di_size, inumber);
165392Sswilcox 		goto bogus;
166392Sswilcox 	}
167392Sswilcox 
168392Sswilcox 	/*
169392Sswilcox 	 * A more precise test of the type is done later on.  Just get
170392Sswilcox 	 * rid of the blatantly-wrong ones before we do any
171392Sswilcox 	 * significant work.
172392Sswilcox 	 */
173392Sswilcox 	if ((dp->di_mode & IFMT) == IFMT) {
174392Sswilcox 		pfatal("BAD MODE 0%o I=%d",
175392Sswilcox 		    dp->di_mode & IFMT, inumber);
176392Sswilcox 		if (reply("BAD MODE: MAKE IT A FILE") == 1) {
177392Sswilcox 			statemap[inumber] = FSTATE;
178392Sswilcox 			dp = ginode(inumber);
179392Sswilcox 			dp->di_mode = IFREG | 0600;
180392Sswilcox 			inodirty();
181392Sswilcox 			truncino(inumber, sblock.fs_fsize, TI_NOPARENT);
182392Sswilcox 			dp = getnextrefresh();
183392Sswilcox 		} else {
184392Sswilcox 			iscorrupt = 1;
185392Sswilcox 		}
186392Sswilcox 	}
187392Sswilcox 
188392Sswilcox 	ndb = howmany(dp->di_size, (u_offset_t)sblock.fs_bsize);
189392Sswilcox 	if (ndb < 0) {
190392Sswilcox 		/* extra space to distinguish from previous pfatal() */
191392Sswilcox 		pfatal("NEGATIVE SIZE %lld  I=%d",
192392Sswilcox 		    (longlong_t)dp->di_size, inumber);
193392Sswilcox 		goto bogus;
194392Sswilcox 	}
195392Sswilcox 
196392Sswilcox 	if ((dp->di_mode & IFMT) == IFBLK ||
197392Sswilcox 	    (dp->di_mode & IFMT) == IFCHR) {
198392Sswilcox 		if (dp->di_size != 0) {
199392Sswilcox 			pfatal("SPECIAL FILE WITH NON-ZERO LENGTH %lld I=%d",
200392Sswilcox 			    (longlong_t)dp->di_size, inumber);
201392Sswilcox 			goto bogus;
202392Sswilcox 		}
203392Sswilcox 
204392Sswilcox 		for (j = 0; j < NDADDR; j++) {
205392Sswilcox 			/*
206392Sswilcox 			 * It's a device, so all the block pointers
207392Sswilcox 			 * should be zero except for di_ordev.
208392Sswilcox 			 * di_ordev is overlayed on the block array,
209392Sswilcox 			 * but where varies between big and little
210392Sswilcox 			 * endian, so make sure that the only non-zero
211392Sswilcox 			 * element is the correct one.  There can be
212392Sswilcox 			 * a device whose ordev is zero, so we can't
213392Sswilcox 			 * check for the reverse.
214392Sswilcox 			 */
215392Sswilcox 			if (dp->di_db[j] != 0 &&
216392Sswilcox 			    &dp->di_db[j] != &dp->di_ordev) {
217392Sswilcox 				if (debug) {
218392Sswilcox 					(void) printf(
219392Sswilcox 					    "spec file di_db[%d] has %d\n",
220392Sswilcox 					    j, dp->di_db[j]);
221392Sswilcox 				}
222392Sswilcox 				pfatal(
223392Sswilcox 			    "SPECIAL FILE WITH NON-ZERO FRAGMENT LIST  I=%d",
224392Sswilcox 				    inumber);
225392Sswilcox 				goto bogus;
226392Sswilcox 			}
227392Sswilcox 		}
228392Sswilcox 
229392Sswilcox 		for (j = 0; j < NIADDR; j++) {
230392Sswilcox 			if (dp->di_ib[j] != 0) {
231392Sswilcox 				if (debug)
232392Sswilcox 					(void) printf(
233392Sswilcox 					    "special has %d at ib[%d]\n",
234392Sswilcox 					    dp->di_ib[j], j);
235392Sswilcox 				pfatal(
236392Sswilcox 			    "SPECIAL FILE WITH NON-ZERO FRAGMENT LIST  I=%d",
237392Sswilcox 				    inumber);
238392Sswilcox 				goto bogus;
239392Sswilcox 			}
240392Sswilcox 		}
241392Sswilcox 	} else {
242392Sswilcox 		/*
243392Sswilcox 		 * This assignment is mostly here to appease lint, but
244392Sswilcox 		 * doesn't hurt.
245392Sswilcox 		 */
246392Sswilcox 		err = "Internal error: unexpected variant of having "
247392Sswilcox 		    "blocks past end of file  I=%d";
248392Sswilcox 
249392Sswilcox 		clear = 0;
250392Sswilcox 
251392Sswilcox 		/*
252392Sswilcox 		 * If it's not a device, it has to follow the
253392Sswilcox 		 * rules for files.  In particular, no blocks after
254392Sswilcox 		 * the last one that di_size says is in use.
255392Sswilcox 		 */
256392Sswilcox 		for (j = ndb; j < NDADDR; j++) {
257392Sswilcox 			if (dp->di_db[j] != 0) {
258392Sswilcox 				if (debug) {
259392Sswilcox 					(void) printf("bad file direct "
260392Sswilcox 					    "addr[%d]: block 0x%x "
261392Sswilcox 					    "format: 0%o\n",
262392Sswilcox 					    j, dp->di_db[j],
263392Sswilcox 					    dp->di_mode & IFMT);
264392Sswilcox 				}
265392Sswilcox 				err = "FILE WITH FRAGMENTS PAST END  I=%d";
266392Sswilcox 				clear = 1;
267392Sswilcox 				break;
268392Sswilcox 			}
269392Sswilcox 		}
270392Sswilcox 
271392Sswilcox 		/*
272392Sswilcox 		 * Find last indirect pointer that should be in use,
273392Sswilcox 		 * and make sure any after it are clear.
274392Sswilcox 		 */
275392Sswilcox 		if (!clear) {
276392Sswilcox 			for (j = 0, ndb -= NDADDR; ndb > 0; j++) {
277392Sswilcox 				ndb /= NINDIR(&sblock);
278392Sswilcox 			}
279392Sswilcox 			for (; j < NIADDR; j++) {
280392Sswilcox 				if (dp->di_ib[j] != 0) {
281392Sswilcox 					if (debug) {
282392Sswilcox 						(void) printf("bad file "
283392Sswilcox 						    "indirect addr: block %d\n",
284392Sswilcox 						    dp->di_ib[j]);
285392Sswilcox 					}
286392Sswilcox 					err =
287392Sswilcox 					    "FILE WITH FRAGMENTS PAST END I=%d";
288392Sswilcox 					clear = 2;
289392Sswilcox 					break;
290392Sswilcox 				}
291392Sswilcox 			}
292392Sswilcox 		}
293392Sswilcox 
294392Sswilcox 		if (clear) {
295392Sswilcox 			/*
296392Sswilcox 			 * The discarded blocks will be garbage-
297392Sswilcox 			 * collected in pass5.  If we're told not to
298392Sswilcox 			 * discard them, it's just lost blocks, which
299392Sswilcox 			 * isn't worth setting iscorrupt for.
300392Sswilcox 			 */
301392Sswilcox 			pwarn(err, inumber);
302392Sswilcox 			if (preen || reply("DISCARD EXCESS FRAGMENTS") == 1) {
303392Sswilcox 				dp = ginode(inumber);
304392Sswilcox 				if (clear == 1) {
305392Sswilcox 					for (; j < NDADDR; j++)
306392Sswilcox 						dp->di_db[j] = 0;
307392Sswilcox 					j = 0;
308392Sswilcox 				}
309392Sswilcox 				for (; j < NIADDR; j++)
310392Sswilcox 					dp->di_ib[j] = 0;
311392Sswilcox 				inodirty();
312392Sswilcox 				dp = getnextrefresh();
313392Sswilcox 				if (preen)
314392Sswilcox 					(void) printf(" (TRUNCATED)");
315392Sswilcox 			}
316392Sswilcox 		}
317392Sswilcox 	}
318392Sswilcox 
319392Sswilcox 	if (ftypeok(dp) == 0) {
320392Sswilcox 		pfatal("UNKNOWN FILE TYPE 0%o  I=%d", dp->di_mode, inumber);
321392Sswilcox 		goto bogus;
322392Sswilcox 	}
323392Sswilcox 	n_files++;
324392Sswilcox 	TRACK_LNCNTP(inumber, lncntp[inumber] = dp->di_nlink);
325392Sswilcox 
326392Sswilcox 	/*
327392Sswilcox 	 * We can't do anything about it right now, so note that its
328392Sswilcox 	 * processing is being delayed.  Otherwise, we'd be changing
329392Sswilcox 	 * the block allocations out from under ourselves, which causes
330392Sswilcox 	 * no end of confusion.
331392Sswilcox 	 */
332392Sswilcox 	flags = statemap[inumber] & INDELAYD;
333392Sswilcox 
334392Sswilcox 	/*
335392Sswilcox 	 * if errorlocked or logging, then open deleted files will
336392Sswilcox 	 * manifest as di_nlink <= 0 and di_mode != 0
337392Sswilcox 	 * so skip them; they're ok.
338*6106Sowenr 	 * Also skip anything already marked to be cleared.
339392Sswilcox 	 */
340392Sswilcox 	if (dp->di_nlink <= 0 &&
341*6106Sowenr 	    !((errorlocked || islog) && dp->di_mode == 0) &&
342*6106Sowenr 	    !(flags & INCLEAR)) {
343392Sswilcox 		flags |= INZLINK;
344392Sswilcox 		if (debug)
345392Sswilcox 			(void) printf(
346392Sswilcox 		    "marking i=%d INZLINK; nlink %d, mode 0%o, islog %d\n",
347392Sswilcox 			    inumber, dp->di_nlink, dp->di_mode, islog);
348392Sswilcox 	}
349392Sswilcox 
350392Sswilcox 	switch (dp->di_mode & IFMT) {
351392Sswilcox 	case IFDIR:
352392Sswilcox 	case IFATTRDIR:
353392Sswilcox 		if (dp->di_size == 0) {
354392Sswilcox 			/*
355392Sswilcox 			 * INCLEAR means it will be ignored by passes 2 & 3.
356392Sswilcox 			 */
357392Sswilcox 			if ((dp->di_mode & IFMT) == IFDIR)
358392Sswilcox 				(void) printf("ZERO-LENGTH DIR  I=%d\n",
359392Sswilcox 				    inumber);
360392Sswilcox 			else
361392Sswilcox 				(void) printf("ZERO-LENGTH ATTRDIR  I=%d\n",
362392Sswilcox 				    inumber);
363392Sswilcox 			add_orphan_dir(inumber);
364392Sswilcox 			flags |= INCLEAR;
365*6106Sowenr 			flags &= ~INZLINK;	/* It will be cleared anyway */
366392Sswilcox 		}
367392Sswilcox 		statemap[inumber] = DSTATE | flags;
368392Sswilcox 		cacheino(dp, inumber);
369392Sswilcox 		countdirs++;
370392Sswilcox 		break;
371392Sswilcox 
372392Sswilcox 	case IFSHAD:
373392Sswilcox 		if (dp->di_size == 0) {
374392Sswilcox 			(void) printf("ZERO-LENGTH SHADOW  I=%d\n", inumber);
375392Sswilcox 			flags |= INCLEAR;
376*6106Sowenr 			flags &= ~INZLINK;	/* It will be cleared anyway */
377392Sswilcox 		}
378392Sswilcox 		statemap[inumber] = SSTATE | flags;
379392Sswilcox 		cacheacl(dp, inumber);
380392Sswilcox 		break;
381392Sswilcox 
382392Sswilcox 	default:
383392Sswilcox 		statemap[inumber] = FSTATE | flags;
384392Sswilcox 	}
385392Sswilcox 
386392Sswilcox 	badblk = 0;
387392Sswilcox 	dupblk = 0;
388392Sswilcox 	idesc->id_number = inumber;
389392Sswilcox 	idesc->id_fix = DONTKNOW;
390392Sswilcox 	if (dp->di_size > (u_offset_t)MAXOFF_T) {
391392Sswilcox 		largefile_count++;
392392Sswilcox 	}
393392Sswilcox 
394392Sswilcox 	(void) ckinode(dp, idesc, CKI_TRAVERSE);
395392Sswilcox 	if (isdir && (idesc->id_firsthole >= 0))
396392Sswilcox 		check_dirholes(inumber, idesc);
397392Sswilcox 
398392Sswilcox 	if (dp->di_blocks != idesc->id_entryno) {
399392Sswilcox 		/*
400392Sswilcox 		 * The kernel releases any blocks it finds in the lists,
401392Sswilcox 		 * ignoring the block count itself.  So, a bad count is
402392Sswilcox 		 * not grounds for setting iscorrupt.
403392Sswilcox 		 */
404392Sswilcox 		pwarn("INCORRECT DISK BLOCK COUNT I=%u (%d should be %d)",
405392Sswilcox 		    inumber, (uint32_t)dp->di_blocks, idesc->id_entryno);
406392Sswilcox 		if (!preen && (reply("CORRECT") == 0))
407392Sswilcox 			return;
408392Sswilcox 		dp = ginode(inumber);
409392Sswilcox 		dp->di_blocks = idesc->id_entryno;
410392Sswilcox 		iip = getinoinfo(inumber);
411392Sswilcox 		if (iip != NULL)
412392Sswilcox 			iip->i_isize = dp->di_size;
413392Sswilcox 		inodirty();
414392Sswilcox 		if (preen)
415392Sswilcox 			(void) printf(" (CORRECTED)\n");
416392Sswilcox 	}
417392Sswilcox 	if (isdir && (dp->di_blocks == 0)) {
418392Sswilcox 		/*
419392Sswilcox 		 * INCLEAR will cause passes 2 and 3 to skip it.
420392Sswilcox 		 */
421392Sswilcox 		(void) printf("DIR WITH ZERO BLOCKS  I=%d\n", inumber);
422392Sswilcox 		statemap[inumber] = DCLEAR;
423392Sswilcox 		add_orphan_dir(inumber);
424392Sswilcox 	}
425392Sswilcox 
426392Sswilcox 	/*
427392Sswilcox 	 * Check that the ACL is on a valid file type
428392Sswilcox 	 */
429392Sswilcox 	shadow = dp->di_shadow;
430392Sswilcox 	if (shadow != 0) {
431392Sswilcox 		if (acltypeok(dp) == 0) {
432392Sswilcox 			clear_attr_acl(inumber, -1,
433392Sswilcox 			    "NON-ZERO ACL REFERENCE, I=%d\n");
434392Sswilcox 		} else if ((shadow <= UFSROOTINO) ||
435392Sswilcox 		    (shadow > maxinumber)) {
436392Sswilcox 			clear_attr_acl(inumber, -1,
437392Sswilcox 			    "BAD ACL REFERENCE I=%d\n");
438392Sswilcox 		} else {
439392Sswilcox 			registershadowclient(shadow,
440392Sswilcox 			    inumber, &shadowclientinfo);
441392Sswilcox 		}
442392Sswilcox 	}
443392Sswilcox 
444392Sswilcox 	attrinode = dp->di_oeftflag;
445392Sswilcox 	if (attrinode != 0) {
446392Sswilcox 		if ((attrinode <= UFSROOTINO) ||
447392Sswilcox 		    (attrinode > maxinumber)) {
448392Sswilcox 			clear_attr_acl(attrinode, inumber,
449392Sswilcox 			    "BAD ATTRIBUTE REFERENCE TO I=%d FROM I=%d\n");
450392Sswilcox 		} else {
451392Sswilcox 			dp = ginode(attrinode);
452392Sswilcox 			if ((dp->di_mode & IFMT) != IFATTRDIR) {
453392Sswilcox 				clear_attr_acl(attrinode, inumber,
454392Sswilcox 			    "BAD ATTRIBUTE DIR REF TO I=%d FROM I=%d\n");
455392Sswilcox 			} else if (dp->di_size == 0) {
456392Sswilcox 				clear_attr_acl(attrinode, inumber,
457392Sswilcox 		    "REFERENCE TO ZERO-LENGTH ATTRIBUTE DIR I=%d from I=%d\n");
458392Sswilcox 			} else {
459392Sswilcox 				registershadowclient(attrinode, inumber,
460392Sswilcox 				    &attrclientinfo);
461392Sswilcox 			}
462392Sswilcox 		}
463392Sswilcox 	}
464392Sswilcox 	return;
465392Sswilcox 
466392Sswilcox 	/*
467392Sswilcox 	 * If we got here, we've not had the chance to see if a
468392Sswilcox 	 * directory has holes, but we know the directory's bad,
469392Sswilcox 	 * so it's safe to always return false (no holes found).
470392Sswilcox 	 *
471392Sswilcox 	 * Also, a pfatal() is always done before jumping here, so
472392Sswilcox 	 * we know we're not in preen mode.
473392Sswilcox 	 */
474392Sswilcox bogus:
475392Sswilcox 	if (isdir) {
476392Sswilcox 		/*
477392Sswilcox 		 * INCLEAR makes passes 2 & 3 skip it.
478392Sswilcox 		 */
479392Sswilcox 		statemap[inumber] = DCLEAR;
480392Sswilcox 		add_orphan_dir(inumber);
481392Sswilcox 		cacheino(dp, inumber);
482392Sswilcox 	} else {
483392Sswilcox 		statemap[inumber] = FCLEAR;
484392Sswilcox 	}
485392Sswilcox 	if (reply("CLEAR") == 1) {
486392Sswilcox 		(void) tdelete((void *)inumber, &limbo_dirs, ino_t_cmp);
487392Sswilcox 		freeino(inumber, TI_PARENT);
488392Sswilcox 		inodirty();
489392Sswilcox 	} else {
490392Sswilcox 		iscorrupt = 1;
491392Sswilcox 	}
492392Sswilcox }
493392Sswilcox 
494392Sswilcox /*
495392Sswilcox  * Do fixup for bad acl/attr references.  If PARENT is -1, then
496392Sswilcox  * we assume we're working on a shadow, otherwise an extended attribute.
497392Sswilcox  * FMT must be a printf format string, with one %d directive for
498392Sswilcox  * the inode number.
499392Sswilcox  */
500392Sswilcox static void
clear_attr_acl(fsck_ino_t inumber,fsck_ino_t parent,char * fmt)501392Sswilcox clear_attr_acl(fsck_ino_t inumber, fsck_ino_t parent, char *fmt)
502392Sswilcox {
503392Sswilcox 	fsck_ino_t victim = inumber;
504392Sswilcox 	struct dinode *dp;
505392Sswilcox 
506392Sswilcox 	if (parent != -1)
507392Sswilcox 		victim = parent;
508392Sswilcox 
509392Sswilcox 	if (fmt != NULL) {
510392Sswilcox 		if (parent == -1)
511392Sswilcox 			pwarn(fmt, (int)inumber);
512392Sswilcox 		else
513392Sswilcox 			pwarn(fmt, (int)inumber, (int)parent);
514392Sswilcox 	}
515392Sswilcox 
516392Sswilcox 	if (debug)
517392Sswilcox 		(void) printf("parent file/dir I=%d\nvictim I=%d",
518392Sswilcox 		    (int)parent, (int)victim);
519392Sswilcox 
520392Sswilcox 	if (!preen && (reply("REMOVE REFERENCE") == 0)) {
521392Sswilcox 		iscorrupt = 1;
522392Sswilcox 		return;
523392Sswilcox 	}
524392Sswilcox 
525392Sswilcox 	dp = ginode(victim);
526392Sswilcox 	if (parent == -1) {
527392Sswilcox 		/*
528392Sswilcox 		 * The file had a bad shadow/acl, so lock it down
529392Sswilcox 		 * until someone can protect it the way they need it
530392Sswilcox 		 * to be (i.e., be conservatively paranoid).
531392Sswilcox 		 */
532392Sswilcox 		dp->di_shadow = 0;
533392Sswilcox 		dp->di_mode &= IFMT;
534392Sswilcox 	} else {
535392Sswilcox 		dp->di_oeftflag = 0;
536392Sswilcox 	}
537392Sswilcox 
538392Sswilcox 	inodirty();
539392Sswilcox 	if (preen)
540392Sswilcox 		(void) printf(" (CORRECTED)\n");
541392Sswilcox }
542392Sswilcox 
543392Sswilcox /*
544392Sswilcox  * Check if we have holes in the directory's indirect
545392Sswilcox  * blocks.  If there are, get rid of everything after
546392Sswilcox  * the first hole.
547392Sswilcox  */
548392Sswilcox static void
check_dirholes(fsck_ino_t inumber,struct inodesc * idesc)549392Sswilcox check_dirholes(fsck_ino_t inumber, struct inodesc *idesc)
550392Sswilcox {
551392Sswilcox 	char pathbuf[MAXPATHLEN + 1];
552392Sswilcox 
553392Sswilcox 	getpathname(pathbuf, idesc->id_number, idesc->id_number);
554392Sswilcox 	pfatal("I=%d  DIRECTORY %s: CONTAINS EMPTY BLOCKS",
555392Sswilcox 	    idesc->id_number, pathbuf);
556392Sswilcox 	if (reply("TRUNCATE AT FIRST EMPTY BLOCK") == 1) {
557392Sswilcox 		/*
558392Sswilcox 		 * We found a hole, so get rid of it.
559392Sswilcox 		 */
560392Sswilcox 		collapse_dirhole(inumber, idesc);
561392Sswilcox 
562392Sswilcox 		if (preen)
563392Sswilcox 			(void) printf(" (TRUNCATED)\n");
564392Sswilcox 	} else {
565392Sswilcox 		iscorrupt = 1;
566392Sswilcox 	}
567392Sswilcox }
568392Sswilcox 
569392Sswilcox /*
570392Sswilcox  * Truncate a directory to its first hole.  If there are non-holes
571392Sswilcox  * in the direct blocks after the problem block, move them down so
572392Sswilcox  * that there's somewhat less lossage.  Doing this for indirect blocks
573392Sswilcox  * is left as an exercise for the reader.
574392Sswilcox  */
575392Sswilcox static void
collapse_dirhole(fsck_ino_t inumber,struct inodesc * idesc)576392Sswilcox collapse_dirhole(fsck_ino_t inumber, struct inodesc *idesc)
577392Sswilcox {
578392Sswilcox 	offset_t new_size;
579392Sswilcox 	int blocks;
580392Sswilcox 
581392Sswilcox 	if (idesc->id_firsthole < 0) {
582392Sswilcox 		return;
583392Sswilcox 	}
584392Sswilcox 
585392Sswilcox 	/*
586392Sswilcox 	 * Since truncino() adjusts the size, we don't need to do that here,
587392Sswilcox 	 * but we have to tell it what final size we want.
588392Sswilcox 	 *
589392Sswilcox 	 * We need to count from block zero up through the last block
590392Sswilcox 	 * before the hole.  If the hole is in the indirect blocks, chop at
591392Sswilcox 	 * the start of the nearest level of indirection.  Orphans will
592392Sswilcox 	 * get reconnected, so we're not actually losing anything by doing
593392Sswilcox 	 * it this way, and we're simplifying truncation significantly.
594392Sswilcox 	 */
595392Sswilcox 	new_size = idesc->id_firsthole * (offset_t)sblock.fs_bsize;
596392Sswilcox 	blocks = howmany(new_size, sblock.fs_bsize);
597392Sswilcox 	if (blocks > NDADDR) {
598392Sswilcox 		if (blocks < (NDADDR + NINDIR(&sblock)))
599392Sswilcox 			blocks = NDADDR;
600392Sswilcox 		else if (blocks < (NDADDR + NINDIR(&sblock) +
601392Sswilcox 		    (NINDIR(&sblock) * NINDIR(&sblock))))
602392Sswilcox 			blocks = NDADDR + NINDIR(&sblock);
603392Sswilcox 		else
604392Sswilcox 			blocks = NDADDR + NINDIR(&sblock) +
605*6106Sowenr 			    (NINDIR(&sblock) * NINDIR(&sblock));
606392Sswilcox 		new_size = blocks * sblock.fs_bsize;
607392Sswilcox 		if (debug)
608392Sswilcox 			(void) printf("to %lld (blocks %d)\n",
609392Sswilcox 			    (longlong_t)new_size, blocks);
610392Sswilcox 	}
611392Sswilcox 	truncino(inumber, new_size, TI_NOPARENT);
612392Sswilcox 
613392Sswilcox 	/*
614392Sswilcox 	 * Technically, there are still the original number of fragments
615392Sswilcox 	 * associated with the object.  However, that number is not used
616392Sswilcox 	 * to control anything, so we can do the in-memory truncation of
617392Sswilcox 	 * it without bad things happening.
618392Sswilcox 	 */
619392Sswilcox 	idesc->id_entryno = btodb(new_size);
620392Sswilcox }
621392Sswilcox 
622392Sswilcox int
pass1check(struct inodesc * idesc)623392Sswilcox pass1check(struct inodesc *idesc)
6240Sstevel@tonic-gate {
6250Sstevel@tonic-gate 	int res = KEEPON;
6260Sstevel@tonic-gate 	int anyout;
627392Sswilcox 	int nfrags;
628392Sswilcox 	daddr32_t lbn;
629392Sswilcox 	daddr32_t fragno = idesc->id_blkno;
630392Sswilcox 	struct dinode *dp;
6310Sstevel@tonic-gate 
632923Ssdebnath 	/*
633923Ssdebnath 	 * If this is a fallocate'd file, block numbers may be stored
634923Ssdebnath 	 * as negative. In that case negate the negative numbers.
635923Ssdebnath 	 */
636923Ssdebnath 	dp = ginode(idesc->id_number);
637923Ssdebnath 	if (dp->di_cflags & IFALLOCATE && fragno < 0)
638923Ssdebnath 		fragno = -fragno;
639923Ssdebnath 
640392Sswilcox 	if ((anyout = chkrange(fragno, idesc->id_numfrags)) != 0) {
641392Sswilcox 		/*
642392Sswilcox 		 * Note that blkerror() exits when preening.
643392Sswilcox 		 */
644392Sswilcox 		blkerror(idesc->id_number, "OUT OF RANGE",
645392Sswilcox 		    fragno, idesc->id_lbn * sblock.fs_frag);
646392Sswilcox 
647392Sswilcox 		dp = ginode(idesc->id_number);
648392Sswilcox 		if ((((dp->di_mode & IFMT) == IFDIR) ||
649392Sswilcox 		    ((dp->di_mode & IFMT) == IFATTRDIR)) &&
650392Sswilcox 		    (idesc->id_firsthole < 0)) {
651392Sswilcox 			idesc->id_firsthole = idesc->id_lbn;
652392Sswilcox 		}
653392Sswilcox 
6540Sstevel@tonic-gate 		if (++badblk >= MAXBAD) {
655392Sswilcox 			pwarn("EXCESSIVE BAD FRAGMENTS I=%u",
656392Sswilcox 			    idesc->id_number);
657392Sswilcox 			if (reply("CONTINUE") == 0)
658392Sswilcox 				errexit("Program terminated.");
659392Sswilcox 			/*
660392Sswilcox 			 * See discussion below as to why we don't
661392Sswilcox 			 * want to short-circuit the processing of
662392Sswilcox 			 * this inode.  However, we know that this
663392Sswilcox 			 * particular block is bad, so we don't need
664392Sswilcox 			 * to go through the dup check loop.
665392Sswilcox 			 */
666392Sswilcox 			return (SKIP | STOP);
6670Sstevel@tonic-gate 		}
6680Sstevel@tonic-gate 	}
669392Sswilcox 
670392Sswilcox 	/*
671392Sswilcox 	 * For each fragment, verify that it is a legal one (either
672392Sswilcox 	 * by having already found the entire run to be legal, or by
673392Sswilcox 	 * individual inspection), and if it is legal, see if we've
674392Sswilcox 	 * seen it before or not.  If we haven't, note that we've seen
675392Sswilcox 	 * it and continue on.  If we have (our in-core bitmap shows
676392Sswilcox 	 * it as already being busy), then this must be a duplicate
677392Sswilcox 	 * allocation.  Whine and moan accordingly.
678392Sswilcox 	 *
679392Sswilcox 	 * Note that for full-block allocations, this will produce
680392Sswilcox 	 * a complaint for each fragment making up the block (i.e.,
681392Sswilcox 	 * fs_frags' worth).  Among other things, this could be
682392Sswilcox 	 * considered artificially inflating the dup-block count.
683392Sswilcox 	 * However, since it is possible that one file has a full
684392Sswilcox 	 * fs block allocated, but another is only claiming a frag
685392Sswilcox 	 * or two out of the middle, we'll just live it.
686392Sswilcox 	 */
687392Sswilcox 	for (nfrags = 0; nfrags < idesc->id_numfrags; fragno++, nfrags++) {
688392Sswilcox 		if (anyout && chkrange(fragno, 1)) {
689392Sswilcox 			/* bad fragment number */
6900Sstevel@tonic-gate 			res = SKIP;
691392Sswilcox 		} else if (!testbmap(fragno)) {
692392Sswilcox 			/* no other claims seen as yet */
693392Sswilcox 			note_used(fragno);
6940Sstevel@tonic-gate 		} else {
695392Sswilcox 			/*
696392Sswilcox 			 * We have a duplicate claim for the same fragment.
697392Sswilcox 			 *
698392Sswilcox 			 * blkerror() exits when preening.
699392Sswilcox 			 *
700392Sswilcox 			 * We want to report all the dups up until
701392Sswilcox 			 * hitting MAXDUP.  Fortunately, blkerror()'s
702392Sswilcox 			 * side-effects on statemap[] are idempotent,
703392Sswilcox 			 * so the ``extra'' calls are harmless.
704392Sswilcox 			 */
705392Sswilcox 			lbn = idesc->id_lbn * sblock.fs_frag + nfrags;
706392Sswilcox 			if (dupblk < MAXDUP)
707392Sswilcox 				blkerror(idesc->id_number, "DUP", fragno, lbn);
708392Sswilcox 
709392Sswilcox 			/*
710392Sswilcox 			 * Use ==, so we only complain once, no matter
711392Sswilcox 			 * how far over the limit we end up going.
712392Sswilcox 			 */
713392Sswilcox 			if (++dupblk == MAXDUP) {
714392Sswilcox 				pwarn("EXCESSIVE DUPLICATE FRAGMENTS I=%u",
715*6106Sowenr 				    idesc->id_number);
716392Sswilcox 				if (reply("CONTINUE") == 0)
717392Sswilcox 					errexit("Program terminated.");
718392Sswilcox 
719392Sswilcox 				/*
720392Sswilcox 				 * If we stop the traversal here, then
721392Sswilcox 				 * there may be more dups in the
722392Sswilcox 				 * inode's block list that don't get
723392Sswilcox 				 * flagged.  Later, if we're told to
724392Sswilcox 				 * clear one of the files claiming
725392Sswilcox 				 * these blocks, but not the other, we
726392Sswilcox 				 * will release blocks that are
727392Sswilcox 				 * actually still in use.  An additional
728392Sswilcox 				 * fsck run would be necessary to undo
729392Sswilcox 				 * the damage.  So, instead of the
730392Sswilcox 				 * traditional return (STOP) when told
731392Sswilcox 				 * to continue, we really do just continue.
732392Sswilcox 				 */
7330Sstevel@tonic-gate 			}
734392Sswilcox 			(void) find_dup_ref(fragno, idesc->id_number, lbn,
735*6106Sowenr 			    DB_CREATE | DB_INCR);
7360Sstevel@tonic-gate 		}
7370Sstevel@tonic-gate 		/*
738392Sswilcox 		 * id_entryno counts the number of disk blocks found.
7390Sstevel@tonic-gate 		 */
740392Sswilcox 		idesc->id_entryno += btodb(sblock.fs_fsize);
7410Sstevel@tonic-gate 	}
7420Sstevel@tonic-gate 	return (res);
7430Sstevel@tonic-gate }
744392Sswilcox 
745392Sswilcox static void
note_used(daddr32_t frag)746392Sswilcox note_used(daddr32_t frag)
747392Sswilcox {
748392Sswilcox 	n_blks++;
749392Sswilcox 	setbmap(frag);
750392Sswilcox }
751