xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/pass3b.c (revision 0eca9a24)
17c478bd9Sstevel@tonic-gate /*
2*0eca9a24Sjr  * Copyright 2007 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 
307c478bd9Sstevel@tonic-gate #include <stdio.h>
317c478bd9Sstevel@tonic-gate #include <stdlib.h>
327c478bd9Sstevel@tonic-gate #include <sys/param.h>
337c478bd9Sstevel@tonic-gate #include <sys/types.h>
347c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
357c478bd9Sstevel@tonic-gate #include <sys/acl.h>
367c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_acl.h>
377c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
387c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
397c478bd9Sstevel@tonic-gate #include <string.h>
407c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
417c478bd9Sstevel@tonic-gate #include "fsck.h"
427c478bd9Sstevel@tonic-gate 
43355d6bb5Sswilcox /*
44355d6bb5Sswilcox  * We can be run on multiple filesystems (processed serially), so
45355d6bb5Sswilcox  * these need to be re-initialized each time we start the pass.
46355d6bb5Sswilcox  */
47355d6bb5Sswilcox static caddr_t aclbuf;		/* hold acl's for parsing */
48355d6bb5Sswilcox static int64_t aclbufoff;	/* offset into aclbuf */
49355d6bb5Sswilcox static int64_t maxaclsize;	/* how big aclbuf is */
507c478bd9Sstevel@tonic-gate 
51355d6bb5Sswilcox static int aclblksort(const void *, const void *);
52355d6bb5Sswilcox static int bufchk(char *, int64_t, fsck_ino_t);
53355d6bb5Sswilcox static void clear_shadow_client(struct shadowclientinfo *,
54355d6bb5Sswilcox 	    struct shadowclients *, int);
55355d6bb5Sswilcox 
56355d6bb5Sswilcox void
pass3b(void)57355d6bb5Sswilcox pass3b(void)
587c478bd9Sstevel@tonic-gate {
59355d6bb5Sswilcox 	fsck_ino_t inumber;
607c478bd9Sstevel@tonic-gate 	struct dinode *dp;
61355d6bb5Sswilcox 	struct inoinfo *aclp;
627c478bd9Sstevel@tonic-gate 	struct inodesc curino;
637c478bd9Sstevel@tonic-gate 	struct shadowclientinfo *sci;
647c478bd9Sstevel@tonic-gate 	struct shadowclients *scc;
65355d6bb5Sswilcox 	int64_t acl_size_limit;
667c478bd9Sstevel@tonic-gate 	int i;
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate 	/*
697c478bd9Sstevel@tonic-gate 	 * Sort the acl list into disk block order.
707c478bd9Sstevel@tonic-gate 	 */
717c478bd9Sstevel@tonic-gate 	qsort((char *)aclpsort, (int)aclplast, sizeof (*aclpsort), aclblksort);
727c478bd9Sstevel@tonic-gate 	/*
737c478bd9Sstevel@tonic-gate 	 * Scan all the acl inodes, finding the largest acl file.
74355d6bb5Sswilcox 	 *
75355d6bb5Sswilcox 	 * The largest legal size is (4 * MAX_ACL_ENTRIES + 8) entries.
76355d6bb5Sswilcox 	 * The four are the categories of specific users, specific
77355d6bb5Sswilcox 	 * groups, default specific users, and default specific groups.
78355d6bb5Sswilcox 	 * The eight are the entries for the owning user/group/other/class
79355d6bb5Sswilcox 	 * plus the equivalent defaults.
80355d6bb5Sswilcox 	 *
81355d6bb5Sswilcox 	 * We double this to allow for a truly worst-case but legal
82355d6bb5Sswilcox 	 * situation of every single acl having its own fsd_t wrapper.
83355d6bb5Sswilcox 	 * Doubling is a bit pessimistic (sizeof (acl_t) > sizeof (fsd_t)).
847c478bd9Sstevel@tonic-gate 	 */
85355d6bb5Sswilcox 	acl_size_limit = sizeof (ufs_acl_t) * (4 * MAX_ACL_ENTRIES + 8);
86355d6bb5Sswilcox 	acl_size_limit *= 2;
87355d6bb5Sswilcox 
88355d6bb5Sswilcox 	maxaclsize = 0;
897c478bd9Sstevel@tonic-gate 	for (inumber = 0; inumber < aclplast; inumber++) {
907c478bd9Sstevel@tonic-gate 		aclp = aclpsort[inumber];
91355d6bb5Sswilcox 		if ((int64_t)aclp->i_isize > acl_size_limit) {
92355d6bb5Sswilcox 			(void) printf(
93355d6bb5Sswilcox 			    "ACL I=%d is excessively large (%lld > %lld)",
94355d6bb5Sswilcox 			    inumber,
95355d6bb5Sswilcox 			    (longlong_t)aclp->i_isize,
96355d6bb5Sswilcox 			    (longlong_t)acl_size_limit);
97355d6bb5Sswilcox 			if (preen) {
98355d6bb5Sswilcox 				(void) printf(" (IGNORING)\n");
99355d6bb5Sswilcox 			} else if (reply("CLEAR") == 1) {
100355d6bb5Sswilcox 				freeino(inumber, TI_PARENT);
101355d6bb5Sswilcox 			} else {
102355d6bb5Sswilcox 				iscorrupt = 1;
103355d6bb5Sswilcox 				(void) printf("IGNORING SHADOW I=%d\n",
104355d6bb5Sswilcox 				    inumber);
105355d6bb5Sswilcox 			}
106355d6bb5Sswilcox 			continue;
107355d6bb5Sswilcox 		}
1087c478bd9Sstevel@tonic-gate 		if ((int64_t)aclp->i_isize > maxaclsize)
1097c478bd9Sstevel@tonic-gate 			maxaclsize = (int64_t)aclp->i_isize;
1107c478bd9Sstevel@tonic-gate 	}
111355d6bb5Sswilcox 
112355d6bb5Sswilcox 	maxaclsize = ((maxaclsize / sblock.fs_bsize) + 1) * sblock.fs_bsize;
113355d6bb5Sswilcox 	if (maxaclsize == 0)
114355d6bb5Sswilcox 		goto noacls;
115355d6bb5Sswilcox 
116355d6bb5Sswilcox 	if (aclbuf != NULL) {
117355d6bb5Sswilcox 		free((void *)aclbuf);
118355d6bb5Sswilcox 	}
1197c478bd9Sstevel@tonic-gate 	if ((aclbuf = malloc(maxaclsize)) == NULL) {
120355d6bb5Sswilcox 		errexit("cannot alloc %lld bytes for aclbuf\n",
121355d6bb5Sswilcox 			(longlong_t)maxaclsize);
1227c478bd9Sstevel@tonic-gate 	}
1237c478bd9Sstevel@tonic-gate 	/*
1247c478bd9Sstevel@tonic-gate 	 * Scan all the acl inodes, checking contents
1257c478bd9Sstevel@tonic-gate 	 */
1267c478bd9Sstevel@tonic-gate 	for (inumber = 0; inumber < aclplast; inumber++) {
1277c478bd9Sstevel@tonic-gate 		aclp = aclpsort[inumber];
128355d6bb5Sswilcox 		if ((int64_t)aclp->i_isize > acl_size_limit) {
129355d6bb5Sswilcox 			continue;
130355d6bb5Sswilcox 		}
131355d6bb5Sswilcox 		if ((statemap[aclp->i_number] & STMASK) != SSTATE) {
132355d6bb5Sswilcox 			continue;
133355d6bb5Sswilcox 		}
1347c478bd9Sstevel@tonic-gate 		dp = ginode(aclp->i_number);
135355d6bb5Sswilcox 		init_inodesc(&curino);
1367c478bd9Sstevel@tonic-gate 		curino.id_fix = FIX;
1377c478bd9Sstevel@tonic-gate 		curino.id_type = ACL;
1387c478bd9Sstevel@tonic-gate 		curino.id_func = pass3bcheck;
1397c478bd9Sstevel@tonic-gate 		curino.id_number = aclp->i_number;
1407c478bd9Sstevel@tonic-gate 		curino.id_filesize = aclp->i_isize;
1417c478bd9Sstevel@tonic-gate 		aclbufoff = 0;
142355d6bb5Sswilcox 		(void) memset(aclbuf, 0, (size_t)maxaclsize);
143355d6bb5Sswilcox 		if ((ckinode(dp, &curino, CKI_TRAVERSE) & KEEPON) == 0 ||
144355d6bb5Sswilcox 		    bufchk(aclbuf, (int64_t)aclp->i_isize, aclp->i_number)) {
145355d6bb5Sswilcox 			dp = ginode(aclp->i_number); /* defensive no-op */
1467c478bd9Sstevel@tonic-gate 			if (dp->di_nlink <= 0) {
1477c478bd9Sstevel@tonic-gate 				statemap[aclp->i_number] = FSTATE;
1487c478bd9Sstevel@tonic-gate 				continue;
1497c478bd9Sstevel@tonic-gate 			}
150355d6bb5Sswilcox 			(void) printf("ACL I=%d BAD/CORRUPT", aclp->i_number);
1517c478bd9Sstevel@tonic-gate 			if (preen || reply("CLEAR") == 1) {
1527c478bd9Sstevel@tonic-gate 				if (preen)
153355d6bb5Sswilcox 					(void) printf("\n");
154355d6bb5Sswilcox 				freeino(aclp->i_number, TI_PARENT);
155355d6bb5Sswilcox 			} else {
156355d6bb5Sswilcox 				iscorrupt = 1;
1577c478bd9Sstevel@tonic-gate 			}
1587c478bd9Sstevel@tonic-gate 		}
1597c478bd9Sstevel@tonic-gate 	}
1607c478bd9Sstevel@tonic-gate 	/*
1617c478bd9Sstevel@tonic-gate 	 * Now scan all shadow inodes, checking that any inodes that previously
1627c478bd9Sstevel@tonic-gate 	 * had an acl still have an acl.
1637c478bd9Sstevel@tonic-gate 	 */
164355d6bb5Sswilcox noacls:
1657c478bd9Sstevel@tonic-gate 	for (sci = shadowclientinfo; sci; sci = sci->next) {
166355d6bb5Sswilcox 		if ((statemap[sci->shadow] & STMASK) != SSTATE) {
1677c478bd9Sstevel@tonic-gate 			for (scc = sci->clients; scc; scc = scc->next) {
1687c478bd9Sstevel@tonic-gate 				for (i = 0; i < scc->nclients; i++) {
169355d6bb5Sswilcox 					clear_shadow_client(sci, scc, i);
1707c478bd9Sstevel@tonic-gate 				}
1717c478bd9Sstevel@tonic-gate 			}
1727c478bd9Sstevel@tonic-gate 		}
1737c478bd9Sstevel@tonic-gate 	}
174355d6bb5Sswilcox 	free((void *)aclbuf);
175355d6bb5Sswilcox 	aclbuf = NULL;
176355d6bb5Sswilcox }
177355d6bb5Sswilcox 
178355d6bb5Sswilcox static void
clear_shadow_client(struct shadowclientinfo * sci,struct shadowclients * scc,int client)179355d6bb5Sswilcox clear_shadow_client(struct shadowclientinfo *sci, struct shadowclients *scc,
180355d6bb5Sswilcox 	int client)
181355d6bb5Sswilcox {
182355d6bb5Sswilcox 	int suppress_update = 0;
183355d6bb5Sswilcox 	caddr_t flow;
184355d6bb5Sswilcox 	struct inodesc ldesc;
185355d6bb5Sswilcox 	struct dinode *dp;
186355d6bb5Sswilcox 
187355d6bb5Sswilcox 	(void) printf("I=%d HAS BAD/CLEARED ACL I=%d",
188355d6bb5Sswilcox 	    scc->client[client], sci->shadow);
189355d6bb5Sswilcox 	if (preen || reply("FIX") == 1) {
190355d6bb5Sswilcox 		if (preen)
191355d6bb5Sswilcox 			(void) printf("\n");
192355d6bb5Sswilcox 
193355d6bb5Sswilcox 		/*
194355d6bb5Sswilcox 		 * If we clear the ACL, then the permissions should
195355d6bb5Sswilcox 		 * be as restrictive as possible until the user can
196355d6bb5Sswilcox 		 * set it to something reasonable.  If we keep the
197355d6bb5Sswilcox 		 * ACL, then the permissions are pretty much
198355d6bb5Sswilcox 		 * irrelevant.  So, just always clear the permission
199355d6bb5Sswilcox 		 * bits.
200355d6bb5Sswilcox 		 */
201355d6bb5Sswilcox 		dp = ginode(scc->client[client]);
202355d6bb5Sswilcox 		dp->di_mode &= IFMT;
203355d6bb5Sswilcox 		dp->di_shadow = 0;
204355d6bb5Sswilcox 		inodirty();
205355d6bb5Sswilcox 
206355d6bb5Sswilcox 		/*
207355d6bb5Sswilcox 		 * Decrement in-memory link count - pass1 made sure
208355d6bb5Sswilcox 		 * the shadow inode # is a valid inode number.  But
209355d6bb5Sswilcox 		 * first, see if we're going to overflow our sixteen
210355d6bb5Sswilcox 		 * bits.
211355d6bb5Sswilcox 		 */
212355d6bb5Sswilcox 		LINK_RANGE(flow, lncntp[dp->di_shadow], 1);
213355d6bb5Sswilcox 		if (flow != NULL) {
214355d6bb5Sswilcox 			LINK_CLEAR(flow, scc->client[client], dp->di_mode,
215355d6bb5Sswilcox 			    &ldesc);
216355d6bb5Sswilcox 			if (statemap[scc->client[client]] == USTATE)
217355d6bb5Sswilcox 				suppress_update = 1;
218355d6bb5Sswilcox 		}
219355d6bb5Sswilcox 
220355d6bb5Sswilcox 		/*
221355d6bb5Sswilcox 		 * We don't touch the shadow's on-disk link count,
222355d6bb5Sswilcox 		 * because we've already cleared its state in pass3b().
223355d6bb5Sswilcox 		 * Here we're just trying to keep lncntp[] in sync, so
224355d6bb5Sswilcox 		 * we can detect spurious links.
225355d6bb5Sswilcox 		 */
226355d6bb5Sswilcox 		if (!suppress_update)
227355d6bb5Sswilcox 			TRACK_LNCNTP(sci->shadow, lncntp[sci->shadow]++);
228355d6bb5Sswilcox 	} else {
229355d6bb5Sswilcox 		iscorrupt = 1;
230355d6bb5Sswilcox 	}
2317c478bd9Sstevel@tonic-gate }
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate /*
2347c478bd9Sstevel@tonic-gate  * Collect all the (data) blocks of an acl file into a buffer.
2357c478bd9Sstevel@tonic-gate  * Later we'll scan the buffer and validate the acl data.
2367c478bd9Sstevel@tonic-gate  */
2377c478bd9Sstevel@tonic-gate int
pass3bcheck(struct inodesc * idesc)2387c478bd9Sstevel@tonic-gate pass3bcheck(struct inodesc *idesc)
2397c478bd9Sstevel@tonic-gate {
2407c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
241355d6bb5Sswilcox 	size_t size, bsize;
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	if (aclbufoff == idesc->id_filesize) {
2447c478bd9Sstevel@tonic-gate 		return (STOP);
2457c478bd9Sstevel@tonic-gate 	}
2467c478bd9Sstevel@tonic-gate 	bsize = size = sblock.fs_fsize * idesc->id_numfrags;
2477c478bd9Sstevel@tonic-gate 	if ((size + aclbufoff) > idesc->id_filesize)
2487c478bd9Sstevel@tonic-gate 		size = idesc->id_filesize - aclbufoff;
249355d6bb5Sswilcox 	if (aclbufoff + size > maxaclsize)
250355d6bb5Sswilcox 		errexit("acl size %lld exceeds maximum calculated "
251355d6bb5Sswilcox 			"size of %lld bytes",
252355d6bb5Sswilcox 			(longlong_t)aclbufoff + size, (longlong_t)maxaclsize);
2537c478bd9Sstevel@tonic-gate 	bp = getdatablk(idesc->id_blkno, bsize);
254355d6bb5Sswilcox 	if (bp->b_errs != 0) {
255355d6bb5Sswilcox 		brelse(bp);
256355d6bb5Sswilcox 		return (STOP);
257355d6bb5Sswilcox 	}
258355d6bb5Sswilcox 	(void) memmove((void *)(aclbuf + aclbufoff), (void *)bp->b_un.b_buf,
259355d6bb5Sswilcox 		(size_t)size);
2607c478bd9Sstevel@tonic-gate 	aclbufoff += size;
2617c478bd9Sstevel@tonic-gate 	brelse(bp);
2627c478bd9Sstevel@tonic-gate 	return (KEEPON);
2637c478bd9Sstevel@tonic-gate }
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate /*
2667c478bd9Sstevel@tonic-gate  * Routine to sort disk blocks.
2677c478bd9Sstevel@tonic-gate  */
268355d6bb5Sswilcox static int
aclblksort(const void * pp1,const void * pp2)269355d6bb5Sswilcox aclblksort(const void *pp1, const void *pp2)
2707c478bd9Sstevel@tonic-gate {
271355d6bb5Sswilcox 	const struct inoinfo **aclpp1 = (const struct inoinfo **)pp1;
272355d6bb5Sswilcox 	const struct inoinfo **aclpp2 = (const struct inoinfo **)pp2;
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	return ((*aclpp1)->i_blks[0] - (*aclpp2)->i_blks[0]);
2757c478bd9Sstevel@tonic-gate }
2767c478bd9Sstevel@tonic-gate 
277355d6bb5Sswilcox /*
278355d6bb5Sswilcox  * Scan a chunk of a shadow file.  Return zero if no ACLs were found,
279355d6bb5Sswilcox  * or when all that were found were valid.
280355d6bb5Sswilcox  */
281355d6bb5Sswilcox static int
bufchk(char * buf,int64_t len,fsck_ino_t inum)282355d6bb5Sswilcox bufchk(char *buf, int64_t len, fsck_ino_t inum)
2837c478bd9Sstevel@tonic-gate {
2847c478bd9Sstevel@tonic-gate 	ufs_fsd_t *fsdp;
2857c478bd9Sstevel@tonic-gate 	ufs_acl_t *ufsaclp = NULL;
2867c478bd9Sstevel@tonic-gate 	int numacls;
287355d6bb5Sswilcox 	int curacl;
288355d6bb5Sswilcox 	struct type_counts_s {
289355d6bb5Sswilcox 		int nuser_objs;
290355d6bb5Sswilcox 		int ngroup_objs;
291355d6bb5Sswilcox 		int nother_objs;
292355d6bb5Sswilcox 		int nclass_objs;
293355d6bb5Sswilcox 		int ndef_user_objs;
294355d6bb5Sswilcox 		int ndef_group_objs;
295355d6bb5Sswilcox 		int ndef_other_objs;
296355d6bb5Sswilcox 		int ndef_class_objs;
297355d6bb5Sswilcox 		int nusers;
298355d6bb5Sswilcox 		int ngroups;
299355d6bb5Sswilcox 		int ndef_users;
300355d6bb5Sswilcox 		int ndef_groups;
301355d6bb5Sswilcox 	} type_counts[3];	/* indexed by FSD_ACL and FSD_DFACL */
302355d6bb5Sswilcox 	struct type_counts_s *tcp, *tcp_all, *tcp_def, *tcp_norm;
303355d6bb5Sswilcox 	int numdefs;
304355d6bb5Sswilcox 	caddr_t bad;
305355d6bb5Sswilcox 	caddr_t end = buf + len;
306355d6bb5Sswilcox 	int64_t recsz = 0;
307355d6bb5Sswilcox 	int64_t min_recsz = FSD_RECSZ(fsdp, sizeof (*fsdp));
308*0eca9a24Sjr 	struct shadowclientinfo *sci;
309*0eca9a24Sjr 	struct shadowclients *scc;
310*0eca9a24Sjr 	fsck_ino_t target;
311*0eca9a24Sjr 	int numtargets = 0;
312*0eca9a24Sjr 
313*0eca9a24Sjr 	/*
314*0eca9a24Sjr 	 * check we have a non-zero length for this shadow inode
315*0eca9a24Sjr 	 */
316*0eca9a24Sjr 	if (len == 0) {
317*0eca9a24Sjr 		pwarn("ACL I=%d HAS ZERO LENGTH\n", inum);
318*0eca9a24Sjr 		return (1);
319*0eca9a24Sjr 	}
320355d6bb5Sswilcox 
321355d6bb5Sswilcox 	(void) memset(type_counts, 0, sizeof (type_counts));
3227c478bd9Sstevel@tonic-gate 
323355d6bb5Sswilcox 	/* LINTED pointer cast alignment (aligned buffer always passed in) */
3247c478bd9Sstevel@tonic-gate 	for (fsdp = (ufs_fsd_t *)buf;
325*0eca9a24Sjr 	    (caddr_t)fsdp < end;
326355d6bb5Sswilcox 	    /* LINTED as per the above */
327355d6bb5Sswilcox 	    fsdp = (ufs_fsd_t *)((caddr_t)fsdp + recsz)) {
328355d6bb5Sswilcox 
329355d6bb5Sswilcox 		recsz = FSD_RECSZ(fsdp, fsdp->fsd_size);
330355d6bb5Sswilcox 		if ((recsz < min_recsz) ||
331355d6bb5Sswilcox 		    (((caddr_t)fsdp + recsz) > (buf + len))) {
332355d6bb5Sswilcox 			pwarn("Bad FSD entry size %lld in shadow inode %d",
333355d6bb5Sswilcox 			    recsz, inum);
334355d6bb5Sswilcox 			if (reply("CLEAR SHADOW INODE") == 1) {
335355d6bb5Sswilcox 				freeino(inum, TI_PARENT);
336355d6bb5Sswilcox 			} else {
337355d6bb5Sswilcox 				/*
338355d6bb5Sswilcox 				 * Bad size can cause the kernel to
339355d6bb5Sswilcox 				 * go traipsing off into never-never land.
340355d6bb5Sswilcox 				 */
341355d6bb5Sswilcox 				iscorrupt = 1;
342355d6bb5Sswilcox 			}
343355d6bb5Sswilcox 			return (0);
344355d6bb5Sswilcox 		}
345355d6bb5Sswilcox 
3467c478bd9Sstevel@tonic-gate 		switch (fsdp->fsd_type) {
347355d6bb5Sswilcox 		case FSD_FREE:	/* ignore empty slots */
348355d6bb5Sswilcox 			break;
3497c478bd9Sstevel@tonic-gate 		case FSD_ACL:
3507c478bd9Sstevel@tonic-gate 		case FSD_DFACL:
351355d6bb5Sswilcox 			/*
352355d6bb5Sswilcox 			 * Subtract out the two ints in the fsd_type,
353355d6bb5Sswilcox 			 * leaving us just the size of fsd_data[].
354355d6bb5Sswilcox 			 */
3557c478bd9Sstevel@tonic-gate 			numacls = (fsdp->fsd_size - 2 * sizeof (int)) /
3567c478bd9Sstevel@tonic-gate 							sizeof (ufs_acl_t);
357355d6bb5Sswilcox 			tcp = &type_counts[fsdp->fsd_type];
358355d6bb5Sswilcox 			curacl = 0;
359355d6bb5Sswilcox 			/* LINTED pointer cast alignment */
3607c478bd9Sstevel@tonic-gate 			for (ufsaclp = (ufs_acl_t *)fsdp->fsd_data;
361355d6bb5Sswilcox 						numacls; ufsaclp++, curacl++) {
3627c478bd9Sstevel@tonic-gate 				switch (ufsaclp->acl_tag) {
3637c478bd9Sstevel@tonic-gate 				case USER_OBJ:		/* Owner */
364355d6bb5Sswilcox 					tcp->nuser_objs++;
3657c478bd9Sstevel@tonic-gate 					break;
3667c478bd9Sstevel@tonic-gate 				case GROUP_OBJ:		/* Group */
367355d6bb5Sswilcox 					tcp->ngroup_objs++;
3687c478bd9Sstevel@tonic-gate 					break;
3697c478bd9Sstevel@tonic-gate 				case OTHER_OBJ:		/* Other */
370355d6bb5Sswilcox 					tcp->nother_objs++;
3717c478bd9Sstevel@tonic-gate 					break;
3727c478bd9Sstevel@tonic-gate 				case CLASS_OBJ:		/* Mask */
373355d6bb5Sswilcox 					tcp->nclass_objs++;
3747c478bd9Sstevel@tonic-gate 					break;
3757c478bd9Sstevel@tonic-gate 				case DEF_USER_OBJ:	/* Default Owner */
376355d6bb5Sswilcox 					tcp->ndef_user_objs++;
3777c478bd9Sstevel@tonic-gate 					break;
3787c478bd9Sstevel@tonic-gate 				case DEF_GROUP_OBJ:	/* Default Group */
379355d6bb5Sswilcox 					tcp->ndef_group_objs++;
3807c478bd9Sstevel@tonic-gate 					break;
3817c478bd9Sstevel@tonic-gate 				case DEF_OTHER_OBJ:	/* Default Other */
382355d6bb5Sswilcox 					tcp->ndef_other_objs++;
3837c478bd9Sstevel@tonic-gate 					break;
3847c478bd9Sstevel@tonic-gate 				case DEF_CLASS_OBJ:	/* Default Mask */
385355d6bb5Sswilcox 					tcp->ndef_class_objs++;
3867c478bd9Sstevel@tonic-gate 					break;
3877c478bd9Sstevel@tonic-gate 				case USER:		/* Users */
388355d6bb5Sswilcox 					tcp->nusers++;
3897c478bd9Sstevel@tonic-gate 					break;
3907c478bd9Sstevel@tonic-gate 				case GROUP:		/* Groups */
391355d6bb5Sswilcox 					tcp->ngroups++;
3927c478bd9Sstevel@tonic-gate 					break;
3937c478bd9Sstevel@tonic-gate 				case DEF_USER:		/* Default Users */
394355d6bb5Sswilcox 					tcp->ndef_users++;
3957c478bd9Sstevel@tonic-gate 					break;
3967c478bd9Sstevel@tonic-gate 				case DEF_GROUP:		/* Default Groups */
397355d6bb5Sswilcox 					tcp->ndef_groups++;
3987c478bd9Sstevel@tonic-gate 					break;
3997c478bd9Sstevel@tonic-gate 				default:
4007c478bd9Sstevel@tonic-gate 					return (1);
4017c478bd9Sstevel@tonic-gate 				}
402355d6bb5Sswilcox 
403355d6bb5Sswilcox 				if ((ufsaclp->acl_perm & ~07) != 0) {
404355d6bb5Sswilcox 					/*
405355d6bb5Sswilcox 					 * Caller will report inode, etc
406355d6bb5Sswilcox 					 */
407355d6bb5Sswilcox 					pwarn("Bad permission 0%o in ACL\n",
408355d6bb5Sswilcox 					    ufsaclp->acl_perm);
409355d6bb5Sswilcox 					return (1);
410355d6bb5Sswilcox 				}
411355d6bb5Sswilcox 
4127c478bd9Sstevel@tonic-gate 				numacls--;
4137c478bd9Sstevel@tonic-gate 			}
4147c478bd9Sstevel@tonic-gate 			break;
4157c478bd9Sstevel@tonic-gate 		default:
416355d6bb5Sswilcox 			if (fsdp->fsd_type >= FSD_RESERVED3 &&
417355d6bb5Sswilcox 			    fsdp->fsd_type <= FSD_RESERVED7)
418355d6bb5Sswilcox 				bad = "Unexpected";
419355d6bb5Sswilcox 			else
420355d6bb5Sswilcox 				bad = "Unknown";
421355d6bb5Sswilcox 			pwarn("%s FSD type %d in shadow inode %d",
422355d6bb5Sswilcox 			    bad, fsdp->fsd_type, inum);
423355d6bb5Sswilcox 			/*
424355d6bb5Sswilcox 			 * This is relatively harmless, since the
425355d6bb5Sswilcox 			 * kernel will ignore any entries it doesn't
426355d6bb5Sswilcox 			 * recognize.  Don't bother with iscorrupt.
427355d6bb5Sswilcox 			 */
428355d6bb5Sswilcox 			if (preen) {
429355d6bb5Sswilcox 				(void) printf(" (IGNORED)\n");
430355d6bb5Sswilcox 			} else if (reply("IGNORE") == 0) {
431355d6bb5Sswilcox 				if (reply("CLEAR SHADOW INODE") == 1) {
432355d6bb5Sswilcox 					freeino(inum, TI_PARENT);
433355d6bb5Sswilcox 				}
434355d6bb5Sswilcox 				return (0);
435355d6bb5Sswilcox 			}
4367c478bd9Sstevel@tonic-gate 			break;
4377c478bd9Sstevel@tonic-gate 		}
4387c478bd9Sstevel@tonic-gate 	}
4397c478bd9Sstevel@tonic-gate 	if ((caddr_t)fsdp != (buf + len)) {
4407c478bd9Sstevel@tonic-gate 		return (1);
4417c478bd9Sstevel@tonic-gate 	}
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate 	/* If we didn't find any acls, ignore the unknown attribute */
4447c478bd9Sstevel@tonic-gate 	if (ufsaclp == NULL)
4457c478bd9Sstevel@tonic-gate 		return (0);
4467c478bd9Sstevel@tonic-gate 
447355d6bb5Sswilcox 	/*
448355d6bb5Sswilcox 	 * Should only have default ACLs in FSD_DFACL records.
449355d6bb5Sswilcox 	 * However, the kernel can handle it, so just report that
450355d6bb5Sswilcox 	 * something odd might be going on.
451355d6bb5Sswilcox 	 */
452355d6bb5Sswilcox 	tcp = &type_counts[FSD_DFACL];
453355d6bb5Sswilcox 	if (verbose &&
454355d6bb5Sswilcox 	    (tcp->nuser_objs != 0 ||
455355d6bb5Sswilcox 	    tcp->ngroup_objs != 0 ||
456355d6bb5Sswilcox 	    tcp->nother_objs != 0 ||
457355d6bb5Sswilcox 	    tcp->nclass_objs != 0 ||
458355d6bb5Sswilcox 	    tcp->nusers != 0 ||
459355d6bb5Sswilcox 	    tcp->ngroups != 0)) {
460355d6bb5Sswilcox 		(void) printf("NOTE: ACL I=%d has miscategorized ACLs.  ",
461355d6bb5Sswilcox 		    inum);
462355d6bb5Sswilcox 		(void) printf("This is harmless, but not normal.\n");
463355d6bb5Sswilcox 	}
464355d6bb5Sswilcox 
465355d6bb5Sswilcox 	/*
466355d6bb5Sswilcox 	 * Similarly for default ACLs in FSD_ACL records.
467355d6bb5Sswilcox 	 */
468355d6bb5Sswilcox 	tcp = &type_counts[FSD_ACL];
469355d6bb5Sswilcox 	if (verbose &&
470355d6bb5Sswilcox 	    (tcp->ndef_user_objs != 0 ||
471355d6bb5Sswilcox 	    tcp->ndef_group_objs != 0 ||
472355d6bb5Sswilcox 	    tcp->ndef_other_objs != 0 ||
473355d6bb5Sswilcox 	    tcp->ndef_class_objs != 0 ||
474355d6bb5Sswilcox 	    tcp->ndef_users != 0 ||
475355d6bb5Sswilcox 	    tcp->ndef_groups != 0)) {
476355d6bb5Sswilcox 		(void) printf("NOTE: ACL I=%d has miscategorized ACLs.",
477355d6bb5Sswilcox 		    inum);
478355d6bb5Sswilcox 		(void) printf("  This is harmless, but not normal.\n");
479355d6bb5Sswilcox 	}
480355d6bb5Sswilcox 
481355d6bb5Sswilcox 	/*
482355d6bb5Sswilcox 	 * Get consolidated totals, now that we're done with checking
483355d6bb5Sswilcox 	 * the segregation above.  Assumes that neither FSD_ACL nor
484355d6bb5Sswilcox 	 * FSD_DFACL are zero.
485355d6bb5Sswilcox 	 */
486355d6bb5Sswilcox 	tcp_all = &type_counts[0];
487355d6bb5Sswilcox 	tcp_norm = &type_counts[FSD_ACL];
488355d6bb5Sswilcox 	tcp_def = &type_counts[FSD_DFACL];
489355d6bb5Sswilcox 
490355d6bb5Sswilcox 	tcp_all->nuser_objs = tcp_def->nuser_objs + tcp_norm->nuser_objs;
491355d6bb5Sswilcox 	tcp_all->ngroup_objs = tcp_def->ngroup_objs + tcp_norm->ngroup_objs;
492355d6bb5Sswilcox 	tcp_all->nother_objs = tcp_def->nother_objs + tcp_norm->nother_objs;
493355d6bb5Sswilcox 	tcp_all->nclass_objs = tcp_def->nclass_objs + tcp_norm->nclass_objs;
494355d6bb5Sswilcox 	tcp_all->ndef_user_objs =
495355d6bb5Sswilcox 		tcp_def->ndef_user_objs + tcp_norm->ndef_user_objs;
496355d6bb5Sswilcox 	tcp_all->ndef_group_objs =
497355d6bb5Sswilcox 		tcp_def->ndef_group_objs + tcp_norm->ndef_group_objs;
498355d6bb5Sswilcox 	tcp_all->ndef_other_objs =
499355d6bb5Sswilcox 		tcp_def->ndef_other_objs + tcp_norm->ndef_other_objs;
500355d6bb5Sswilcox 	tcp_all->ndef_class_objs =
501355d6bb5Sswilcox 		tcp_def->ndef_class_objs + tcp_norm->ndef_class_objs;
502355d6bb5Sswilcox 	tcp_all->nusers = tcp_def->nusers + tcp_norm->nusers;
503355d6bb5Sswilcox 	tcp_all->ngroups = tcp_def->ngroups + tcp_norm->ngroups;
504355d6bb5Sswilcox 	tcp_all->ndef_users = tcp_def->ndef_users + tcp_norm->ndef_users;
505355d6bb5Sswilcox 	tcp_all->ndef_groups = tcp_def->ndef_groups + tcp_norm->ndef_groups;
506355d6bb5Sswilcox 
507355d6bb5Sswilcox 	/*
508