]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - repair/sb.c
libxfs: refactor manage_zones()
[thirdparty/xfsprogs-dev.git] / repair / sb.c
index 5133f2063aad0b4524b2c37b28fc71cfac23f2db..119bf2190b13f6b0c9548d8e1592b44c0a0c6b99 100644 (file)
@@ -1,62 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
- * 
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * This program is distributed in the hope that it would be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * 
- * Further, this software is distributed without any warranty that it is
- * free of the rightful claim of any third person regarding infringement
- * or the like.  Any license provided herein, whether implied or
- * otherwise, applies only to this software file.  Patent licenses, if
- * any, provided herein do not apply to combinations of this program with
- * other software, or any other product whatsoever.
- * 
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston MA 02111-1307, USA.
- * 
- * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
- * Mountain View, CA  94043, or:
- * 
- * http://www.sgi.com 
- * 
- * For further information regarding this notice, see: 
- * 
- * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
  */
-
-#include <errno.h>
-#include <libxfs.h>
-#include <malloc.h>
+#include "libfrog.h"
+#include "libxfs.h"
+#include "libxcmd.h"
+#include "libxlog.h"
 #include "agheader.h"
 #include "globals.h"
 #include "protos.h"
 #include "err_protos.h"
+#include "xfs_multidisk.h"
 
+#define BSIZE  (1024 * 1024)
 
 /*
  * copy the fields of a superblock that are present in primary and
  * secondaries -- preserve fields that are different in the primary.
  */
-void
+static void
 copy_sb(xfs_sb_t *source, xfs_sb_t *dest)
 {
        xfs_ino_t       rootino;
        xfs_ino_t       rbmino;
        xfs_ino_t       rsumino;
        xfs_ino_t       uquotino;
+       xfs_ino_t       gquotino;
        xfs_ino_t       pquotino;
-       __uint16_t      versionnum;
+       uint16_t        versionnum;
 
        rootino = dest->sb_rootino;
        rbmino = dest->sb_rbmino;
        rsumino = dest->sb_rsumino;
        uquotino = dest->sb_uquotino;
+       gquotino = dest->sb_gquotino;
        pquotino = dest->sb_pquotino;
 
        versionnum = dest->sb_versionnum;
@@ -67,6 +45,7 @@ copy_sb(xfs_sb_t *source, xfs_sb_t *dest)
        dest->sb_rbmino = rbmino;
        dest->sb_rsumino = rsumino;
        dest->sb_uquotino = uquotino;
+       dest->sb_gquotino = gquotino;
        dest->sb_pquotino = pquotino;
 
        dest->sb_versionnum = versionnum;
@@ -76,10 +55,9 @@ copy_sb(xfs_sb_t *source, xfs_sb_t *dest)
         * secondaries and cannot be changed at run time in
         * the primary superblock
         */
-       if (XFS_SB_VERSION_HASDALIGN(source))
-               XFS_SB_VERSION_ADDDALIGN(dest);
-       if (XFS_SB_VERSION_HASEXTFLGBIT(source))
-               XFS_SB_VERSION_ADDEXTFLGBIT(dest);
+       if (xfs_sb_version_hasdalign(source))
+               dest->sb_versionnum |= XFS_SB_VERSION_DALIGNBIT;
+       dest->sb_versionnum |= XFS_SB_VERSION_EXTFLGBIT;
 
        /*
         * these are all supposed to be zero or will get reset anyway
@@ -89,16 +67,34 @@ copy_sb(xfs_sb_t *source, xfs_sb_t *dest)
        dest->sb_fdblocks = 0;
        dest->sb_frextents = 0;
 
-       bzero(source->sb_fname, 12);
+       memset(source->sb_fname, 0, 12);
 }
 
-#define BSIZE  (1024 * 1024)
+static int
+verify_sb_blocksize(xfs_sb_t *sb)
+{
+       /* check to make sure blocksize is legal 2^N, 9 <= N <= 16 */
+       if (sb->sb_blocksize == 0)
+               return XR_BAD_BLOCKSIZE;
+       if (sb->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG ||
+           sb->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG)
+               return XR_BAD_BLOCKLOG;
+       if (sb->sb_blocksize != (1 << sb->sb_blocklog))
+               return XR_BAD_BLOCKLOG;
+
+       return 0;
+}
 
 /*
- * find a secondary superblock, copy it into the sb buffer
+ * find a secondary superblock, copy it into the sb buffer.
+ * start is the point to begin reading BSIZE bytes.
+ * skip contains a byte-count of how far to advance for next read.
  */
