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
pass1(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
verify_inode(fsck_ino_t inumber,struct inodesc * idesc,fsck_ino_t maxinumber)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
clear_attr_acl(fsck_ino_t inumber,fsck_ino_t parent,char * fmt)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
check_dirholes(fsck_ino_t inumber,struct inodesc * idesc)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
collapse_dirhole(fsck_ino_t inumber,struct inodesc * idesc)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
pass1check(struct inodesc * idesc)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
note_used(daddr32_t frag)746355d6bb5Sswilcox note_used(daddr32_t frag)
747355d6bb5Sswilcox {
748355d6bb5Sswilcox n_blks++;
749355d6bb5Sswilcox setbmap(frag);
750355d6bb5Sswilcox }
751