diff --git a/.gitignore b/.gitignore
index 3f64e93..6a05b6a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/xfsprogs-3.2.1.tar.gz
+SOURCES/xfsprogs-3.2.2.tar.gz
diff --git a/.xfsprogs.metadata b/.xfsprogs.metadata
index 2717604..886193f 100644
--- a/.xfsprogs.metadata
+++ b/.xfsprogs.metadata
@@ -1 +1 @@
-9926245f0dafc0f19fd698a065b5384d5e305926 SOURCES/xfsprogs-3.2.1.tar.gz
+a7f50a73fbac86b185767ed925ed2c7ab047edff SOURCES/xfsprogs-3.2.2.tar.gz
diff --git a/SOURCES/xfsprogs-3.2.1-add-supported-file-attributes-to-xfs.5-manpage.patch b/SOURCES/xfsprogs-3.2.1-add-supported-file-attributes-to-xfs.5-manpage.patch
deleted file mode 100644
index 6c00bfe..0000000
--- a/SOURCES/xfsprogs-3.2.1-add-supported-file-attributes-to-xfs.5-manpage.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-commit 794b62f68063d3af74ab7e79a9d7049887ec3ece
-Author: Eric Sandeen <sandeen@redhat.com>
-Date:   Tue Sep 16 09:23:45 2014 +1000
-
-    xfsprogs: add supported file attributes to xfs.5 manpage
-    
-    The chattr(1) manpage suffers from the same problems mount(1) had:
-    many options listed, not kept up to date for various filesystems.
-    
-    I've submitted a manpage update for chattr(1) which says to refer to
-    filesystem-specific manpages for supported attributes; this patch
-    updates xfs(5) to list the attributes supported by xfs.
-    
-    Signed-off-by: Eric Sandeen <sandeen@redhat.com>
-    Reviewed-by: Dave Chinner <dchinner@redhat.com>
-    Signed-off-by: Dave Chinner <david@fromorbit.com>
-
-diff --git a/man/man5/xfs.5 b/man/man5/xfs.5
-index 5e47c4c..3214455 100644
---- a/man/man5/xfs.5
-+++ b/man/man5/xfs.5
-@@ -1,6 +1,6 @@
- .TH xfs 5
- .SH NAME
--xfs \- layout and mount options for the XFS filesystem
-+xfs \- layout, mount options, and supported file attributes for the XFS filesystem
- .SH DESCRIPTION
- An XFS filesystem can reside on a regular disk partition or on a
- logical volume.
-@@ -302,7 +302,27 @@ namespace is on stable storage. This is useful in HA setups
- where failover must not result in clients seeing
- inconsistent namespace presentation during or after a
- failover event.
-+.SH FILE ATTRIBUTES
-+The XFS filesystem supports setting the following file
-+attributes on Linux systems using the
-+.BR chattr (1)
-+utility:
-+.sp
-+.BR a " - append only"
-+.sp
-+.BR A " - no atime updates"
-+.sp
-+.BR d " - no dump"
-+.sp
-+.BR i " - immutable"
-+.sp
-+.BR S " - synchronous updates"
-+.sp
-+For descriptions of these attribute flags, please refer to the
-+.BR chattr (1)
-+man page.
- .SH SEE ALSO
-+.BR chattr (1),
- .BR xfsctl (3),
- .BR mount (8),
- .BR mkfs.xfs (8),
diff --git a/SOURCES/xfsprogs-3.2.1-copy-stripe-geometry.patch b/SOURCES/xfsprogs-3.2.1-copy-stripe-geometry.patch
deleted file mode 100644
index 46cb250..0000000
--- a/SOURCES/xfsprogs-3.2.1-copy-stripe-geometry.patch
+++ /dev/null
@@ -1,92 +0,0 @@
-From: Eric Sandeen <sandeen@redhat.com>
-Date: Wed, 16 Jul 2014 03:52:47 +0000 (+1000)
-Subject: repair: copy, don't clear, stripe geometry in backup SB
-X-Git-Url: http://oss.sgi.com/cgi-bin/gitweb.cgi?p=xfs%2Fcmds%2Fxfsprogs.git;a=commitdiff_plain;h=6bf4721d47d9755029a7ec944af2832bd115a851
-
-repair: copy, don't clear, stripe geometry in backup SB
-
-Today, if we have a filesystem with stripe geometry and
-a damaged primary superblock, we will zero out stripe geometry
-if we have copied the backup.
-
-I'm guessing this might be because changing geometry with mount
-options only updates the primary, so backups aren't guaranteed
-to be current or correct.
-
-Unfortunately, that leaves us with sb 0 w/ no geom, and backups
-*with* geom, so the next repair finds the mismatch, and complains.
-(In other words, the 2nd repair does not come up clean.)_
-And ... the second repair copies the backup stripe geometry back
-into the primary!
-
-Rather than clearing stripe geometry in this case, just leave it
-at what was found in the backup super, and inform the user that this
-was done.  This leaves a consistent filesystem, and gives the user
-a heads-up to double-check the result.
-
-This can all be demonstrated and tested by running xfs/030 with
-geometry set in MKFS_OPTIONS.  (To really make the test pass,
-we need to filter the warning out of repair output.)
-
-Signed-off-by: Eric Sandeen <sandeen@redhat.com>
-Reviewed-by: Brian Foster <bfoster@redhat.com>
-Signed-off-by: Dave Chinner <david@fromorbit.com>
----
-
-diff --git a/repair/globals.h b/repair/globals.h
-index f6e0a22..6207ca1 100644
---- a/repair/globals.h
-+++ b/repair/globals.h
-@@ -124,7 +124,7 @@ EXTERN int	lazy_count;		/* What to set if to if converting */
- 
- EXTERN int		primary_sb_modified;
- EXTERN int		bad_ino_btree;
--EXTERN int		clear_sunit;
-+EXTERN int		copied_sunit;
- EXTERN int		fs_is_dirty;
- 
- /* for hunting down the root inode */
-diff --git a/repair/sb.c b/repair/sb.c
-index bc421cc..ad27756 100644
---- a/repair/sb.c
-+++ b/repair/sb.c
-@@ -151,7 +151,7 @@ find_secondary_sb(xfs_sb_t *rsb)
- 			 */
- 			memmove(rsb, &bufsb, sizeof(xfs_sb_t));
- 			rsb->sb_inprogress = 0;
--			clear_sunit = 1;
-+			copied_sunit = 1;
- 
- 			if (verify_set_primary_sb(rsb, 0, &dirty) == XR_OK)  {
- 				do_warn(
-diff --git a/repair/xfs_repair.c b/repair/xfs_repair.c
-index 9eb2fa4..834697a 100644
---- a/repair/xfs_repair.c
-+++ b/repair/xfs_repair.c
-@@ -193,7 +193,7 @@ process_args(int argc, char **argv)
- 	delete_attr_ok = 1;
- 	force_geo = 0;
- 	assume_xfs = 0;
--	clear_sunit = 0;
-+	copied_sunit = 0;
- 	sb_inoalignmt = 0;
- 	sb_unit = 0;
- 	sb_width = 0;
-@@ -898,13 +898,11 @@ _("Warning:  project quota information would be cleared.\n"
- 		dsb->sb_qflags &= cpu_to_be16(~XFS_ALL_QUOTA_CHKD);
- 	}
- 
--	if (clear_sunit) {
-+	if (copied_sunit) {
- 		do_warn(
--_("Note - stripe unit (%d) and width (%d) fields have been reset.\n"
--  "Please set with mount -o sunit=<value>,swidth=<value>\n"),
-+_("Note - stripe unit (%d) and width (%d) were copied from a backup superblock.\n"
-+  "Please reset with mount -o sunit=<value>,swidth=<value> if necessary\n"),
- 			be32_to_cpu(dsb->sb_unit), be32_to_cpu(dsb->sb_width));
--		dsb->sb_unit = 0;
--		dsb->sb_width = 0;
- 	}
- 
- 	libxfs_writebuf(sbp, 0);
-
diff --git a/SOURCES/xfsprogs-3.2.1-libxcmd-make-all-comparisons-using-realpathd-paths.patch b/SOURCES/xfsprogs-3.2.1-libxcmd-make-all-comparisons-using-realpathd-paths.patch
deleted file mode 100644
index 019b6bd..0000000
--- a/SOURCES/xfsprogs-3.2.1-libxcmd-make-all-comparisons-using-realpathd-paths.patch
+++ /dev/null
@@ -1,176 +0,0 @@
-commit ed350fc6c49155ec398866ebef1d59be02636bce
-Author: Eric Sandeen <sandeen@sandeen.net>
-Date:   Wed Jul 16 13:53:47 2014 +1000
-
-    libxcmd: make all comparisons using realpath'd paths
-    
-    Both mountpoints and devices can be symlinks, so given a path
-    to look for, and mountpoints/devices from the system, use
-    realpath() on *everything* before making the comparison to see
-    if our path is a match.
-    
-    So, with symlinks for mount points as well as for devices:
-    
-    # ls -l /dev/mapper/testvg-lvol0
-    lrwxrwxrwx. 1 root root 7 Jul 11 19:24 /dev/mapper/testvg-lvol0 -> ../dm-3
-    # ls -l /mnt/scratch2
-    lrwxrwxrwx. 1 root root 12 Jul 11 19:57 /mnt/scratch2 -> /mnt/scratch
-    
-    this should all work, and does now:
-    
-    # xfs_quota -xc "report -h" /mnt/scratch2
-    User quota on /mnt/scratch (/dev/mapper/testvg-lvol0)
-                            Blocks
-    User ID      Used   Soft   Hard Warn/Grace
-    ---------- ---------------------------------
-    root            0      0      0  00 [------]
-    
-    # xfs_quota -xc "report -h" /mnt/scratch
-    User quota on /mnt/scratch (/dev/mapper/testvg-lvol0)
-                            Blocks
-    User ID      Used   Soft   Hard Warn/Grace
-    ---------- ---------------------------------
-    root            0      0      0  00 [------]
-    
-    # xfs_quota -xc "report -h" /dev/dm-3
-    User quota on /mnt/scratch (/dev/mapper/testvg-lvol0)
-                            Blocks
-    User ID      Used   Soft   Hard Warn/Grace
-    ---------- ---------------------------------
-    root            0      0      0  00 [------]
-    
-    # xfs_quota -xc "report -h" /dev/mapper/testvg-lvol0
-    User quota on /mnt/scratch (/dev/mapper/testvg-lvol0)
-                            Blocks
-    User ID      Used   Soft   Hard Warn/Grace
-    ---------- ---------------------------------
-    root            0      0      0  00 [------]
-    
-    The commit:
-    
-    050a7f1 xfsprogs: handle symlinks etc in fs_table_initialise_mounts()
-    
-    tried to fix this earlier, but only worked one way;
-    it compared the argument path in both given and realpath
-    form to the paths in getmntent, but did not compare to
-    the realpaths of the getmntent devices.
-    
-    If we reduce everything, everywhere, to a realpath(), we've
-    got our best shot at finding the match.
-    
-    Signed-off-by: Eric Sandeen <sandeen@redhat.com>
-    Reviewed-by: Christoph Hellwig <hch@lst.de>
-    Signed-off-by: Dave Chinner <david@fromorbit.com>
-
-diff --git a/libxcmd/paths.c b/libxcmd/paths.c
-index 7b0e434..443adbb 100644
---- a/libxcmd/paths.c
-+++ b/libxcmd/paths.c
-@@ -269,6 +269,9 @@ out_nomem:
- /*
-  * If *path is NULL, initialize the fs table with all xfs mount points in mtab
-  * If *path is specified, search for that path in mtab
-+ *
-+ * Everything - path, devices, and mountpoints - are boiled down to realpath()
-+ * for comparison, but fs_table is populated with what comes from getmntent.
-  */
- static int
- fs_table_initialise_mounts(
-@@ -278,7 +281,7 @@ fs_table_initialise_mounts(
- 	FILE		*mtp;
- 	char		*fslog, *fsrt;
- 	int		error, found;
--	char		*rpath = NULL;
-+	char		rpath[PATH_MAX], rmnt_fsname[PATH_MAX], rmnt_dir[PATH_MAX];
- 
- 	error = found = 0;
- 	fslog = fsrt = NULL;
-@@ -294,17 +297,20 @@ fs_table_initialise_mounts(
- 
- 	/* Use realpath to resolve symlinks, relative paths, etc */
- 	if (path)
--		if ((rpath = realpath(path, NULL)) == NULL)
--			return ENOENT;
-+		if (!realpath(path, rpath))
-+			return errno;
- 
- 	while ((mnt = getmntent(mtp)) != NULL) {
- 		if (strcmp(mnt->mnt_type, "xfs") != 0)
- 			continue;
-+		if (!realpath(mnt->mnt_dir, rmnt_dir))
-+			continue;
-+		if (!realpath(mnt->mnt_fsname, rmnt_fsname))
-+			continue;
-+
- 		if (path &&
--		    ((strcmp(path, mnt->mnt_dir) != 0) &&
--		     (strcmp(path, mnt->mnt_fsname) != 0) &&
--		     (strcmp(rpath, mnt->mnt_dir) != 0) &&
--		     (strcmp(rpath, mnt->mnt_fsname) != 0)))
-+		    ((strcmp(rpath, rmnt_dir) != 0) &&
-+		     (strcmp(rpath, rmnt_fsname) != 0)))
- 			continue;
- 		if (fs_extract_mount_options(mnt, &fslog, &fsrt))
- 			continue;
-@@ -316,7 +322,6 @@ fs_table_initialise_mounts(
- 		}
- 	}
- 	endmntent(mtp);
--	free(rpath);
- 
- 	if (path && !found)
- 		error = ENXIO;
-@@ -330,6 +335,9 @@ fs_table_initialise_mounts(
- /*
-  * If *path is NULL, initialize the fs table with all xfs mount points in mtab
-  * If *path is specified, search for that path in mtab
-+ *
-+ * Everything - path, devices, and mountpoints - are boiled down to realpath()
-+ * for comparison, but fs_table is populated with what comes from getmntinfo.
-  */
- static int
- fs_table_initialise_mounts(
-@@ -337,7 +345,7 @@ fs_table_initialise_mounts(
- {
- 	struct statfs	*stats;
- 	int		i, count, error, found;
--	char		*rpath = NULL;
-+	char		rpath[PATH_MAX], rmntfromname[PATH_MAX], rmntonname[PATH_MAX];
- 
- 	error = found = 0;
- 	if ((count = getmntinfo(&stats, 0)) < 0) {
-@@ -348,17 +356,20 @@ fs_table_initialise_mounts(
- 
- 	/* Use realpath to resolve symlinks, relative paths, etc */
- 	if (path)
--		if ((rpath = realpath(path, NULL)) == NULL)
--			return ENOENT;
-+		if (!realpath(path, rpath))
-+			return errno;
- 
- 	for (i = 0; i < count; i++) {
- 		if (strcmp(stats[i].f_fstypename, "xfs") != 0)
- 			continue;
-+		if (!realpath(stats[i].f_mntfromname, rmntfromname))
-+			continue;
-+		if (!realpath(stats[i].f_mntonname, rmnttomname)))
-+			continue;
-+
- 		if (path &&
--		    ((strcmp(path, stats[i].f_mntonname) != 0) &&
--		     (strcmp(path, stats[i].f_mntfromname) != 0) &&
--		     (strcmp(rpath, stats[i].f_mntonname) != 0) &&
--		     (strcmp(rpath, stats[i].f_mntfromname) != 0)))
-+		    ((strcmp(rpath, rmntonname) != 0) &&
-+		     (strcmp(rpath, rmntfromname) != 0)))
- 			continue;
- 		/* TODO: external log and realtime device? */
- 		(void) fs_table_insert(stats[i].f_mntonname, 0,
-@@ -369,7 +380,6 @@ fs_table_initialise_mounts(
- 			break;
- 		}
- 	}
--	free(rpath);
- 	if (path && !found)
- 		error = ENXIO;
- 
diff --git a/SOURCES/xfsprogs-3.2.1-quota-fix-NULL-pointer-dereference-in-report_f.patch b/SOURCES/xfsprogs-3.2.1-quota-fix-NULL-pointer-dereference-in-report_f.patch
deleted file mode 100644
index b1cc75d..0000000
--- a/SOURCES/xfsprogs-3.2.1-quota-fix-NULL-pointer-dereference-in-report_f.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-commit a14d40939de7d38029f99c10bc237bb68e83d119
-Author: Jie Liu <jeff.liu@oracle.com>
-Date:   Wed Jul 16 13:54:47 2014 +1000
-
-    quota: fix NULL pointer dereference in report_f
-    
-    Run xfs_quota report against an invalid XFS path without desired quota
-    limitation is enabled will hit SEGSEGV as fs_path is uninitialized, e.g.
-    
-    # xfs_quota -xc 'report -up' /invalid_path
-    xfs_quota: cannot setup path for mount /invalid_path: No such file or directory
-    Segmentation fault (core dumped)
-    
-    (gdb) r -xc 'report -up' /invalid_path
-    xfs_quota: cannot setup path for mount /invalid_path: No such file or directory
-    
-    Program received signal SIGSEGV, Segmentation fault.
-    0x0000000000408b4d in report_f (argc=2, argv=0x105ea70) at report.c:627
-    627			else if (fs_path->fs_flags & FS_MOUNT_POINT)
-    
-    This patch fixes report_f() to only do report if the fs_path is initialized.
-    
-    Signed-off-by: Jie Liu <jeff.liu@oracle.com>
-    Reviewed-by: Eric Sandeen <sandeen@redhat.com>
-    Reviewed-by: Christoph Hellwig <hch@lst.de>
-    Signed-off-by: Dave Chinner <david@fromorbit.com>
-
-diff --git a/quota/report.c b/quota/report.c
-index 70894a2..8e3316e 100644
---- a/quota/report.c
-+++ b/quota/report.c
-@@ -624,7 +624,7 @@ report_f(
- 		if (flags & ALL_MOUNTS_FLAG)
- 			report_any_type(fp, form, type, NULL,
- 					lower, upper, flags);
--		else if (fs_path->fs_flags & FS_MOUNT_POINT)
-+		else if (fs_path && (fs_path->fs_flags & FS_MOUNT_POINT))
- 			report_any_type(fp, form, type, fs_path->fs_dir,
- 					lower, upper, flags);
- 	} else while (argc > optind) {
diff --git a/SOURCES/xfsprogs-3.2.1-xfs_copy-simplify-first_agbno-calculation.patch b/SOURCES/xfsprogs-3.2.1-xfs_copy-simplify-first_agbno-calculation.patch
deleted file mode 100644
index a7063ae..0000000
--- a/SOURCES/xfsprogs-3.2.1-xfs_copy-simplify-first_agbno-calculation.patch
+++ /dev/null
@@ -1,75 +0,0 @@
-commit 263b53767a3df33f392262f539bfb35ec578f5e5
-Author: Eric Sandeen <sandeen@redhat.com>
-Date:   Thu Nov 13 10:02:22 2014 +1100
-
-    xfs_copy: simplify first_agbno calculation
-    
-    After ffe9a9a xfsprogs: xfs_copy: fix data corruption of target,
-    xfs_copy started hitting an ASSERT for a 4k sector / 4k blocksize
-    filesystem:
-    
-    # dd if=/dev/zero of=test.img bs=1M count=1024
-    # mkfs.xfs -s size=4096 test.img
-    # xfs_copy test.img xfs.img
-    xfs_copy: xfs_copy.c:720: main: Assertion `((((((xfs_daddr_t)(3 << (mp)->m_sectbb_log)) + 1) * (1<<9)) + first_residue) % source_blocksize) == 0' failed.
-    Aborted
-    
-    I started digging through all the calculations below, and realized
-    that in the end, all it wants is the first filesystem block after
-    the AG header.  XFS_AGFL_BLOCK(mp) + 1 suffices for this purpose;
-    rip out the rest which seems overly complex and apparently bug-prone.
-    
-    I tested this by creating a 4g filesystem with combinations of
-    sector & block size between 512 and 4k, copying in /lib/modules,
-    running an xfs_copy of that, and running repair against the copy;
-    it all looks good.  It took a long time, but I will create a
-    simpler/shorter xfstest based on this.
-    
-    Reported-by: Zorro Lang <zlang@redhat.com>
-    Signed-off-by: Eric Sandeen <sandeen@redhat.com>
-    Reviewed-by: Brian Foster <bfoster@redhat.com>
-    Signed-off-by: Dave Chinner <david@fromorbit.com>
-
-diff --git a/copy/xfs_copy.c b/copy/xfs_copy.c
-index 7ce5ec9..279527c 100644
---- a/copy/xfs_copy.c
-+++ b/copy/xfs_copy.c
-@@ -475,7 +475,7 @@ main(int argc, char **argv)
- 	int		open_flags;
- 	xfs_off_t	pos, end_pos;
- 	size_t		length;
--	int		c, first_residue, tmp_residue;
-+	int		c;
- 	__uint64_t	size, sizeb;
- 	__uint64_t	numblocks = 0;
- 	int		wblocks = 0;
-@@ -697,27 +697,13 @@ main(int argc, char **argv)
- 	ASSERT(source_blocksize % source_sectorsize == 0);
- 	ASSERT(source_sectorsize % BBSIZE == 0);
- 
--	if (source_blocksize > source_sectorsize)  {
--		/* get number of leftover sectors in last block of ag header */
--
--		tmp_residue = ((XFS_AGFL_DADDR(mp) + 1) * BBSIZE)
--					% source_blocksize;
--		first_residue = (tmp_residue == 0) ? 0 :
--			source_blocksize - tmp_residue;
--		ASSERT(first_residue % source_sectorsize == 0);
--	} else if (source_blocksize == source_sectorsize)  {
--		first_residue = 0;
--	} else  {
-+	if (source_blocksize < source_sectorsize)  {
- 		do_log(_("Error:  filesystem block size is smaller than the"
- 			" disk sectorsize.\nAborting XFS copy now.\n"));
- 		exit(1);
- 	}
- 
--	first_agbno = (((XFS_AGFL_DADDR(mp) + 1) * BBSIZE)
--				+ first_residue) / source_blocksize;
--	ASSERT(first_agbno != 0);
--	ASSERT(((((XFS_AGFL_DADDR(mp) + 1) * BBSIZE)
--				+ first_residue) % source_blocksize) == 0);
-+	first_agbno = XFS_AGFL_BLOCK(mp) + 1;
- 
- 	/* now open targets */
- 
diff --git a/SOURCES/xfsprogs-3.2.1-xfs_quota-manpage.patch b/SOURCES/xfsprogs-3.2.1-xfs_quota-manpage.patch
deleted file mode 100644
index 26163ca..0000000
--- a/SOURCES/xfsprogs-3.2.1-xfs_quota-manpage.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-Two patches on upstream list:
-
-[PATCH] xfs_quota: fix typo in manpage
-
-and
-
-[PATCH] xfs_quota: man page fix - project command requires arguments
-
-The xfs_quota man page states that the "project" command without
-arguments will list all project names and identifiers, but it has
-never done this; the project_f command has always been defined as
-requiring at least one argument.
-
-Fix the man page to reflect reality.
-
-Signed-off-by: Eric Sandeen <sandeen@redhat.com>
----
-
-diff --git a/man/man8/xfs_quota.8 b/man/man8/xfs_quota.8
-index 8cc8ab7..3ca2fa5 100644
---- a/man/man8/xfs_quota.8
-+++ b/man/man8/xfs_quota.8
-@@ -324,7 +324,7 @@ path to the
- list entry (the current path is used by many
- of the commands described here, it identifies the filesystem toward
- which a command is directed).
--The patch list can come from several places \- the command line,
-+The path list can come from several places \- the command line,
- the mount table, and the
- .I /etc/projects
- file.
-@@ -565,12 +565,7 @@ instead of stdout.
- .I name
- ]
- .br
--Without arguments, this command lists known project names and identifiers
--(based on entries in the
--.I /etc/projects
--and
--.I /etc/projid
--files). The
-+The
- .BR \-c ,
- .BR \-C ,
- and
diff --git a/SOURCES/xfsprogs-3.2.1-xfs_repair-fix-max-block-offset-test.patch b/SOURCES/xfsprogs-3.2.1-xfs_repair-fix-max-block-offset-test.patch
deleted file mode 100644
index 732730f..0000000
--- a/SOURCES/xfsprogs-3.2.1-xfs_repair-fix-max-block-offset-test.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-[PATCH V2] xfs_repair: fix max block offset test
-
-Eryu pointed out that in fstest xfs/071, we find corruption
-reported at the end.  This test attempts to do IO at the
-maximum possible offsets, and repair yields:
-
-inode 1027 - extent offset too large - start 70, count 1, offset 2251799813685247
-correcting nextents for inode 1027
-bad data fork in inode 1027
-would have cleared inode 1027
-
-Repair is complaining that an extent *starts* at the maximum
-block, but AFAICT, starting there is just fine, as long as
-we also end there.  i.e. a one-block extent at the limit
-is just fine.
-
-So change the xfs_repair test to allow this situation.
-
-Also, the warning text is a bit unclear, mixing in the physical
-block w/ the logical block... rearrange that a little to make
-it obvious.
-
-Reported-by: Eryu Guan <eguan@redhat.com>
-Signed-off-by: Eric Sandeen <sandeen@redhat.com>
-Reviewed-by: Brian Foster <bfoster@redhat.com>
----
-
-V2: Update the warning text
-
-diff --git a/repair/dinode.c b/repair/dinode.c
-index 38a6562..59824ec 100644
---- a/repair/dinode.c
-+++ b/repair/dinode.c
-@@ -667,12 +667,14 @@ _("inode %" PRIu64 " - bad extent overflows - start %" PRIu64 ", "
- 					irec.br_startoff);
- 				goto done;
- 		}
--		if (irec.br_startoff >= fs_max_file_offset)  {
-+		/* Ensure this extent does not extend beyond the max offset */
-+		if (irec.br_startoff + irec.br_blockcount - 1 >
-+							fs_max_file_offset) {
- 			do_warn(
--_("inode %" PRIu64 " - extent offset too large - start %" PRIu64 ", "
--  "count %" PRIu64 ", offset %" PRIu64 "\n"),
--				ino, irec.br_startblock, irec.br_blockcount,
--				irec.br_startoff);
-+_("inode %" PRIu64 " - extent exceeds max offset - start %" PRIu64 ", "
-+  "count %" PRIu64 ", physical block %" PRIu64 "\n"),
-+				ino, irec.br_startoff, irec.br_blockcount,
-+				irec.br_startblock);
- 			goto done;
- 		}
- 
-
diff --git a/SOURCES/xfsprogs-3.2.3-fixes.patch b/SOURCES/xfsprogs-3.2.3-fixes.patch
new file mode 100644
index 0000000..aa583a7
--- /dev/null
+++ b/SOURCES/xfsprogs-3.2.3-fixes.patch
@@ -0,0 +1,1412 @@
+diff --git a/copy/Makefile b/copy/Makefile
+index 54f6dfb..beabbd4 100644
+--- a/copy/Makefile
++++ b/copy/Makefile
+@@ -11,7 +11,7 @@ HFILES = xfs_copy.h
+ 
+ LLDLIBS = $(LIBXFS) $(LIBUUID) $(LIBPTHREAD) $(LIBRT)
+ LTDEPENDENCIES = $(LIBXFS)
+-LLDFLAGS = -static
++LLDFLAGS = -static-libtool-libs
+ 
+ default: depend $(LTCOMMAND)
+ 
+diff --git a/db/Makefile b/db/Makefile
+index bae6154..fb01bdd 100644
+--- a/db/Makefile
++++ b/db/Makefile
+@@ -18,7 +18,7 @@ LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
+ 
+ LLDLIBS	= $(LIBXFS) $(LIBXLOG) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
+ LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG)
+-LLDFLAGS += -static
++LLDFLAGS += -static-libtool-libs
+ 
+ ifeq ($(ENABLE_READLINE),yes)
+ LLDLIBS += $(LIBREADLINE) $(LIBTERMCAP)
+diff --git a/db/check.c b/db/check.c
+index 4fd9fd0..c4c972f 100644
+--- a/db/check.c
++++ b/db/check.c
+@@ -1832,7 +1832,13 @@ init(
+ 	error = sbver_err = serious_error = 0;
+ 	fdblocks = frextents = icount = ifree = 0;
+ 	sbversion = XFS_SB_VERSION_4;
+-	if (mp->m_sb.sb_inoalignmt)
++	/*
++	 * Note that inoalignmt == 0 is valid when fsb size is large enough for
++	 * at least one full inode record per block. Check this case explicitly.
++	 */
++	if (mp->m_sb.sb_inoalignmt ||
++	    (xfs_sb_version_hasalign(&mp->m_sb) &&
++	     mp->m_sb.sb_inopblock >= XFS_INODES_PER_CHUNK))
+ 		sbversion |= XFS_SB_VERSION_ALIGNBIT;
+ 	if ((mp->m_sb.sb_uquotino && mp->m_sb.sb_uquotino != NULLFSINO) ||
+ 	    (mp->m_sb.sb_gquotino && mp->m_sb.sb_gquotino != NULLFSINO) ||
+diff --git a/db/inode.c b/db/inode.c
+index 24170ba..dfefbf5 100644
+--- a/db/inode.c
++++ b/db/inode.c
+@@ -369,7 +369,7 @@ inode_core_nlinkv2_count(
+ 	ASSERT(startoff == 0);
+ 	ASSERT(obj == iocur_top->data);
+ 	dic = obj;
+-	return dic->di_version == 2;
++	return dic->di_version >= 2;
+ }
+ 
+ static int
+@@ -382,7 +382,7 @@ inode_core_onlink_count(
+ 	ASSERT(startoff == 0);
+ 	ASSERT(obj == iocur_top->data);
+ 	dic = obj;
+-	return dic->di_version == 2;
++	return dic->di_version >= 2;
+ }
+ 
+ static int
+@@ -395,7 +395,7 @@ inode_core_projid_count(
+ 	ASSERT(startoff == 0);
+ 	ASSERT(obj == iocur_top->data);
+ 	dic = obj;
+-	return dic->di_version == 2;
++	return dic->di_version >= 2;
+ }
+ 
+ static int
+@@ -684,13 +684,22 @@ set_cur_inode(
+ 		numblks, DB_RING_IGN, NULL);
+ 	off_cur(offset << mp->m_sb.sb_inodelog, mp->m_sb.sb_inodesize);
+ 	dip = iocur_top->data;
+-	iocur_top->ino_crc_ok = libxfs_dinode_verify(mp, ino, dip);
+ 	iocur_top->ino_buf = 1;
+ 	iocur_top->ino = ino;
+ 	iocur_top->mode = be16_to_cpu(dip->di_mode);
+ 	if ((iocur_top->mode & S_IFMT) == S_IFDIR)
+ 		iocur_top->dirino = ino;
+ 
++	if (xfs_sb_version_hascrc(&mp->m_sb)) {
++		iocur_top->ino_crc_ok = libxfs_verify_cksum((char *)dip,
++						    mp->m_sb.sb_inodesize,
++						    XFS_DINODE_CRC_OFF);
++		if (!iocur_top->ino_crc_ok)
++			dbprintf(
++_("Metadata CRC error detected for ino %lld\n"),
++				ino);
++	}
++
+ 	/* track updated info in ring */
+ 	ring_add();
+ }
+diff --git a/db/io.c b/db/io.c
+index 7f1b76a..d906123 100644
+--- a/db/io.c
++++ b/db/io.c
+@@ -457,6 +457,13 @@ write_cur_bbs(void)
+ }
+ 
+ void
++xfs_dummy_verify(
++	struct xfs_buf *bp)
++{
++	return;
++}
++
++void
+ write_cur(void)
+ {
+ 	if (iocur_sp < 0) {
+@@ -464,8 +471,10 @@ write_cur(void)
+ 		return;
+ 	}
+ 
+-	if (iocur_top->ino_buf)
++	if (xfs_sb_version_hascrc(&mp->m_sb) && iocur_top->ino_buf) {
+ 		libxfs_dinode_calc_crc(mp, iocur_top->data);
++		iocur_top->ino_crc_ok = 1;
++	}
+ 	if (iocur_top->dquot_buf)
+ 		xfs_update_cksum(iocur_top->data, sizeof(struct xfs_dqblk),
+ 				 XFS_DQUOT_CRC_OFF);
+diff --git a/db/io.h b/db/io.h
+index 71082e6..31d96b4 100644
+--- a/db/io.h
++++ b/db/io.h
+@@ -63,6 +63,7 @@ extern void	set_cur(const struct typ *t, __int64_t d, int c, int ring_add,
+ 			bbmap_t *bbmap);
+ extern void     ring_add(void);
+ extern void	set_iocur_type(const struct typ *t);
++extern void	xfs_dummy_verify(struct xfs_buf *bp);
+ 
+ /*
+  * returns -1 for unchecked, 0 for bad and 1 for good
+diff --git a/db/metadump.c b/db/metadump.c
+index 38cd441..a599571 100644
+--- a/db/metadump.c
++++ b/db/metadump.c
+@@ -1865,6 +1865,7 @@ copy_inode_chunk(
+ 			(mp->m_sb.sb_inopblock > XFS_INODES_PER_CHUNK &&
+ 					off % XFS_INODES_PER_CHUNK != 0) ||
+ 			(xfs_sb_version_hasalign(&mp->m_sb) &&
++					mp->m_sb.sb_inoalignmt != 0 &&
+ 					agbno % mp->m_sb.sb_inoalignmt != 0)) {
+ 		if (show_warnings)
+ 			print_warning("badly aligned inode (start = %llu)",
+@@ -2112,7 +2113,7 @@ copy_ino(
+ 	int			offset;
+ 	int			rval = 0;
+ 
+-	if (ino == 0)
++	if (ino == 0 || ino == NULLFSINO)
+ 		return 1;
+ 
+ 	agno = XFS_INO_TO_AGNO(mp, ino);
+diff --git a/db/sb.c b/db/sb.c
+index 6cb665d..974abd2 100644
+--- a/db/sb.c
++++ b/db/sb.c
+@@ -363,6 +363,18 @@ uuid_f(
+ 			return 0;
+ 		}
+ 
++		/*
++		 * For now, changing the UUID of V5 superblock filesystems is
++		 * not supported; we do not have the infrastructure to fix all
++		 * other metadata when a new superblock UUID is generated.
++		 */
++		if (xfs_sb_version_hascrc(&mp->m_sb) &&
++		    strcasecmp(argv[1], "rewrite")) {
++			dbprintf(_("%s: only 'rewrite' supported on V5 fs\n"),
++				progname);
++			return 0;
++		}
++
+ 		if (!strcasecmp(argv[1], "generate")) {
+ 			platform_uuid_generate(&uu);
+ 		} else if (!strcasecmp(argv[1], "nil")) {
+@@ -646,6 +658,8 @@ version_string(
+ 		strcat(s, ",CRC");
+ 	if (xfs_sb_version_hasftype(sbp))
+ 		strcat(s, ",FTYPE");
++	if (xfs_sb_version_hasfinobt(sbp))
++		strcat(s, ",FINOBT");
+ 	return s;
+ }
+ 
+diff --git a/db/write.c b/db/write.c
+index a0f14f4..655b618 100644
+--- a/db/write.c
++++ b/db/write.c
+@@ -38,7 +38,7 @@ static int	write_f(int argc, char **argv);
+ static void     write_help(void);
+ 
+ static const cmdinfo_t	write_cmd =
+-	{ "write", NULL, write_f, 0, -1, 0, N_("[field or value]..."),
++	{ "write", NULL, write_f, 0, -1, 0, N_("[-c] [field or value]..."),
+ 	  N_("write value to disk"), write_help };
+ 
+ void
+@@ -79,6 +79,7 @@ write_help(void)
+ "  String mode: 'write \"This_is_a_filename\" - write null terminated string.\n"
+ "\n"
+ " In data mode type 'write' by itself for a list of specific commands.\n\n"
++" Specifying the -c option will allow writes of invalid (corrupt) data.\n\n"
+ ));
+ 
+ }
+@@ -90,6 +91,10 @@ write_f(
+ {
+ 	pfunc_t	pf;
+ 	extern char *progname;
++	int c;
++	int corrupt = 0;		/* Allow write of corrupt data; skip verification */
++	struct xfs_buf_ops nowrite_ops;
++	const struct xfs_buf_ops *stashed_ops = NULL;
+ 
+ 	if (x.isreadonly & LIBXFS_ISREADONLY) {
+ 		dbprintf(_("%s started in read only mode, writing disabled\n"),
+@@ -109,12 +114,34 @@ write_f(
+ 		return 0;
+ 	}
+ 
+-	/* move past the "write" command */
+-	argc--;
+-	argv++;
++	while ((c = getopt(argc, argv, "c")) != EOF) {
++		switch (c) {
++		case 'c':
++			corrupt = 1;
++			break;
++		default:
++			dbprintf(_("bad option for write command\n"));
++			return 0;
++		}
++	}
++
++	argc -= optind;
++	argv += optind;
++
++	if (iocur_top->bp->b_ops && corrupt) {
++		/* Temporarily remove write verifier to write bad data */
++		stashed_ops = iocur_top->bp->b_ops;
++		nowrite_ops.verify_read = stashed_ops->verify_read;
++		nowrite_ops.verify_write = xfs_dummy_verify;
++		iocur_top->bp->b_ops = &nowrite_ops;
++		dbprintf(_("Allowing write of corrupted data\n"));
++	}
+ 
+ 	(*pf)(DB_WRITE, cur_typ->fields, argc, argv);
+ 
++	if (stashed_ops)
++		iocur_top->bp->b_ops = stashed_ops;
++
+ 	return 0;
+ }
+ 
+diff --git a/growfs/Makefile b/growfs/Makefile
+index 88cbf4f..19616de 100644
+--- a/growfs/Makefile
++++ b/growfs/Makefile
+@@ -19,7 +19,7 @@ LLDLIBS += $(LIBEDITLINE) $(LIBTERMCAP)
+ endif
+ 
+ LTDEPENDENCIES = $(LIBXFS) $(LIBXCMD)
+-LLDFLAGS = -static
++LLDFLAGS = -static-libtool-libs
+ LSRCFILES = xfs_info.sh
+ 
+ default: depend $(LTCOMMAND)
+diff --git a/include/builddefs.in b/include/builddefs.in
+index 944bcf6..7e9f53d 100644
+--- a/include/builddefs.in
++++ b/include/builddefs.in
+@@ -109,7 +109,7 @@ GCCFLAGS = -funsigned-char -fno-strict-aliasing -Wall
+ #	   -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-decl
+ 
+ ifeq ($(PKG_PLATFORM),linux)
+-PCFLAGS = -D_GNU_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=500 -D_FILE_OFFSET_BITS=64 $(GCCFLAGS)
++PCFLAGS = -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 $(GCCFLAGS)
+ ifeq ($(HAVE_UMODE_T),yes)
+ PCFLAGS += -DHAVE_UMODE_T
+ endif
+diff --git a/include/handle.h b/include/handle.h
+index b211a2f..ee69a11 100644
+--- a/include/handle.h
++++ b/include/handle.h
+@@ -28,6 +28,7 @@ struct parent;
+ 
+ extern int  path_to_handle (char *__path, void **__hanp, size_t *__hlen);
+ extern int  path_to_fshandle (char *__path, void **__fshanp, size_t *__fshlen);
++extern int  fd_to_handle (int fd, void **hanp, size_t *hlen);
+ extern int  handle_to_fshandle (void *__hanp, size_t __hlen, void **__fshanp,
+ 				size_t *__fshlen);
+ extern void free_handle (void *__hanp, size_t __hlen);
+diff --git a/include/libxfs.h b/include/libxfs.h
+index 45a924f..962e319 100644
+--- a/include/libxfs.h
++++ b/include/libxfs.h
+@@ -782,6 +782,8 @@ extern uint32_t crc32c_le(uint32_t crc, unsigned char const *p, size_t len);
+ 
+ #include <xfs/xfs_cksum.h>
+ 
++#define libxfs_verify_cksum	xfs_verify_cksum
++
+ static inline int
+ xfs_buf_verify_cksum(struct xfs_buf *bp, unsigned long cksum_offset)
+ {
+diff --git a/include/xfs_da_format.h b/include/xfs_da_format.h
+index 89a1a21..11f1420 100644
+--- a/include/xfs_da_format.h
++++ b/include/xfs_da_format.h
+@@ -561,7 +561,6 @@ xfs_dir3_dirent_get_ftype(
+ 	if (xfs_sb_version_hasftype(&mp->m_sb)) {
+ 		__uint8_t	type = dep->name[dep->namelen];
+ 
+-		ASSERT(type < XFS_DIR3_FT_MAX);
+ 		if (type < XFS_DIR3_FT_MAX)
+ 			return type;
+ 
+diff --git a/include/xfs_dir2.h b/include/xfs_dir2.h
+index 3900130..2d41c5f 100644
+--- a/include/xfs_dir2.h
++++ b/include/xfs_dir2.h
+@@ -100,6 +100,8 @@ extern void xfs_dir2_data_use_free(struct xfs_trans *tp, struct xfs_buf *bp,
+ extern struct xfs_dir2_data_free *xfs_dir2_data_freefind(
+ 		struct xfs_dir2_data_hdr *hdr, struct xfs_dir2_data_unused *dup);
+ 
++extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
++
+ extern const struct xfs_buf_ops xfs_dir3_block_buf_ops;
+ extern const struct xfs_buf_ops xfs_dir3_leafn_buf_ops;
+ extern const struct xfs_buf_ops xfs_dir3_leaf1_buf_ops;
+diff --git a/include/xfs_types.h b/include/xfs_types.h
+index 65c6e66..accb95d 100644
+--- a/include/xfs_types.h
++++ b/include/xfs_types.h
+@@ -100,11 +100,14 @@ typedef __uint64_t	xfs_filblks_t;	/* number of blocks in a file */
+  * Minimum and maximum blocksize and sectorsize.
+  * The blocksize upper limit is pretty much arbitrary.
+  * The sectorsize upper limit is due to sizeof(sb_sectsize).
++ * CRC enable filesystems use 512 byte inodes, meaning 512 byte block sizes
++ * cannot be used.
+  */
+ #define XFS_MIN_BLOCKSIZE_LOG	9	/* i.e. 512 bytes */
+ #define XFS_MAX_BLOCKSIZE_LOG	16	/* i.e. 65536 bytes */
+ #define XFS_MIN_BLOCKSIZE	(1 << XFS_MIN_BLOCKSIZE_LOG)
+ #define XFS_MAX_BLOCKSIZE	(1 << XFS_MAX_BLOCKSIZE_LOG)
++#define XFS_MIN_CRC_BLOCKSIZE	(1 << (XFS_MIN_BLOCKSIZE_LOG + 1))
+ #define XFS_MIN_SECTORSIZE_LOG	9	/* i.e. 512 bytes */
+ #define XFS_MAX_SECTORSIZE_LOG	15	/* i.e. 32768 bytes */
+ #define XFS_MIN_SECTORSIZE	(1 << XFS_MIN_SECTORSIZE_LOG)
+diff --git a/io/Makefile b/io/Makefile
+index 82593a6..a08a782 100644
+--- a/io/Makefile
++++ b/io/Makefile
+@@ -15,7 +15,7 @@ CFILES = init.c \
+ 
+ LLDLIBS = $(LIBXCMD) $(LIBHANDLE)
+ LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE)
+-LLDFLAGS = -static
++LLDFLAGS = -static-libtool-libs
+ 
+ ifeq ($(HAVE_FADVISE),yes)
+ CFILES += fadvise.c
+diff --git a/io/prealloc.c b/io/prealloc.c
+index aba6b44..4ab3d8c 100644
+--- a/io/prealloc.c
++++ b/io/prealloc.c
+@@ -37,6 +37,10 @@
+ #define FALLOC_FL_ZERO_RANGE 0x10
+ #endif
+ 
++#ifndef FALLOC_FL_INSERT_RANGE
++#define FALLOC_FL_INSERT_RANGE 0x20
++#endif
++
+ static cmdinfo_t allocsp_cmd;
+ static cmdinfo_t freesp_cmd;
+ static cmdinfo_t resvsp_cmd;
+@@ -46,6 +50,7 @@ static cmdinfo_t zero_cmd;
+ static cmdinfo_t falloc_cmd;
+ static cmdinfo_t fpunch_cmd;
+ static cmdinfo_t fcollapse_cmd;
++static cmdinfo_t finsert_cmd;
+ static cmdinfo_t fzero_cmd;
+ #endif
+ 
+@@ -169,11 +174,14 @@ fallocate_f(
+ 	int		mode = 0;
+ 	int		c;
+ 
+-	while ((c = getopt(argc, argv, "ckp")) != EOF) {
++	while ((c = getopt(argc, argv, "cikp")) != EOF) {
+ 		switch (c) {
+ 		case 'c':
+ 			mode = FALLOC_FL_COLLAPSE_RANGE;
+ 			break;
++		case 'i':
++			mode = FALLOC_FL_INSERT_RANGE;
++			break;
+ 		case 'k':
+ 			mode = FALLOC_FL_KEEP_SIZE;
+ 			break;
+@@ -237,6 +245,25 @@ fcollapse_f(
+ }
+ 
+ static int
++finsert_f(
++	int		argc,
++	char		**argv)
++{
++	xfs_flock64_t	segment;
++	int		mode = FALLOC_FL_INSERT_RANGE;
++
++	if (!offset_length(argv[1], argv[2], &segment))
++		return 0;
++
++	if (fallocate(file->fd, mode,
++			segment.l_start, segment.l_len)) {
++		perror("fallocate");
++		return 0;
++	}
++	return 0;
++}
++
++static int
+ fzero_f(
+ 	int		argc,
+ 	char		**argv)
+@@ -345,6 +372,16 @@ prealloc_init(void)
+ 	_("de-allocates space and eliminates the hole by shifting extents");
+ 	add_command(&fcollapse_cmd);
+ 
++	finsert_cmd.name = "finsert";
++	finsert_cmd.cfunc = finsert_f;
++	finsert_cmd.argmin = 2;
++	finsert_cmd.argmax = 2;
++	finsert_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
++	finsert_cmd.args = _("off len");
++	finsert_cmd.oneline =
++	_("creates new space for writing within file by shifting extents");
++	add_command(&finsert_cmd);
++
+ 	fzero_cmd.name = "fzero";
+ 	fzero_cmd.cfunc = fzero_f;
+ 	fzero_cmd.argmin = 2;
+diff --git a/libxfs/rdwr.c b/libxfs/rdwr.c
+index 7d73477..35fb366 100644
+--- a/libxfs/rdwr.c
++++ b/libxfs/rdwr.c
+@@ -411,6 +411,7 @@ __initbuf(xfs_buf_t *bp, struct xfs_buftarg *btp, xfs_daddr_t bno,
+ 			strerror(errno));
+ 		exit(1);
+ 	}
++	memset(bp->b_addr, 0, bytes);
+ #ifdef XFS_BUF_TRACING
+ 	list_head_init(&bp->b_lock_list);
+ #endif
+diff --git a/libxfs/util.c b/libxfs/util.c
+index 9504e33..49eb76d 100644
+--- a/libxfs/util.c
++++ b/libxfs/util.c
+@@ -700,8 +700,6 @@ libxfs_inode_alloc(
+ 		if (error)
+ 			return error;
+ 	}
+-	if (!ip)
+-		error = ENOSPC;
+ 
+ 	*ipp = ip;
+ 	return error;
+@@ -718,6 +716,7 @@ libxfs_fs_repair_cmn_err(int level, xfs_mount_t *mp, char *fmt, ...)
+ 	va_start(ap, fmt);
+ 	vfprintf(stderr, fmt, ap);
+ 	fprintf(stderr, "  This is a bug.\n");
++	fprintf(stderr, "%s version %s\n", progname, VERSION);
+ 	fprintf(stderr, "Please capture the filesystem metadata with "
+ 			"xfs_metadump and\nreport it to xfs@oss.sgi.com.\n");
+ 	va_end(ap);
+diff --git a/libxfs/xfs_dir2_priv.h b/libxfs/xfs_dir2_priv.h
+index 1bad84c..926715f 100644
+--- a/libxfs/xfs_dir2_priv.h
++++ b/libxfs/xfs_dir2_priv.h
+@@ -21,7 +21,6 @@
+ struct dir_context;
+ 
+ /* xfs_dir2.c */
+-extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
+ extern int xfs_dir2_grow_inode(struct xfs_da_args *args, int space,
+ 				xfs_dir2_db_t *dbp);
+ extern int xfs_dir_cilookup_result(struct xfs_da_args *args,
+diff --git a/logprint/Makefile b/logprint/Makefile
+index 2d656a4..7bcf27f 100644
+--- a/logprint/Makefile
++++ b/logprint/Makefile
+@@ -14,7 +14,7 @@ CFILES = logprint.c \
+ 
+ LLDLIBS	= $(LIBXFS) $(LIBXLOG) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
+ LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG)
+-LLDFLAGS = -static
++LLDFLAGS = -static-libtool-libs
+ 
+ default: depend $(LTCOMMAND)
+ 
+diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
+index 4d8d4ff..d527230 100644
+--- a/man/man8/xfs_db.8
++++ b/man/man8/xfs_db.8
+@@ -711,7 +711,7 @@ and
+ bits respectively, and their string equivalent reported
+ (but no modifications are made).
+ .TP
+-.BI "write [" "field value" "] ..."
++.BI "write [\-c] [" "field value" "] ..."
+ Write a value to disk.
+ Specific fields can be set in structures (struct mode),
+ or a block can be set to data values (data mode),
+@@ -729,6 +729,12 @@ contents of the block can be shifted or rotated left or right, or filled
+ with a sequence, a constant value, or a random value. In this mode
+ .B write
+ with no arguments gives more information on the allowed commands.
++.RS 1.0i
++.TP 0.4i
++.B \-c
++Skip write verifiers and CRC recalculation; allows invalid data to be written
++to disk.
++.RE
+ .SH TYPES
+ This section gives the fields in each structure type and their meanings.
+ Note that some types of block cover multiple actual structures,
+diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
+index cf27b99..416206f 100644
+--- a/man/man8/xfs_io.8
++++ b/man/man8/xfs_io.8
+@@ -404,6 +404,11 @@ Call fallocate with FALLOC_FL_COLLAPSE_RANGE flag as described in the
+ manual page to de-allocates blocks and eliminates the hole created in this process
+ by shifting data blocks into the hole.
+ .TP
++.BI finsert " offset length"
++Call fallocate with FALLOC_FL_INSERT_RANGE flag as described in the
++.BR fallocate (2)
++manual page to create the hole by shifting data blocks.
++.TP
+ .BI fpunch " offset length"
+ Punches (de-allocates) blocks in the file by calling fallocate with 
+ the FALLOC_FL_PUNCH_HOLE flag as described in the
+diff --git a/man/man8/xfs_quota.8 b/man/man8/xfs_quota.8
+index 8cc8ab7..9b555e9 100644
+--- a/man/man8/xfs_quota.8
++++ b/man/man8/xfs_quota.8
+@@ -324,7 +324,7 @@ path to the
+ list entry (the current path is used by many
+ of the commands described here, it identifies the filesystem toward
+ which a command is directed).
+-The patch list can come from several places \- the command line,
++The path list can come from several places \- the command line,
+ the mount table, and the
+ .I /etc/projects
+ file.
+@@ -565,12 +565,7 @@ instead of stdout.
+ .I name
+ ]
+ .br
+-Without arguments, this command lists known project names and identifiers
+-(based on entries in the
+-.I /etc/projects
+-and
+-.I /etc/projid
+-files). The
++The
+ .BR \-c ,
+ .BR \-C ,
+ and
+diff --git a/mkfs/Makefile b/mkfs/Makefile
+index 75da633..fd1f615 100644
+--- a/mkfs/Makefile
++++ b/mkfs/Makefile
+@@ -21,7 +21,7 @@ endif
+ 
+ LLDLIBS += $(LIBXFS) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
+ LTDEPENDENCIES += $(LIBXFS)
+-LLDFLAGS = -static
++LLDFLAGS = -static-libtool-libs
+ 
+ LSRCFILES = $(FSTYP).c
+ LDIRT = $(FSTYP)
+diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
+index 66711cb..8e756d1 100644
+--- a/mkfs/xfs_mkfs.c
++++ b/mkfs/xfs_mkfs.c
+@@ -817,6 +817,13 @@ zero_old_xfs_structures(
+ 	__uint32_t		bsize;
+ 	int			i;
+ 	xfs_off_t		off;
++	int			tmp;
++
++	/*
++	 * We open regular files with O_TRUNC|O_CREAT. Nothing to do here...
++	 */
++	if (xi->disfile && xi->dcreat)
++		return;
+ 
+ 	/*
+ 	 * read in existing filesystem superblock, use its geometry
+@@ -830,11 +837,16 @@ zero_old_xfs_structures(
+ 	}
+ 	memset(buf, 0, new_sb->sb_sectsize);
+ 
+-	if (pread(xi->dfd, buf, new_sb->sb_sectsize, 0) != new_sb->sb_sectsize) {
++	tmp = pread(xi->dfd, buf, new_sb->sb_sectsize, 0);
++	if (tmp < 0) {
+ 		fprintf(stderr, _("existing superblock read failed: %s\n"),
+ 			strerror(errno));
+-		free(buf);
+-		return;
++		goto done;
++	}
++	if (tmp != new_sb->sb_sectsize) {
++		fprintf(stderr,
++	_("warning: could not read existing superblock, skip zeroing\n"));
++		goto done;
+ 	}
+ 	libxfs_sb_from_disk(&sb, buf);
+ 
+@@ -1743,6 +1755,12 @@ _("cannot specify both crc and ftype\n"));
+ 		fprintf(stderr, _("illegal block size %d\n"), blocksize);
+ 		usage();
+ 	}
++	if (crcs_enabled && blocksize < XFS_MIN_CRC_BLOCKSIZE) {
++		fprintf(stderr,
++_("Minimum block size for CRC enabled filesystems is %d bytes.\n"),
++			XFS_MIN_CRC_BLOCKSIZE);
++		usage();
++	}
+ 
+ 	memset(&ft, 0, sizeof(ft));
+ 	get_topology(&xi, &ft, force_overwrite);
+@@ -2441,9 +2459,11 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
+ 			 */
+ 			logblocks = (dblocks << blocklog) / 2048;
+ 			logblocks = logblocks >> blocklog;
+-			logblocks = MAX(min_logblocks, logblocks);
+ 		}
+ 
++		/* Ensure the chosen size meets minimum log size requirements */
++		logblocks = MAX(min_logblocks, logblocks);
++
+ 		/* make sure the log fits wholly within an AG */
+ 		if (logblocks >= agsize)
+ 			logblocks = min_logblocks;
+diff --git a/repair/Makefile b/repair/Makefile
+index 17a30fd..6d84ade 100644
+--- a/repair/Makefile
++++ b/repair/Makefile
+@@ -22,7 +22,7 @@ CFILES = agheader.c attr_repair.c avl.c avl64.c bmap.c btree.c \
+ 
+ LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
+ LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG)
+-LLDFLAGS = -static
++LLDFLAGS = -static-libtool-libs
+ 
+ default: depend $(LTCOMMAND)
+ 
+diff --git a/repair/agheader.c b/repair/agheader.c
+index 416dbd8..5dbf992 100644
+--- a/repair/agheader.c
++++ b/repair/agheader.c
+@@ -377,7 +377,13 @@ secondary_sb_wack(
+ 			rval |= XR_AG_SB_SEC;
+ 	}
+ 
+-	if (sb->sb_inprogress == 1 && sb->sb_pquotino != NULLFSINO)  {
++	/*
++	 * Note that sb_pquotino is not considered a valid sb field for pre-v5
++	 * superblocks. If it is anything other than 0 it is considered garbage
++	 * data beyond the valid sb and explicitly zeroed above.
++	 */
++	if (xfs_sb_version_has_pquotino(&mp->m_sb) &&
++	    sb->sb_inprogress == 1 && sb->sb_pquotino != NULLFSINO)  {
+ 		if (!no_modify) {
+ 			sb->sb_pquotino = 0;
+ 			dsb->sb_pquotino = 0;
+diff --git a/repair/attr_repair.c b/repair/attr_repair.c
+index d60b664..5ce7bb6 100644
+--- a/repair/attr_repair.c
++++ b/repair/attr_repair.c
+@@ -746,9 +746,10 @@ valuecheck(
+ 	void *valuep;
+ 	int clearit = 0;
+ 
+-	if ((strncmp(namevalue, SGI_ACL_FILE, SGI_ACL_FILE_SIZE) == 0) ||
+-			(strncmp(namevalue, SGI_ACL_DEFAULT,
+-				SGI_ACL_DEFAULT_SIZE) == 0)) {
++	if ((namelen == SGI_ACL_FILE_SIZE &&
++	     strncmp(namevalue, SGI_ACL_FILE, SGI_ACL_FILE_SIZE) == 0) ||
++	    (namelen == SGI_ACL_DEFAULT_SIZE &&
++	     strncmp(namevalue, SGI_ACL_DEFAULT, SGI_ACL_DEFAULT_SIZE) == 0)) {
+ 		if (value == NULL) {
+ 			valuep = malloc(valuelen);
+ 			if (!valuep)
+diff --git a/repair/dinode.c b/repair/dinode.c
+index 38a6562..035212c 100644
+--- a/repair/dinode.c
++++ b/repair/dinode.c
+@@ -667,12 +667,14 @@ _("inode %" PRIu64 " - bad extent overflows - start %" PRIu64 ", "
+ 					irec.br_startoff);
+ 				goto done;
+ 		}
+-		if (irec.br_startoff >= fs_max_file_offset)  {
++		/* Ensure this extent does not extend beyond the max offset */
++		if (irec.br_startoff + irec.br_blockcount - 1 >
++							fs_max_file_offset) {
+ 			do_warn(
+-_("inode %" PRIu64 " - extent offset too large - start %" PRIu64 ", "
+-  "count %" PRIu64 ", offset %" PRIu64 "\n"),
+-				ino, irec.br_startblock, irec.br_blockcount,
+-				irec.br_startoff);
++_("inode %" PRIu64 " - extent exceeds max offset - start %" PRIu64 ", "
++  "count %" PRIu64 ", physical block %" PRIu64 "\n"),
++				ino, irec.br_startoff, irec.br_blockcount,
++				irec.br_startblock);
+ 			goto done;
+ 		}
+ 
+@@ -1333,7 +1335,7 @@ process_symlink(
+ 	xfs_dinode_t	*dino,
+ 	blkmap_t 	*blkmap)
+ {
+-	char			*symlink, *cptr;
++	char			*symlink;
+ 	char			data[MAXPATHLEN];
+ 
+ 	/*
+@@ -1380,31 +1382,6 @@ _("found illegal null character in symlink inode %" PRIu64 "\n"),
+ 		return(1);
+ 	}
+ 
+-	/*
+-	 * check for any component being too long
+-	 */
+-	if (be64_to_cpu(dino->di_size) >= MAXNAMELEN)  {
+-		cptr = strchr(symlink, '/');
+-
+-		while (cptr != NULL)  {
+-			if (cptr - symlink >= MAXNAMELEN)  {
+-				do_warn(
+-_("component of symlink in inode %" PRIu64 " too long\n"),
+-					lino);
+-				return(1);
+-			}
+-			symlink = cptr + 1;
+-			cptr = strchr(symlink, '/');
+-		}
+-
+-		if (strlen(symlink) >= MAXNAMELEN)  {
+-			do_warn(
+-_("component of symlink in inode %" PRIu64 " too long\n"),
+-				lino);
+-			return(1);
+-		}
+-	}
+-
+ 	return(0);
+ }
+ 
+@@ -2314,6 +2291,30 @@ process_dinode_int(xfs_mount_t *mp,
+ 	 */
+ 	ASSERT(uncertain == 0 || verify_mode != 0);
+ 
++	/*
++	 * This is the only valid point to check the CRC; after this we may have
++	 * made changes which invalidate it, and the CRC is only updated again
++	 * when it gets written out.
++	 *
++	 * Of course if we make any modifications after this, the inode gets
++	 * rewritten, and the CRC is updated automagically.
++	 */
++	if (xfs_sb_version_hascrc(&mp->m_sb) &&
++	    !xfs_verify_cksum((char *)dino, mp->m_sb.sb_inodesize,
++				XFS_DINODE_CRC_OFF)) {
++		retval = 1;
++		if (!uncertain)
++			do_warn(_("bad CRC for inode %" PRIu64 "%c"),
++				lino, verify_mode ? '\n' : ',');
++		if (!verify_mode) {
++			if (!no_modify) {
++				do_warn(_(" will rewrite\n"));
++				*dirty = 1;
++			} else
++				do_warn(_(" would rewrite\n"));
++		}
++	}
++
+ 	if (be16_to_cpu(dino->di_magic) != XFS_DINODE_MAGIC)  {
+ 		retval = 1;
+ 		if (!uncertain)
+diff --git a/repair/dir2.c b/repair/dir2.c
+index 6b8964d..644c214 100644
+--- a/repair/dir2.c
++++ b/repair/dir2.c
+@@ -198,6 +198,13 @@ _("bad dir magic number 0x%x in inode %" PRIu64 " bno = %u\n"),
+ 					da_cursor->ino, bno);
+ 			goto error_out;
+ 		}
++		/* corrupt node; rebuild the dir. */
++		if (bp->b_error == EFSBADCRC || bp->b_error == EFSCORRUPTED) {
++			do_warn(
++_("corrupt tree block %u for directory inode %" PRIu64 "\n"),
++				bno, da_cursor->ino);
++			goto error_out;
++		}
+ 		btree = xfs_da3_node_tree_p(node);
+ 		if (nodehdr.count > mp->m_dir_node_ents)  {
+ 			libxfs_putbuf(bp);
+@@ -861,94 +868,33 @@ process_sf_dir2(
+ _("entry \"%*.*s\" in shortform directory %" PRIu64 " references %s inode %" PRIu64 "\n"),
+ 				namelen, namelen, sfep->name, ino, junkreason,
+ 				lino);
+-		if (namelen == 0)  {
+-			/*
+-			 * if we're really lucky, this is
+-			 * the last entry in which case we
+-			 * can use the dir size to set the
+-			 * namelen value.  otherwise, forget
+-			 * it because we're not going to be
+-			 * able to find the next entry.
+-			 */
+-			bad_sfnamelen = 1;
+ 
+-			if (i == num_entries - 1)  {
+-				namelen = ino_dir_size -
+-					((__psint_t) &sfep->name[0] -
+-					 (__psint_t) sfp);
+-				if (!no_modify)  {
+-					do_warn(
+-_("zero length entry in shortform dir %" PRIu64 ", resetting to %d\n"),
+-						ino, namelen);
+-					sfep->namelen = namelen;
+-				} else  {
+-					do_warn(
+-_("zero length entry in shortform dir %" PRIu64 ", would set to %d\n"),
+-						ino, namelen);
+-				}
+-			} else  {
+-				do_warn(
+-_("zero length entry in shortform dir %" PRIu64 ""),
+-					ino);
+-				if (!no_modify)
+-					do_warn(_(", junking %d entries\n"),
+-						num_entries - i);
+-				else
+-					do_warn(_(", would junk %d entries\n"),
+-						num_entries - i);
+-				/*
+-				 * don't process the rest of the directory,
+-				 * break out of processing looop
+-				 */
+-				break;
+-			}
++		/* is dir namelen 0 or does this entry extend past dir size? */
++		if (namelen == 0) {
++			junkreason = _("is zero length");
++			bad_sfnamelen = 1;
+ 		} else if ((__psint_t) sfep - (__psint_t) sfp +
+ 				xfs_dir3_sf_entsize(mp, sfp, sfep->namelen)
+ 							> ino_dir_size)  {
++			junkreason = _("extends past end of dir");
+ 			bad_sfnamelen = 1;
++		}
+ 
+-			if (i == num_entries - 1)  {
+-				namelen = ino_dir_size -
+-					((__psint_t) &sfep->name[0] -
+-					 (__psint_t) sfp);
+-				do_warn(
+-_("size of last entry overflows space left in in shortform dir %" PRIu64 ", "),
+-					ino);
+-				if (!no_modify)  {
+-					do_warn(_("resetting to %d\n"),
+-						namelen);
+-					sfep->namelen = namelen;
+-					*dino_dirty = 1;
+-				} else  {
+-					do_warn(_("would reset to %d\n"),
+-						namelen);
+-				}
+-			} else  {
+-				do_warn(
+-_("size of entry #%d overflows space left in in shortform dir %" PRIu64 "\n"),
+-					i, ino);
+-				if (!no_modify)  {
+-					if (i == num_entries - 1)
+-						do_warn(
+-						_("junking entry #%d\n"),
+-							i);
+-					else
+-						do_warn(
+-						_("junking %d entries\n"),
+-							num_entries - i);
+-				} else  {
+-					if (i == num_entries - 1)
+-						do_warn(
+-						_("would junk entry #%d\n"),
+-							i);
+-					else
+-						do_warn(
+-						_("would junk %d entries\n"),
+-							num_entries - i);
+-				}
+-
+-				break;
+-			}
++		if (bad_sfnamelen) {
++			do_warn(
++_("entry #%d %s in shortform dir %" PRIu64),
++				i, junkreason, ino);
++			if (!no_modify)
++				do_warn(_(", junking %d entries\n"),
++					num_entries - i);
++			else
++				do_warn(_(", would junk %d entries\n"),
++					num_entries - i);
++			/*
++			 * don't process the rest of the directory,
++			 * break out of processing loop
++			 */
++			break;
+ 		}
+ 
+ 		/*
+@@ -1379,6 +1325,18 @@ _("entry \"%*.*s\" at block %d offset %" PRIdPTR " in directory inode %" PRIu64
+ 				dep->namelen, dep->namelen, dep->name,
+ 				da_bno, (intptr_t)ptr - (intptr_t)d, ino,
+ 				clearreason, ent_ino);
++
++		/*
++		 * We have a special dot & dotdot fixer-upper below which can
++		 * sort out the proper inode number, so don't clear it.
++		 */
++		if ((dep->namelen == 1 && dep->name[0] == '.') ||
++		    (dep->namelen == 2 &&
++		     dep->name[0] == '.' && dep->name[1] == '.')) {
++			clearino = 0;
++			clearreason = NULL;
++		}
++
+ 		/*
+ 		 * If the name length is 0 (illegal) make it 1 and blast
+ 		 * the entry.
+@@ -1468,6 +1426,7 @@ _("bad .. entry in root directory inode %" PRIu64 ", was %" PRIu64 ": "),
+ 					} else {
+ 						do_warn(_("would correct\n"));
+ 					}
++					*parent = ino;
+ 				}
+ 			}
+ 			/*
+diff --git a/repair/globals.h b/repair/globals.h
+index 6207ca1..f386686 100644
+--- a/repair/globals.h
++++ b/repair/globals.h
+@@ -57,7 +57,6 @@
+ #define XR_LOG2BSIZE_MIN	9	/* min/max fs blocksize (log2) */
+ #define XR_LOG2BSIZE_MAX	16	/* 2^XR_* == blocksize */
+ 
+-#define	NUM_SBS			8	/* max # of sbs to verify */
+ #define NUM_AGH_SECTS		4	/* # of components in an ag header */
+ 
+ /*
+@@ -88,7 +87,6 @@ EXTERN char	*iobuf;			/* large buffer */
+ EXTERN int	iobuf_size;
+ EXTERN char	*smallbuf;		/* small (1-4 page) buffer */
+ EXTERN int	smallbuf_size;
+-EXTERN char	*sb_bufs[NUM_SBS];	/* superblock buffers */
+ EXTERN int	sbbuf_size;
+ 
+ /* direct I/O info */
+diff --git a/repair/phase6.c b/repair/phase6.c
+index 0ec4f07..105bce4 100644
+--- a/repair/phase6.c
++++ b/repair/phase6.c
+@@ -1321,7 +1321,8 @@ longform_dir2_rebuild(
+ 	 * for the libxfs_dir_init() call).
+ 	 */
+ 	pip.i_ino = get_inode_parent(irec, ino_offset);
+-	if (pip.i_ino == NULLFSINO)
++	if (pip.i_ino == NULLFSINO ||
++	    xfs_dir_ino_validate(mp, pip.i_ino))
+ 		pip.i_ino = mp->m_sb.sb_rootino;
+ 
+ 	xfs_bmap_init(&flist, &firstblock);
+@@ -1348,12 +1349,19 @@ longform_dir2_rebuild(
+ 
+ 	ASSERT(done);
+ 
+-	libxfs_dir_init(tp, ip, &pip);
++	error = libxfs_dir_init(tp, ip, &pip);
++	if (error) {
++		do_warn(_("xfs_dir_init failed -- error - %d\n"), error);
++		goto out_bmap_cancel;
++	}
+ 
+ 	error = libxfs_bmap_finish(&tp, &flist, &committed);
+ 
+ 	libxfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+ 
++	if (ino == mp->m_sb.sb_rootino)
++		need_root_dotdot = 0;
++
+ 	/* go through the hash list and re-add the inodes */
+ 
+ 	for (p = hashtab->first; p; p = p->nextbyorder) {
+@@ -1922,6 +1930,66 @@ _("entry \"%s\" in dir inode %" PRIu64 " inconsistent with .. value (%" PRIu64 "
+ 	freetab->ents[db].s = 0;
+ }
+ 
++/* check v5 metadata */
++static int
++__check_dir3_header(
++	struct xfs_mount	*mp,
++	struct xfs_buf		*bp,
++	xfs_ino_t		ino,
++	__be64			owner,
++	__be64			blkno,
++	uuid_t			*uuid)
++{
++
++	/* verify owner */
++	if (be64_to_cpu(owner) != ino) {
++		do_warn(
++_("expected owner inode %" PRIu64 ", got %llu, directory block %" PRIu64 "\n"),
++			ino, be64_to_cpu(owner), bp->b_bn);
++		return 1;
++	}
++	/* verify block number */
++	if (be64_to_cpu(blkno) != bp->b_bn) {
++		do_warn(
++_("expected block %" PRIu64 ", got %llu, directory inode %" PRIu64 "\n"),
++			bp->b_bn, be64_to_cpu(blkno), ino);
++		return 1;
++	}
++	/* verify uuid */
++	if (platform_uuid_compare(uuid, &mp->m_sb.sb_uuid) != 0) {
++		do_warn(
++_("wrong FS UUID, directory inode %" PRIu64 " block %" PRIu64 "\n"),
++			ino, bp->b_bn);
++		return 1;
++	}
++
++	return 0;
++}
++
++static int
++check_da3_header(
++	struct xfs_mount	*mp,
++	struct xfs_buf		*bp,
++	xfs_ino_t		ino)
++{
++	struct xfs_da3_blkinfo	*info = bp->b_addr;
++
++	return __check_dir3_header(mp, bp, ino, info->owner, info->blkno,
++				   &info->uuid);
++}
++
++static int
++check_dir3_header(
++	struct xfs_mount	*mp,
++	struct xfs_buf		*bp,
++	xfs_ino_t		ino)
++{
++	struct xfs_dir3_blk_hdr	*info = bp->b_addr;
++
++	return __check_dir3_header(mp, bp, ino, info->owner, info->blkno,
++				   &info->uuid);
++}
++
+ /*
+  * Check contents of leaf-form block.
+  */
+@@ -1948,7 +2016,12 @@ longform_dir2_check_leaf(
+ 	da_bno = mp->m_dirleafblk;
+ 	error = dir_read_buf(ip, da_bno, -1, &bp, &xfs_dir3_leaf1_buf_ops,
+ 			     &fixit);
+-	if (error) {
++	if (error == EFSBADCRC || error == EFSCORRUPTED || fixit) {
++		do_warn(
++	_("leaf block %u for directory inode %" PRIu64 " bad CRC\n"),
++			da_bno, ip->i_ino);
++		return 1;
++	} else if (error) {
+ 		do_error(
+ 	_("can't read block %u for directory inode %" PRIu64 ", error %d\n"),
+ 			da_bno, ip->i_ino, error);
+@@ -1973,6 +2046,15 @@ longform_dir2_check_leaf(
+ 		libxfs_putbuf(bp);
+ 		return 1;
+ 	}
++
++	if (leafhdr.magic == XFS_DIR3_LEAF1_MAGIC) {
++		error = check_da3_header(mp, bp, ip->i_ino);
++		if (error) {
++			libxfs_putbuf(bp);
++			return error;
++		}
++	}
++
+ 	seeval = dir_hash_see_all(hashtab, ents, leafhdr.count, leafhdr.stale);
+ 	if (dir_hash_check(hashtab, ip, seeval)) {
+ 		libxfs_putbuf(bp);
+@@ -2047,12 +2129,9 @@ longform_dir2_check_node(
+ 		xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+ 		ents = xfs_dir3_leaf_ents_p(leaf);
+ 		if (!(leafhdr.magic == XFS_DIR2_LEAFN_MAGIC ||
+-		      leafhdr.magic == XFS_DIR3_LEAFN_MAGIC)) {
+-			if (leafhdr.magic == XFS_DA_NODE_MAGIC ||
+-			    leafhdr.magic == XFS_DA3_NODE_MAGIC) {
+-				libxfs_putbuf(bp);
+-				continue;
+-			}
++		      leafhdr.magic == XFS_DIR3_LEAFN_MAGIC ||
++		      leafhdr.magic == XFS_DA_NODE_MAGIC ||
++		      leafhdr.magic == XFS_DA3_NODE_MAGIC)) {
+ 			do_warn(
+ 	_("unknown magic number %#x for block %u in directory inode %" PRIu64 "\n"),
+ 				leafhdr.magic, da_bno, ip->i_ino);
+@@ -2060,6 +2139,23 @@ longform_dir2_check_node(
+ 			return 1;
+ 		}
+ 
++		/* check v5 metadata */
++		if (leafhdr.magic == XFS_DIR3_LEAFN_MAGIC ||
++		    leafhdr.magic == XFS_DA3_NODE_MAGIC) {
++			error = check_da3_header(mp, bp, ip->i_ino);
++			if (error) {
++				libxfs_putbuf(bp);
++				return error;
++			}
++		}
++
++		/* ignore nodes */
++		if (leafhdr.magic == XFS_DA_NODE_MAGIC ||
++		    leafhdr.magic == XFS_DA3_NODE_MAGIC) {
++			libxfs_putbuf(bp);
++			continue;
++		}
++
+ 		/*
+ 		 * If there's a validator error, we need to ensure that we got
+ 		 * the right ops on the buffer for when we write it back out.
+@@ -2113,6 +2209,14 @@ longform_dir2_check_node(
+ 			libxfs_putbuf(bp);
+ 			return 1;
+ 		}
++
++		if (freehdr.magic == XFS_DIR3_FREE_MAGIC) {
++			error = check_dir3_header(mp, bp, ip->i_ino);
++			if (error) {
++				libxfs_putbuf(bp);
++				return error;
++			}
++		}
+ 		for (i = used = 0; i < freehdr.nvalid; i++) {
+ 			if (i + freehdr.firstdb >= freetab->nents ||
+ 					freetab->ents[i + freehdr.firstdb].v !=
+@@ -2204,6 +2308,7 @@ longform_dir2_entry_check(xfs_mount_t	*mp,
+ 	     da_bno = (xfs_dablk_t)next_da_bno) {
+ 		const struct xfs_buf_ops *ops;
+ 		int			 error;
++		struct xfs_dir2_data_hdr *d;
+ 
+ 		next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
+ 		if (bmap_next_offset(NULL, ip, &next_da_bno, XFS_DATA_FORK)) {
+@@ -2252,6 +2357,20 @@ longform_dir2_entry_check(xfs_mount_t	*mp,
+ 			}
+ 			continue;
+ 		}
++
++		/* check v5 metadata */
++		d = bplist[db]->b_addr;
++		if (be32_to_cpu(d->magic) == XFS_DIR3_BLOCK_MAGIC ||
++		    be32_to_cpu(d->magic) == XFS_DIR3_DATA_MAGIC) {
++			struct xfs_buf		 *bp = bplist[db];
++
++			error = check_dir3_header(mp, bp, ino);
++			if (error) {
++				fixit++;
++				continue;
++			}
++		}
++
+ 		longform_dir2_entry_check_data(mp, ip, num_illegal, need_dot,
+ 				irec, ino_offset, &bplist[db], hashtab,
+ 				&freetab, da_bno, isblock);
+diff --git a/repair/sb.c b/repair/sb.c
+index ad27756..ce20d0d 100644
+--- a/repair/sb.c
++++ b/repair/sb.c
+@@ -196,7 +196,8 @@ sb_validate_ino_align(struct xfs_sb *sb)
+ 	if (!xfs_sb_version_hascrc(sb))
+ 		return false;
+ 
+-	align *= sb->sb_inodesize / XFS_DINODE_MIN_SIZE;
++	align = (XFS_INODE_BIG_CLUSTER_SIZE *
++		 sb->sb_inodesize / XFS_DINODE_MIN_SIZE) >> sb->sb_blocklog;
+ 	if (align == sb->sb_inoalignmt)
+ 		return true;
+ 
+@@ -702,20 +703,11 @@ verify_set_primary_sb(xfs_sb_t		*rsb,
+ 	xfs_sb_t	*sb;
+ 	fs_geo_list_t	*list;
+ 	fs_geo_list_t	*current;
+-	char		*checked;
+ 	xfs_agnumber_t	agno;
+ 	int		num_sbs;
+-	int		skip;
+ 	int		size;
+ 	int		num_ok;
+ 	int		retval;
+-	int		round;
+-
+-	/*
+-	 * select the number of secondaries to try for
+-	 */
+-	num_sbs = MIN(NUM_SBS, rsb->sb_agcount);
+-	skip = howmany(num_sbs, rsb->sb_agcount);
+ 
+ 	/*
+ 	 * We haven't been able to validate the sector size yet properly
+@@ -724,61 +716,48 @@ verify_set_primary_sb(xfs_sb_t		*rsb,
+ 	 * sector size rather than the sector size in @rsb.
+ 	 */
+ 	size = NUM_AGH_SECTS * (1 << (XFS_MAX_SECTORSIZE_LOG));
+-	retval = 0;
+ 	list = NULL;
+ 	num_ok = 0;
+ 	*sb_modified = 0;
++	num_sbs = rsb->sb_agcount;
+ 
+ 	sb = (xfs_sb_t *) alloc_ag_buf(size);
+-	checked = calloc(rsb->sb_agcount, sizeof(char));
+-	if (!checked) {
+-		do_error(_("calloc failed in verify_set_primary_sb\n"));
+-		exit(1);
+-	}
+ 
+ 	/*
+ 	 * put the primary sb geometry info onto the geometry list
+ 	 */
+-	checked[sb_index] = 1;
+ 	get_sb_geometry(&geo, rsb);
+ 	list = add_geo(list, &geo, sb_index);
+ 
+ 	/*
+-	 * grab N secondaries.  check them off as we get them
+-	 * so we only process each one once
++	 * scan the secondaries and check them off as we get them so we only
++	 * process each one once
+ 	 */
+-	for (round = 0; round < skip; round++)  {
+-		for (agno = round; agno < rsb->sb_agcount; agno += skip)  {
+-			if (checked[agno])
+-				continue;
++	for (agno = 1; agno < rsb->sb_agcount; agno++) {
++		off = (xfs_off_t)agno * rsb->sb_agblocks << rsb->sb_blocklog;
+ 
+-			off = (xfs_off_t)agno * rsb->sb_agblocks << rsb->sb_blocklog;
+-
+-			checked[agno] = 1;
+-			retval = get_sb(sb, off, size, agno);
+-			if (retval == XR_EOF)
+-				goto out_free_list;
+-
+-			if (retval == XR_OK) {
+-				/*
+-				 * save away geometry info.
+-				 * don't bother checking the sb
+-				 * against the agi/agf as the odds
+-				 * of the sb being corrupted in a way
+-				 * that it is internally consistent
+-				 * but not consistent with the rest
+-				 * of the filesystem is really really low.
+-				 */
+-				get_sb_geometry(&geo, sb);
+-				list = add_geo(list, &geo, agno);
+-				num_ok++;
+-			}
++		retval = get_sb(sb, off, size, agno);
++		if (retval == XR_EOF)
++			goto out_free_list;
++
++		if (retval == XR_OK) {
++			/*
++			 * save away geometry info. don't bother checking the
++			 * sb against the agi/agf as the odds of the sb being
++			 * corrupted in a way that it is internally consistent
++			 * but not consistent with the rest of the filesystem is
++			 * really really low.
++			 */
++			get_sb_geometry(&geo, sb);
++			list = add_geo(list, &geo, agno);
++			num_ok++;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * see if we have enough superblocks to bother with
+ 	 */
++	retval = 0;
+ 	if (num_ok < num_sbs / 2) {
+ 		retval = XR_INSUFF_SEC_SB;
+ 		goto out_free_list;
+@@ -867,6 +846,5 @@ verify_set_primary_sb(xfs_sb_t		*rsb,
+ out_free_list:
+ 	free_geo(list);
+ 	free(sb);
+-	free(checked);
+-	return(retval);
++	return retval;
+ }
+diff --git a/repair/scan.c b/repair/scan.c
+index 142d8d7..6508a47 100644
+--- a/repair/scan.c
++++ b/repair/scan.c
+@@ -225,7 +225,24 @@ _("expected level %d got %d in inode %" PRIu64 ", (%s fork) bmbt block %" PRIu64
+ 			do_warn(
+ _("expected owner inode %" PRIu64 ", got %llu, bmbt block %" PRIu64 "\n"),
+ 				ino, be64_to_cpu(block->bb_u.l.bb_owner), bno);
+-			return(1);
++			return 1;
++		}
++		/* verify block number */
++		if (be64_to_cpu(block->bb_u.l.bb_blkno) !=
++		    XFS_FSB_TO_DADDR(mp, bno)) {
++			do_warn(
++_("expected block %" PRIu64 ", got %llu, bmbt block %" PRIu64 "\n"),
++				XFS_FSB_TO_DADDR(mp, bno),
++				be64_to_cpu(block->bb_u.l.bb_blkno), bno);
++			return 1;
++		}
++		/* verify uuid */
++		if (platform_uuid_compare(&block->bb_u.l.bb_uuid,
++					  &mp->m_sb.sb_uuid) != 0) {
++			do_warn(
++_("wrong FS UUID, bmbt block %" PRIu64 "\n"),
++				bno);
++			return 1;
+ 		}
+ 	}
+ 
+@@ -770,7 +787,8 @@ scan_single_ino_chunk(
+ 	    (inodes_per_block <= XFS_INODES_PER_CHUNK && off !=  0) ||
+ 	    (inodes_per_block > XFS_INODES_PER_CHUNK &&
+ 	     off % XFS_INODES_PER_CHUNK != 0) ||
+-	    (fs_aligned_inodes && agbno % fs_ino_alignment != 0))  {
++	    (fs_aligned_inodes && fs_ino_alignment &&
++	     agbno % fs_ino_alignment != 0))  {
+ 		do_warn(
+ 	_("badly aligned inode rec (starting inode = %" PRIu64 ")\n"),
+ 			lino);
+@@ -929,7 +947,8 @@ scan_single_finobt_chunk(
+ 	    (inodes_per_block <= XFS_INODES_PER_CHUNK && off !=  0) ||
+ 	    (inodes_per_block > XFS_INODES_PER_CHUNK &&
+ 	     off % XFS_INODES_PER_CHUNK != 0) ||
+-	    (fs_aligned_inodes && agbno % fs_ino_alignment != 0)) {
++	    (fs_aligned_inodes && fs_ino_alignment &&
++	     agbno % fs_ino_alignment != 0)) {
+ 		do_warn(
+ 	_("badly aligned finobt inode rec (starting inode = %" PRIu64 ")\n"),
+ 			lino);
+@@ -1483,7 +1502,7 @@ scan_ag(
+ 	int		status;
+ 	char		*objname = NULL;
+ 
+-	sb = (struct xfs_sb *)calloc(BBSIZE, 1);
++	sb = (struct xfs_sb *)calloc(BBTOB(XFS_FSS_TO_BB(mp, 1)), 1);
+ 	if (!sb) {
+ 		do_error(_("can't allocate memory for superblock\n"));
+ 		return;
+diff --git a/repair/versions.c b/repair/versions.c
+index c1dff72..10bcd29 100644
+--- a/repair/versions.c
++++ b/repair/versions.c
+@@ -175,6 +175,20 @@ _("WARNING:  you have disallowed superblock-feature-bits-allowed\n"
+ 		}
+ 	}
+ 
++	/* Look for V5 feature flags we don't know about */
++	if (XFS_SB_VERSION_NUM(sb) >= XFS_SB_VERSION_5 &&
++	    (xfs_sb_has_compat_feature(sb, XFS_SB_FEAT_COMPAT_UNKNOWN) ||
++	     xfs_sb_has_ro_compat_feature(sb, XFS_SB_FEAT_RO_COMPAT_UNKNOWN) ||
++	     xfs_sb_has_incompat_feature(sb, XFS_SB_FEAT_INCOMPAT_UNKNOWN))) {
++		do_warn(
++_("Superblock has unknown compat/rocompat/incompat features (0x%x/0x%x/0x%x).\n"
++  "Using a more recent xfs_repair is recommended.\n"),
++			sb->sb_features_compat & XFS_SB_FEAT_COMPAT_UNKNOWN,
++			sb->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_UNKNOWN,
++			sb->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_UNKNOWN);
++		return 1;
++	}
++
+ 	if (xfs_sb_version_hasattr(sb))  {
+ 		if (!fs_attributes_allowed)  {
+ 			if (!no_modify)  {
diff --git a/SOURCES/xfsprogs-xfs_metadump-CVE-2012-2150.patch b/SOURCES/xfsprogs-xfs_metadump-CVE-2012-2150.patch
new file mode 100644
index 0000000..ff59497
--- /dev/null
+++ b/SOURCES/xfsprogs-xfs_metadump-CVE-2012-2150.patch
@@ -0,0 +1,1016 @@
+diff --git a/db/metadump.c b/db/metadump.c
+index a599571..238f864 100644
+--- a/db/metadump.c
++++ b/db/metadump.c
+@@ -17,6 +17,7 @@
+  */
+ 
+ #include <libxfs.h>
++#include <libxlog.h>
+ #include "bmap.h"
+ #include "command.h"
+ #include "metadump.h"
+@@ -56,7 +57,7 @@ static void	metadump_help(void);
+ 
+ static const cmdinfo_t	metadump_cmd =
+ 	{ "metadump", NULL, metadump_f, 0, -1, 0,
+-		N_("[-e] [-g] [-m max_extent] [-w] [-o] filename"),
++		N_("[-a] [-e] [-g] [-m max_extent] [-w] [-o] filename"),
+ 		N_("dump metadata to a file"), metadump_help };
+ 
+ static FILE		*outf;		/* metadump file */
+@@ -73,7 +74,8 @@ static xfs_ino_t	cur_ino;
+ static int		show_progress = 0;
+ static int		stop_on_read_error = 0;
+ static int		max_extent_size = DEFAULT_MAX_EXT_SIZE;
+-static int		dont_obfuscate = 0;
++static int		obfuscate = 1;
++static int		zero_stale_data = 1;
+ static int		show_warnings = 0;
+ static int		progress_since_warning = 0;
+ 
+@@ -92,6 +94,7 @@ metadump_help(void)
+ " for compressing and sending to an XFS maintainer for corruption analysis \n"
+ " or xfs_repair failures.\n\n"
+ " Options:\n"
++"   -a -- Copy full metadata blocks without zeroing unused space\n"
+ "   -e -- Ignore read errors and keep going\n"
+ "   -g -- Display dump progress\n"
+ "   -m -- Specify max extent size in blocks to copy (default = %d blocks)\n"
+@@ -242,6 +245,124 @@ write_buf(
+ 	return seenint() ? -EINTR : 0;
+ }
+ 
++static void
++zero_btree_node(
++	struct xfs_btree_block	*block,
++	typnm_t			btype)
++{
++	int			nrecs;
++	xfs_bmbt_ptr_t		*bpp;
++	xfs_bmbt_key_t		*bkp;
++	xfs_inobt_ptr_t		*ipp;
++	xfs_inobt_key_t		*ikp;
++	xfs_alloc_ptr_t		*app;
++	xfs_alloc_key_t		*akp;
++	void			*zp1, *zp2;
++	int			zlen1, zlen2;
++
++	nrecs = be16_to_cpu(block->bb_numrecs);
++
++	switch (btype) {
++	case TYP_BMAPBTA:
++	case TYP_BMAPBTD:
++		bkp = XFS_BMBT_KEY_ADDR(mp, block, 1);
++		bpp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]);
++		zp1 = &bkp[nrecs];
++		zlen1 = (char *)&bpp[0] - (char *)&bkp[nrecs];
++		zp2 = &bpp[nrecs];
++		zlen2 = (char *)block + mp->m_sb.sb_blocksize -
++							(char *)&bpp[nrecs];
++		break;
++	case TYP_INOBT:
++	case TYP_FINOBT:
++		ikp = XFS_INOBT_KEY_ADDR(mp, block, 1);
++		ipp = XFS_INOBT_PTR_ADDR(mp, block, 1, mp->m_inobt_mxr[1]);
++		zp1 = &ikp[nrecs];
++		zlen1 = (char *)&ipp[0] - (char *)&ikp[nrecs];
++		zp2 = &ipp[nrecs];
++		zlen2 = (char *)block + mp->m_sb.sb_blocksize -
++							(char *)&ipp[nrecs];
++		break;
++	case TYP_BNOBT:
++	case TYP_CNTBT:
++		akp = XFS_ALLOC_KEY_ADDR(mp, block, 1);
++		app = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
++		zp1 = &akp[nrecs];
++		zlen1 = (char *)&app[0] - (char *)&akp[nrecs];
++		zp2 = &app[nrecs];
++		zlen2 = (char *)block + mp->m_sb.sb_blocksize -
++							(char *)&app[nrecs];
++		break;
++	default:
++		zp1 = NULL;
++		break;
++	}
++
++	if (zp1 && zp2) {
++		/* Zero from end of keys to beginning of pointers */
++		memset(zp1, 0, zlen1);
++		/* Zero from end of pointers to end of block */
++		memset(zp2, 0, zlen2);
++	}
++}
++
++static void
++zero_btree_leaf(
++	struct xfs_btree_block	*block,
++	typnm_t			btype)
++{
++	int			nrecs;
++	struct xfs_bmbt_rec	*brp;
++	struct xfs_inobt_rec	*irp;
++	struct xfs_alloc_rec	*arp;
++	void			*zp;
++	int			zlen;
++
++	nrecs = be16_to_cpu(block->bb_numrecs);
++
++	switch (btype) {
++	case TYP_BMAPBTA:
++	case TYP_BMAPBTD:
++		brp = XFS_BMBT_REC_ADDR(mp, block, 1);
++		zp = &brp[nrecs];
++		zlen = (char *)block + mp->m_sb.sb_blocksize - (char *)&brp[nrecs];
++		break;
++	case TYP_INOBT:
++	case TYP_FINOBT:
++		irp = XFS_INOBT_REC_ADDR(mp, block, 1);
++		zp = &irp[nrecs];
++		zlen = (char *)block + mp->m_sb.sb_blocksize - (char *)&irp[nrecs];
++		break;
++	case TYP_BNOBT:
++	case TYP_CNTBT:
++		arp = XFS_ALLOC_REC_ADDR(mp, block, 1);
++		zp = &arp[nrecs];
++		zlen = (char *)block + mp->m_sb.sb_blocksize - (char *)&arp[nrecs];
++		break;
++	default:
++		zp = NULL;
++		break;
++	}
++
++	/* Zero from end of records to end of block */
++	if (zp && zlen < mp->m_sb.sb_blocksize)
++		memset(zp, 0, zlen);
++}
++
++static void
++zero_btree_block(
++	struct xfs_btree_block	*block,
++	typnm_t			btype)
++{
++	int			level;
++
++	level = be16_to_cpu(block->bb_level);
++
++	if (level > 0)
++		zero_btree_node(block, btype);
++	else
++		zero_btree_leaf(block, btype);
++}
+ 
+ static int
+ scan_btree(
+@@ -268,6 +389,12 @@ scan_btree(
+ 		rval = !stop_on_read_error;
+ 		goto pop_out;
+ 	}
++
++	if (zero_stale_data) {
++		zero_btree_block(iocur_top->data, btype);
++		iocur_top->need_crc = 1;
++	}
++
+ 	if (write_buf(iocur_top))
+ 		goto pop_out;
+ 
+@@ -960,7 +1087,7 @@ generate_obfuscated_name(
+ }
+ 
+ static void
+-obfuscate_sf_dir(
++process_sf_dir(
+ 	xfs_dinode_t		*dip)
+ {
+ 	struct xfs_dir2_sf_hdr	*sfp;
+@@ -1007,12 +1134,18 @@ obfuscate_sf_dir(
+ 					 (char *)sfp);
+ 		}
+ 
+-		generate_obfuscated_name(xfs_dir3_sfe_get_ino(mp, sfp, sfep),
++		if (obfuscate)
++			generate_obfuscated_name(
++					 xfs_dir3_sfe_get_ino(mp, sfp, sfep),
+ 					 namelen, &sfep->name[0]);
+ 
+ 		sfep = (xfs_dir2_sf_entry_t *)((char *)sfep +
+ 				xfs_dir3_sf_entsize(mp, sfp, namelen));
+ 	}
++
++	/* zero stale data in rest of space in data fork, if any */
++	if (zero_stale_data && (ino_dir_size < XFS_DFORK_DSIZE(dip, mp)))
++		memset(sfep, 0, XFS_DFORK_DSIZE(dip, mp) - ino_dir_size);
+ }
+ 
+ /*
+@@ -1059,7 +1192,7 @@ obfuscate_path_components(
+ }
+ 
+ static void
+-obfuscate_sf_symlink(
++process_sf_symlink(
+ 	xfs_dinode_t		*dip)
+ {
+ 	__uint64_t		len;
+@@ -1074,16 +1207,21 @@ obfuscate_sf_symlink(
+ 	}
+ 
+ 	buf = (char *)XFS_DFORK_DPTR(dip);
+-	obfuscate_path_components(buf, len);
++	if (obfuscate)
++		obfuscate_path_components(buf, len);
++
++	/* zero stale data in rest of space in data fork, if any */
++	if (zero_stale_data && len < XFS_DFORK_DSIZE(dip, mp))
++		memset(&buf[len], 0, XFS_DFORK_DSIZE(dip, mp) - len);
+ }
+ 
+ static void
+-obfuscate_sf_attr(
++process_sf_attr(
+ 	xfs_dinode_t		*dip)
+ {
+ 	/*
+-	 * with extended attributes, obfuscate the names and zero the actual
+-	 * values.
++	 * with extended attributes, obfuscate the names and fill the actual
++	 * values with 'v' (to see a valid string length, as opposed to NULLs)
+ 	 */
+ 
+ 	xfs_attr_shortform_t	*asfp;
+@@ -1122,16 +1260,24 @@ obfuscate_sf_attr(
+ 			break;
+ 		}
+ 
+-		generate_obfuscated_name(0, asfep->namelen, &asfep->nameval[0]);
+-		memset(&asfep->nameval[asfep->namelen], 0, asfep->valuelen);
++		if (obfuscate) {
++			generate_obfuscated_name(0, asfep->namelen,
++						 &asfep->nameval[0]);
++			memset(&asfep->nameval[asfep->namelen], 'v',
++			       asfep->valuelen);
++		}
+ 
+ 		asfep = (xfs_attr_sf_entry_t *)((char *)asfep +
+ 				XFS_ATTR_SF_ENTSIZE(asfep));
+ 	}
++
++	/* zero stale data in rest of space in attr fork, if any */
++	if (zero_stale_data && (ino_attr_size < XFS_DFORK_ASIZE(dip, mp)))
++		memset(asfep, 0, XFS_DFORK_ASIZE(dip, mp) - ino_attr_size);
+ }
+ 
+ static void
+-obfuscate_dir_data_block(
++process_dir_data_block(
+ 	char		*block,
+ 	xfs_dfiloff_t	offset,
+ 	int		is_block_format)
+@@ -1151,9 +1297,6 @@ obfuscate_dir_data_block(
+ 
+ 	datahdr = (struct xfs_dir2_data_hdr *)block;
+ 
+-	if (offset % mp->m_dirblkfsbs != 0)
+-		return;	/* corrupted, leave it alone */
+-
+ 	if (is_block_format) {
+ 		xfs_dir2_leaf_entry_t	*blp;
+ 		xfs_dir2_block_tail_t	*btp;
+@@ -1186,7 +1329,7 @@ obfuscate_dir_data_block(
+ 
+ 	dir_offset = xfs_dir3_data_entry_offset(datahdr);
+ 	ptr = block + dir_offset;
+-	endptr = block + mp->m_sb.sb_blocksize;
++	endptr = block + mp->m_dirblksize;
+ 
+ 	while (ptr < endptr && dir_offset < end_of_data) {
+ 		xfs_dir2_data_entry_t	*dep;
+@@ -1210,6 +1353,20 @@ obfuscate_dir_data_block(
+ 				return;
+ 			dir_offset += length;
+ 			ptr += length;
++			/*
++			 * Zero the unused space up to the tag - the tag is
++			 * actually at a variable offset, so zeroing &dup->tag
++			 * is zeroing the free space in between
++			 */
++			if (zero_stale_data) {
++				int zlen = length -
++						sizeof(xfs_dir2_data_unused_t);
++
++				if (zlen > 0) {
++					memset(&dup->tag, 0, zlen);
++					iocur_top->need_crc = 1;
++				}
++			}
+ 			if (dir_offset >= end_of_data || ptr >= endptr)
+ 				return;
+ 		}
+@@ -1228,19 +1385,48 @@ obfuscate_dir_data_block(
+ 		if (be16_to_cpu(*xfs_dir3_data_entry_tag_p(mp, dep)) !=
+ 				dir_offset)
+ 			return;
+-		generate_obfuscated_name(be64_to_cpu(dep->inumber),
++
++		if (obfuscate)
++			generate_obfuscated_name(be64_to_cpu(dep->inumber),
+ 					 dep->namelen, &dep->name[0]);
+ 		dir_offset += length;
+ 		ptr += length;
++		/* Zero the unused space after name, up to the tag */
++		if (zero_stale_data) {
++			/* 1 byte for ftype; don't bother with conditional */
++			int zlen =
++				(char *)xfs_dir3_data_entry_tag_p(mp, dep) -
++				(char *)&dep->name[dep->namelen] - 1;
++			if (zlen > 0) {
++				memset(&dep->name[dep->namelen] + 1, 0, zlen);
++				iocur_top->need_crc = 1;
++			}
++		}
+ 	}
+ }
+ 
+ static void
+-obfuscate_symlink_block(
++process_symlink_block(
+ 	char			*block)
+ {
+-	/* XXX: need to handle CRC headers */
+-	obfuscate_path_components(block, mp->m_sb.sb_blocksize);
++	char *link = block;
++
++	if (xfs_sb_version_hascrc(&(mp)->m_sb))
++		link += sizeof(struct xfs_dsymlink_hdr);
++
++	if (obfuscate)
++		obfuscate_path_components(link, XFS_SYMLINK_BUF_SPACE(mp,
++							mp->m_sb.sb_blocksize));
++	if (zero_stale_data) {
++		size_t	linklen, zlen;
++
++		linklen = strlen(link);
++		zlen = mp->m_sb.sb_blocksize - linklen;
++		if (xfs_sb_version_hascrc(&mp->m_sb))
++			zlen -= sizeof(struct xfs_dsymlink_hdr);
++		if (zlen < mp->m_sb.sb_blocksize)
++			memset(link + linklen, 0, zlen);
++	}
+ }
+ 
+ #define MAX_REMOTE_VALS		4095
+@@ -1268,39 +1454,61 @@ add_remote_vals(
+ 	}
+ }
+ 
++/* Handle remote and leaf attributes */
+ static void
+-obfuscate_attr_block(
+-	char			*block,
+-	xfs_dfiloff_t		offset)
++process_attr_block(
++	char				*block,
++	xfs_fileoff_t			offset)
+ {
+-	xfs_attr_leafblock_t	*leaf;
+-	int			i;
+-	int			nentries;
+-	xfs_attr_leaf_entry_t 	*entry;
+-	xfs_attr_leaf_name_local_t *local;
+-	xfs_attr_leaf_name_remote_t *remote;
++	struct xfs_attr_leafblock	*leaf;
++	struct xfs_attr3_icleaf_hdr	hdr;
++	int				i;
++	int				nentries;
++	xfs_attr_leaf_entry_t 		*entry;
++	xfs_attr_leaf_name_local_t 	*local;
++	xfs_attr_leaf_name_remote_t 	*remote;
++	__uint32_t			bs = mp->m_sb.sb_blocksize;
++	char				*first_name;
++
+ 
+ 	leaf = (xfs_attr_leafblock_t *)block;
+ 
+-	if (be16_to_cpu(leaf->hdr.info.magic) != XFS_ATTR_LEAF_MAGIC) {
++	/* Remote attributes - attr3 has XFS_ATTR3_RMT_MAGIC, attr has none */
++	if ((be16_to_cpu(leaf->hdr.info.magic) != XFS_ATTR_LEAF_MAGIC) &&
++	    (be16_to_cpu(leaf->hdr.info.magic) != XFS_ATTR3_LEAF_MAGIC)) {
+ 		for (i = 0; i < attr_data.remote_val_count; i++) {
+-			/* XXX: need to handle CRC headers */
+-			if (attr_data.remote_vals[i] == offset)
+-				memset(block, 0, XFS_LBSIZE(mp));
++			if (obfuscate && attr_data.remote_vals[i] == offset)
++				/* Macros to handle both attr and attr3 */
++				memset(block +
++					(bs - XFS_ATTR3_RMT_BUF_SPACE(mp, bs)),
++				      'v', XFS_ATTR3_RMT_BUF_SPACE(mp, bs));
+ 		}
+ 		return;
+ 	}
+ 
+-	nentries = be16_to_cpu(leaf->hdr.count);
++	/* Ok, it's a leaf - get header; accounts for crc & non-crc */
++	xfs_attr3_leaf_hdr_from_disk(&hdr, leaf);
++
++	nentries = hdr.count;
+ 	if (nentries * sizeof(xfs_attr_leaf_entry_t) +
+-			sizeof(xfs_attr_leaf_hdr_t) > XFS_LBSIZE(mp)) {
++			xfs_attr3_leaf_hdr_size(leaf) >
++				XFS_ATTR3_RMT_BUF_SPACE(mp, bs)) {
+ 		if (show_warnings)
+ 			print_warning("invalid attr count in inode %llu",
+ 					(long long)cur_ino);
+ 		return;
+ 	}
+ 
+-	for (i = 0, entry = &leaf->entries[0]; i < nentries; i++, entry++) {
++	entry = xfs_attr3_leaf_entryp(leaf);
++	/* We will move this as we parse */
++	first_name = NULL;
++	for (i = 0; i < nentries; i++, entry++) {
++		int nlen, vlen, zlen;
++
++		/* Grows up; if this name is topmost, move first_name */
++		if (!first_name || xfs_attr3_leaf_name(leaf, i) < first_name)
++			first_name = xfs_attr3_leaf_name(leaf, i);
++
+ 		if (be16_to_cpu(entry->nameidx) > XFS_LBSIZE(mp)) {
+ 			if (show_warnings)
+ 				print_warning(
+@@ -1317,10 +1525,20 @@ obfuscate_attr_block(
+ 						(long long)cur_ino);
+ 				break;
+ 			}
+-			generate_obfuscated_name(0, local->namelen,
+-				&local->nameval[0]);
+-			memset(&local->nameval[local->namelen], 0,
+-				be16_to_cpu(local->valuelen));
++			if (obfuscate) {
++				generate_obfuscated_name(0, local->namelen,
++					&local->nameval[0]);
++				memset(&local->nameval[local->namelen], 'v',
++					be16_to_cpu(local->valuelen));
++			}
++			/* zero from end of nameval[] to next name start */
++			nlen = local->namelen;
++			vlen = be16_to_cpu(local->valuelen);
++			zlen = xfs_attr_leaf_entsize_local(nlen, vlen) -
++				(sizeof(xfs_attr_leaf_name_local_t) - 1 +
++				 nlen + vlen);
++			if (zero_stale_data)
++				memset(&local->nameval[nlen + vlen], 0, zlen);
+ 		} else {
+ 			remote = xfs_attr3_leaf_name_remote(leaf, i);
+ 			if (remote->namelen == 0 || remote->valueblk == 0) {
+@@ -1330,14 +1548,33 @@ obfuscate_attr_block(
+ 						(long long)cur_ino);
+ 				break;
+ 			}
+-			generate_obfuscated_name(0, remote->namelen,
+-						 &remote->name[0]);
+-			add_remote_vals(be32_to_cpu(remote->valueblk),
+-					be32_to_cpu(remote->valuelen));
++			if (obfuscate) {
++				generate_obfuscated_name(0, remote->namelen,
++							 &remote->name[0]);
++				add_remote_vals(be32_to_cpu(remote->valueblk),
++						be32_to_cpu(remote->valuelen));
++			}
++			/* zero from end of name[] to next name start */
++			nlen = remote->namelen;
++			zlen = xfs_attr_leaf_entsize_remote(nlen) -
++				(sizeof(xfs_attr_leaf_name_remote_t) - 1 +
++				 nlen);
++			if (zero_stale_data)
++				memset(&remote->name[nlen], 0, zlen);
+ 		}
+ 	}
++
++	/* Zero from end of entries array to the first name/val */
++	if (zero_stale_data) {
++		struct xfs_attr_leaf_entry *entries;
++
++		entries = xfs_attr3_leaf_entryp(leaf);
++		memset(&entries[nentries], 0,
++		       first_name - (char *)&entries[nentries]);
++	}
+ }
+ 
++/* Processes symlinks, attrs, directories ... */
+ static int
+ process_single_fsb_objects(
+ 	xfs_dfiloff_t	o,
+@@ -1367,25 +1604,50 @@ process_single_fsb_objects(
+ 
+ 		}
+ 
+-		if (dont_obfuscate)
++		if (!obfuscate && !zero_stale_data)
+ 			goto write;
+ 
++		/* Zero unused part of interior nodes */
++		if (zero_stale_data) {
++			xfs_da_intnode_t *node = iocur_top->data;
++			int magic = be16_to_cpu(node->hdr.info.magic);
++
++			if (magic == XFS_DA_NODE_MAGIC ||
++			    magic == XFS_DA3_NODE_MAGIC) {
++				struct xfs_da3_icnode_hdr hdr;
++				int used;
++
++				xfs_da3_node_hdr_from_disk(&hdr, node);
++				used = xfs_da3_node_hdr_size(node);
++
++				used += hdr.count
++					* sizeof(struct xfs_da_node_entry);
++
++				if (used < mp->m_sb.sb_blocksize) {
++					memset((char *)node + used, 0,
++						mp->m_sb.sb_blocksize - used);
++					iocur_top->need_crc = 1;
++				}
++			}
++		}
++
++		/* Handle leaf nodes */
+ 		dp = iocur_top->data;
+ 		switch (btype) {
+ 		case TYP_DIR2:
+ 			if (o >= mp->m_dirleafblk)
+ 				break;
+ 
+-			obfuscate_dir_data_block(dp, o,
++			process_dir_data_block(dp, o,
+ 						 last == mp->m_dirblkfsbs);
+ 			iocur_top->need_crc = 1;
+ 			break;
+ 		case TYP_SYMLINK:
+-			obfuscate_symlink_block(dp);
++			process_symlink_block(dp);
+ 			iocur_top->need_crc = 1;
+ 			break;
+ 		case TYP_ATTR:
+-			obfuscate_attr_block(dp, o);
++			process_attr_block(dp, o);
+ 			iocur_top->need_crc = 1;
+ 			break;
+ 		default:
+@@ -1459,13 +1721,14 @@ process_multi_fsb_objects(
+ 
+ 			}
+ 
+-			if (dont_obfuscate || o >= mp->m_dirleafblk) {
++			if ((!obfuscate && !zero_stale_data) ||
++			     o >= mp->m_dirleafblk) {
+ 				ret = write_buf(iocur_top);
+ 				goto out_pop;
+ 			}
+ 
+-			obfuscate_dir_data_block(iocur_top->data, o,
+-						  last == mp->m_dirblkfsbs);
++			process_dir_data_block(iocur_top->data, o,
++					       last == mp->m_dirblkfsbs);
+ 			iocur_top->need_crc = 1;
+ 			ret = write_buf(iocur_top);
+ out_pop:
+@@ -1700,19 +1963,26 @@ process_exinode(
+ 	typnm_t			itype)
+ {
+ 	int			whichfork;
++	int			used;
+ 	xfs_extnum_t		nex;
+ 
+ 	whichfork = (itype == TYP_ATTR) ? XFS_ATTR_FORK : XFS_DATA_FORK;
+ 
+ 	nex = XFS_DFORK_NEXTENTS(dip, whichfork);
+-	if (nex < 0 || nex > XFS_DFORK_SIZE(dip, mp, whichfork) /
+-						sizeof(xfs_bmbt_rec_t)) {
++	used = nex * sizeof(xfs_bmbt_rec_t);
++	if (nex < 0 || used > XFS_DFORK_SIZE(dip, mp, whichfork)) {
+ 		if (show_warnings)
+ 			print_warning("bad number of extents %d in inode %lld",
+ 				nex, (long long)cur_ino);
+ 		return 1;
+ 	}
+ 
++	/* Zero unused data fork past used extents */
++	if (zero_stale_data && (used < XFS_DFORK_SIZE(dip, mp, whichfork)))
++		memset(XFS_DFORK_PTR(dip, whichfork) + used, 0,
++		       XFS_DFORK_SIZE(dip, mp, whichfork) - used);
++
++
+ 	return process_bmbt_reclist((xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip,
+ 					whichfork), nex, itype);
+ }
+@@ -1724,14 +1994,14 @@ process_inode_data(
+ {
+ 	switch (dip->di_format) {
+ 		case XFS_DINODE_FMT_LOCAL:
+-			if (!dont_obfuscate)
++			if (obfuscate || zero_stale_data)
+ 				switch (itype) {
+ 					case TYP_DIR2:
+-						obfuscate_sf_dir(dip);
++						process_sf_dir(dip);
+ 						break;
+ 
+ 					case TYP_SYMLINK:
+-						obfuscate_sf_symlink(dip);
++						process_sf_symlink(dip);
+ 						break;
+ 
+ 					default: ;
+@@ -1758,7 +2028,8 @@ static int
+ process_inode(
+ 	xfs_agnumber_t		agno,
+ 	xfs_agino_t 		agino,
+-	xfs_dinode_t 		*dip)
++	xfs_dinode_t 		*dip,
++	bool			free_inode)
+ {
+ 	int			success;
+ 	bool			crc_was_ok = false; /* no recalc by default */
+@@ -1767,13 +2038,22 @@ process_inode(
+ 	success = 1;
+ 	cur_ino = XFS_AGINO_TO_INO(mp, agno, agino);
+ 
+-	/* we only care about crc recalculation if we are obfuscating names. */
+-	if (!dont_obfuscate) {
++	/* we only care about crc recalculation if we will modify the inode. */
++	if (obfuscate || zero_stale_data) {
+ 		crc_was_ok = xfs_verify_cksum((char *)dip,
+ 					mp->m_sb.sb_inodesize,
+ 					offsetof(struct xfs_dinode, di_crc));
+ 	}
+ 
++	if (free_inode) {
++		if (zero_stale_data) {
++			/* Zero all of the inode literal area */
++			memset(XFS_DFORK_DPTR(dip), 0,
++			       XFS_LITINO(mp, dip->di_version));
++		}
++		goto done;
++	}
++
+ 	/* copy appropriate data fork metadata */
+ 	switch (be16_to_cpu(dip->di_mode) & S_IFMT) {
+ 		case S_IFDIR:
+@@ -1800,8 +2080,8 @@ process_inode(
+ 		switch (dip->di_aformat) {
+ 			case XFS_DINODE_FMT_LOCAL:
+ 				need_new_crc = 1;
+-				if (!dont_obfuscate)
+-					obfuscate_sf_attr(dip);
++				if (obfuscate || zero_stale_data)
++					process_sf_attr(dip);
+ 				break;
+ 
+ 			case XFS_DINODE_FMT_EXTENTS:
+@@ -1815,6 +2095,11 @@ process_inode(
+ 		nametable_clear();
+ 	}
+ 
++done:
++	/* Heavy handed but low cost; just do it as a catch-all. */
++	if (zero_stale_data)
++		need_new_crc = 1;
++
+ 	if (crc_was_ok && need_new_crc)
+ 		xfs_dinode_calc_crc(mp, dip);
+ 	return success;
+@@ -1880,13 +2165,12 @@ copy_inode_chunk(
+ 	for (i = 0; i < XFS_INODES_PER_CHUNK; i++) {
+ 		xfs_dinode_t            *dip;
+ 
+-		if (XFS_INOBT_IS_FREE_DISK(rp, i))
+-			continue;
+-
+ 		dip = (xfs_dinode_t *)((char *)iocur_top->data +
+ 				((off + i) << mp->m_sb.sb_inodelog));
+ 
+-		if (!process_inode(agno, agino + i, dip))
++		/* process_inode handles free inodes, too */
++		if (!process_inode(agno, agino + i, dip,
++				   XFS_INOBT_IS_FREE_DISK(rp, i)))
+ 			goto pop_out;
+ 	}
+ skip_processing:
+@@ -2031,6 +2315,13 @@ scan_ag(
+ 		if (stop_on_read_error)
+ 			goto pop_out;
+ 	} else {
++		/* Replace any filesystem label with "L's" */
++		if (obfuscate) {
++			struct xfs_sb *sb = iocur_top->data;
++			memset(sb->sb_fname, 'L',
++			       min(strlen(sb->sb_fname), sizeof(sb->sb_fname)));
++			iocur_top->need_crc = 1;
++		}
+ 		if (write_buf(iocur_top))
+ 			goto pop_out;
+ 	}
+@@ -2075,6 +2366,23 @@ scan_ag(
+ 		if (stop_on_read_error)
+ 			goto pop_out;
+ 	} else {
++		if (agf && zero_stale_data) {
++			/* Zero out unused bits of agfl */
++			int i;
++			 __be32  *agfl_bno;
++
++			agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, iocur_top->bp);
++			i = be32_to_cpu(agf->agf_fllast);
++
++			for (;;) {
++				if (++i == XFS_AGFL_SIZE(mp))
++					i = 0;
++				if (i == be32_to_cpu(agf->agf_flfirst))
++					break;
++				agfl_bno[i] = cpu_to_be32(NULLAGBLOCK);
++			}
++			iocur_top->need_crc = 1;
++		}
+ 		if (write_buf(iocur_top))
+ 			goto pop_out;
+ 	}
+@@ -2169,6 +2477,8 @@ copy_sb_inodes(void)
+ static int
+ copy_log(void)
+ {
++	int dirty;
++
+ 	if (show_progress)
+ 		print_progress("Copying log");
+ 
+@@ -2180,6 +2490,34 @@ copy_log(void)
+ 		print_warning("cannot read log");
+ 		return !stop_on_read_error;
+ 	}
++
++	/* If not obfuscating or zeroing, just copy the log as it is */
++	if (!obfuscate && !zero_stale_data)
++		goto done;
++
++	dirty = xlog_is_dirty(mp, &x, 0);
++
++	switch (dirty) {
++	case 0:
++		/* clear out a clean log */
++		if (show_progress)
++			print_progress("Zeroing clean log");
++		memset(iocur_top->data, 0,
++			mp->m_sb.sb_logblocks * mp->m_sb.sb_blocksize);
++		break;
++	case 1:
++		/* keep the dirty log */
++		print_warning(
++_("Filesystem log is dirty; image will contain unobfuscated metadata in log."));
++		break;
++	case -1:
++		/* log detection error */
++		print_warning(
++_("Could not discern log; image will contain unobfuscated metadata in log."));
++		break;
++	}
++
++done:
+ 	return !write_buf(iocur_top);
+ }
+ 
+@@ -2204,8 +2542,11 @@ metadump_f(
+ 		return 0;
+ 	}
+ 
+-	while ((c = getopt(argc, argv, "egm:ow")) != EOF) {
++	while ((c = getopt(argc, argv, "aegm:ow")) != EOF) {
+ 		switch (c) {
++			case 'a':
++				zero_stale_data = 0;
++				break;
+ 			case 'e':
+ 				stop_on_read_error = 1;
+ 				break;
+@@ -2221,7 +2562,7 @@ metadump_f(
+ 				}
+ 				break;
+ 			case 'o':
+-				dont_obfuscate = 1;
++				obfuscate = 0;
+ 				break;
+ 			case 'w':
+ 				show_warnings = 1;
+diff --git a/db/sb.c b/db/sb.c
+index 974abd2..f0c2145 100644
+--- a/db/sb.c
++++ b/db/sb.c
+@@ -225,8 +225,7 @@ int xlog_recover_do_trans(struct xlog *log, xlog_recover_t *t, int p)
+ int
+ sb_logcheck(void)
+ {
+-	struct xlog	log;
+-	xfs_daddr_t	head_blk, tail_blk;
++	int		dirty;
+ 
+ 	if (mp->m_sb.sb_logstart) {
+ 		if (x.logdev && x.logdev != x.ddev) {
+@@ -242,26 +241,13 @@ sb_logcheck(void)
+ 		}
+ 	}
+ 
+-	memset(&log, 0, sizeof(log));
+ 	libxfs_buftarg_init(mp, x.ddev, x.logdev, x.rtdev);
+-	x.logBBsize = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks);
+-	x.logBBstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart);
+-	x.lbsize = BBSIZE;
+-	if (xfs_sb_version_hassector(&mp->m_sb))
+-		x.lbsize <<= (mp->m_sb.sb_logsectlog - BBSHIFT);
+-
+-	log.l_dev = mp->m_logdev_targp;
+-	log.l_logsize = BBTOB(log.l_logBBsize);
+-	log.l_logBBsize = x.logBBsize;
+-	log.l_logBBstart = x.logBBstart;
+-	log.l_sectBBsize  = BTOBB(x.lbsize);
+-	log.l_mp = mp;
+-
+-	if (xlog_find_tail(&log, &head_blk, &tail_blk)) {
++
++	dirty = xlog_is_dirty(mp, &x, 0);
++	if (dirty == -1) {
+ 		dbprintf(_("ERROR: cannot find log head/tail, run xfs_repair\n"));
+ 		return 0;
+-	}
+-	if (head_blk != tail_blk) {
++	} else if (dirty == 1) {
+ 		dbprintf(_(
+ "ERROR: The filesystem has valuable metadata changes in a log which needs to\n"
+ "be replayed.  Mount the filesystem to replay the log, and unmount it before\n"
+@@ -271,6 +257,7 @@ sb_logcheck(void)
+ "of the filesystem before doing this.\n"), progname);
+ 		return 0;
+ 	}
++	/* Log is clean */
+ 	return 1;
+ }
+ 
+diff --git a/db/type.c b/db/type.c
+index b29f2a4..0aa3137 100644
+--- a/db/type.c
++++ b/db/type.c
+@@ -70,6 +70,7 @@ static const typ_t	__typtab[] = {
+ 	{ TYP_SB, "sb", handle_struct, sb_hfld, NULL },
+ 	{ TYP_SYMLINK, "symlink", handle_string, NULL, NULL },
+ 	{ TYP_TEXT, "text", handle_text, NULL, NULL },
++	{ TYP_FINOBT, "finobt", handle_struct, inobt_hfld, NULL },
+ 	{ TYP_NONE, NULL }
+ };
+ 
+@@ -104,6 +105,8 @@ static const typ_t	__typtab_crc[] = {
+ 	{ TYP_SYMLINK, "symlink", handle_struct, symlink_crc_hfld,
+ 		&xfs_symlink_buf_ops },
+ 	{ TYP_TEXT, "text", handle_text, NULL, NULL },
++	{ TYP_FINOBT, "finobt", handle_struct, inobt_crc_hfld,
++		&xfs_inobt_buf_ops },
+ 	{ TYP_NONE, NULL }
+ };
+ 
+diff --git a/db/type.h b/db/type.h
+index 3bb26f1..e8d8df7 100644
+--- a/db/type.h
++++ b/db/type.h
+@@ -27,7 +27,7 @@ typedef enum typnm
+ 	TYP_BMAPBTD, TYP_BNOBT, TYP_CNTBT, TYP_DATA,
+ 	TYP_DIR2, TYP_DQBLK, TYP_INOBT, TYP_INODATA, TYP_INODE,
+ 	TYP_LOG, TYP_RTBITMAP, TYP_RTSUMMARY, TYP_SB, TYP_SYMLINK,
+-	TYP_TEXT, TYP_NONE
++	TYP_TEXT, TYP_FINOBT, TYP_NONE
+ } typnm_t;
+ 
+ #define DB_WRITE 1
+diff --git a/db/xfs_metadump.sh b/db/xfs_metadump.sh
+index a95d5a5..836d3ae 100755
+--- a/db/xfs_metadump.sh
++++ b/db/xfs_metadump.sh
+@@ -5,11 +5,12 @@
+ 
+ OPTS=" "
+ DBOPTS=" "
+-USAGE="Usage: xfs_metadump [-efFogwV] [-m max_extents] [-l logdev] source target"
++USAGE="Usage: xfs_metadump [-aefFogwV] [-m max_extents] [-l logdev] source target"
+ 
+-while getopts "efgl:m:owFV" c
++while getopts "aefgl:m:owFV" c
+ do
+ 	case $c in
++	a)	OPTS=$OPTS"-a ";;
+ 	e)	OPTS=$OPTS"-e ";;
+ 	g)	OPTS=$OPTS"-g ";;
+ 	m)	OPTS=$OPTS"-m "$OPTARG" ";;
+diff --git a/include/libxlog.h b/include/libxlog.h
+index a61e437..d2a33c1 100644
+--- a/include/libxlog.h
++++ b/include/libxlog.h
+@@ -78,6 +78,8 @@ extern int	print_record_header;
+ /* libxfs parameters */
+ extern libxfs_init_t	x;
+ 
++
++extern int xlog_is_dirty(xfs_mount_t *mp, libxfs_init_t *x, int verbose);
+ extern struct xfs_buf *xlog_get_bp(struct xlog *, int);
+ extern void	xlog_put_bp(struct xfs_buf *);
+ extern int	xlog_bread(struct xlog *log, xfs_daddr_t blk_no, int nbblks,
+diff --git a/libxlog/util.c b/libxlog/util.c
+index 949b79d..edb23a8 100644
+--- a/libxlog/util.c
++++ b/libxlog/util.c
+@@ -23,6 +23,62 @@ int print_skip_uuid;
+ int print_record_header;
+ libxfs_init_t x;
+ 
++/*
++ * Return 1 for dirty, 0 for clean, -1 for errors
++ */
++int
++xlog_is_dirty(
++	xfs_mount_t *mp,
++	libxfs_init_t *x,
++	int verbose)
++{
++	int error;
++	struct xlog	log;
++	xfs_daddr_t head_blk, tail_blk;
++
++	memset(&log, 0, sizeof(log));
++
++	/* We (re-)init members of libxfs_init_t here?  really? */
++	x->logBBsize = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks);
++	x->logBBstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart);
++	x->lbsize = BBSIZE;
++	if (xfs_sb_version_hassector(&mp->m_sb))
++		x->lbsize <<= (mp->m_sb.sb_logsectlog - BBSHIFT);
++
++	log.l_dev = mp->m_logdev_targp;
++	log.l_logBBsize = x->logBBsize;
++	log.l_logBBstart = x->logBBstart;
++	log.l_sectBBsize = BTOBB(x->lbsize);
++	log.l_mp = mp;
++	if (xfs_sb_version_hassector(&mp->m_sb)) {
++		log.l_sectbb_log = mp->m_sb.sb_logsectlog - BBSHIFT;
++		ASSERT(log.l_sectbb_log <= mp->m_sectbb_log);
++		/* for larger sector sizes, must have v2 or external log */
++		ASSERT(log.l_sectbb_log == 0 ||
++			log.l_logBBstart == 0 ||
++			xfs_sb_version_haslogv2(&mp->m_sb));
++		ASSERT(mp->m_sb.sb_logsectlog >= BBSHIFT);
++	}
++	log.l_sectbb_mask = (1 << log.l_sectbb_log) - 1;
++
++	if ((error = xlog_find_tail(&log, &head_blk, &tail_blk))) {
++		xlog_warn(_("%s: cannot find log head/tail "
++			  "(xlog_find_tail=%d)\n"),
++			__func__, error);
++		return -1;
++	}
++
++	if (verbose)
++		xlog_warn(
++	_("%s: head block %" PRId64 " tail block %" PRId64 "\n"),
++			__func__, head_blk, tail_blk);
++
++	if (head_blk != tail_blk)
++		return 1;
++
++	return 0;
++}
++
+ static int
+ header_check_uuid(xfs_mount_t *mp, xlog_rec_header_t *head)
+ {
+diff --git a/man/man8/xfs_metadump.8 b/man/man8/xfs_metadump.8
+index 077fff5..81a17a9 100644
+--- a/man/man8/xfs_metadump.8
++++ b/man/man8/xfs_metadump.8
+@@ -4,7 +4,7 @@ xfs_metadump \- copy XFS filesystem metadata to a file
+ .SH SYNOPSIS
+ .B xfs_metadump
+ [
+-.B \-efFgow
++.B \-aefFgow
+ ] [
+ .B \-m
+ .I max_extents
+@@ -74,6 +74,14 @@ tool.
+ .PP
+ .SH OPTIONS
+ .TP
++.B \-a
++Copies entire metadata blocks.  Normally,
++.B xfs_metadump
++will zero any stale
++bytes interspersed with in-use metadata.  Use this option to copy full metadata
++blocks, to provide more debugging information for a corrupted filesystem.  Note
++that the extra data will be unobfuscated.
++.TP
+ .B \-e
+ Stops the dump on a read error. Normally, it will ignore read errors and copy
+ all the metadata that is accessible.
diff --git a/SPECS/xfsprogs.spec b/SPECS/xfsprogs.spec
index c3d490b..18096f3 100644
--- a/SPECS/xfsprogs.spec
+++ b/SPECS/xfsprogs.spec
@@ -1,7 +1,7 @@
 Summary:	Utilities for managing the XFS filesystem
 Name:		xfsprogs
-Version:	3.2.1
-Release:	6%{?dist}
+Version:	3.2.2
+Release:	2%{?dist}
 # Licensing based on generic "GNU GENERAL PUBLIC LICENSE"
 # in source, with no mention of version.
 # doc/COPYING file specifies what is GPL and what is LGPL
@@ -18,13 +18,8 @@ Provides:	xfs-cmds
 Obsoletes:	xfs-cmds <= %{version}
 Conflicts:	xfsdump < 3.0.1
 
-Patch0:		xfsprogs-3.2.1-quota-fix-NULL-pointer-dereference-in-report_f.patch
-Patch1:		xfsprogs-3.2.1-libxcmd-make-all-comparisons-using-realpathd-paths.patch
-Patch2:		xfsprogs-3.2.1-add-supported-file-attributes-to-xfs.5-manpage.patch
-Patch3:		xfsprogs-3.2.1-copy-stripe-geometry.patch
-Patch4:		xfsprogs-3.2.1-xfs_quota-manpage.patch
-Patch5:		xfsprogs-3.2.1-xfs_copy-simplify-first_agbno-calculation.patch
-Patch6:		xfsprogs-3.2.1-xfs_repair-fix-max-block-offset-test.patch
+Patch0:		xfsprogs-3.2.3-fixes.patch
+Patch1:		xfsprogs-xfs_metadump-CVE-2012-2150.patch
 
 %description
 A set of commands to use the XFS filesystem, including mkfs.xfs.
@@ -70,11 +65,6 @@ in building or running the xfstests QA suite.
 %setup -q
 %patch0 -p1
 %patch1 -p1
-%patch2 -p1
-%patch3 -p1
-%patch4 -p1
-%patch5 -p1
-%patch6 -p1
 
 %build
 export tagname=CC
@@ -194,6 +184,14 @@ rm -rf $RPM_BUILD_ROOT
 %{_includedir}/xfs/xfs_trans_space.h
 
 %changelog
+* Fri Aug 07 2015 Eric Sandeen <sandeen@redhat.com> 3.2.2-2
+- Fix xfs_metadump disclosure flaw, CVE-2012-2150  (#1251118)
+
+* Mon Jun 15 2015 Eric Sandeen <sandeen@redhat.com> 3.2.2-1
+- Update to upstream v3.2.2, plus fixes from v3.2.3 (#1223991)
+- repair: fix unnecessary secondary scan if only last sb is corrupt (#1201238)
+- repair: check ino alignment value to avoid mod by zero (#1223444)
+
 * Fri Dec 19 2014 Eric Sandeen <sandeen@redhat.com> 3.2.1-6
 - xfs_repair: fix maximum block offset test (#1173146)
 - xfs_copy: fix assert failure on 4k sector devices (#1162414)