-int
-find_secondary_sb(xfs_sb_t *rsb)
+static int
+__find_secondary_sb(
+       xfs_sb_t        *rsb,
+       uint64_t        start,
+       uint64_t        skip)
 {
        xfs_off_t       off;
        xfs_sb_t        *sb;
@@ -110,16 +106,14 @@ find_secondary_sb(xfs_sb_t *rsb)
        int             retval;
        int             bsize;
 
-       do_warn("\nattempting to find secondary superblock...\n");
-
-       sb = (xfs_sb_t *) memalign(MEM_ALIGN, BSIZE);
+       sb = (xfs_sb_t *)memalign(libxfs_device_alignment(), BSIZE);
        if (!sb) {
                do_error(
-       "error finding secondary superblock -- failed to memalign buffer\n");
+       _("error finding secondary superblock -- failed to memalign buffer\n"));
                exit(1);
        }
 
-       bzero(&bufsb, sizeof(xfs_sb_t));
+       memset(&bufsb, 0, sizeof(xfs_sb_t));
        retval = 0;
        dirty = 0;
        bsize = 0;
@@ -127,15 +121,15 @@ find_secondary_sb(xfs_sb_t *rsb)
        /*
         * skip first sector since we know that's bad
         */
-       for (done = 0, off = XFS_AG_MIN_BYTES; !done ; off += bsize)  {
+       for (done = 0, off = start; !done ; off += skip)  {
                /*
                 * read disk 1 MByte at a time.
                 */
-               if (lseek64(fs_fd, off, SEEK_SET) != off)  {
+               if (lseek(x.dfd, off, SEEK_SET) != off)  {
                        done = 1;
                }
 
-               if (!done && (bsize = read(fs_fd, sb, BSIZE)) == 0)  {
+               if (!done && (bsize = read(x.dfd, sb, BSIZE)) <= 0)  {
                        done = 1;
                }
 
@@ -146,50 +140,174 @@ find_secondary_sb(xfs_sb_t *rsb)
                 * we don't know how big the sectors really are.
                 */
                for (i = 0; !done && i < bsize; i += BBSIZE)  {
-                       c_bufsb = (char *) sb + i;
-                       libxfs_xlate_sb(c_bufsb, &bufsb, 1, ARCH_CONVERT,
-                               XFS_SB_ALL_BITS);
+                       c_bufsb = (char *)sb + i;
+                       libxfs_sb_from_disk(&bufsb, (xfs_dsb_t *)c_bufsb);
 
-                       if (verify_sb(&bufsb, 0) != XR_OK)
+                       if (verify_sb(c_bufsb, &bufsb, 0) != XR_OK)
                                continue;
 
-                       do_warn("found candidate secondary superblock...\n");
+                       do_warn(_("found candidate secondary superblock...\n"));
 
                        /*
                         * found one.  now verify it by looking
                         * for other secondaries.
                         */
-                       bcopy(&bufsb, rsb, bufsb.sb_sectsize);
+                       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("verified secondary superblock...\n");
+                               do_warn(
+                       _("verified secondary superblock...\n"));
                                done = 1;
                                retval = 1;
                        } else  {
                                do_warn(
-                               "unable to verify superblock, continuing...\n");
+                       _("unable to verify superblock, continuing...\n"));
                        }
                }
        }
 
        free(sb);
-       return(retval);
+       return retval;
+}
+
+static int
+guess_default_geometry(
+       uint64_t                *agsize,
+       uint64_t                *agcount,
+       libxfs_init_t           *x)
+{
+       struct fs_topology      ft;
+       int                     blocklog;
+       uint64_t                dblocks;
+       int                     multidisk;
+
+       memset(&ft, 0, sizeof(ft));
+       get_topology(x, &ft, 1);
+
+       /*
+        * get geometry from get_topology result.
+        * Use default block size (2^12)
+        */
+       blocklog = 12;
+       multidisk = ft.dswidth | ft.dsunit;
+       dblocks = x->dsize >> (blocklog - BBSHIFT);
+       calc_default_ag_geometry(blocklog, dblocks, multidisk,
+                                agsize, agcount);
+
+       return blocklog;
+}
+
+int
+find_secondary_sb(xfs_sb_t *rsb)
+{
+       int             retval = 0;
+       uint64_t        agcount;
+       uint64_t        agsize;
+       uint64_t        skip;
+       int             blocklog;
+
+       /*
+        * Attempt to find secondary sb with a coarse approach,
+        * first trying agblocks and blocksize read from sb, providing
+        * they're sane.
+        */
+       do_warn(_("\nattempting to find secondary superblock...\n"));
+
+       if (verify_sb_blocksize(rsb) == 0) {
+               skip = (uint64_t)rsb->sb_agblocks * rsb->sb_blocksize;
+               if (skip >= XFS_AG_MIN_BYTES && skip <= XFS_AG_MAX_BYTES)
+                       retval = __find_secondary_sb(rsb, skip, skip);
+       }
+
+        /* If that failed, retry coarse approach, using default geometry */
+        if (!retval) {
+                blocklog = guess_default_geometry(&agsize, &agcount, &x);
+                skip = agsize << blocklog;
+                retval = __find_secondary_sb(rsb, skip, skip);
+        }
+
+        /* If that failed, fall back to the brute force method */
+        if (!retval)
+                retval = __find_secondary_sb(rsb, XFS_AG_MIN_BYTES, BSIZE);
+
+       return retval;
 }
 
 /*
- * calculate what inode alignment field ought to be
- * based on internal superblock info
+ * Calculate what the inode alignment field ought to be based on internal
+ * superblock info and determine if it is valid.
+ *
+ * For standard v5 superblocks, the inode alignment must either match
+ * XFS_INODE_BIG_CLUSTER_SIZE or a multiple based on the inode size. For v5
+ * superblocks with sparse inode chunks enabled, inode alignment must match the
+ * inode chunk size.
+ *
+ * Return true if the alignment is valid, false otherwise.
  */
-int
-calc_ino_align(xfs_sb_t *sb)
+static bool
+sb_validate_ino_align(struct xfs_sb *sb)
 {
-       xfs_extlen_t align;
+       xfs_extlen_t    align;
+
+       if (!xfs_sb_version_hasalign(sb))
+               return true;
 
+       /* standard cluster size alignment is always valid */
        align = XFS_INODE_BIG_CLUSTER_SIZE >> sb->sb_blocklog;
+       if (align == sb->sb_inoalignmt)
+               return true;
+
+       /* alignment scaled by inode size is v5 only for now */
+       if (!xfs_sb_version_hascrc(sb))
+               return false;
+
+       align = (XFS_INODE_BIG_CLUSTER_SIZE *
+                sb->sb_inodesize / XFS_DINODE_MIN_SIZE) >> sb->sb_blocklog;
+       if (align == sb->sb_inoalignmt)
+               return true;
 
-       return(align);
+       /*
+        * Sparse inodes requires inoalignmt to match full inode chunk size and
+        * spino_align to match the scaled alignment (as calculated above).
+        */
+       if (xfs_sb_version_hassparseinodes(sb)) {
+               if (align != sb->sb_spino_align)
+                       return false;
+
+               align = (sb->sb_inodesize * XFS_INODES_PER_CHUNK)
+                       >> sb->sb_blocklog;
+               if (align == sb->sb_inoalignmt)
+                       return true;
+       }
+
+       return false;
+}
+
+/*
+ * Validate the given log space.  Derived from xfs_log_mount, though we
+ * can't validate the minimum log size until later.  We only do this
+ * validation on V5 filesystems because the kernel doesn't reject malformed
+ * log geometry on older revision filesystems.
+ *
+ * Returns false if the log is garbage.
+ */
+static bool
+verify_sb_loginfo(
+       struct xfs_sb   *sb)
+{
+       if (xfs_sb_version_hascrc(sb) &&
+           (sb->sb_logblocks == 0 ||
+            sb->sb_logblocks > XFS_MAX_LOG_BLOCKS ||
+            ((unsigned long long)sb->sb_logblocks << sb->sb_blocklog) >
+            XFS_MAX_LOG_BYTES))
+               return false;
+
+       if (sb->sb_logsunit > 1 && sb->sb_logsunit % sb->sb_blocksize)
+               return false;
+
+       return true;
 }
 
 /*
@@ -220,6 +338,8 @@ calc_ino_align(xfs_sb_t *sb)
  * sector size info -
  *                     sb_sectsize
  *                     sb_sectlog
+ *                     sb_logsectsize
+ *                     sb_logsectlog
  *
  * not checked here -
  *                     sb_rootino
@@ -233,18 +353,18 @@ calc_ino_align(xfs_sb_t *sb)
  */
 
 int
-verify_sb(xfs_sb_t *sb, int is_primary_sb)
+verify_sb(char *sb_buf, xfs_sb_t *sb, int is_primary_sb)
 {
-       __uint32_t      bsize;
-       xfs_extlen_t    align;
+       uint32_t        bsize;
        int             i;
-       
+       int             ret;
+
        /* check magic number and version number */
 
        if (sb->sb_magicnum != XFS_SB_MAGIC)
                return(XR_BAD_MAGIC);
 
-       if (!XFS_SB_GOOD_VERSION(sb))
+       if (!xfs_sb_good_version(sb))
                return(XR_BAD_VERSION);
 
        /* does sb think mkfs really finished ? */
@@ -252,52 +372,83 @@ verify_sb(xfs_sb_t *sb, int is_primary_sb)
        if (is_primary_sb && sb->sb_inprogress == 1)
                return(XR_BAD_INPROGRESS);
 
-       /* check to make sure blocksize is legal 2^N, 9 <= N <= 16 */
+       /*
+        * before going *any further*, validate the sector size and if the
+        * version says we should have CRCs enabled, validate that.
+        */
 
-       if (sb->sb_blocksize == 0)
-               return(XR_BAD_BLOCKSIZE);
+       /* check to make sure sectorsize is legal 2^N, 9 <= N <= 15 */
+       if (sb->sb_sectsize == 0)
+               return(XR_BAD_SECT_SIZE_DATA);
 
        bsize = 1;
-
-       for (i = 0; bsize < sb->sb_blocksize && i < 32; i++)  {
+       for (i = 0; bsize < sb->sb_sectsize &&
+               i < sizeof(sb->sb_sectsize) * NBBY; i++)  {
                bsize <<= 1;
        }
 
-       if (i < XR_LOG2BSIZE_MIN || i > XR_LOG2BSIZE_MAX)
-               return(XR_BAD_BLOCKSIZE);
+       if (i < XFS_MIN_SECTORSIZE_LOG || i > XFS_MAX_SECTORSIZE_LOG)
+               return(XR_BAD_SECT_SIZE_DATA);
+
+       /* check sb sectorsize field against sb sectlog field */
+       if (i != sb->sb_sectlog)
+               return(XR_BAD_SECT_SIZE_DATA);
 
-       /* check sb blocksize field against sb blocklog field */
+       /* sector size in range - CRC check time */
+       if (xfs_sb_version_hascrc(sb) &&
+           !libxfs_verify_cksum(sb_buf, sb->sb_sectsize, XFS_SB_CRC_OFF))
+               return XR_BAD_CRC;
 
-       if (i != sb->sb_blocklog)
-               return(XR_BAD_BLOCKLOG);
+       /* check to ensure blocksize and blocklog are legal */
+       ret = verify_sb_blocksize(sb);
+       if (ret != 0)
+               return ret;
 
        /* sanity check ag count, size fields against data size field */
 
        if (sb->sb_dblocks == 0 ||
-               sb->sb_dblocks > sb->sb_agcount * sb->sb_agblocks ||
-               sb->sb_dblocks < (sb->sb_agcount - 1)
-                       * sb->sb_agblocks + XFS_MIN_AG_BLOCKS)
+               sb->sb_dblocks > XFS_MAX_DBLOCKS(sb) ||
+               sb->sb_dblocks < XFS_MIN_DBLOCKS(sb))
                return(XR_BAD_FS_SIZE_DATA);
 
-       if (sb->sb_agblklog != (__uint8_t)libxfs_log2_roundup(sb->sb_agblocks))
+       if (sb->sb_agblklog != (uint8_t)log2_roundup(sb->sb_agblocks))
                return(XR_BAD_FS_SIZE_DATA);
 
-       if (sb->sb_inodesize < XFS_DINODE_MIN_SIZE ||
-               sb->sb_inodesize > XFS_DINODE_MAX_SIZE ||
-               sb->sb_inopblock != howmany(sb->sb_blocksize,sb->sb_inodesize))
-               return(XR_BAD_INO_SIZE_DATA);
+       if (sb->sb_inodesize < XFS_DINODE_MIN_SIZE                     ||
+           sb->sb_inodesize > XFS_DINODE_MAX_SIZE                     ||
+           sb->sb_inodelog < XFS_DINODE_MIN_LOG                       ||
+           sb->sb_inodelog > XFS_DINODE_MAX_LOG                       ||
+           sb->sb_inodesize != (1 << sb->sb_inodelog)                 ||
+           sb->sb_logsunit > XLOG_MAX_RECORD_BSIZE                    ||
+           sb->sb_inopblock != howmany(sb->sb_blocksize, sb->sb_inodesize) ||
+           (sb->sb_blocklog - sb->sb_inodelog != sb->sb_inopblog))
+               return XR_BAD_INO_SIZE_DATA;
 
-       /* check sector size against log(sector size) field */
+       if (!verify_sb_loginfo(sb))
+               return XR_BAD_LOG_GEOMETRY;
 
-       bsize = 1;
+       if (xfs_sb_version_hassector(sb))  {
 
-       for (i = 0; bsize < sb->sb_sectsize && i < 15; i++)  {
-               bsize <<= 1;
-       }
+               /* check to make sure log sector is legal 2^N, 9 <= N <= 15 */
 
-       if (sb->sb_sectsize == 0 || i == 16 ||
-                       sb->sb_sectsize != (1 << i))
-               return(XR_BAD_SECT_SIZE_DATA);
+               if (sb->sb_logsectsize == 0)
+                       return(XR_BAD_SECT_SIZE_DATA);
+
+               bsize = 1;
+
+               for (i = 0; bsize < sb->sb_logsectsize &&
+                       i < sizeof(sb->sb_logsectsize) * NBBY; i++)  {
+                       bsize <<= 1;
+               }
+
+               if (i < XFS_MIN_SECTORSIZE_LOG || i > XFS_MAX_SECTORSIZE_LOG)
+                       return(XR_BAD_SECT_SIZE_DATA);
+
+               /* check sb log sectorsize field against sb log sectlog field */
+
+               if (i != sb->sb_logsectlog)
+                       return(XR_BAD_SECT_SIZE_DATA);
+       }
 
        /*
         * real-time extent size is always set
@@ -339,12 +490,8 @@ verify_sb(xfs_sb_t *sb, int is_primary_sb)
        /*
         * verify correctness of inode alignment if it's there
         */
-       if (XFS_SB_VERSION_HASALIGN(sb))  {
-               align = calc_ino_align(sb);
-
-               if (align != sb->sb_inoalignmt)
-                       return(XR_BAD_INO_ALIGN);
-       }
+       if (!sb_validate_ino_align(sb))
+               return(XR_BAD_INO_ALIGN);
 
        /*
         * verify max. % of inodes (sb_imax_pct)
@@ -355,69 +502,19 @@ verify_sb(xfs_sb_t *sb, int is_primary_sb)
        /*
         * verify stripe alignment fields if present
         */
-       if (XFS_SB_VERSION_HASDALIGN(sb)) {
-               if ((!sb->sb_unit && sb->sb_width) || 
-                   (sb->sb_unit && sb->sb_agblocks % sb->sb_unit)) 
+       if (xfs_sb_version_hasdalign(sb)) {
+               if ((!sb->sb_unit && sb->sb_width) ||
+                   (sb->sb_unit && sb->sb_agblocks % sb->sb_unit))
                        return(XR_BAD_SB_UNIT);
                if ((sb->sb_unit && !sb->sb_width) ||
                    (sb->sb_width && sb->sb_unit && sb->sb_width % sb->sb_unit))
                        return(XR_BAD_SB_WIDTH);
-       }
+       } else if (sb->sb_unit || sb->sb_width)
+               return XR_BAD_SB_WIDTH;
 
-       /*
-        * if shared bit is set, verify that the version number is sane
-        */
-       if (XFS_SB_VERSION_HASSHARED(sb))  {
-               if (sb->sb_shared_vn > XFS_SB_MAX_SHARED_VN)
-                       return(XR_BAD_SVN);
-       }
-
-       /*
-        * mkfs's that stamped a feature bit besides the ones in the
-        * mask below could leave garbage in the secondary superblock
-        * sectors.  Anything stamping the shared fs bit or better into
-        * the secondaries is ok and should generate clean secondary
-        * superblock sectors.
-        *
-        * check primary and clean secondary superblocks more strictly
-        */
-       if (is_primary_sb || sb->sb_versionnum & XR_PART_SECSB_VNMASK)  {
-               /*
-                * return errors if shared vn or alignment fields
-                * are set without their feature bits being set
-                */
-               if (!pre_65_beta && sb->sb_versionnum & XR_PART_SECSB_VNMASK ||
-                   pre_65_beta && sb->sb_versionnum & XR_ALPHA_SECSB_VNMASK)  {
-                       /*
-                        * shared version # and inode alignment fields
-                        * should be valid
-                        */
-                       if (sb->sb_shared_vn && !XFS_SB_VERSION_HASSHARED(sb))
-                               return(XR_BAD_SVN);
-                       if (sb->sb_inoalignmt && !XFS_SB_VERSION_HASALIGN(sb))
-                               return(XR_BAD_INO_ALIGN);
-               }
-               if ((!pre_65_beta &&
-                    (sb->sb_versionnum & XR_GOOD_SECSB_VNMASK)) ||
-                   (pre_65_beta &&
-                    (sb->sb_versionnum & XFS_SB_VERSION_DALIGNBIT)))  {
-                       /*
-                        * stripe alignment values should be valid
-                        */
-                       if (sb->sb_unit && !XFS_SB_VERSION_HASDALIGN(sb))
-                               return(XR_BAD_SB_UNIT);
-                       if (sb->sb_width && !XFS_SB_VERSION_HASDALIGN(sb))
-                               return(XR_BAD_SB_WIDTH);
-               }
-
-#if 0
-               /*
-                * checks involving later superblock fields get added here...
-                */
-               if (sb->sb_versionnum & XR_GOOD_SECSB_VNMASK)  {
-               }
-#endif
-       }
+       /* Directory block log */
+       if (sb->sb_blocklog + sb->sb_dirblklog > XFS_MAX_BLOCKSIZE_LOG)
+               return XR_BAD_DIR_SIZE_DATA;
 
        return(XR_OK);
 }
