public inbox for linux-xfs@vger.kernel.org
 help / color / mirror / Atom feed
From: Michael Nishimoto <miken@agami.com>
To: Barry Naujok <bnaujok@sgi.com>
Cc: xfs@oss.sgi.com, xfs-dev <xfs-dev@sgi.com>,
	Chandan Talukdar <chandan@agami.com>
Subject: Re: [REVIEW] Refactor xfs_repair's process_dinode_int
Date: Tue, 15 Jan 2008 17:03:13 -0800	[thread overview]
Message-ID: <478D57D1.2000600@agami.com> (raw)
In-Reply-To: <op.t1tl53ac3jf8g2@pc-bnaujok.melbourne.sgi.com>

Hi,

I'm posting this for Chandan Talukdar because his email address has been
booted from the xfs mailing list again.

    Michael
===============================================
- In process_misc_ino_types(), dino->di_core.di_size is being accessed
without being converted to machine format.  The check is being performed
against 0; so, it should be fine.  But for better code readability, I
guess it should be accessed through be64_to_cpu().

- In change_dinode_fmt(), it might be worthwhile to add an ASSERT
against someone passing a value greater than 16 bit for 'new_fmt'.

- In process_inode_attr_fork(), di_anextents should be accessed using
be16_to_cpu as it is a 16 bit quantity.

- In process_dinode_int() line 2691, dinoc->di_extsize should be
accessed using be32_to_cpu().

- In process_dinode_int(), we should be checking for 'dblkmap' not being
NULL before freeing it.  There are a few error conditions which can
cause the control to go to 'clear_bad_out' with dblkmap being NULL.

Thanks,
Chandan

