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 -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 - Reviewed-by: Dave Chinner - Signed-off-by: Dave Chinner - -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 -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 -Reviewed-by: Brian Foster -Signed-off-by: Dave Chinner ---- - -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=,swidth=\n"), -+_("Note - stripe unit (%d) and width (%d) were copied from a backup superblock.\n" -+ "Please reset with mount -o sunit=,swidth= 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 -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 - Reviewed-by: Christoph Hellwig - Signed-off-by: Dave Chinner - -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 -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 - Reviewed-by: Eric Sandeen - Reviewed-by: Christoph Hellwig - Signed-off-by: Dave Chinner - -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 -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 - Signed-off-by: Eric Sandeen - Reviewed-by: Brian Foster - Signed-off-by: Dave Chinner - -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 ---- - -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 -Signed-off-by: Eric Sandeen -Reviewed-by: Brian Foster ---- - -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 + ++#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 ++#include + #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 3.2.2-2 +- Fix xfs_metadump disclosure flaw, CVE-2012-2150 (#1251118) + +* Mon Jun 15 2015 Eric Sandeen 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 3.2.1-6 - xfs_repair: fix maximum block offset test (#1173146) - xfs_copy: fix assert failure on 4k sector devices (#1162414)