@@ -425,97 +522,81 @@ verify_sb(xfs_sb_t *sb, int is_primary_sb)
 void
 write_primary_sb(xfs_sb_t *sbp, int size)
 {
-        void *buf;
-        
+       xfs_dsb_t       *buf;
+
        if (no_modify)
                return;
-        
-        if ((buf = calloc(size, 1)) == NULL) {
-               do_error("failed to malloc superblock buffer\n");
-                return;
+
+       buf = memalign(libxfs_device_alignment(), size);
+       if (buf == NULL) {
+               do_error(_("failed to memalign superblock buffer\n"));
+               return;
        }
+       memset(buf, 0, size);
 
-       if (lseek64(fs_fd, 0LL, SEEK_SET) != 0LL) {
-                free(buf);
-               do_error("couldn't seek to offset 0 in filesystem\n");
-        }
-        
-       libxfs_xlate_sb(buf, sbp, -1, ARCH_CONVERT, XFS_SB_ALL_BITS);
+       if (lseek(x.dfd, 0LL, SEEK_SET) != 0LL) {
+               free(buf);
+               do_error(_("couldn't seek to offset 0 in filesystem\n"));
+       }
 
-       if (write(fs_fd, buf, size) != size) {
-                free(buf);
-               do_error("primary superblock write failed!\n");
-        }
+       libxfs_sb_to_disk(buf, sbp);
+
+       if (xfs_sb_version_hascrc(sbp))
+               xfs_update_cksum((char *)buf, size, XFS_SB_CRC_OFF);
+
+       if (write(x.dfd, buf, size) != size) {
+               free(buf);
+               do_error(_("primary superblock write failed!\n"));
+       }
 
-        free(buf);
+       free(buf);
 }
 
 /*
- * get a possible superblock -- don't check for internal consistency
+ * get a possible superblock -- checks for internal consistency
  */
 int
 get_sb(xfs_sb_t *sbp, xfs_off_t off, int size, xfs_agnumber_t agno)
 {
        int error, rval;
-        void *buf;
-        
-        if ((buf = calloc(size, 1)) == NULL) {
+       xfs_dsb_t *buf;
+
+       buf = memalign(libxfs_device_alignment(), size);
+       if (buf == NULL) {
                do_error(
-       "error reading superblock %u -- failed to malloc buffer\n",
-                       agno, off);
+       _("error reading superblock %u -- failed to memalign buffer\n"),
+                       agno);
                exit(1);
        }
+       memset(buf, 0, size);
+       memset(sbp, 0, sizeof(*sbp));
 
        /* try and read it first */
 
-       if (lseek64(fs_fd, off, SEEK_SET) != off)  {
+       if (lseek(x.dfd, off, SEEK_SET) != off)  {
                do_warn(
-       "error reading superblock %u -- seek to offset %lld failed\n",
+       _("error reading superblock %u -- seek to offset %" PRId64 " failed\n"),
                        agno, off);
+               free(buf);
                return(XR_EOF);
        }
 
-       if ((rval = read(fs_fd, buf, size)) != size)  {
+       if ((rval = read(x.dfd, buf, size)) != size)  {
                error = errno;
                do_warn(
-"superblock read failed, offset %lld, size %d, ag %u, rval %d\n",
-                       off, size, rval, agno);
+       _("superblock read failed, offset %" PRId64 ", size %d, ag %u, rval %d\n"),
+                       off, size, agno, rval);
                do_error("%s\n", strerror(error));
        }
-       libxfs_xlate_sb(buf, sbp, 1, ARCH_CONVERT, XFS_SB_ALL_BITS);
-       free(buf);
+       libxfs_sb_from_disk(sbp, buf);
 
-       return (verify_sb(sbp, 0));
+       rval = verify_sb((char *)buf, sbp, agno == 0);
+       free(buf);
+       return rval;
 }
 
-#if 0
-int
-check_growfs(xfs_off_t off, int bufnum, xfs_agnumber_t agnum)
-{
-       int rval;
-
-       ASSERT(bufnum < NUM_SBS);
-
-       /* try and read it first */
-
-       if (lseek64(fs_fd, off, SEEK_SET) != off)
-               return(XR_EOF);
-
-       if ((rval = read(fs_fd, sb_bufs[bufnum], sbbuf_size)) != sbbuf_size)  {
-               /*
-                * we didn't get a full block so the filesystem
-                * could not have been grown.  return a non-XR_OK
-                * result code.
-                */
-               return(XR_EOF);
-       }
-
-       return(get_sb(off, bufnum, agnum));
-}
-#endif
 /* returns element on list with highest reference count */
-
-fs_geo_list_t *
+static fs_geo_list_t *
 get_best_geo(fs_geo_list_t *list)
 {
        int cnt = 0;
@@ -535,12 +616,11 @@ get_best_geo(fs_geo_list_t *list)
 }
 
 /* adds geometry info to linked list.  returns (sometimes new) head of list */
-
-fs_geo_list_t *
+static fs_geo_list_t *
 add_geo(fs_geo_list_t *list, fs_geometry_t *geo_p, int index)
 {
        fs_geo_list_t   *current = list;
-       
+
        while (current != NULL)  {
                if (memcmp(geo_p, &current->geo, sizeof(fs_geometry_t)) == 0)  {
                        current->refs++;
@@ -551,7 +631,7 @@ add_geo(fs_geo_list_t *list, fs_geometry_t *geo_p, int index)
        }
 
        if ((current = malloc(sizeof(fs_geo_list_t))) == NULL) {
-               do_error("couldn't malloc geometry structure\n");
+               do_error(_("couldn't malloc geometry structure\n"));
                exit(1);
        }
 
@@ -563,14 +643,12 @@ add_geo(fs_geo_list_t *list, fs_geometry_t *geo_p, int index)
        return(current);
 }
 
-void
+static void
 free_geo(fs_geo_list_t *list)
 {
        fs_geo_list_t   *next;
        fs_geo_list_t   *current;
 
-       current = list;
-
        for (current = list; current != NULL; current = next)  {
                next = current->next;
                free(current);
@@ -580,7 +658,7 @@ free_geo(fs_geo_list_t *list)
 void
 get_sb_geometry(fs_geometry_t *geo, xfs_sb_t *sbp)
 {
-       bzero(geo, sizeof(fs_geometry_t));
+       memset(geo, 0, sizeof(fs_geometry_t));
 
        /*
         * blindly set fields that we know are always good
@@ -598,61 +676,14 @@ get_sb_geometry(fs_geometry_t *geo, xfs_sb_t *sbp)
        geo->sb_sectsize = sbp->sb_sectsize;
        geo->sb_inodesize = sbp->sb_inodesize;
 
-       if (XFS_SB_VERSION_HASALIGN(sbp))
+       if (xfs_sb_version_hasalign(sbp))
                geo->sb_ialignbit = 1;
 
-       if (XFS_SB_VERSION_HASSHARED(sbp) ||
-           sbp->sb_versionnum & XR_PART_SECSB_VNMASK)
-               geo->sb_sharedbit = 1;
-
-       if (XFS_SB_VERSION_HASDALIGN(sbp))
+       if (xfs_sb_version_hasdalign(sbp))
                geo->sb_salignbit = 1;
 
-       if (XFS_SB_VERSION_HASEXTFLGBIT(sbp))
-               geo->sb_extflgbit = 1;
-
-       /*
-        * protect against pre-6.5 mkfs-generated garbaged
-        * fields in the secondary superblocks.  pay attention
-        * to those fields if and only if their corresponding
-        * feature bits are set in the feature bits of the
-        * version number or we can deduce from the version bits
-        * that are set that our field was properly initialized
-        * because a field after the field we care about was
-        * properly initialized as well.
-        */
-
-       /*
-        * inode alignment field lives before the data alignment field
-        */
-       if (!pre_65_beta && sbp->sb_versionnum & XR_PART_SECSB_VNMASK ||
-           pre_65_beta && sbp->sb_versionnum & XR_ALPHA_SECSB_VNMASK)
-               geo->sb_inoalignmt = sbp->sb_inoalignmt;
-
-       if (!pre_65_beta && sbp->sb_versionnum & XR_GOOD_SECSB_VNMASK ||
-           pre_65_beta && XFS_SB_VERSION_HASDALIGN(sbp))  {
-               geo->sb_unit = sbp->sb_unit;
-               geo->sb_width = sbp->sb_width;
-       }
-
-       /*
-        * shared vn always set if either ino or data alignment is on
-        * since that field lives between the quota and inode alignment
-        * fields
-        */
-       if (sbp->sb_versionnum & XR_PART_SECSB_VNMASK)
-               geo->sb_shared_vn = sbp->sb_shared_vn;
-
-       /*
-        * superblock fields located after sb_widthfields get set
-        * into the geometry structure only if we can determine
-        * from the features enabled in this superblock whether
-        * or not the sector was bzero'd at mkfs time.
-        */
-       if (!pre_65_beta && sbp->sb_versionnum & XR_GOOD_SECSB_VNMASK ||
-           pre_65_beta && sbp->sb_versionnum & XR_ALPHA_SECSB_VNMASK)  {
-               geo->sb_fully_zeroed = 1;
-       }
+       geo->sb_extflgbit = 1;
+       geo->sb_fully_zeroed = 1;
 }
 
 /*
@@ -661,92 +692,77 @@ get_sb_geometry(fs_geometry_t *geo, xfs_sb_t *sbp)
  * primary and compare the geometries in the secondaries against
  * the geometry indicated by the primary.
  *
- * returns 1 if bad, 0 if ok
+ * returns 0 if ok, else error code (XR_EOF, XR_INSUFF_SEC_SB, etc).
  */
 int
 verify_set_primary_sb(xfs_sb_t         *rsb,
                        int             sb_index,
                        int             *sb_modified)
 {
-       xfs_off_t               off;
+       xfs_off_t       off;
        fs_geometry_t   geo;
        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
+        * We haven't been able to validate the sector size yet properly
+        * (e.g. in the case of repairing an image in a file), so we need to
+        * take into account sector mismatches and so use the maximum possible
+        * sector size rather than the sector size in @rsb.
         */
-       num_sbs = MIN(NUM_SBS, rsb->sb_agcount);
-       skip = howmany(num_sbs, rsb->sb_agcount);
-       size = NUM_AGH_SECTS * rsb->sb_sectsize;
-       retval = 0;
+       size = NUM_AGH_SECTS * (1 << (XFS_MAX_SECTORSIZE_LOG));
        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;
+               retval = get_sb(sb, off, size, agno);
+               if (retval == XR_EOF)
+                       goto out_free_list;
 
-                       checked[agno] = 1;
-
-                       if (get_sb(sb, off, size, agno) == XR_EOF)  {
-                               retval = 1;
-                               goto out;
-                       }
-
-                       if (verify_sb(sb, 0) == 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++;
-                       }
+               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
         */
-       if (num_ok < num_sbs / 2)
-               return(XR_INSUFF_SEC_SB);
+       retval = 0;
+       if (num_ok < num_sbs / 2) {
+               retval = XR_INSUFF_SEC_SB;
+               goto out_free_list;
+       }
 
        current = get_best_geo(list);
 
@@ -758,26 +774,37 @@ verify_set_primary_sb(xfs_sb_t            *rsb,
        switch (num_sbs)  {
        case 2:
                /*
-                * all them have to be right.  if not, report geometry
-                * and get out unless force option is in effect (-F)
+                * If we only have two allocation groups, and the superblock
+                * in the second allocation group differs from the primary
+                * superblock we can't verify the geometry information.
+                * Warn the user about this situation and get out unless
+                * explicitly overridden.
                 */
                if (current->refs != 2)  {
                        if (!force_geo)  {
-                               do_warn("Only two AGs detected and they do not match - cannot proceed.\n");
+                               do_warn(
+       _("Only two AGs detected and they do not match - "
+         "cannot validate filesystem geometry.\n"
+         "Use the -o force_geometry option to proceed.\n"));
                                exit(1);
                        }
                }
-               break;
+               goto out_free_list;
        case 1:
                /*
-                * just report the geometry info and get out.
-                * refuse to run further unless the force (-F)
-                * option is in effect.
+                * If we only have a single allocation group there is no
+                * secondary superblock that we can use to verify the geometry
+                * information.  Warn the user about this situation and get
+                * out unless explicitly overridden.
                 */
                if (!force_geo)  {
-                       do_warn("Only one AG detected - cannot proceed.\n");
+                       do_warn(
+       _("Only one AG detected - "
+         "cannot validate filesystem geometry.\n"
+         "Use the -o force_geometry option to proceed.\n"));
                        exit(1);
                }
+               goto out_free_list;
        default:
                /*
                 * at least half of the probed superblocks have
@@ -786,7 +813,8 @@ verify_set_primary_sb(xfs_sb_t              *rsb,
                 * XFS normally doesn't alter the secondary superblocks.
                 */
                if (current->refs < num_sbs / 2)  {
-                       do_warn("Not enough matching superblocks - cannot proceed.\n");
+                       do_warn(
+               _("Not enough matching superblocks - cannot proceed.\n"));
                        exit(1);
                }
        }
@@ -797,11 +825,11 @@ verify_set_primary_sb(xfs_sb_t            *rsb,
 
        if (current->index != sb_index)  {
                *sb_modified = 1;
-               off = current->index * current->geo.sb_agblocks 
+               off = (xfs_off_t)current->index * current->geo.sb_agblocks
                        * current->geo.sb_blocksize;
                if (get_sb(sb, off, current->geo.sb_sectsize,
                                current->index) != XR_OK)
-                       do_error("could not read superblock\n");
+                       do_error(_("could not read superblock\n"));
 
                copy_sb(sb, rsb);
 
@@ -816,9 +844,8 @@ verify_set_primary_sb(xfs_sb_t              *rsb,
                sb_width = sb->sb_width;
        }
 
+out_free_list:
        free_geo(list);
-out:
        free(sb);
-       free(checked);
-       return(retval);
+       return retval;
 }