Barry Naujok wrote:
> Implementing casefold-table checking in xfs_repair, I have to
> touch process_dinode_int. It's a horrendous function. The attached
> patch hopefully makes it much clearer what it does and removes a
> lot of duplicate code when bad inodes are found. There are some
> obscure bug fixes too (eg. two places where the inode's di_mode is
> updated, but not marked dirty - libxfs would have tossed it).
> 
> The refactoring involved removing unused variables, working out
> what various variables actually did and use them appropriately
> and break blocks of functionality into separate functions.
> 
> Barry.
> 
> 
> ------------------------------------------------------------------------
> 
> 
> ===========================================================================
> xfsprogs/repair/dino_chunks.c
> ===========================================================================
> 
> --- a/xfsprogs/repair/dino_chunks.c	2007-11-15 17:24:33.000000000 +1100
> +++ b/xfsprogs/repair/dino_chunks.c	2007-11-14 15:41:03.188152397 +1100
> @@ -593,7 +593,6 @@ process_inode_chunk(
>  	xfs_agino_t		agino;
>  	xfs_agblock_t		agbno;
>  	int			dirty = 0;
> -	int			cleared = 0;
>  	int			isa_dir = 0;
>  	int			blks_per_cluster;
>  	int			cluster_count;
> @@ -777,8 +776,7 @@ process_inode_chunk(
>  
>  		status = process_dinode(mp, dino, agno, agino,
>  				is_inode_free(ino_rec, irec_offset),
> -				&ino_dirty, &cleared, &is_used,
> -				ino_discovery, check_dups,
> +				&ino_dirty, &is_used,ino_discovery, check_dups,
>  				extra_attr_check, &isa_dir, &parent);
>  
>  		ASSERT(is_used != 3);
> 
> ===========================================================================
> xfsprogs/repair/dinode.c
> ===========================================================================
> 
> --- a/xfsprogs/repair/dinode.c	2007-11-15 17:24:33.000000000 +1100
> +++ b/xfsprogs/repair/dinode.c	2007-11-15 17:23:49.322691248 +1100
> @@ -58,9 +58,6 @@ calc_attr_offset(xfs_mount_t *mp, xfs_di
>  	case XFS_DINODE_FMT_LOCAL:
>  		offset += INT_GET(dinoc->di_size, ARCH_CONVERT);
>  		break;
> -	case XFS_DINODE_FMT_UUID:
> -		offset += sizeof(uuid_t);
> -		break;
>  	case XFS_DINODE_FMT_EXTENTS:
>  		offset += INT_GET(dinoc->di_nextents, ARCH_CONVERT) * sizeof(xfs_bmbt_rec_32_t);
>  		break;
> @@ -1563,8 +1560,11 @@ null_check(char *name, int length)
>   * bogus
>   */
>  int
> -process_symlink(xfs_mount_t *mp, xfs_ino_t lino, xfs_dinode_t *dino,
> -		blkmap_t *blkmap)
> +process_symlink(
> +	xfs_mount_t	*mp,
> +	xfs_ino_t	lino,
> +	xfs_dinode_t	*dino,
> +	blkmap_t 	*blkmap)
>  {
>  	xfs_dfsbno_t		fsbno;
>  	xfs_dinode_core_t	*dinoc = &dino->di_core;
> @@ -1673,8 +1673,7 @@ process_symlink(xfs_mount_t *mp, xfs_ino
>   * called to process the set of misc inode special inode types
>   * that have no associated data storage (fifos, pipes, devices, etc.).
>   */
> -/* ARGSUSED */
> -int
> +static int
>  process_misc_ino_types(xfs_mount_t	*mp,
>  			xfs_dinode_t	*dino,
>  			xfs_ino_t	lino,
> @@ -1693,27 +1692,27 @@ process_misc_ino_types(xfs_mount_t	*mp,
>  	/*
>  	 * must also have a zero size
>  	 */
> -	if (INT_GET(dino->di_core.di_size, ARCH_CONVERT) != 0)  {
> +	if (dino->di_core.di_size != 0)  {
>  		switch (type)  {
>  		case XR_INO_CHRDEV:
>  			do_warn(_("size of character device inode %llu != 0 "
>  				  "(%lld bytes)\n"), lino,
> -				INT_GET(dino->di_core.di_size, ARCH_CONVERT));
> +				be64_to_cpu(dino->di_core.di_size));
>  			break;
>  		case XR_INO_BLKDEV:
>  			do_warn(_("size of block device inode %llu != 0 "
>  				  "(%lld bytes)\n"), lino,
> -				INT_GET(dino->di_core.di_size, ARCH_CONVERT));
> +				be64_to_cpu(dino->di_core.di_size));
>  			break;
>  		case XR_INO_SOCK:
>  			do_warn(_("size of socket inode %llu != 0 "
>  				  "(%lld bytes)\n"), lino,
> -				INT_GET(dino->di_core.di_size, ARCH_CONVERT));
> +				be64_to_cpu(dino->di_core.di_size));
>  			break;
>  		case XR_INO_FIFO:
>  			do_warn(_("size of fifo inode %llu != 0 "
>  				  "(%lld bytes)\n"), lino,
> -				INT_GET(dino->di_core.di_size, ARCH_CONVERT));
> +				be64_to_cpu(dino->di_core.di_size));
>  			break;
>  		default:
>  			do_warn(_("Internal error - process_misc_ino_types, "
> @@ -1769,712 +1768,393 @@ process_misc_ino_types_blocks(xfs_drfsbn
>  	return (0);
>  }
>  
> -/*
> - * returns 0 if the inode is ok, 1 if the inode is corrupt
> - * check_dups can be set to 1 *only* when called by the
> - * first pass of the duplicate block checking of phase 4.
> - * *dirty is set > 0 if the dinode has been altered and
> - * needs to be written out.
> - *
> - * for detailed, info, look at process_dinode() comments.
> - */
> -/* ARGSUSED */
> -int
> -process_dinode_int(xfs_mount_t *mp,
> -		xfs_dinode_t *dino,
> -		xfs_agnumber_t agno,
> -		xfs_agino_t ino,
> -		int was_free,		/* 1 if inode is currently free */
> -		int *dirty,		/* out == > 0 if inode is now dirty */
> -		int *cleared,		/* out == 1 if inode was cleared */
> -		int *used,		/* out == 1 if inode is in use */
> -		int verify_mode,	/* 1 == verify but don't modify inode */
> -		int uncertain,		/* 1 == inode is uncertain */
> -		int ino_discovery,	/* 1 == check dirs for unknown inodes */
> -		int check_dups,		/* 1 == check if inode claims
> -					 * duplicate blocks		*/
> -		int extra_attr_check, /* 1 == do attribute format and value checks */
> -		int *isa_dir,		/* out == 1 if inode is a directory */
> -		xfs_ino_t *parent)	/* out -- parent if ino is a dir */
> +static inline int
> +dinode_fmt(
> +	xfs_dinode_core_t *dinoc)
>  {
> -	xfs_drfsbno_t		totblocks = 0;
> -	xfs_drfsbno_t		atotblocks = 0;
> -	xfs_dinode_core_t	*dinoc;
> -	char			*rstring;
> -	int			type;
> -	int			rtype;
> -	int			do_rt;
> -	int			err;
> -	int			retval = 0;
> -	__uint64_t		nextents;
> -	__uint64_t		anextents;
> -	xfs_ino_t		lino;
> -	const int		is_free = 0;
> -	const int		is_used = 1;
> -	int			repair = 0;
> -	blkmap_t		*ablkmap = NULL;
> -	blkmap_t		*dblkmap = NULL;
> -	static char		okfmts[] = {
> -		0,				/* free inode */
> -		1 << XFS_DINODE_FMT_DEV,	/* FIFO */
> -		1 << XFS_DINODE_FMT_DEV,	/* CHR */
> -		0,				/* type 3 unused */
> -		(1 << XFS_DINODE_FMT_LOCAL) |
> -		(1 << XFS_DINODE_FMT_EXTENTS) |
> -		(1 << XFS_DINODE_FMT_BTREE),	/* DIR */
> -		0,				/* type 5 unused */
> -		1 << XFS_DINODE_FMT_DEV,	/* BLK */
> -		0,				/* type 7 unused */
> -		(1 << XFS_DINODE_FMT_EXTENTS) |
> -		(1 << XFS_DINODE_FMT_BTREE),	/* REG */
> -		0,				/* type 9 unused */
> -		(1 << XFS_DINODE_FMT_LOCAL) |
> -		(1 << XFS_DINODE_FMT_EXTENTS),	/* LNK */
> -		0,				/* type 11 unused */
> -		1 << XFS_DINODE_FMT_DEV,	/* SOCK */
> -		0,				/* type 13 unused */
> -		1 << XFS_DINODE_FMT_UUID,	/* MNT */
> -		0				/* type 15 unused */
> -	};
> -
> -	retval = 0;
> -	totblocks = atotblocks = 0;
> -	*dirty = *isa_dir = *cleared = 0;
> -	*used = is_used;
> -	type = rtype = XR_INO_UNKNOWN;
> -	rstring = NULL;
> -	do_rt = 0;
> +	return be16_to_cpu(dinoc->di_mode) & S_IFMT;
> +}
>  
> -	dinoc = &dino->di_core;
> -	lino = XFS_AGINO_TO_INO(mp, agno, ino);
> +static inline void
> +change_dinode_fmt(
> +	xfs_dinode_core_t *dinoc,
> +	int		new_fmt)
> +{
> +	int		mode = be16_to_cpu(dinoc->di_mode);
>  
> -	/*
> -	 * if in verify mode, don't modify the inode.
> -	 *
> -	 * if correcting, reset stuff that has known values
> -	 *
> -	 * if in uncertain mode, be silent on errors since we're
> -	 * trying to find out if these are inodes as opposed
> -	 * to assuming that they are.  Just return the appropriate
> -	 * return code in that case.
> -	 */
> +	mode &= ~S_IFMT;
> +	mode |= new_fmt;
> +	dinoc->di_mode = cpu_to_be16(mode);
> +}
>  
> -	if (INT_GET(dinoc->di_magic, ARCH_CONVERT) != XFS_DINODE_MAGIC)  {
> -		retval++;
> -		if (!verify_mode)  {
> -			do_warn(_("bad magic number 0x%x on inode %llu, "),
> -				INT_GET(dinoc->di_magic, ARCH_CONVERT), lino);
> +static int
> +check_dinode_mode_format(
> +	xfs_dinode_core_t *dinoc)
> +{
> +	if ((uchar_t)dinoc->di_format >= XFS_DINODE_FMT_UUID)
> +		return -1;	/* FMT_UUID is not used */
> +
> +	switch (dinode_fmt(dinoc)) {
> +	case S_IFIFO:
> +	case S_IFCHR:
> +	case S_IFBLK:
> +	case S_IFSOCK:
> +		return (dinoc->di_format != XFS_DINODE_FMT_DEV) ? -1 : 0;
> +
> +	case S_IFDIR:
> +		return (dinoc->di_format < XFS_DINODE_FMT_LOCAL ||
> +			dinoc->di_format > XFS_DINODE_FMT_BTREE) ? -1 : 0;
> +
> +	case S_IFREG:
> +		return (dinoc->di_format < XFS_DINODE_FMT_EXTENTS ||
> +			dinoc->di_format > XFS_DINODE_FMT_BTREE) ? -1 : 0;
> +
> +	case S_IFLNK:
> +		return (dinoc->di_format < XFS_DINODE_FMT_LOCAL ||
> +			dinoc->di_format > XFS_DINODE_FMT_EXTENTS) ? -1 : 0;
> +
> +	default: ;
> +	}
> +	return 0;	/* invalid modes are checked elsewhere */
> +}
> +
> +/*
> + * If inode is a superblock inode, does type check to make sure is it valid.
> + * Returns 0 if it's valid, non-zero if it needs to be cleared.
> + */
> +
> +static int
> +process_check_sb_inodes(
> +	xfs_mount_t	*mp,
> +	xfs_dinode_core_t *dinoc,
> +	xfs_ino_t	lino,
> +	int		*type,
> +	int		*dirty)
> +{
> +	if (lino == mp->m_sb.sb_rootino) {
> +	 	if (*type != XR_INO_DIR)  {
> +			do_warn(_("root inode %llu has bad type 0x%x\n"),
> +				lino, dinode_fmt(dinoc));
> +			*type = XR_INO_DIR;
>  			if (!no_modify)  {
> -				do_warn(_("resetting magic number\n"));
> +				do_warn(_("resetting to directory\n"));
> +				change_dinode_fmt(dinoc, S_IFDIR);
>  				*dirty = 1;
> -				INT_SET(dinoc->di_magic, ARCH_CONVERT,
> -					XFS_DINODE_MAGIC);
> -			} else  {
> -				do_warn(_("would reset magic number\n"));
> -			}
> -		} else if (!uncertain) {
> -			do_warn(_("bad magic number 0x%x on inode %llu\n"),
> -				INT_GET(dinoc->di_magic, ARCH_CONVERT), lino);
> +			} else
> +				do_warn(_("would reset to directory\n"));
>  		}
> +		return 0;
>  	}
> -
> -	if (!XFS_DINODE_GOOD_VERSION(dinoc->di_version) ||
> -	    (!fs_inode_nlink && dinoc->di_version > XFS_DINODE_VERSION_1))  {
> -		retval++;
> -		if (!verify_mode)  {
> -			do_warn(_("bad version number 0x%x on inode %llu, "),
> -				dinoc->di_version, lino);
> +	if (lino == mp->m_sb.sb_uquotino)  {
> +		if (*type != XR_INO_DATA)  {
> +			do_warn(_("user quota inode %llu has bad type 0x%x\n"),
> +				lino, dinode_fmt(dinoc));
> +			mp->m_sb.sb_uquotino = NULLFSINO;
> +			return 1;
> +		}
> +		return 0;
> +	}
> +	if (lino == mp->m_sb.sb_gquotino)  {
> +		if (*type != XR_INO_DATA)  {
> +			do_warn(_("group quota inode %llu has bad type 0x%x\n"),
> +				lino, dinode_fmt(dinoc));
> +			mp->m_sb.sb_gquotino = NULLFSINO;
> +			return 1;
> +		}
> +		return 0;
> +	}
> +	if (lino == mp->m_sb.sb_rsumino) {
> +		if (*type != XR_INO_RTSUM) {
> +			do_warn(_("realtime summary inode %llu has bad type 0x%x, "),
> +				lino, dinode_fmt(dinoc));
>  			if (!no_modify)  {
> -				do_warn(_("resetting version number\n"));
> +				do_warn(_("resetting to regular file\n"));
> +				change_dinode_fmt(dinoc, S_IFREG);
>  				*dirty = 1;
> -				dinoc->di_version = (fs_inode_nlink) ?
> -					XFS_DINODE_VERSION_2 :
> -					XFS_DINODE_VERSION_1;
>  			} else  {
> -				do_warn(_("would reset version number\n"));
> +				do_warn(_("would reset to regular file\n"));
>  			}
> -		} else  if (!uncertain) {
> -			do_warn(_("bad version number 0x%x on inode %llu\n"),
> -				dinoc->di_version, lino);
>  		}
> +		if (mp->m_sb.sb_rblocks == 0 && dinoc->di_nextents != 0)  {
> +			do_warn(_("bad # of extents (%u) for realtime summary inode %llu\n"),
> +				be32_to_cpu(dinoc->di_nextents), lino);
> +			return 1;
> +		}
> +		return 0;
>  	}
> -
> -	/*
> -	 * blow out of here if the inode size is < 0
> -	 */
> -	if (INT_GET(dinoc->di_size, ARCH_CONVERT) < 0)  {
> -		retval++;
> -		if (!verify_mode)  {
> -			do_warn(_("bad (negative) size %lld on inode %llu\n"),
> -				INT_GET(dinoc->di_size, ARCH_CONVERT), lino);
> +	if (lino == mp->m_sb.sb_rbmino) {
> +		if (*type != XR_INO_RTBITMAP) {
> +			do_warn(_("realtime bitmap inode %llu has bad type 0x%x, "),
> +				lino, dinode_fmt(dinoc));
>  			if (!no_modify)  {
> -				*dirty += clear_dinode(mp, dino, lino);
> -				*cleared = 1;
> -			} else  {
> +				do_warn(_("resetting to regular file\n"));
> +				change_dinode_fmt(dinoc, S_IFREG);
>  				*dirty = 1;
> -				*cleared = 1;
> +			} else  {
> +				do_warn(_("would reset to regular file\n"));
>  			}
> -			*used = is_free;
> -		} else if (!uncertain)  {
> -			do_warn(_("bad (negative) size %lld on inode %llu\n"),
> -				INT_GET(dinoc->di_size, ARCH_CONVERT), lino);
>  		}
> -
> -		return(1);
> +		if (mp->m_sb.sb_rblocks == 0 && dinoc->di_nextents != 0)  {
> +			do_warn(_("bad # of extents (%u) for realtime bitmap inode %llu\n"),
> +				be32_to_cpu(dinoc->di_nextents), lino);
> +			return 1;
> +		}
> +		return 0;
>  	}
> +	return 0;
> +}
>  
> -	/*
> -	 * was_free value is not meaningful if we're in verify mode
> -	 */
> -	if (!verify_mode && INT_GET(dinoc->di_mode, ARCH_CONVERT) == 0 && was_free == 1)  {
> -		/*
> -		 * easy case, inode free -- inode and map agree, clear
> -		 * it just in case to ensure that format, etc. are
> -		 * set correctly
> -		 */
> -		if (!no_modify)  {
> -			err =  clear_dinode(mp, dino, lino);
> -			if (err)  {
> -				*dirty = 1;
> -				*cleared = 1;
> -			}
> +/*
> + * general size/consistency checks:
> + *
> + * if the size <= size of the data fork, directories  must be
> + * local inodes unlike regular files which would be extent inodes.
> + * all the other mentioned types have to have a zero size value.
> + *
> + * if the size and format don't match, get out now rather than
> + * risk trying to process a non-existent extents or btree
> + * type data fork.
> + */
> +static int
> +process_check_inode_sizes(
> +	xfs_mount_t	*mp,
> +	xfs_dinode_t	*dino,
> +	xfs_ino_t	lino,
> +	int		type)
> +{
> +	xfs_dinode_core_t *dinoc = &dino->di_core;
> +	xfs_fsize_t	size = be64_to_cpu(dinoc->di_size);
> +
> +	switch (type)  {
> +
> +	case XR_INO_DIR:
> +		if (size <= XFS_DFORK_DSIZE(dino, mp) &&
> +				dinoc->di_format != XFS_DINODE_FMT_LOCAL) {
> +			do_warn(_("mismatch between format (%d) and size "
> +				"(%lld) in directory ino %llu\n"),
> +				dinoc->di_format, size, lino);
> +			return 1;
>  		}
> -		*used = is_free;
> -		return(0);
> -	} else if (!verify_mode && INT_GET(dinoc->di_mode, ARCH_CONVERT) == 0 && was_free == 0)  {
> +		break;
> +
> +	case XR_INO_SYMLINK:
> +		if (process_symlink_extlist(mp, lino, dino))  {
> +			do_warn(_("bad data fork in symlink %llu\n"), lino);
> +			return 1;
> +		}
> +		break;
> +
> +	case XR_INO_CHRDEV:	/* fall through to FIFO case ... */
> +	case XR_INO_BLKDEV:	/* fall through to FIFO case ... */
> +	case XR_INO_SOCK:	/* fall through to FIFO case ... */
> +	case XR_INO_MOUNTPOINT:	/* fall through to FIFO case ... */
> +	case XR_INO_FIFO:
> +		if (process_misc_ino_types(mp, dino, lino, type))
> +			return 1;
> +		break;
> +
> +	case XR_INO_RTDATA:
>  		/*
> -		 * the inode looks free but the map says it's in use.
> -		 * clear the inode just to be safe and mark the inode
> -		 * free.
> +		 * if we have no realtime blocks, any inode claiming
> +		 * to be a real-time file is bogus
>  		 */
> -		do_warn(_("imap claims a free inode %llu is in use, "), lino);
> -
> -		if (!no_modify)  {
> -			do_warn(_("correcting imap and clearing inode\n"));
> +		if (mp->m_sb.sb_rblocks == 0)  {
> +			do_warn(_("found inode %llu claiming to be a "
> +				"real-time file\n"), lino);
> +			return 1;
> +		}
> +		break;
>  
> -			err =  clear_dinode(mp, dino, lino);
> -			if (err)  {
> -				retval++;
> -				*dirty = 1;
> -				*cleared = 1;
> -			}
> -		} else  {
> -			do_warn(_("would correct imap and clear inode\n"));
> +	case XR_INO_RTBITMAP:
> +		if (size != (__int64_t)mp->m_sb.sb_rbmblocks *
> +					mp->m_sb.sb_blocksize) {
> +			do_warn(_("realtime bitmap inode %llu has bad size "
> +				"%lld (should be %lld)\n"),
> +				lino, size, (__int64_t) mp->m_sb.sb_rbmblocks *
> +					mp->m_sb.sb_blocksize);
> +			return 1;
> +		}
> +		break;
>  
> -			*dirty = 1;
> -			*cleared = 1;
> +	case XR_INO_RTSUM:
> +		if (size != mp->m_rsumsize)  {
> +			do_warn(_("realtime summary inode %llu has bad size "
> +				"%lld (should be %d)\n"),
> +				lino, size, mp->m_rsumsize);
> +			return 1;
>  		}
> +		break;
>  
> -		*used = is_free;
> +	default:
> +		break;
> +	}
> +	return 0;
> +}
>  
> -		return(retval > 0 ? 1 : 0);
> +/*
> + * check for illegal values of forkoff
> + */
> +static int
> +process_check_inode_forkoff(
> +	xfs_mount_t	*mp,
> +	xfs_dinode_core_t *dinoc,
> +	xfs_ino_t	lino)
> +{
> +	if (dinoc->di_forkoff == 0)
> +		return 0;
> +
> +	switch (dinoc->di_format)  {
> +	case XFS_DINODE_FMT_DEV:
> +		if (dinoc->di_forkoff != (roundup(sizeof(xfs_dev_t), 8) >> 3)) {
> +			do_warn(_("bad attr fork offset %d in dev inode %llu, "
> +				"should be %d\n"), dinoc->di_forkoff, lino,
> +				(int)(roundup(sizeof(xfs_dev_t), 8) >> 3));
> +			return 1;
> +		}
> +		break;
> +	case XFS_DINODE_FMT_LOCAL:	/* fall through ... */
> +	case XFS_DINODE_FMT_EXTENTS:	/* fall through ... */
> +	case XFS_DINODE_FMT_BTREE:
> +		if (dinoc->di_forkoff >= (XFS_LITINO(mp) >> 3)) {
> +			do_warn(_("bad attr fork offset %d in inode %llu, "
> +				"max=%d\n"), dinoc->di_forkoff, lino,
> +				XFS_LITINO(mp) >> 3);
> +			return 1;
> +		}
> +		break;
> +	default:
> +		do_error(_("unexpected inode format %d\n"), dinoc->di_format);
> +		break;
>  	}
> +	return 0;
> +}
>  
> -	/*
> -	 * because of the lack of any write ordering guarantee, it's
> -	 * possible that the core got updated but the forks didn't.
> -	 * so rather than be ambitious (and probably incorrect),
> -	 * if there's an inconsistency, we get conservative and
> -	 * just pitch the file.  blow off checking formats of
> -	 * free inodes since technically any format is legal
> -	 * as we reset the inode when we re-use it.
> -	 */
> -	if (INT_GET(dinoc->di_mode, ARCH_CONVERT) != 0 &&
> -		((((INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT) >> 12) > 15) ||
> -		(uchar_t) dinoc->di_format > XFS_DINODE_FMT_UUID ||
> -			(!(okfmts[(INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT) >> 12] &
> -			  (1 << dinoc->di_format))))) {
> -		/* bad inode format */
> -		retval++;
> -		if (!uncertain)
> -			do_warn(_("bad inode format in inode %llu\n"), lino);
> -		if (!verify_mode)  {
> -			if (!no_modify)  {
> -				*dirty += clear_dinode(mp, dino, lino);
> -				ASSERT(*dirty > 0);
> -			}
> +/*
> + * Updates the inodes block and extent counts if they are wrong
> + */
> +static int
> +process_inode_blocks_and_extents(
> +	xfs_dinode_core_t *dinoc,
> +	xfs_drfsbno_t	nblocks,
> +	__uint64_t	nextents,
> +	__uint64_t	anextents,
> +	xfs_ino_t	lino,
> +	int		*dirty)
> +{
> +	if (nblocks != be64_to_cpu(dinoc->di_nblocks))  {
> +		if (!no_modify)  {
> +			do_warn(_("correcting nblocks for inode %llu, "
> +				"was %llu - counted %llu\n"), lino,
> +				be64_to_cpu(dinoc->di_nblocks), nblocks);
> +			dinoc->di_nblocks = cpu_to_be64(nblocks);
> +			*dirty = 1;
> +		} else  {
> +			do_warn(_("bad nblocks %llu for inode %llu, "
> +				"would reset to %llu\n"),
> +				be64_to_cpu(dinoc->di_nblocks), lino, nblocks);
>  		}
> -		*cleared = 1;
> -		*used = is_free;
> +	}
>  
> -		return(retval > 0 ? 1 : 0);
> +	if (nextents > MAXEXTNUM)  {
> +		do_warn(_("too many data fork extents (%llu) in inode %llu\n"),
> +			nextents, lino);
> +		return 1;
> +	}
> +	if (nextents != be32_to_cpu(dinoc->di_nextents))  {
> +		if (!no_modify)  {
> +			do_warn(_("correcting nextents for inode %llu, "
> +				"was %d - counted %llu\n"), lino,
> +				be32_to_cpu(dinoc->di_nextents), nextents);
> +			dinoc->di_nextents = cpu_to_be32(nextents);
> +			*dirty = 1;
> +		} else  {
> +			do_warn(_("bad nextents %d for inode %llu, would reset "
> +				"to %llu\n"), be32_to_cpu(dinoc->di_nextents),
> +				lino, nextents);
> +		}
>  	}
>  
> -	if (verify_mode)
> -		return(retval > 0 ? 1 : 0);
> +	if (anextents > MAXAEXTNUM)  {
> +		do_warn(_("too many attr fork extents (%llu) in inode %llu\n"),
> +			anextents, lino);
> +		return 1;
> +	}
> +	if (anextents != be16_to_cpu(dinoc->di_anextents))  {
> +		if (!no_modify)  {
> +			do_warn(_("correcting anextents for inode %llu, "
> +				"was %d - counted %llu\n"), lino,
> +				be16_to_cpu(dinoc->di_anextents), anextents);
> +			dinoc->di_anextents = cpu_to_be16(anextents);
> +			*dirty = 1;
> +		} else  {
> +			do_warn(_("bad anextents %d for inode %llu, would reset"
> +				" to %llu\n"), be16_to_cpu(dinoc->di_anextents),
> +				lino, anextents);
> +		}
> +	}
> +	return 0;
> +}
>  
> -	/*
> -	 * clear the next unlinked field if necessary on a good
> -	 * inode only during phase 4 -- when checking for inodes
> -	 * referencing duplicate blocks.  then it's safe because
> -	 * we've done the inode discovery and have found all the inodes
> -	 * we're going to find.  check_dups is set to 1 only during
> -	 * phase 4.  Ugly.
> -	 */
> -	if (check_dups && !no_modify)
> -		*dirty += clear_dinode_unlinked(mp, dino);
> +/*
> + * check data fork -- if it's bad, clear the inode
> + */
> +static int
> +process_inode_data_fork(
> +	xfs_mount_t	*mp,
> +	xfs_agnumber_t	agno,
> +	xfs_agino_t	ino,
> +	xfs_dinode_t	*dino,
> +	int		type,
> +	int		*dirty,
> +	xfs_drfsbno_t	*totblocks,
> +	__uint64_t	*nextents,
> +	blkmap_t	**dblkmap,
> +	int		check_dups)
> +{
> +	xfs_dinode_core_t *dinoc = &dino->di_core;
> +	xfs_ino_t	lino = XFS_AGINO_TO_INO(mp, agno, ino);
> +	int		err = 0;
>  
> -	/* set type and map type info */
> +	*nextents = be32_to_cpu(dinoc->di_nextents);
> +	if (*nextents > be64_to_cpu(dinoc->di_nblocks) ||
> +			*nextents > XFS_MAX_INCORE_EXTENTS)
> +		*nextents = 1;
> +
> +	if (dinoc->di_format != XFS_DINODE_FMT_LOCAL && type != XR_INO_RTDATA)
> +		*dblkmap = blkmap_alloc(*nextents);
> +	*nextents = 0;
>  
> -	switch (INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT) {
> -	case S_IFDIR:
> -		type = XR_INO_DIR;
> -		*isa_dir = 1;
> -		break;
> -	case S_IFREG:
> -		if (INT_GET(dinoc->di_flags, ARCH_CONVERT) & XFS_DIFLAG_REALTIME)
> -			type = XR_INO_RTDATA;
> -		else if (lino == mp->m_sb.sb_rbmino)
> -			type = XR_INO_RTBITMAP;
> -		else if (lino == mp->m_sb.sb_rsumino)
> -			type = XR_INO_RTSUM;
> -		else
> -			type = XR_INO_DATA;
> -		break;
> -	case S_IFLNK:
> -		type = XR_INO_SYMLINK;
> -		break;
> -	case S_IFCHR:
> -		type = XR_INO_CHRDEV;
> -		break;
> -	case S_IFBLK:
> -		type = XR_INO_BLKDEV;
> -		break;
> -	case S_IFSOCK:
> -		type = XR_INO_SOCK;
> -		break;
> -	case S_IFIFO:
> -		type = XR_INO_FIFO;
> -		break;
> -	default:
> -		retval++;
> -		if (!verify_mode)  {
> -			do_warn(_("bad inode type %#o inode %llu\n"),
> -				(int) (INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT), lino);
> -			if (!no_modify)
> -				*dirty += clear_dinode(mp, dino, lino);
> -			else
> -				*dirty = 1;
> -			*cleared = 1;
> -			*used = is_free;
> -		} else if (!uncertain)  {
> -			do_warn(_("bad inode type %#o inode %llu\n"),
> -				(int) (INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT), lino);
> -		}
> -		return 1;
> -	}
> -
> -	/*
> -	 * type checks for root, realtime inodes, and quota inodes
> -	 */
> -	if (lino == mp->m_sb.sb_rootino && type != XR_INO_DIR)  {
> -		do_warn(_("bad inode type for root inode %llu, "), lino);
> -		type = XR_INO_DIR;
> -
> -		if (!no_modify)  {
> -			do_warn(_("resetting to directory\n"));
> -			INT_MOD_EXPR(dinoc->di_mode, ARCH_CONVERT,
> -			  &= ~(INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT));
> -			INT_MOD_EXPR(dinoc->di_mode, ARCH_CONVERT,
> -			  |= INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFDIR);
> -		} else  {
> -			do_warn(_("would reset to directory\n"));
> -		}
> -	} else if (lino == mp->m_sb.sb_rsumino)  {
> -		do_rt = 1;
> -		rstring = _("summary");
> -		rtype = XR_INO_RTSUM;
> -	} else if (lino == mp->m_sb.sb_rbmino)  {
> -		do_rt = 1;
> -		rstring = _("bitmap");
> -		rtype = XR_INO_RTBITMAP;
> -	} else if (lino == mp->m_sb.sb_uquotino)  {
> -		if (type != XR_INO_DATA)  {
> -			do_warn(_("user quota inode has bad type 0x%x\n"),
> -				INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT);
> -
> -			if (!no_modify)  {
> -				*dirty += clear_dinode(mp, dino, lino);
> -				ASSERT(*dirty > 0);
> -			}
> -
> -			*cleared = 1;
> -			*used = is_free;
> -			*isa_dir = 0;
> -
> -			mp->m_sb.sb_uquotino = NULLFSINO;
> -
> -			return(1);
> -		}
> -	} else if (lino == mp->m_sb.sb_gquotino)  {
> -		if (type != XR_INO_DATA)  {
> -			do_warn(_("group quota inode has bad type 0x%x\n"),
> -				INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT);
> -
> -			if (!no_modify)  {
> -				*dirty += clear_dinode(mp, dino, lino);
> -				ASSERT(*dirty > 0);
> -			}
> -
> -			*cleared = 1;
> -			*used = is_free;
> -			*isa_dir = 0;
> -
> -			mp->m_sb.sb_gquotino = NULLFSINO;
> -
> -			return(1);
> -		}
> -	}
> -
> -	if (do_rt && type != rtype)  {
> -		type = XR_INO_DATA;
> -
> -		do_warn(_("bad inode type for realtime %s inode %llu, "),
> -			rstring, lino);
> -
> -		if (!no_modify)  {
> -			do_warn(_("resetting to regular file\n"));
> -			INT_MOD_EXPR(dinoc->di_mode, ARCH_CONVERT,
> -			  &= ~(INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT));
> -			INT_MOD_EXPR(dinoc->di_mode, ARCH_CONVERT,
> -			  |= INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFREG);
> -		} else  {
> -			do_warn(_("would reset to regular file\n"));
> -		}
> -	}
> -
> -	/*
> -	 * only regular files with REALTIME or EXTSIZE flags set can have
> -	 * extsize set, or directories with EXTSZINHERIT.
> -	 */
> -	if (INT_GET(dinoc->di_extsize, ARCH_CONVERT) != 0)  {
> -		if ((type == XR_INO_RTDATA) ||
> -		    (type == XR_INO_DIR &&
> -				(INT_GET(dinoc->di_flags, ARCH_CONVERT) &
> -				 XFS_DIFLAG_EXTSZINHERIT)) ||
> -		    (type == XR_INO_DATA &&
> -				(INT_GET(dinoc->di_flags, ARCH_CONVERT) &
> -				 XFS_DIFLAG_EXTSIZE)))  {
> -			/* s'okay */ ;
> -		} else {
> -			do_warn(
> -	_("bad non-zero extent size %u for non-realtime/extsize inode %llu, "),
> -				INT_GET(dinoc->di_extsize, ARCH_CONVERT), lino);
> -
> -			if (!no_modify)  {
> -				do_warn(_("resetting to zero\n"));
> -				dinoc->di_extsize = 0;
> -				*dirty = 1;
> -			} else  {
> -				do_warn(_("would reset to zero\n"));
> -			}
> -		}
> -	}
> -
> -	/*
> -	 * for realtime inodes, check sizes to see that
> -	 * they are consistent with the # of realtime blocks.
> -	 * also, verify that they contain only one extent and
> -	 * are extent format files.  If anything's wrong, clear
> -	 * the inode -- we'll recreate it in phase 6.
> -	 */
> -	if (do_rt &&
> -	    ((lino == mp->m_sb.sb_rbmino &&
> -	      INT_GET(dinoc->di_size, ARCH_CONVERT)
> -			  != mp->m_sb.sb_rbmblocks * mp->m_sb.sb_blocksize) ||
> -	     (lino == mp->m_sb.sb_rsumino &&
> -	      INT_GET(dinoc->di_size, ARCH_CONVERT) != mp->m_rsumsize))) {
> -
> -		do_warn(_("bad size %llu for realtime %s inode %llu\n"),
> -			INT_GET(dinoc->di_size, ARCH_CONVERT), rstring, lino);
> -
> -		if (!no_modify)  {
> -			*dirty += clear_dinode(mp, dino, lino);
> -			ASSERT(*dirty > 0);
> -		}
> -
> -		*cleared = 1;
> -		*used = is_free;
> -		*isa_dir = 0;
> -
> -		return(1);
> -	}
> -
> -	if (do_rt && mp->m_sb.sb_rblocks == 0 && INT_GET(dinoc->di_nextents, ARCH_CONVERT) != 0)  {
> -		do_warn(_("bad # of extents (%u) for realtime %s inode %llu\n"),
> -			INT_GET(dinoc->di_nextents, ARCH_CONVERT), rstring, lino);
> -
> -		if (!no_modify)  {
> -			*dirty += clear_dinode(mp, dino, lino);
> -			ASSERT(*dirty > 0);
> -		}
> -
> -		*cleared = 1;
> -		*used = is_free;
> -		*isa_dir = 0;
> -
> -		return(1);
> -	}
> -
> -	/*
> -	 * Setup nextents and anextents for blkmap_alloc calls.
> -	 */
> -	nextents = INT_GET(dinoc->di_nextents, ARCH_CONVERT);
> -	if (nextents > INT_GET(dinoc->di_nblocks, ARCH_CONVERT) || nextents > XFS_MAX_INCORE_EXTENTS)
> -		nextents = 1;
> -	anextents = INT_GET(dinoc->di_anextents, ARCH_CONVERT);
> -	if (anextents > INT_GET(dinoc->di_nblocks, ARCH_CONVERT) || anextents > XFS_MAX_INCORE_EXTENTS)
> -		anextents = 1;
> -
> -	/*
> -	 * general size/consistency checks:
> -	 *
> -	 * if the size <= size of the data fork, directories  must be
> -	 * local inodes unlike regular files which would be extent inodes.
> -	 * all the other mentioned types have to have a zero size value.
> -	 *
> -	 * if the size and format don't match, get out now rather than
> -	 * risk trying to process a non-existent extents or btree
> -	 * type data fork.
> -	 */
> -	switch (type)  {
> -	case XR_INO_DIR:
> -		if (INT_GET(dinoc->di_size, ARCH_CONVERT) <=
> -			XFS_DFORK_DSIZE(dino, mp) &&
> -		    (dinoc->di_format != XFS_DINODE_FMT_LOCAL))  {
> -			do_warn(
> -_("mismatch between format (%d) and size (%lld) in directory ino %llu\n"),
> -				dinoc->di_format,
> -				INT_GET(dinoc->di_size, ARCH_CONVERT),
> -				lino);
> -
> -			if (!no_modify)  {
> -				*dirty += clear_dinode(mp,
> -						dino, lino);
> -				ASSERT(*dirty > 0);
> -			}
> -
> -			*cleared = 1;
> -			*used = is_free;
> -			*isa_dir = 0;
> -
> -			return(1);
> -		}
> -		if (dinoc->di_format != XFS_DINODE_FMT_LOCAL)
> -			dblkmap = blkmap_alloc(nextents);
> -		break;
> -	case XR_INO_SYMLINK:
> -		if (process_symlink_extlist(mp, lino, dino))  {
> -			do_warn(_("bad data fork in symlink %llu\n"), lino);
> -
> -			if (!no_modify)  {
> -				*dirty += clear_dinode(mp,
> -						dino, lino);
> -				ASSERT(*dirty > 0);
> -			}
> -
> -			*cleared = 1;
> -			*used = is_free;
> -			*isa_dir = 0;
> -
> -			return(1);
> -		}
> -		if (dinoc->di_format != XFS_DINODE_FMT_LOCAL)
> -			dblkmap = blkmap_alloc(nextents);
> -		break;
> -	case XR_INO_CHRDEV:	/* fall through to FIFO case ... */
> -	case XR_INO_BLKDEV:	/* fall through to FIFO case ... */
> -	case XR_INO_SOCK:	/* fall through to FIFO case ... */
> -	case XR_INO_MOUNTPOINT:	/* fall through to FIFO case ... */
> -	case XR_INO_FIFO:
> -		if (process_misc_ino_types(mp, dino, lino, type))  {
> -			if (!no_modify)  {
> -				*dirty += clear_dinode(mp, dino, lino);
> -				ASSERT(*dirty > 0);
> -			}
> -
> -			*cleared = 1;
> -			*used = is_free;
> -			*isa_dir = 0;
> -
> -			return(1);
> -		}
> -		break;
> -	case XR_INO_RTDATA:
> -		/*
> -		 * if we have no realtime blocks, any inode claiming
> -		 * to be a real-time file is bogus
> -		 */
> -		if (mp->m_sb.sb_rblocks == 0)  {
> -			do_warn(
> -			_("found inode %llu claiming to be a real-time file\n"),
> -				lino);
> -
> -			if (!no_modify)  {
> -				*dirty += clear_dinode(mp, dino, lino);
> -				ASSERT(*dirty > 0);
> -			}
> -
> -			*cleared = 1;
> -			*used = is_free;
> -			*isa_dir = 0;
> -
> -			return(1);
> -		}
> -		break;
> -	case XR_INO_RTBITMAP:
> -		if (INT_GET(dinoc->di_size, ARCH_CONVERT) !=
> -		    (__int64_t)mp->m_sb.sb_rbmblocks * mp->m_sb.sb_blocksize)  {
> -			do_warn(
> -	_("realtime bitmap inode %llu has bad size %lld (should be %lld)\n"),
> -				lino, INT_GET(dinoc->di_size, ARCH_CONVERT),
> -				(__int64_t) mp->m_sb.sb_rbmblocks *
> -				mp->m_sb.sb_blocksize);
> -
> -			if (!no_modify)  {
> -				*dirty += clear_dinode(mp, dino, lino);
> -				ASSERT(*dirty > 0);
> -			}
> -
> -			*cleared = 1;
> -			*used = is_free;
> -			*isa_dir = 0;
> -
> -			return(1);
> -		}
> -		dblkmap = blkmap_alloc(nextents);
> -		break;
> -	case XR_INO_RTSUM:
> -		if (INT_GET(dinoc->di_size, ARCH_CONVERT) != mp->m_rsumsize)  {
> -			do_warn(
> -	_("realtime summary inode %llu has bad size %lld (should be %d)\n"),
> -				lino, INT_GET(dinoc->di_size, ARCH_CONVERT),
> -				mp->m_rsumsize);
> -
> -			if (!no_modify)  {
> -				*dirty += clear_dinode(mp, dino, lino);
> -				ASSERT(*dirty > 0);
> -			}
> -
> -			*cleared = 1;
> -			*used = is_free;
> -			*isa_dir = 0;
> -
> -			return(1);
> -		}
> -		dblkmap = blkmap_alloc(nextents);
> -		break;
> -	default:
> -		break;
> -	}
> -
> -	/*
> -	 * check for illegal values of forkoff
> -	 */
> -	err = 0;
> -	if (dinoc->di_forkoff != 0)  {
> -		switch (dinoc->di_format)  {
> -		case XFS_DINODE_FMT_DEV:
> -			if (dinoc->di_forkoff !=
> -					(roundup(sizeof(xfs_dev_t), 8) >> 3))  {
> -				do_warn(
> -		_("bad attr fork offset %d in dev inode %llu, should be %d\n"),
> -					(int) dinoc->di_forkoff,
> -					lino,
> -					(int) (roundup(sizeof(xfs_dev_t), 8) >> 3));
> -				err = 1;
> -			}
> -			break;
> -		case XFS_DINODE_FMT_UUID:
> -			if (dinoc->di_forkoff !=
> -					(roundup(sizeof(uuid_t), 8) >> 3))  {
> -				do_warn(
> -		_("bad attr fork offset %d in uuid inode %llu, should be %d\n"),
> -					(int) dinoc->di_forkoff,
> -					lino,
> -					(int)(roundup(sizeof(uuid_t), 8) >> 3));
> -				err = 1;
> -			}
> -			break;
> -		case XFS_DINODE_FMT_LOCAL:	/* fall through ... */
> -		case XFS_DINODE_FMT_EXTENTS:	/* fall through ... */
> -		case XFS_DINODE_FMT_BTREE: {
> -			if (dinoc->di_forkoff >= (XFS_LITINO(mp) >> 3))  {
> -				do_warn(
> -		_("bad attr fork offset %d in inode %llu, max=%d\n"),
> -					(int) dinoc->di_forkoff,
> -					lino, XFS_LITINO(mp) >> 3);
> -				err = 1;
> -			}
> -			break;
> -		    }
> -		default:
> -			do_error(_("unexpected inode format %d\n"),
> -				(int) dinoc->di_format);
> -			break;
> -		}
> -	}
> -
> -	if (err)  {
> -		if (!no_modify)  {
> -			*dirty += clear_dinode(mp, dino, lino);
> -			ASSERT(*dirty > 0);
> -		}
> -
> -		*cleared = 1;
> -		*used = is_free;
> -		*isa_dir = 0;
> -		blkmap_free(dblkmap);
> -		return(1);
> -	}
> -
> -	/*
> -	 * check data fork -- if it's bad, clear the inode
> -	 */
> -	nextents = 0;
>  	switch (dinoc->di_format) {
>  	case XFS_DINODE_FMT_LOCAL:
> -		err = process_lclinode(mp, agno, ino, dino, type,
> -			dirty, &totblocks, &nextents, &dblkmap,
> -			XFS_DATA_FORK, check_dups);
> +		err = process_lclinode(mp, agno, ino, dino, type, dirty,
> +			totblocks, nextents, dblkmap, XFS_DATA_FORK,
> +			check_dups);
>  		break;
>  	case XFS_DINODE_FMT_EXTENTS:
> -		err = process_exinode(mp, agno, ino, dino, type,
> -			dirty, &totblocks, &nextents, &dblkmap,
> -			XFS_DATA_FORK, check_dups);
> +		err = process_exinode(mp, agno, ino, dino, type, dirty,
> +			totblocks, nextents, dblkmap, XFS_DATA_FORK,
> +			check_dups);
>  		break;
>  	case XFS_DINODE_FMT_BTREE:
> -		err = process_btinode(mp, agno, ino, dino, type,
> -			dirty, &totblocks, &nextents, &dblkmap,
> -			XFS_DATA_FORK, check_dups);
> +		err = process_btinode(mp, agno, ino, dino, type, dirty,
> +			totblocks, nextents, dblkmap, XFS_DATA_FORK,
> +			check_dups);
>  		break;
>  	case XFS_DINODE_FMT_DEV:	/* fall through */
> -	case XFS_DINODE_FMT_UUID:
>  		err = 0;
>  		break;
>  	default:
>  		do_error(_("unknown format %d, ino %llu (mode = %d)\n"),
> -			dinoc->di_format, lino,
> -			INT_GET(dinoc->di_mode, ARCH_CONVERT));
> +			dinoc->di_format, lino, be16_to_cpu(dinoc->di_mode));
>  	}
>  
>  	if (err)  {
> -		/*
> -		 * problem in the data fork, clear out the inode
> -		 * and get out
> -		 */
>  		do_warn(_("bad data fork in inode %llu\n"), lino);
> -
>  		if (!no_modify)  {
>  			*dirty += clear_dinode(mp, dino, lino);
>  			ASSERT(*dirty > 0);
>  		}
> -
> -		*cleared = 1;
> -		*used = is_free;
> -		*isa_dir = 0;
> -		blkmap_free(dblkmap);
> -		return(1);
> +		return 1;
>  	}
>  
>  	if (check_dups)  {
> @@ -2486,465 +2166,633 @@ _("mismatch between format (%d) and size
>  		switch (dinoc->di_format) {
>  		case XFS_DINODE_FMT_LOCAL:
>  			err = process_lclinode(mp, agno, ino, dino, type,
> -				dirty, &totblocks, &nextents, &dblkmap,
> +				dirty, totblocks, nextents, dblkmap,
>  				XFS_DATA_FORK, 0);
>  			break;
>  		case XFS_DINODE_FMT_EXTENTS:
>  			err = process_exinode(mp, agno, ino, dino, type,
> -				dirty, &totblocks, &nextents, &dblkmap,
> +				dirty, totblocks, nextents, dblkmap,
>  				XFS_DATA_FORK, 0);
>  			break;
>  		case XFS_DINODE_FMT_BTREE:
>  			err = process_btinode(mp, agno, ino, dino, type,
> -				dirty, &totblocks, &nextents, &dblkmap,
> +				dirty, totblocks, nextents, dblkmap,
>  				XFS_DATA_FORK, 0);
>  			break;
>  		case XFS_DINODE_FMT_DEV:	/* fall through */
> -		case XFS_DINODE_FMT_UUID:
>  			err = 0;
>  			break;
>  		default:
>  			do_error(_("unknown format %d, ino %llu (mode = %d)\n"),
>  				dinoc->di_format, lino,
> -				INT_GET(dinoc->di_mode, ARCH_CONVERT));
> +				be16_to_cpu(dinoc->di_mode));
>  		}
>  
> -		if (no_modify && err != 0)  {
> -			*cleared = 1;
> -			*used = is_free;
> -			*isa_dir = 0;
> -			blkmap_free(dblkmap);
> -			return(1);
> -		}
> +		if (no_modify && err != 0)
> +			return 1;
>  
>  		ASSERT(err == 0);
>  	}
> +	return 0;
> +}
>  
> -	/*
> -	 * check attribute fork if necessary.  attributes are
> -	 * always stored in the regular filesystem.
> -	 */
> +/*
> + * Process extended attribute fork in inode
> + */
> +static int
> +process_inode_attr_fork(
> +	xfs_mount_t	*mp,
> +	xfs_agnumber_t	agno,
> +	xfs_agino_t	ino,
> +	xfs_dinode_t	*dino,
> +	int		type,
> +	int		*dirty,
> +	xfs_drfsbno_t	*atotblocks,
> +	__uint64_t	*anextents,
> +	int		check_dups,
> +	int		extra_attr_check,
> +	int		*retval)
> +{
> +	xfs_dinode_core_t *dinoc = &dino->di_core;
> +	xfs_ino_t	lino = XFS_AGINO_TO_INO(mp, agno, ino);
> +	blkmap_t	*ablkmap = NULL;
> +	int		repair = 0;
> +	int		err;
> +
> +	if (!XFS_DFORK_Q(dino)) {
> +		*anextents = 0;
> +		if (dinoc->di_aformat != XFS_DINODE_FMT_EXTENTS) {
> +			do_warn(_("bad attribute format %d in inode %llu, "),
> +				dinoc->di_aformat, lino);
> +			if (!no_modify) {
> +				do_warn(_("resetting value\n"));
> +				dinoc->di_aformat = XFS_DINODE_FMT_EXTENTS;
> +				*dirty = 1;
> +			} else
> +				do_warn(_("would reset value\n"));
> +		}
> +		return 0;
> +	}
>  
> -	if (!XFS_DFORK_Q(dino) &&
> -	    dinoc->di_aformat != XFS_DINODE_FMT_EXTENTS) {
> -		do_warn(_("bad attribute format %d in inode %llu, "),
> -			dinoc->di_aformat, lino);
> -		if (!no_modify) {
> -			do_warn(_("resetting value\n"));
> -			dinoc->di_aformat = XFS_DINODE_FMT_EXTENTS;
> -			*dirty = 1;
> -		} else
> -			do_warn(_("would reset value\n"));
> -		anextents = 0;
> -	} else if (XFS_DFORK_Q(dino)) {
> +	*anextents = be32_to_cpu(dinoc->di_anextents);
> +	if (*anextents > be64_to_cpu(dinoc->di_nblocks) ||
> +			*anextents > XFS_MAX_INCORE_EXTENTS)
> +		*anextents = 1;
> +
> +	switch (dinoc->di_aformat) {
> +	case XFS_DINODE_FMT_LOCAL:
> +		*anextents = 0;
> +		err = process_lclinode(mp, agno, ino, dino, type, dirty,
> +				atotblocks, anextents, &ablkmap,
> +				XFS_ATTR_FORK, check_dups);
> +		break;
> +	case XFS_DINODE_FMT_EXTENTS:
> +		ablkmap = blkmap_alloc(*anextents);
> +		*anextents = 0;
> +		err = process_exinode(mp, agno, ino, dino, type, dirty,
> +				atotblocks, anextents, &ablkmap,
> +				XFS_ATTR_FORK, check_dups);
> +		break;
> +	case XFS_DINODE_FMT_BTREE:
> +		ablkmap = blkmap_alloc(*anextents);
> +		*anextents = 0;
> +		err = process_btinode(mp, agno, ino, dino, type, dirty,
> +				atotblocks, anextents, &ablkmap,
> +				XFS_ATTR_FORK, check_dups);
> +		break;
> +	default:
> +		do_warn(_("illegal attribute format %d, ino %llu\n"),
> +				dinoc->di_aformat, lino);
> +		err = 1;
> +		break;
> +	}
> +
> +	if (err) {
> +		/*
> +		 * clear the attribute fork if necessary.  we can't
> +		 * clear the inode because we've already put the
> +		 * inode space info into the blockmap.
> +		 *
> +		 * XXX - put the inode onto the "move it" list and
> +		 *	log the the attribute scrubbing
> +		 */
> +		do_warn(_("bad attribute fork in inode %llu"), lino);
> +
> +		if (!no_modify)  {
> +			if (delete_attr_ok)  {
> +				do_warn(_(", clearing attr fork\n"));
> +				*dirty += clear_dinode_attr(mp, dino, lino);
> +				dinoc->di_aformat = XFS_DINODE_FMT_LOCAL;
> +			} else  {
> +				do_warn("\n");
> +				*dirty += clear_dinode(mp, dino, lino);
> +			}
> +			ASSERT(*dirty > 0);
> +		} else  {
> +			do_warn(_(", would clear attr fork\n"));
> +		}
> +
> +		*atotblocks = 0;
> +		*anextents = 0;
> +		blkmap_free(ablkmap);
> +		*retval = 1;
> +
> +		return delete_attr_ok ? 0 : 1;
> +	}
> +
> +	if (check_dups)  {
>  		switch (dinoc->di_aformat) {
>  		case XFS_DINODE_FMT_LOCAL:
> -			anextents = 0;
>  			err = process_lclinode(mp, agno, ino, dino,
> -				type, dirty, &atotblocks, &anextents, &ablkmap,
> -				XFS_ATTR_FORK, check_dups);
> +				type, dirty, atotblocks, anextents,
> +				&ablkmap, XFS_ATTR_FORK, 0);
>  			break;
>  		case XFS_DINODE_FMT_EXTENTS:
> -			ablkmap = blkmap_alloc(anextents);
> -			anextents = 0;
>  			err = process_exinode(mp, agno, ino, dino,
> -				type, dirty, &atotblocks, &anextents, &ablkmap,
> -				XFS_ATTR_FORK, check_dups);
> +				type, dirty, atotblocks, anextents,
> +				&ablkmap, XFS_ATTR_FORK, 0);
>  			break;
>  		case XFS_DINODE_FMT_BTREE:
> -			ablkmap = blkmap_alloc(anextents);
> -			anextents = 0;
>  			err = process_btinode(mp, agno, ino, dino,
> -				type, dirty, &atotblocks, &anextents, &ablkmap,
> -				XFS_ATTR_FORK, check_dups);
> +				type, dirty, atotblocks, anextents,
> +				&ablkmap, XFS_ATTR_FORK, 0);
>  			break;
>  		default:
> -			anextents = 0;
> -			do_warn(_("illegal attribute format %d, ino %llu\n"),
> -					dinoc->di_aformat, lino);
> -			err = 1;
> -			break;
> +			do_error(_("illegal attribute fmt %d, ino %llu\n"),
> +				dinoc->di_aformat, lino);
>  		}
>  
> -		if (err)  {
> -			/*
> -			 * clear the attribute fork if necessary.  we can't
> -			 * clear the inode because we've already put the
> -			 * inode space info into the blockmap.
> -			 *
> -			 * XXX - put the inode onto the "move it" list and
> -			 *	log the the attribute scrubbing
> -			 */
> -			do_warn(_("bad attribute fork in inode %llu"), lino);
> +		if (no_modify && err != 0) {
> +			blkmap_free(ablkmap);
> +			return 1;
> +		}
> +
> +		ASSERT(err == 0);
> +	}
> +
> +	/*
> +	 * do attribute semantic-based consistency checks now
> +	 */
>  
> +	/* get this only in phase 3, not in both phase 3 and 4 */
> +	if (extra_attr_check &&
> +			process_attributes(mp, lino, dino, ablkmap, &repair)) {
> +		do_warn(_("problem with attribute contents in inode %llu\n"),
> +			lino);
> +		if (!repair) {
> +			/* clear attributes if not done already */
>  			if (!no_modify)  {
> -				if (delete_attr_ok)  {
> -					do_warn(_(", clearing attr fork\n"));
> -					*dirty += clear_dinode_attr(mp,
> -							dino, lino);
> -				} else  {
> -					do_warn("\n");
> -					*dirty += clear_dinode(mp,
> -							dino, lino);
> -				}
> -				ASSERT(*dirty > 0);
> +				*dirty += clear_dinode_attr(mp, dino, lino);
> +				dinoc->di_aformat = XFS_DINODE_FMT_LOCAL;
>  			} else  {
> -				do_warn(_(", would clear attr fork\n"));
> +				do_warn(_("would clear attr fork\n"));
>  			}
> +			*atotblocks = 0;
> +			*anextents = 0;
> +		}
> +		else {
> +			*dirty = 1; /* it's been repaired */
> +		}
> +	}
> +	blkmap_free(ablkmap);
> +	return 0;
> +}
>  
> -			atotblocks = 0;
> -			anextents = 0;
> +/*
> + * check nlinks feature, if it's a version 1 inode,
> + * just leave nlinks alone.  even if it's set wrong,
> + * it'll be reset when read in.
> + */
>  
> -			if (delete_attr_ok)  {
> -				if (!no_modify)
> -					dinoc->di_aformat = XFS_DINODE_FMT_LOCAL;
> +static int
> +process_check_inode_nlink_version(
> +	xfs_dinode_core_t *dinoc,
> +	xfs_ino_t	lino)
> +{
> +	int		dirty = 0;
> +
> +	if (dinoc->di_version > XFS_DINODE_VERSION_1 && !fs_inode_nlink)  {
> +		/*
> +		 * do we have a fs/inode version mismatch with a valid
> +		 * version 2 inode here that has to stay version 2 or
> +		 * lose links?
> +		 */
> +		if (be32_to_cpu(dinoc->di_nlink) > XFS_MAXLINK_1)  {
> +			/*
> +			 * yes.  are nlink inodes allowed?
> +			 */
> +			if (fs_inode_nlink_allowed)  {
> +				/*
> +				 * yes, update status variable which will
> +				 * cause sb to be updated later.
> +				 */
> +				fs_inode_nlink = 1;
> +				do_warn(_("version 2 inode %llu claims > %u links, "),
> +					lino, XFS_MAXLINK_1);
> +				if (!no_modify)  {
> +					do_warn(_("updating superblock "
> +						"version number\n"));
> +				} else  {
> +					do_warn(_("would update superblock "
> +						"version number\n"));
> +				}
>  			} else  {
> -				*cleared = 1;
> -				*used = is_free;
> -				*isa_dir = 0;
> -				blkmap_free(dblkmap);
> -				blkmap_free(ablkmap);
> +				/*
> +				 * no, have to convert back to onlinks
> +				 * even if we lose some links
> +				 */
> +				do_warn(_("WARNING:  version 2 inode %llu "
> +					"claims > %u links, "),
> +					lino, XFS_MAXLINK_1);
> +				if (!no_modify)  {
> +					do_warn(_("converting back to version 1,\n"
> +						"this may destroy %d links\n"),
> +						be32_to_cpu(dinoc->di_nlink) -
> +							XFS_MAXLINK_1);
> +
> +					dinoc->di_version = XFS_DINODE_VERSION_1;
> +					dinoc->di_nlink = cpu_to_be32(XFS_MAXLINK_1);
> +					dinoc->di_onlink = cpu_to_be16(XFS_MAXLINK_1);
> +					dirty = 1;
> +				} else  {
> +					do_warn(_("would convert back to version 1,\n"
> +						"\tthis might destroy %d links\n"),
> +						be32_to_cpu(dinoc->di_nlink) -
> +							XFS_MAXLINK_1);
> +				}
>  			}
> -			return(1);
> +		} else  {
> +			/*
> +			 * do we have a v2 inode that we could convert back
> +			 * to v1 without losing any links?  if we do and
> +			 * we have a mismatch between superblock bits and the
> +			 * version bit, alter the version bit in this case.
> +			 *
> +			 * the case where we lost links was handled above.
> +			 */
> +			do_warn(_("found version 2 inode %llu, "), lino);
> +			if (!no_modify)  {
> +				do_warn(_("converting back to version 1\n"));
> +				dinoc->di_version = XFS_DINODE_VERSION_1;
> +				dinoc->di_onlink = cpu_to_be16(
> +					be32_to_cpu(dinoc->di_nlink));
> +				dirty = 1;
> +			} else  {
> +				do_warn(_("would convert back to version 1\n"));
> +			}
> +		}
> +	}
>  
> -		} else if (check_dups)  {
> -			switch (dinoc->di_aformat) {
> -			case XFS_DINODE_FMT_LOCAL:
> -				err = process_lclinode(mp, agno, ino, dino,
> -					type, dirty, &atotblocks, &anextents,
> -					&ablkmap, XFS_ATTR_FORK, 0);
> -				break;
> -			case XFS_DINODE_FMT_EXTENTS:
> -				err = process_exinode(mp, agno, ino, dino,
> -					type, dirty, &atotblocks, &anextents,
> -					&ablkmap, XFS_ATTR_FORK, 0);
> -				break;
> -			case XFS_DINODE_FMT_BTREE:
> -				err = process_btinode(mp, agno, ino, dino,
> -					type, dirty, &atotblocks, &anextents,
> -					&ablkmap, XFS_ATTR_FORK, 0);
> -				break;
> -			default:
> -				do_error(
> -				_("illegal attribute fmt %d, ino %llu\n"),
> -					dinoc->di_aformat, lino);
> +	/*
> +	 * ok, if it's still a version 2 inode, it's going
> +	 * to stay a version 2 inode.  it should have a zero
> +	 * onlink field, so clear it.
> +	 */
> +	if (dinoc->di_version > XFS_DINODE_VERSION_1 &&
> +			dinoc->di_onlink != 0 && fs_inode_nlink > 0) {
> +		if (!no_modify) {
> +			do_warn(_("clearing obsolete nlink field in "
> +				"version 2 inode %llu, was %d, now 0\n"),
> +				lino, be16_to_cpu(dinoc->di_onlink));
> +			dinoc->di_onlink = 0;
> +			dirty = 1;
> +		} else  {
> +			do_warn(_("would clear obsolete nlink field in "
> +				"version 2 inode %llu, currently %d\n"),
> +				lino, be16_to_cpu(dinoc->di_onlink));
> +		}
> +	}
> +	return dirty;
> +}
> +
> +/*
> + * returns 0 if the inode is ok, 1 if the inode is corrupt
> + * check_dups can be set to 1 *only* when called by the
> + * first pass of the duplicate block checking of phase 4.
> + * *dirty is set > 0 if the dinode has been altered and
> + * needs to be written out.
> + *
> + * for detailed, info, look at process_dinode() comments.
> + */
> +/* ARGSUSED */
> +int
> +process_dinode_int(xfs_mount_t *mp,
> +		xfs_dinode_t *dino,
> +		xfs_agnumber_t agno,
> +		xfs_agino_t ino,
> +		int was_free,		/* 1 if inode is currently free */
> +		int *dirty,		/* out == > 0 if inode is now dirty */
> +		int *used,		/* out == 1 if inode is in use */
> +		int verify_mode,	/* 1 == verify but don't modify inode */
> +		int uncertain,		/* 1 == inode is uncertain */
> +		int ino_discovery,	/* 1 == check dirs for unknown inodes */
> +		int check_dups,		/* 1 == check if inode claims
> +					 * duplicate blocks		*/
> +		int extra_attr_check, /* 1 == do attribute format and value checks */
> +		int *isa_dir,		/* out == 1 if inode is a directory */
> +		xfs_ino_t *parent)	/* out -- parent if ino is a dir */
> +{
> +	xfs_drfsbno_t		totblocks = 0;
> +	xfs_drfsbno_t		atotblocks = 0;
> +	xfs_dinode_core_t	*dinoc;
> +	int			di_mode;
> +	int			type;
> +	int			retval = 0;
> +	__uint64_t		nextents;
> +	__uint64_t		anextents;
> +	xfs_ino_t		lino;
> +	const int		is_free = 0;
> +	const int		is_used = 1;
> +	blkmap_t		*dblkmap = NULL;
> +
> +	*dirty = *isa_dir = 0;
> +	*used = is_used;
> +	type = XR_INO_UNKNOWN;
> +
> +	dinoc = &dino->di_core;
> +	lino = XFS_AGINO_TO_INO(mp, agno, ino);
> +	di_mode = be16_to_cpu(dinoc->di_mode);
> +
> +	/*
> +	 * if in verify mode, don't modify the inode.
> +	 *
> +	 * if correcting, reset stuff that has known values
> +	 *
> +	 * if in uncertain mode, be silent on errors since we're
> +	 * trying to find out if these are inodes as opposed
> +	 * to assuming that they are.  Just return the appropriate
> +	 * return code in that case.
> +	 *
> +	 * If uncertain is set, verify_mode MUST be set.
> +	 */
> +	ASSERT(uncertain == 0 || verify_mode != 0);
> +
> +	if (be16_to_cpu(dinoc->di_magic) != XFS_DINODE_MAGIC)  {
> +		retval = 1;
> +		if (!uncertain)
> +			do_warn(_("bad magic number 0x%x on inode %llu\n"),
> +				be16_to_cpu(dinoc->di_magic), lino);
> +		if (!verify_mode) {
> +			if (!no_modify)  {
> +				do_warn(_("resetting magic number\n"));
> +				dinoc->di_magic = cpu_to_be16(XFS_DINODE_MAGIC);
> +				*dirty = 1;
> +			} else
> +				do_warn(_("would reset magic number\n"));
> +		}
> +	}
> +
> +	if (!XFS_DINODE_GOOD_VERSION(dinoc->di_version) ||
> +	    (!fs_inode_nlink && dinoc->di_version > XFS_DINODE_VERSION_1))  {
> +		retval = 1;
> +		if (!uncertain)
> +			do_warn(_("bad version number 0x%x on inode %llu, "),
> +				dinoc->di_version, lino);
> +		if (!verify_mode) {
> +			if (!no_modify) {
> +				do_warn(_("resetting version number\n"));
> +				dinoc->di_version = (fs_inode_nlink) ?
> +					XFS_DINODE_VERSION_2 :
> +					XFS_DINODE_VERSION_1;
> +				*dirty = 1;
> +			} else
> +				do_warn(_("would reset version number\n"));
> +		}
> +	}
> +
> +	/*
> +	 * blow out of here if the inode size is < 0
> +	 */
> +	if ((xfs_fsize_t)be64_to_cpu(dinoc->di_size) < 0)  {
> +		if (!uncertain)
> +			do_warn(_("bad (negative) size %lld on inode %llu\n"),
> +				be64_to_cpu(dinoc->di_size), lino);
> +		if (verify_mode)
> +			return 1;
> +		goto clear_bad_out;
> +	}
> +
> +	/*
> +	 * if not in verify mode, check to sii if the inode and imap
> +	 * agree that the inode is free
> +	 */
> +	if (!verify_mode && di_mode == 0) {
> +		/*
> +		 * was_free value is not meaningful if we're in verify mode
> +		 */
> +		if (was_free) {
> +			/*
> +			 * easy case, inode free -- inode and map agree, clear
> +			 * it just in case to ensure that format, etc. are
> +			 * set correctly
> +			 */
> +			if (!no_modify)
> +				*dirty += clear_dinode(mp, dino, lino);
> +			*used = is_free;
> +			return 0;
> +		}
> +		/*
> +		 * the inode looks free but the map says it's in use.
> +		 * clear the inode just to be safe and mark the inode
> +		 * free.
> +		 */
> +		do_warn(_("imap claims a free inode %llu is in use, "), lino);
> +		if (!no_modify)  {
> +			do_warn(_("correcting imap and clearing inode\n"));
> +			if (clear_dinode(mp, dino, lino))  {
> +				retval = 1;
> +				*dirty = 1;
>  			}
> +		} else
> +			do_warn(_("would correct imap and clear inode\n"));
> +		*used = is_free;
> +		return retval;
> +	}
>  
> -			if (no_modify && err != 0)  {
> -				*cleared = 1;
> -				*used = is_free;
> -				*isa_dir = 0;
> -				blkmap_free(dblkmap);
> -				blkmap_free(ablkmap);
> -				return(1);
> -			}
> +	/*
> +	 * because of the lack of any write ordering guarantee, it's
> +	 * possible that the core got updated but the forks didn't.
> +	 * so rather than be ambitious (and probably incorrect),
> +	 * if there's an inconsistency, we get conservative and
> +	 * just pitch the file.  blow off checking formats of
> +	 * free inodes since technically any format is legal
> +	 * as we reset the inode when we re-use it.
> +	 */
> +	if (di_mode != 0 && check_dinode_mode_format(dinoc) != 0) {
> +		if (!uncertain)
> +			do_warn(_("bad inode format in inode %llu\n"), lino);
> +		if (verify_mode)
> +			return 1;
> +		goto clear_bad_out;
> +	}
>  
> -			ASSERT(err == 0);
> -		}
> +	if (verify_mode)
> +		return retval;
>  
> -		/*
> -		 * do attribute semantic-based consistency checks now
> -		 */
> +	/*
> +	 * clear the next unlinked field if necessary on a good
> +	 * inode only during phase 4 -- when checking for inodes
> +	 * referencing duplicate blocks.  then it's safe because
> +	 * we've done the inode discovery and have found all the inodes
> +	 * we're going to find.  check_dups is set to 1 only during
> +	 * phase 4.  Ugly.
> +	 */
> +	if (check_dups && !no_modify)
> +		*dirty += clear_dinode_unlinked(mp, dino);
>  
> -		/* get this only in phase 3, not in both phase 3 and 4 */
> -		if (extra_attr_check) {
> -		    if ((err = process_attributes(mp, lino, dino, ablkmap,
> -				    &repair))) {
> -			    do_warn(
> -		_("problem with attribute contents in inode %llu\n"), lino);
> -			    if(!repair) {
> -				    /* clear attributes if not done already */
> -				    if (!no_modify)  {
> -					    *dirty += clear_dinode_attr(
> -							mp, dino, lino);
> -					    dinoc->di_aformat =
> -						XFS_DINODE_FMT_LOCAL;
> -				    } else  {
> -					    do_warn(
> -					_("would clear attr fork\n"));
> -				    }
> -				    atotblocks = 0;
> -				    anextents = 0;
> -			    }
> -			    else {
> -				    *dirty = 1; /* it's been repaired */
> -			     }
> -		    }
> -		}
> -		blkmap_free(ablkmap);
> +	/* set type and map type info */
>  
> -	} else
> -		anextents = 0;
> +	switch (di_mode & S_IFMT) {
> +	case S_IFDIR:
> +		type = XR_INO_DIR;
> +		*isa_dir = 1;
> +		break;
> +	case S_IFREG:
> +		if (be16_to_cpu(dinoc->di_flags) & XFS_DIFLAG_REALTIME)
> +			type = XR_INO_RTDATA;
> +		else if (lino == mp->m_sb.sb_rbmino)
> +			type = XR_INO_RTBITMAP;
> +		else if (lino == mp->m_sb.sb_rsumino)
> +			type = XR_INO_RTSUM;
> +		else
> +			type = XR_INO_DATA;
> +		break;
> +	case S_IFLNK:
> +		type = XR_INO_SYMLINK;
> +		break;
> +	case S_IFCHR:
> +		type = XR_INO_CHRDEV;
> +		break;
> +	case S_IFBLK:
> +		type = XR_INO_BLKDEV;
> +		break;
> +	case S_IFSOCK:
> +		type = XR_INO_SOCK;
> +		break;
> +	case S_IFIFO:
> +		type = XR_INO_FIFO;
> +		break;
> +	default:
> +		do_warn(_("bad inode type %#o inode %llu\n"),
> +				di_mode & S_IFMT, lino);
> +		goto clear_bad_out;
> +	}
>  
>  	/*
> -	* enforce totblocks is 0 for misc types
> -	*/
> -	if (process_misc_ino_types_blocks(totblocks, lino, type)) {
> -		if (!no_modify)  {
> -			*dirty += clear_dinode(mp, dino, lino);
> -			ASSERT(*dirty > 0);
> -		}
> -		*cleared = 1;
> -		*used = is_free;
> -		*isa_dir = 0;
> -		blkmap_free(dblkmap);
> -		return(1);
> -	}
> +	 * type checks for superblock inodes
> +	 */
> +	if (process_check_sb_inodes(mp, dinoc, lino, &type, dirty) != 0)
> +		goto clear_bad_out;
>  
>  	/*
> -	 * correct space counters if required
> +	 * only regular files with REALTIME or EXTSIZE flags set can have
> +	 * extsize set, or directories with EXTSZINHERIT.
>  	 */
> -	if (totblocks + atotblocks != INT_GET(dinoc->di_nblocks, ARCH_CONVERT))  {
> -		if (!no_modify)  {
> -			do_warn(
> -	_("correcting nblocks for inode %llu, was %llu - counted %llu\n"),
> -				lino, INT_GET(dinoc->di_nblocks, ARCH_CONVERT),
> -				totblocks + atotblocks);
> -			*dirty = 1;
> -			INT_SET(dinoc->di_nblocks, ARCH_CONVERT, totblocks + atotblocks);
> -		} else  {
> -			do_warn(
> -	_("bad nblocks %llu for inode %llu, would reset to %llu\n"),
> -				INT_GET(dinoc->di_nblocks, ARCH_CONVERT), lino,
> -				totblocks + atotblocks);
> +	if (dinoc->di_extsize != 0) {
> +		if ((type == XR_INO_RTDATA) ||
> +		    (type == XR_INO_DIR && (be16_to_cpu(dinoc->di_flags) &
> +					XFS_DIFLAG_EXTSZINHERIT)) ||
> +		    (type == XR_INO_DATA && (be16_to_cpu(dinoc->di_flags) &
> +				 XFS_DIFLAG_EXTSIZE)))  {
> +			/* s'okay */ ;
> +		} else {
> +			do_warn(_("bad non-zero extent size %u for "
> +					"non-realtime/extsize inode %llu, "),
> +					be32_to_cpu(dinoc->di_extsize), lino);
> +			if (!no_modify)  {
> +				do_warn(_("resetting to zero\n"));
> +				dinoc->di_extsize = 0;
> +				*dirty = 1;
> +			} else
> +				do_warn(_("would reset to zero\n"));
>  		}
>  	}
>  
> -	if (nextents > MAXEXTNUM)  {
> -		do_warn(_("too many data fork extents (%llu) in inode %llu\n"),
> -			nextents, lino);
> +	/*
> +	 * general size/consistency checks:
> +	 */
> +	if (process_check_inode_sizes(mp, dino, lino, type) != 0)
> +		goto clear_bad_out;
>  
> -		if (!no_modify)  {
> -			*dirty += clear_dinode(mp, dino, lino);
> -			ASSERT(*dirty > 0);
> -		}
> -		*cleared = 1;
> -		*used = is_free;
> -		*isa_dir = 0;
> -		blkmap_free(dblkmap);
> +	/*
> +	 * check for illegal values of forkoff
> +	 */
> +	if (process_check_inode_forkoff(mp, dinoc, lino) != 0)
> +		goto clear_bad_out;
>  
> -		return(1);
> -	}
> -	if (nextents != INT_GET(dinoc->di_nextents, ARCH_CONVERT))  {
> -		if (!no_modify)  {
> -			do_warn(
> -	_("correcting nextents for inode %llu, was %d - counted %llu\n"),
> -				lino, INT_GET(dinoc->di_nextents, ARCH_CONVERT),
> -				nextents);
> -			*dirty = 1;
> -			INT_SET(dinoc->di_nextents, ARCH_CONVERT,
> -				(xfs_extnum_t) nextents);
> -		} else  {
> -			do_warn(
> -		_("bad nextents %d for inode %llu, would reset to %llu\n"),
> -				INT_GET(dinoc->di_nextents, ARCH_CONVERT),
> -				lino, nextents);
> -		}
> -	}
> +	/*
> +	 * check data fork -- if it's bad, clear the inode
> +	 */
> +	if (process_inode_data_fork(mp, agno, ino, dino, type, dirty,
> +			&totblocks, &nextents, &dblkmap, check_dups) != 0)
> +		goto bad_out;
>  
> -	if (anextents > MAXAEXTNUM)  {
> -		do_warn(_("too many attr fork extents (%llu) in inode %llu\n"),
> -			anextents, lino);
> +	/*
> +	 * check attribute fork if necessary.  attributes are
> +	 * always stored in the regular filesystem.
> +	 */
> +	if (process_inode_attr_fork(mp, agno, ino, dino, type, dirty,
> +			&atotblocks, &anextents, check_dups, extra_attr_check,
> +			&retval))
> +		goto bad_out;
>  
> -		if (!no_modify)  {
> -			*dirty += clear_dinode(mp, dino, lino);
> -			ASSERT(*dirty > 0);
> -		}
> -		*cleared = 1;
> -		*used = is_free;
> -		*isa_dir = 0;
> -		blkmap_free(dblkmap);
> -		return(1);
> -	}
> -	if (anextents != INT_GET(dinoc->di_anextents, ARCH_CONVERT))  {
> -		if (!no_modify)  {
> -			do_warn(
> -	_("correcting anextents for inode %llu, was %d - counted %llu\n"),
> -				lino,
> -				INT_GET(dinoc->di_anextents, ARCH_CONVERT),
> -				anextents);
> -			*dirty = 1;
> -			INT_SET(dinoc->di_anextents, ARCH_CONVERT,
> -				(xfs_aextnum_t) anextents);
> -		} else  {
> -			do_warn(
> -		_("bad anextents %d for inode %llu, would reset to %llu\n"),
> -				INT_GET(dinoc->di_anextents, ARCH_CONVERT),
> -				lino, anextents);
> -		}
> -	}
> +	/*
> +	 * enforce totblocks is 0 for misc types
> +	 */
> +	if (process_misc_ino_types_blocks(totblocks, lino, type))
> +		goto clear_bad_out;
> +
> +	/*
> +	 * correct space counters if required
> +	 */
> +	if (process_inode_blocks_and_extents(dinoc, totblocks + atotblocks,
> +			nextents, anextents, lino, dirty) != 0)
> +		goto clear_bad_out;
>  
>  	/*
>  	 * do any semantic type-based checking here
>  	 */
>  	switch (type)  {
>  	case XR_INO_DIR:
> -		if (XFS_SB_VERSION_HASDIRV2(&mp->m_sb))
> -			err = process_dir2(mp, lino, dino, ino_discovery,
> -					dirty, "", parent, dblkmap);
> -		else
> -			err = process_dir(mp, lino, dino, ino_discovery,
> -					dirty, "", parent, dblkmap);
> -		if (err)
> -			do_warn(
> -			_("problem with directory contents in inode %llu\n"),
> -				lino);
> -		break;
> -	case XR_INO_RTBITMAP:
> -		/* process_rtbitmap XXX */
> -		err = 0;
> -		break;
> -	case XR_INO_RTSUM:
> -		/* process_rtsummary XXX */
> -		err = 0;
> +		if (process_dir2(mp, lino, dino, ino_discovery, dirty, "",
> +				parent, dblkmap) != 0) {
> +			do_warn(_("problem with directory contents in "
> +				"inode %llu\n"), lino);
> +			goto clear_bad_out;
> +		}
>  		break;
>  	case XR_INO_SYMLINK:
> -		if ((err = process_symlink(mp, lino, dino, dblkmap)))
> +		if (process_symlink(mp, lino, dino, dblkmap) != 0) {
>  			do_warn(_("problem with symbolic link in inode %llu\n"),
>  				lino);
> -		break;
> -	case XR_INO_DATA:	/* fall through to FIFO case ... */
> -	case XR_INO_RTDATA:	/* fall through to FIFO case ... */
> -	case XR_INO_CHRDEV:	/* fall through to FIFO case ... */
> -	case XR_INO_BLKDEV:	/* fall through to FIFO case ... */
> -	case XR_INO_SOCK:	/* fall through to FIFO case ... */
> -	case XR_INO_FIFO:
> -		err = 0;
> +			goto clear_bad_out;
> +		}
>  		break;
>  	default:
> -		printf(_("Unexpected inode type\n"));
> -		abort();
> +		break;
>  	}
>  
> -	if (dblkmap)
> -		blkmap_free(dblkmap);
> -
> -	if (err)  {
> -		/*
> -		 * problem in the inode type-specific semantic
> -		 * checking, clear out the inode and get out
> -		 */
> -		if (!no_modify)  {
> -			*dirty += clear_dinode(mp, dino, lino);
> -			ASSERT(*dirty > 0);
> -		}
> -		*cleared = 1;
> -		*used = is_free;
> -		*isa_dir = 0;
> -
> -		return(1);
> -	}
> +	blkmap_free(dblkmap);
>  
>  	/*
>  	 * check nlinks feature, if it's a version 1 inode,
>  	 * just leave nlinks alone.  even if it's set wrong,
>  	 * it'll be reset when read in.
>  	 */
> -	if (dinoc->di_version > XFS_DINODE_VERSION_1 && !fs_inode_nlink)  {
> -		/*
> -		 * do we have a fs/inode version mismatch with a valid
> -		 * version 2 inode here that has to stay version 2 or
> -		 * lose links?
> -		 */
> -		if (INT_GET(dinoc->di_nlink, ARCH_CONVERT) > XFS_MAXLINK_1)  {
> -			/*
> -			 * yes.  are nlink inodes allowed?
> -			 */
> -			if (fs_inode_nlink_allowed)  {
> -				/*
> -				 * yes, update status variable which will
> -				 * cause sb to be updated later.
> -				 */
> -				fs_inode_nlink = 1;
> -				do_warn(
> -				_("version 2 inode %llu claims > %u links, "),
> -					lino, XFS_MAXLINK_1);
> -				if (!no_modify)  {
> -					do_warn(
> -			_("updating superblock version number\n"));
> -				} else  {
> -					do_warn(
> -			_("would update superblock version number\n"));
> -				}
> -			} else  {
> -				/*
> -				 * no, have to convert back to onlinks
> -				 * even if we lose some links
> -				 */
> -				do_warn(
> -			_("WARNING:  version 2 inode %llu claims > %u links, "),
> -					lino, XFS_MAXLINK_1);
> -				if (!no_modify)  {
> -					do_warn(
> -	_("converting back to version 1,\n\tthis may destroy %d links\n"),
> -						INT_GET(dinoc->di_nlink,
> -							ARCH_CONVERT)
> -						- XFS_MAXLINK_1);
> -
> -					dinoc->di_version =
> -						XFS_DINODE_VERSION_1;
> -					INT_SET(dinoc->di_nlink, ARCH_CONVERT,
> -						XFS_MAXLINK_1);
> -					INT_SET(dinoc->di_onlink, ARCH_CONVERT,
> -						XFS_MAXLINK_1);
> -
> -					*dirty = 1;
> -				} else  {
> -					do_warn(
> -	_("would convert back to version 1,\n\tthis might destroy %d links\n"),
> -						INT_GET(dinoc->di_nlink,
> -							ARCH_CONVERT)
> -						- XFS_MAXLINK_1);
> -				}
> -			}
> -		} else  {
> -			/*
> -			 * do we have a v2 inode that we could convert back
> -			 * to v1 without losing any links?  if we do and
> -			 * we have a mismatch between superblock bits and the
> -			 * version bit, alter the version bit in this case.
> -			 *
> -			 * the case where we lost links was handled above.
> -			 */
> -			do_warn(_("found version 2 inode %llu, "), lino);
> -			if (!no_modify)  {
> -				do_warn(_("converting back to version 1\n"));
> -
> -				dinoc->di_version =
> -					XFS_DINODE_VERSION_1;
> -				INT_SET(dinoc->di_onlink, ARCH_CONVERT,
> -					INT_GET(dinoc->di_nlink, ARCH_CONVERT));
> -
> -				*dirty = 1;
> -			} else  {
> -				do_warn(_("would convert back to version 1\n"));
> -			}
> -		}
> -	}
> +	*dirty = process_check_inode_nlink_version(dinoc, lino);
>  
> -	/*
> -	 * ok, if it's still a version 2 inode, it's going
> -	 * to stay a version 2 inode.  it should have a zero
> -	 * onlink field, so clear it.
> -	 */
> -	if (dinoc->di_version > XFS_DINODE_VERSION_1 &&
> -	    INT_GET(dinoc->di_onlink, ARCH_CONVERT) > 0 &&
> -	    fs_inode_nlink > 0)  {
> -		if (!no_modify)  {
> -			do_warn(
> -_("clearing obsolete nlink field in version 2 inode %llu, was %d, now 0\n"),
> -				lino, INT_GET(dinoc->di_onlink, ARCH_CONVERT));
> -			dinoc->di_onlink = 0;
> -			*dirty = 1;
> -		} else  {
> -			do_warn(
> -_("would clear obsolete nlink field in version 2 inode %llu, currently %d\n"),
> -				lino, INT_GET(dinoc->di_onlink, ARCH_CONVERT));
> -			*dirty = 1;
> -		}
> -	}
> +	return retval;
>  
> -	return(retval > 0 ? 1 : 0);
> +clear_bad_out:
> +	if (!no_modify)  {
> +		*dirty += clear_dinode(mp, dino, lino);
> +		ASSERT(*dirty > 0);
> +	}
> +bad_out:
> +	*used = is_free;
> +	*isa_dir = 0;
> +	blkmap_free(dblkmap);
> +	return 1;
>  }
>  
>  /*
> @@ -2983,8 +2831,6 @@ _("would clear obsolete nlink field in v
>   *				claimed blocks using the bitmap.
>   *	Outs:
>   *		dirty -- whether we changed the inode (1 == yes)
> - *		cleared -- whether we cleared the inode (1 == yes).  In
> - *				no modify mode, if we would have cleared it
>   *		used -- 1 if the inode is used, 0 if free.  In no modify
>   *			mode, whether the inode should be used or free
>   *		isa_dir -- 1 if the inode is a directory, 0 if not.  In
> @@ -2994,30 +2840,29 @@ _("would clear obsolete nlink field in v
>   */
>  
>  int
> -process_dinode(xfs_mount_t *mp,
> -		xfs_dinode_t *dino,
> -		xfs_agnumber_t agno,
> -		xfs_agino_t ino,
> -		int was_free,
> -		int *dirty,
> -		int *cleared,
> -		int *used,
> -		int ino_discovery,
> -		int check_dups,
> -		int extra_attr_check,
> -		int *isa_dir,
> -		xfs_ino_t *parent)
> +process_dinode(
> +	xfs_mount_t	*mp,
> +	xfs_dinode_t	*dino,
> +	xfs_agnumber_t	agno,
> +	xfs_agino_t	ino,
> +	int		was_free,
> +	int		*dirty,
> +	int		*used,
> +	int		ino_discovery,
> +	int		check_dups,
> +	int		extra_attr_check,
> +	int		*isa_dir,
> +	xfs_ino_t	*parent)
>  {
> -	const int verify_mode = 0;
> -	const int uncertain = 0;
> +	const int	verify_mode = 0;
> +	const int	uncertain = 0;
>  
>  #ifdef XR_INODE_TRACE
>  	fprintf(stderr, "processing inode %d/%d\n", agno, ino);
>  #endif
> -	return(process_dinode_int(mp, dino, agno, ino, was_free, dirty,
> -				cleared, used, verify_mode, uncertain,
> -				ino_discovery, check_dups, extra_attr_check,
> -				isa_dir, parent));
> +	return process_dinode_int(mp, dino, agno, ino, was_free, dirty, used,
> +				verify_mode, uncertain, ino_discovery,
> +				check_dups, extra_attr_check, isa_dir, parent);
>  }
>  
>  /*
> @@ -3027,25 +2872,24 @@ process_dinode(xfs_mount_t *mp,
>   * if the inode passes the cursory sanity check, 1 otherwise.
>   */
>  int
> -verify_dinode(xfs_mount_t *mp,
> -		xfs_dinode_t *dino,
> -		xfs_agnumber_t agno,
> -		xfs_agino_t ino)
> -{
> -	xfs_ino_t parent;
> -	int cleared = 0;
> -	int used = 0;
> -	int dirty = 0;
> -	int isa_dir = 0;
> -	const int verify_mode = 1;
> -	const int check_dups = 0;
> -	const int ino_discovery = 0;
> -	const int uncertain = 0;
> -
> -	return(process_dinode_int(mp, dino, agno, ino, 0, &dirty,
> -				&cleared, &used, verify_mode,
> -				uncertain, ino_discovery, check_dups,
> -				0, &isa_dir, &parent));
> +verify_dinode(
> +	xfs_mount_t	*mp,
> +	xfs_dinode_t	*dino,
> +	xfs_agnumber_t	agno,
> +	xfs_agino_t	ino)
> +{
> +	xfs_ino_t	parent;
> +	int		used = 0;
> +	int		dirty = 0;
> +	int		isa_dir = 0;
> +	const int	verify_mode = 1;
> +	const int	check_dups = 0;
> +	const int	ino_discovery = 0;
> +	const int	uncertain = 0;
> +
> +	return process_dinode_int(mp, dino, agno, ino, 0, &dirty, &used,
> +				verify_mode, uncertain, ino_discovery,
> +				check_dups, 0, &isa_dir, &parent);
>  }
>  
>  /*
> @@ -3054,23 +2898,22 @@ verify_dinode(xfs_mount_t *mp,
>   * returns 0 if the inode passes the cursory sanity check, 1 otherwise.
>   */
>  int
> -verify_uncertain_dinode(xfs_mount_t *mp,
> -		xfs_dinode_t *dino,
> -		xfs_agnumber_t agno,
> -		xfs_agino_t ino)
> -{
> -	xfs_ino_t parent;
> -	int cleared = 0;
> -	int used = 0;
> -	int dirty = 0;
> -	int isa_dir = 0;
> -	const int verify_mode = 1;
> -	const int check_dups = 0;
> -	const int ino_discovery = 0;
> -	const int uncertain = 1;
> -
> -	return(process_dinode_int(mp, dino, agno, ino, 0, &dirty,
> -				&cleared, &used, verify_mode,
> -				uncertain, ino_discovery, check_dups,
> -				0, &isa_dir, &parent));
> +verify_uncertain_dinode(
> +	xfs_mount_t	*mp,
> +	xfs_dinode_t	*dino,
> +	xfs_agnumber_t	agno,
> +	xfs_agino_t	ino)
> +{
> +	xfs_ino_t	parent;
> +	int		used = 0;
> +	int		dirty = 0;
> +	int		isa_dir = 0;
> +	const int	verify_mode = 1;
> +	const int	check_dups = 0;
> +	const int	ino_discovery = 0;
> +	const int	uncertain = 1;
> +
> +	return process_dinode_int(mp, dino, agno, ino, 0, &dirty, &used,
> +				verify_mode, uncertain, ino_discovery,
> +				check_dups, 0, &isa_dir, &parent);
>  }
> 
> ===========================================================================
> xfsprogs/repair/dinode.h
> ===========================================================================
> 
> --- a/xfsprogs/repair/dinode.h	2007-11-15 17:24:33.000000000 +1100
> +++ b/xfsprogs/repair/dinode.h	2007-11-15 17:22:18.290409320 +1100
> @@ -84,7 +84,6 @@ process_dinode(xfs_mount_t *mp,
>  		xfs_agino_t ino,
>  		int was_free,
>  		int *dirty,
> -		int *tossit,
>  		int *used,
>  		int check_dirs,
>  		int check_dups,

  parent reply	other threads:[~2008-01-16  1:03 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-11-15  6:40 [REVIEW] Refactor xfs_repair's process_dinode_int Barry Naujok
2007-11-21 15:05 ` Christoph Hellwig
2008-01-16  1:03 ` Michael Nishimoto [this message]
     [not found] <4782B72D.8070208@agami.com>
     [not found] ` <47833C0F.6070206@agami.com>
     [not found]   ` <op.t4m0r1an3jf8g2@pc-bnaujok.melbourne.sgi.com>
     [not found]     ` <478D1899.9080201@agami.com>
2008-01-16  0:51       ` Barry Naujok
2008-01-16  2:33         ` Barry Naujok
2008-01-16  3:10         ` Timothy Shimmin
2008-01-16  4:59           ` David Chinner
2008-01-16  6:32             ` Christoph Hellwig

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=478D57D1.2000600@agami.com \
    --to=miken@agami.com \
    --cc=bnaujok@sgi.com \
    --cc=chandan@agami.com \
    --cc=xfs-dev@sgi.com \
    --cc=xfs@oss.sgi.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox