]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - libxfs/xfs_sb.c
xfs: better xfs_trans_alloc interface
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_sb.c
index 8b90b884a8238a7302ed24583cb788a71320faf6..45db6ae8e15a5afb4948d0de3fb31893057e39a7 100644 (file)
  * along with this program; if not, write the Free Software Foundation,
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
-#include <xfs.h>
+#include "libxfs_priv.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_bit.h"
+#include "xfs_sb.h"
+#include "xfs_mount.h"
+#include "xfs_inode.h"
+#include "xfs_ialloc.h"
+#include "xfs_alloc.h"
+#include "xfs_trace.h"
+#include "xfs_cksum.h"
+#include "xfs_trans.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
 
 /*
  * Physical superblock buffer manipulations. Shared with libxfs in userspace.
  */
 
-static const struct {
-       short offset;
-       short type;     /* 0 = integer
-                        * 1 = binary / string (no translation)
-                        */
-} xfs_sb_info[] = {
-       { offsetof(xfs_sb_t, sb_magicnum),      0 },
-       { offsetof(xfs_sb_t, sb_blocksize),     0 },
-       { offsetof(xfs_sb_t, sb_dblocks),       0 },
-       { offsetof(xfs_sb_t, sb_rblocks),       0 },
-       { offsetof(xfs_sb_t, sb_rextents),      0 },
-       { offsetof(xfs_sb_t, sb_uuid),          1 },
-       { offsetof(xfs_sb_t, sb_logstart),      0 },
-       { offsetof(xfs_sb_t, sb_rootino),       0 },
-       { offsetof(xfs_sb_t, sb_rbmino),        0 },
-       { offsetof(xfs_sb_t, sb_rsumino),       0 },
-       { offsetof(xfs_sb_t, sb_rextsize),      0 },
-       { offsetof(xfs_sb_t, sb_agblocks),      0 },
-       { offsetof(xfs_sb_t, sb_agcount),       0 },
-       { offsetof(xfs_sb_t, sb_rbmblocks),     0 },
-       { offsetof(xfs_sb_t, sb_logblocks),     0 },
-       { offsetof(xfs_sb_t, sb_versionnum),    0 },
-       { offsetof(xfs_sb_t, sb_sectsize),      0 },
-       { offsetof(xfs_sb_t, sb_inodesize),     0 },
-       { offsetof(xfs_sb_t, sb_inopblock),     0 },
-       { offsetof(xfs_sb_t, sb_fname[0]),      1 },
-       { offsetof(xfs_sb_t, sb_blocklog),      0 },
-       { offsetof(xfs_sb_t, sb_sectlog),       0 },
-       { offsetof(xfs_sb_t, sb_inodelog),      0 },
-       { offsetof(xfs_sb_t, sb_inopblog),      0 },
-       { offsetof(xfs_sb_t, sb_agblklog),      0 },
-       { offsetof(xfs_sb_t, sb_rextslog),      0 },
-       { offsetof(xfs_sb_t, sb_inprogress),    0 },
-       { offsetof(xfs_sb_t, sb_imax_pct),      0 },
-       { offsetof(xfs_sb_t, sb_icount),        0 },
-       { offsetof(xfs_sb_t, sb_ifree),         0 },
-       { offsetof(xfs_sb_t, sb_fdblocks),      0 },
-       { offsetof(xfs_sb_t, sb_frextents),     0 },
-       { offsetof(xfs_sb_t, sb_uquotino),      0 },
-       { offsetof(xfs_sb_t, sb_gquotino),      0 },
-       { offsetof(xfs_sb_t, sb_qflags),        0 },
-       { offsetof(xfs_sb_t, sb_flags),         0 },
-       { offsetof(xfs_sb_t, sb_shared_vn),     0 },
-       { offsetof(xfs_sb_t, sb_inoalignmt),    0 },
-       { offsetof(xfs_sb_t, sb_unit),          0 },
-       { offsetof(xfs_sb_t, sb_width),         0 },
-       { offsetof(xfs_sb_t, sb_dirblklog),     0 },
-       { offsetof(xfs_sb_t, sb_logsectlog),    0 },
-       { offsetof(xfs_sb_t, sb_logsectsize),   0 },
-       { offsetof(xfs_sb_t, sb_logsunit),      0 },
-       { offsetof(xfs_sb_t, sb_features2),     0 },
-       { offsetof(xfs_sb_t, sb_bad_features2), 0 },
-       { offsetof(xfs_sb_t, sb_features_compat),       0 },
-       { offsetof(xfs_sb_t, sb_features_ro_compat),    0 },
-       { offsetof(xfs_sb_t, sb_features_incompat),     0 },
-       { offsetof(xfs_sb_t, sb_features_log_incompat), 0 },
-       { offsetof(xfs_sb_t, sb_crc),           0 },
-       { offsetof(xfs_sb_t, sb_pad),           0 },
-       { offsetof(xfs_sb_t, sb_pquotino),      0 },
-       { offsetof(xfs_sb_t, sb_lsn),           0 },
-       { sizeof(xfs_sb_t),                     0 }
-};
-
 /*
  * Reference counting access wrappers to the perag structures.
  * Because we never free per-ag structures, the only thing we
@@ -155,23 +109,15 @@ xfs_mount_validate_sb(
        bool            check_inprogress,
        bool            check_version)
 {
-
-       /*
-        * If the log device and data device have the
-        * same device number, the log is internal.
-        * Consequently, the sb_logstart should be non-zero.  If
-        * we have a zero sb_logstart in this case, we may be trying to mount
-        * a volume filesystem in a non-volume manner.
-        */
        if (sbp->sb_magicnum != XFS_SB_MAGIC) {
                xfs_warn(mp, "bad magic number");
-               return XFS_ERROR(EWRONGFS);
+               return -EWRONGFS;
        }
 
 
        if (!xfs_sb_good_version(sbp)) {
                xfs_warn(mp, "bad version");
-               return XFS_ERROR(EWRONGFS);
+               return -EWRONGFS;
        }
 
        /*
@@ -180,49 +126,81 @@ xfs_mount_validate_sb(
         * write validation, we don't need to check feature masks.
         */
        if (check_version && XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5) {
-               xfs_alert(mp,
-"Version 5 superblock detected. xfsprogs has EXPERIMENTAL support enabled!\n"
-"Use of these features is at your own risk!");
-
                if (xfs_sb_has_compat_feature(sbp,
                                        XFS_SB_FEAT_COMPAT_UNKNOWN)) {
                        xfs_warn(mp,
-"Superblock has unknown compatible features (0x%x) enabled.\n"
-"Using a more recent xfsprogs is recommended.",
+"Superblock has unknown compatible features (0x%x) enabled.",
                                (sbp->sb_features_compat &
                                                XFS_SB_FEAT_COMPAT_UNKNOWN));
+                       xfs_warn(mp,
+"Using a more recent kernel is recommended.");
                }
 
                if (xfs_sb_has_ro_compat_feature(sbp,
                                        XFS_SB_FEAT_RO_COMPAT_UNKNOWN)) {
-                       xfs_warn(mp,
-"Superblock has unknown read-only compatible features (0x%x) enabled.\n"
-"Using a more recent xfsprogs is recommended.",
+                       xfs_alert(mp,
+"Superblock has unknown read-only compatible features (0x%x) enabled.",
                                (sbp->sb_features_ro_compat &
                                                XFS_SB_FEAT_RO_COMPAT_UNKNOWN));
+                       if (!(mp->m_flags & XFS_MOUNT_RDONLY)) {
+                               xfs_warn(mp,
+"Attempted to mount read-only compatible filesystem read-write.");
+                               xfs_warn(mp,
+"Filesystem can only be safely mounted read only.");
+
+                               return -EINVAL;
+                       }
                }
                if (xfs_sb_has_incompat_feature(sbp,
                                        XFS_SB_FEAT_INCOMPAT_UNKNOWN)) {
                        xfs_warn(mp,
-"Superblock has unknown incompatible features (0x%x) enabled.\n"
-"Filesystem can not be safely operated on by this xfsprogs installation",
+"Superblock has unknown incompatible features (0x%x) enabled.",
                                (sbp->sb_features_incompat &
                                                XFS_SB_FEAT_INCOMPAT_UNKNOWN));
-                       return XFS_ERROR(EINVAL);
+                       xfs_warn(mp,
+"Filesystem can not be safely mounted by this kernel.");
+                       return -EINVAL;
                }
+       } else if (xfs_sb_version_hascrc(sbp)) {
+               /*
+                * We can't read verify the sb LSN because the read verifier is
+                * called before the log is allocated and processed. We know the
+                * log is set up before write verifier (!check_version) calls,
+                * so just check it here.
+                */
+               if (!xfs_log_check_lsn(mp, sbp->sb_lsn))
+                       return -EFSCORRUPTED;
        }
 
        if (xfs_sb_version_has_pquotino(sbp)) {
                if (sbp->sb_qflags & (XFS_OQUOTA_ENFD | XFS_OQUOTA_CHKD)) {
                        xfs_notice(mp,
-                          "Version 5 of Super block has XFS_OQUOTA bits.\n");
-                       return XFS_ERROR(EFSCORRUPTED);
+                          "Version 5 of Super block has XFS_OQUOTA bits.");
+                       return -EFSCORRUPTED;
                }
        } else if (sbp->sb_qflags & (XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD |
                                XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD)) {
                        xfs_notice(mp,
-"Superblock earlier than Version 5 has XFS_[PQ]UOTA_{ENFD|CHKD} bits.\n");
-                       return XFS_ERROR(EFSCORRUPTED);
+"Superblock earlier than Version 5 has XFS_[PQ]UOTA_{ENFD|CHKD} bits.");
+                       return -EFSCORRUPTED;
+       }
+
+       /*
+        * Full inode chunks must be aligned to inode chunk size when
+        * sparse inodes are enabled to support the sparse chunk
+        * allocation algorithm and prevent overlapping inode records.
+        */
+       if (xfs_sb_version_hassparseinodes(sbp)) {
+               uint32_t        align;
+
+               align = XFS_INODES_PER_CHUNK * sbp->sb_inodesize
+                               >> sbp->sb_blocklog;
+               if (sbp->sb_inoalignmt != align) {
+                       xfs_warn(mp,
+"Inode block alignment (%u) must match chunk size (%u) for sparse inodes.",
+                                sbp->sb_inoalignmt, align);
+                       return -EINVAL;
+               }
        }
 
        if (unlikely(
@@ -230,7 +208,7 @@ xfs_mount_validate_sb(
                xfs_warn(mp,
                "filesystem is marked as having an external log; "
                "specify logdev on the mount command line.");
-               return XFS_ERROR(EINVAL);
+               return -EINVAL;
        }
 
        if (unlikely(
@@ -238,7 +216,7 @@ xfs_mount_validate_sb(
                xfs_warn(mp,
                "filesystem is marked as having an internal log; "
                "do not specify logdev on the mount command line.");
-               return XFS_ERROR(EINVAL);
+               return -EINVAL;
        }
 
        /*
@@ -257,21 +235,24 @@ xfs_mount_validate_sb(
            sbp->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG                    ||
            sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG                    ||
            sbp->sb_blocksize != (1 << sbp->sb_blocklog)                ||
+           sbp->sb_dirblklog > XFS_MAX_BLOCKSIZE_LOG                   ||
            sbp->sb_inodesize < XFS_DINODE_MIN_SIZE                     ||
            sbp->sb_inodesize > XFS_DINODE_MAX_SIZE                     ||
            sbp->sb_inodelog < XFS_DINODE_MIN_LOG                       ||
            sbp->sb_inodelog > XFS_DINODE_MAX_LOG                       ||
            sbp->sb_inodesize != (1 << sbp->sb_inodelog)                ||
+           sbp->sb_logsunit > XLOG_MAX_RECORD_BSIZE                    ||
+           sbp->sb_inopblock != howmany(sbp->sb_blocksize,sbp->sb_inodesize) ||
            (sbp->sb_blocklog - sbp->sb_inodelog != sbp->sb_inopblog)   ||
            (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE)  ||
            (sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE)  ||
            (sbp->sb_imax_pct > 100 /* zero sb_imax_pct is valid */)    ||
            sbp->sb_dblocks == 0                                        ||
            sbp->sb_dblocks > XFS_MAX_DBLOCKS(sbp)                      ||
-           sbp->sb_dblocks < XFS_MIN_DBLOCKS(sbp))) {
-               XFS_CORRUPTION_ERROR("SB sanity check failed",
-                               XFS_ERRLEVEL_LOW, mp, sbp);
-               return XFS_ERROR(EFSCORRUPTED);
+           sbp->sb_dblocks < XFS_MIN_DBLOCKS(sbp)                      ||
+           sbp->sb_shared_vn != 0)) {
+               xfs_notice(mp, "SB sanity check failed");
+               return -EFSCORRUPTED;
        }
 
        /*
@@ -286,22 +267,14 @@ xfs_mount_validate_sb(
        default:
                xfs_warn(mp, "inode size of %d bytes not supported",
                                sbp->sb_inodesize);
-               return XFS_ERROR(ENOSYS);
+               return -ENOSYS;
        }
 
        if (xfs_sb_validate_fsb_count(sbp, sbp->sb_dblocks) ||
            xfs_sb_validate_fsb_count(sbp, sbp->sb_rblocks)) {
                xfs_warn(mp,
                "file system too large to be mounted on this system.");
-               return XFS_ERROR(EFBIG);
-       }
-
-       /*
-        * Version 1 directory format has never worked on Linux.
-        */
-       if (unlikely(!xfs_sb_version_hasdirv2(sbp))) {
-               xfs_warn(mp, "file system using version 1 directory format");
-               return XFS_ERROR(ENOSYS);
+               return -EFBIG;
        }
 
        return 0;
@@ -356,10 +329,11 @@ xfs_sb_quota_from_disk(struct xfs_sb *sbp)
        }
 }
 
-void
-xfs_sb_from_disk(
+static void
+__xfs_sb_from_disk(
        struct xfs_sb   *to,
-       xfs_dsb_t       *from)
+       xfs_dsb_t       *from,
+       bool            convert_xquota)
 {
        to->sb_magicnum = be32_to_cpu(from->sb_magicnum);
        to->sb_blocksize = be32_to_cpu(from->sb_blocksize);
@@ -412,113 +386,162 @@ xfs_sb_from_disk(
        to->sb_features_incompat = be32_to_cpu(from->sb_features_incompat);
        to->sb_features_log_incompat =
                                be32_to_cpu(from->sb_features_log_incompat);
-       to->sb_pad = 0;
+       /* crc is only used on disk, not in memory; just init to 0 here. */
+       to->sb_crc = 0;
+       to->sb_spino_align = be32_to_cpu(from->sb_spino_align);
        to->sb_pquotino = be64_to_cpu(from->sb_pquotino);
        to->sb_lsn = be64_to_cpu(from->sb_lsn);
+       /*
+        * sb_meta_uuid is only on disk if it differs from sb_uuid and the
+        * feature flag is set; if not set we keep it only in memory.
+        */
+       if (xfs_sb_version_hasmetauuid(to))
+               uuid_copy(&to->sb_meta_uuid, &from->sb_meta_uuid);
+       else
+               uuid_copy(&to->sb_meta_uuid, &from->sb_uuid);
+       /* Convert on-disk flags to in-memory flags? */
+       if (convert_xquota)
+               xfs_sb_quota_from_disk(to);
+}
+
+void
+xfs_sb_from_disk(
+       struct xfs_sb   *to,
+       xfs_dsb_t       *from)
+{
+       __xfs_sb_from_disk(to, from, true);
 }
 
-static inline void
+static void
 xfs_sb_quota_to_disk(
-       xfs_dsb_t       *to,
-       xfs_sb_t        *from,
-       __int64_t       *fields)
+       struct xfs_dsb  *to,
+       struct xfs_sb   *from)
 {
        __uint16_t      qflags = from->sb_qflags;
 
+       to->sb_uquotino = cpu_to_be64(from->sb_uquotino);
+       if (xfs_sb_version_has_pquotino(from)) {
+               to->sb_qflags = cpu_to_be16(from->sb_qflags);
+               to->sb_gquotino = cpu_to_be64(from->sb_gquotino);
+               to->sb_pquotino = cpu_to_be64(from->sb_pquotino);
+               return;
+       }
+
        /*
-        * We need to do these manipilations only if we are working
-        * with an older version of on-disk superblock.
+        * The in-core version of sb_qflags do not have XFS_OQUOTA_*
+        * flags, whereas the on-disk version does.  So, convert incore
+        * XFS_{PG}QUOTA_* flags to on-disk XFS_OQUOTA_* flags.
         */
-       if (xfs_sb_version_has_pquotino(from))
-               return;
+       qflags &= ~(XFS_PQUOTA_ENFD | XFS_PQUOTA_CHKD |
+                       XFS_GQUOTA_ENFD | XFS_GQUOTA_CHKD);
 
-       if (*fields & XFS_SB_QFLAGS) {
-               /*
-                * The in-core version of sb_qflags do not have
-                * XFS_OQUOTA_* flags, whereas the on-disk version
-                * does.  So, convert incore XFS_{PG}QUOTA_* flags
-                * to on-disk XFS_OQUOTA_* flags.
-                */
-               qflags &= ~(XFS_PQUOTA_ENFD | XFS_PQUOTA_CHKD |
-                               XFS_GQUOTA_ENFD | XFS_GQUOTA_CHKD);
-
-               if (from->sb_qflags &
-                               (XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD))
-                       qflags |= XFS_OQUOTA_ENFD;
-               if (from->sb_qflags &
-                               (XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD))
-                       qflags |= XFS_OQUOTA_CHKD;
-               to->sb_qflags = cpu_to_be16(qflags);
-               *fields &= ~XFS_SB_QFLAGS;
-       }
+       if (from->sb_qflags &
+                       (XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD))
+               qflags |= XFS_OQUOTA_ENFD;
+       if (from->sb_qflags &
+                       (XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD))
+               qflags |= XFS_OQUOTA_CHKD;
+       to->sb_qflags = cpu_to_be16(qflags);
 
        /*
         * GQUOTINO and PQUOTINO cannot be used together in versions
         * of superblock that do not have pquotino. from->sb_flags
         * tells us which quota is active and should be copied to
-        * disk.
+        * disk. If neither are active, we should NULL the inode.
+        *
+        * In all cases, the separate pquotino must remain 0 because it
+        * it beyond the "end" of the valid non-pquotino superblock.
         */
-       if ((*fields & XFS_SB_GQUOTINO) &&
-                               (from->sb_qflags & XFS_GQUOTA_ACCT))
+       if (from->sb_qflags & XFS_GQUOTA_ACCT)
                to->sb_gquotino = cpu_to_be64(from->sb_gquotino);
-       else if ((*fields & XFS_SB_PQUOTINO) &&
-                               (from->sb_qflags & XFS_PQUOTA_ACCT))
+       else if (from->sb_qflags & XFS_PQUOTA_ACCT)
                to->sb_gquotino = cpu_to_be64(from->sb_pquotino);
+       else {
+               /*
+                * We can't rely on just the fields being logged to tell us
+                * that it is safe to write NULLFSINO - we should only do that
+                * if quotas are not actually enabled. Hence only write
+                * NULLFSINO if both in-core quota inodes are NULL.
+                */
+               if (from->sb_gquotino == NULLFSINO &&
+                   from->sb_pquotino == NULLFSINO)
+                       to->sb_gquotino = cpu_to_be64(NULLFSINO);
+       }
 
-       *fields &= ~(XFS_SB_PQUOTINO | XFS_SB_GQUOTINO);
+       to->sb_pquotino = 0;
 }
 
-/*
- * Copy in core superblock to ondisk one.
- *
- * The fields argument is mask of superblock fields to copy.
- */
 void
 xfs_sb_to_disk(
-       xfs_dsb_t       *to,
-       xfs_sb_t        *from,
-       __int64_t       fields)
+       struct xfs_dsb  *to,
+       struct xfs_sb   *from)
 {
-       xfs_caddr_t     to_ptr = (xfs_caddr_t)to;
-       xfs_caddr_t     from_ptr = (xfs_caddr_t)from;
-       xfs_sb_field_t  f;
-       int             first;
-       int             size;
-
-       ASSERT(fields);
-       if (!fields)
-               return;
+       xfs_sb_quota_to_disk(to, from);
 
-       xfs_sb_quota_to_disk(to, from, &fields);
-       while (fields) {
-               f = (xfs_sb_field_t)xfs_lowbit64((__uint64_t)fields);
-               first = xfs_sb_info[f].offset;
-               size = xfs_sb_info[f + 1].offset - first;
-
-               ASSERT(xfs_sb_info[f].type == 0 || xfs_sb_info[f].type == 1);
-
-               if (size == 1 || xfs_sb_info[f].type == 1) {
-                       memcpy(to_ptr + first, from_ptr + first, size);
-               } else {
-                       switch (size) {
-                       case 2:
-                               *(__be16 *)(to_ptr + first) =
-                                     cpu_to_be16(*(__u16 *)(from_ptr + first));
-                               break;
-                       case 4:
-                               *(__be32 *)(to_ptr + first) =
-                                     cpu_to_be32(*(__u32 *)(from_ptr + first));
-                               break;
-                       case 8:
-                               *(__be64 *)(to_ptr + first) =
-                                     cpu_to_be64(*(__u64 *)(from_ptr + first));
-                               break;
-                       default:
-                               ASSERT(0);
-                       }
-               }
+       to->sb_magicnum = cpu_to_be32(from->sb_magicnum);
+       to->sb_blocksize = cpu_to_be32(from->sb_blocksize);
+       to->sb_dblocks = cpu_to_be64(from->sb_dblocks);
+       to->sb_rblocks = cpu_to_be64(from->sb_rblocks);
+       to->sb_rextents = cpu_to_be64(from->sb_rextents);
+       memcpy(&to->sb_uuid, &from->sb_uuid, sizeof(to->sb_uuid));
+       to->sb_logstart = cpu_to_be64(from->sb_logstart);
+       to->sb_rootino = cpu_to_be64(from->sb_rootino);
+       to->sb_rbmino = cpu_to_be64(from->sb_rbmino);
+       to->sb_rsumino = cpu_to_be64(from->sb_rsumino);
+       to->sb_rextsize = cpu_to_be32(from->sb_rextsize);
+       to->sb_agblocks = cpu_to_be32(from->sb_agblocks);
+       to->sb_agcount = cpu_to_be32(from->sb_agcount);
+       to->sb_rbmblocks = cpu_to_be32(from->sb_rbmblocks);
+       to->sb_logblocks = cpu_to_be32(from->sb_logblocks);
+       to->sb_versionnum = cpu_to_be16(from->sb_versionnum);
+       to->sb_sectsize = cpu_to_be16(from->sb_sectsize);
+       to->sb_inodesize = cpu_to_be16(from->sb_inodesize);
+       to->sb_inopblock = cpu_to_be16(from->sb_inopblock);
+       memcpy(&to->sb_fname, &from->sb_fname, sizeof(to->sb_fname));
+       to->sb_blocklog = from->sb_blocklog;
+       to->sb_sectlog = from->sb_sectlog;
+       to->sb_inodelog = from->sb_inodelog;
+       to->sb_inopblog = from->sb_inopblog;
+       to->sb_agblklog = from->sb_agblklog;
+       to->sb_rextslog = from->sb_rextslog;
+       to->sb_inprogress = from->sb_inprogress;
+       to->sb_imax_pct = from->sb_imax_pct;
+       to->sb_icount = cpu_to_be64(from->sb_icount);
+       to->sb_ifree = cpu_to_be64(from->sb_ifree);
+       to->sb_fdblocks = cpu_to_be64(from->sb_fdblocks);
+       to->sb_frextents = cpu_to_be64(from->sb_frextents);
+
+       to->sb_flags = from->sb_flags;
+       to->sb_shared_vn = from->sb_shared_vn;
+       to->sb_inoalignmt = cpu_to_be32(from->sb_inoalignmt);
+       to->sb_unit = cpu_to_be32(from->sb_unit);
+       to->sb_width = cpu_to_be32(from->sb_width);
+       to->sb_dirblklog = from->sb_dirblklog;
+       to->sb_logsectlog = from->sb_logsectlog;
+       to->sb_logsectsize = cpu_to_be16(from->sb_logsectsize);
+       to->sb_logsunit = cpu_to_be32(from->sb_logsunit);
 
-               fields &= ~(1LL << f);
+       /*
+        * We need to ensure that bad_features2 always matches features2.
+        * Hence we enforce that here rather than having to remember to do it
+        * everywhere else that updates features2.
+        */
+       from->sb_bad_features2 = from->sb_features2;
+       to->sb_features2 = cpu_to_be32(from->sb_features2);
+       to->sb_bad_features2 = cpu_to_be32(from->sb_bad_features2);
+
+       if (xfs_sb_version_hascrc(from)) {
+               to->sb_features_compat = cpu_to_be32(from->sb_features_compat);
+               to->sb_features_ro_compat =
+                               cpu_to_be32(from->sb_features_ro_compat);
+               to->sb_features_incompat =
+                               cpu_to_be32(from->sb_features_incompat);
+               to->sb_features_log_incompat =
+                               cpu_to_be32(from->sb_features_log_incompat);
+               to->sb_spino_align = cpu_to_be32(from->sb_spino_align);
+               to->sb_lsn = cpu_to_be64(from->sb_lsn);
+               if (xfs_sb_version_hasmetauuid(from))
+                       uuid_copy(&to->sb_meta_uuid, &from->sb_meta_uuid);
        }
 }
 
@@ -530,7 +553,11 @@ xfs_sb_verify(
        struct xfs_mount *mp = bp->b_target->bt_mount;
        struct xfs_sb   sb;
 
-       xfs_sb_from_disk(&sb, XFS_BUF_TO_SBP(bp));
+       /*
+        * Use call variant which doesn't convert quota flags from disk
+        * format, because xfs_mount_validate_sb checks the on-disk flags.
+        */
+       __xfs_sb_from_disk(&sb, XFS_BUF_TO_SBP(bp), false);
 
        /*
         * Only check the in progress field for the primary superblock as
@@ -546,6 +573,11 @@ xfs_sb_verify(
  * single bit error could clear the feature bit and unused parts of the
  * superblock are supposed to be zero. Hence a non-null crc field indicates that
  * we've potentially lost a feature bit and we should check it anyway.
+ *
+ * However, past bugs (i.e. in growfs) left non-zeroed regions beyond the
+ * last field in V4 secondary superblocks.  So for secondary superblocks,
+ * we are more forgiving, and ignore CRC failures if the primary doesn't
+ * indicate that the fs version is V5.
  */
 static void
 xfs_sb_read_verify(
@@ -564,19 +596,22 @@ xfs_sb_read_verify(
                                                XFS_SB_VERSION_5) ||
             dsb->sb_crc != 0)) {
 
-               if (!xfs_verify_cksum(bp->b_addr, be16_to_cpu(dsb->sb_sectsize),
-                                     offsetof(struct xfs_sb, sb_crc))) {
-                       error = EFSCORRUPTED;
-                       goto out_error;
+               if (!xfs_buf_verify_cksum(bp, XFS_SB_CRC_OFF)) {
+                       /* Only fail bad secondaries on a known V5 filesystem */
+                       if (bp->b_bn == XFS_SB_DADDR ||
+                           xfs_sb_version_hascrc(&mp->m_sb)) {
+                               error = -EFSBADCRC;
+                               goto out_error;
+                       }
                }
        }
        error = xfs_sb_verify(bp, true);
 
 out_error:
        if (error) {
-               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
-                                    mp, bp->b_addr);
                xfs_buf_ioerror(bp, error);
+               if (error == -EFSCORRUPTED || error == -EFSBADCRC)
+                       xfs_verifier_error(bp);
        }
 }
 
@@ -592,14 +627,13 @@ xfs_sb_quiet_read_verify(
 {
        struct xfs_dsb  *dsb = XFS_BUF_TO_SBP(bp);
 
-
        if (dsb->sb_magicnum == cpu_to_be32(XFS_SB_MAGIC)) {
                /* XFS filesystem, verify noisily! */
                xfs_sb_read_verify(bp);
                return;
        }
        /* quietly fail */
-       xfs_buf_ioerror(bp, EWRONGFS);
+       xfs_buf_ioerror(bp, -EWRONGFS);
 }
 
 static void
@@ -612,9 +646,8 @@ xfs_sb_write_verify(
 
        error = xfs_sb_verify(bp, false);
        if (error) {
-               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
-                                    mp, bp->b_addr);
                xfs_buf_ioerror(bp, error);
+               xfs_verifier_error(bp);
                return;
        }
 
@@ -624,16 +657,17 @@ xfs_sb_write_verify(
        if (bip)
                XFS_BUF_TO_SBP(bp)->sb_lsn = cpu_to_be64(bip->bli_item.li_lsn);
 
-       xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length),
-                        offsetof(struct xfs_sb, sb_crc));
+       xfs_buf_update_cksum(bp, XFS_SB_CRC_OFF);
 }
 
 const struct xfs_buf_ops xfs_sb_buf_ops = {
+       .name = "xfs_sb",
        .verify_read = xfs_sb_read_verify,
        .verify_write = xfs_sb_write_verify,
 };
 
 const struct xfs_buf_ops xfs_sb_quiet_buf_ops = {
+       .name = "xfs_sb_quiet",
        .verify_read = xfs_sb_quiet_read_verify,
        .verify_write = xfs_sb_write_verify,
 };
@@ -681,6 +715,11 @@ xfs_sb_mount_common(
        mp->m_ialloc_inos = (int)MAX((__uint16_t)XFS_INODES_PER_CHUNK,
                                        sbp->sb_inopblock);
        mp->m_ialloc_blks = mp->m_ialloc_inos >> sbp->sb_inopblog;
+
+       if (sbp->sb_spino_align)
+               mp->m_ialloc_min_blks = sbp->sb_spino_align;
+       else
+               mp->m_ialloc_min_blks = mp->m_ialloc_blks;
 }
 
 /*
@@ -727,58 +766,67 @@ xfs_initialize_perag_data(
                btree += pag->pagf_btreeblks;
                xfs_perag_put(pag);
        }
-       /*
-        * Overwrite incore superblock counters with just-read data
-        */
+
+       /* Overwrite incore superblock counters with just-read data */
        spin_lock(&mp->m_sb_lock);
        sbp->sb_ifree = ifree;
        sbp->sb_icount = ialloc;
        sbp->sb_fdblocks = bfree + bfreelst + btree;
        spin_unlock(&mp->m_sb_lock);
 
-       /* Fixup the per-cpu counters as well. */
-       xfs_icsb_reinit_counters(mp);
+       xfs_reinit_percpu_counters(mp);
 
        return 0;
 }
 
 /*
- * xfs_mod_sb() can be used to copy arbitrary changes to the
- * in-core superblock into the superblock buffer to be logged.
- * It does not provide the higher level of locking that is
- * needed to protect the in-core superblock from concurrent
- * access.
+ * xfs_log_sb() can be used to copy arbitrary changes to the in-core superblock
+ * into the superblock buffer to be logged.  It does not provide the higher
+ * level of locking that is needed to protect the in-core superblock from
+ * concurrent access.
  */
 void
-xfs_mod_sb(xfs_trans_t *tp, __int64_t fields)
+xfs_log_sb(
+       struct xfs_trans        *tp)
 {
-       xfs_buf_t       *bp;
-       int             first;
-       int             last;
-       xfs_mount_t     *mp;
-       xfs_sb_field_t  f;
-
-       ASSERT(fields);
-       if (!fields)
-               return;
-       mp = tp->t_mountp;
-       bp = xfs_trans_getsb(tp, mp, 0);
-       first = sizeof(xfs_sb_t);
-       last = 0;
+       struct xfs_mount        *mp = tp->t_mountp;
+       struct xfs_buf          *bp = xfs_trans_getsb(tp, mp, 0);
 
-       /* translate/copy */
+       mp->m_sb.sb_icount = percpu_counter_sum(&mp->m_icount);
+       mp->m_sb.sb_ifree = percpu_counter_sum(&mp->m_ifree);
+       mp->m_sb.sb_fdblocks = percpu_counter_sum(&mp->m_fdblocks);
 
-       xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb, fields);
+       xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb);
+       xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SB_BUF);
+       xfs_trans_log_buf(tp, bp, 0, sizeof(struct xfs_dsb));
+}
 
-       /* find modified range */
-       f = (xfs_sb_field_t)xfs_highbit64((__uint64_t)fields);
-       ASSERT((1LL << f) & XFS_SB_MOD_BITS);
-       last = xfs_sb_info[f + 1].offset - 1;
+/*
+ * xfs_sync_sb
+ *
+ * Sync the superblock to disk.
+ *
+ * Note that the caller is responsible for checking the frozen state of the
+ * filesystem. This procedure uses the non-blocking transaction allocator and
+ * thus will allow modifications to a frozen fs. This is required because this
+ * code can be called during the process of freezing where use of the high-level
+ * allocator would deadlock.
+ */
+int
+xfs_sync_sb(
+       struct xfs_mount        *mp,
+       bool                    wait)
+{
+       struct xfs_trans        *tp;
+       int                     error;
 
-       f = (xfs_sb_field_t)xfs_lowbit64((__uint64_t)fields);
-       ASSERT((1LL << f) & XFS_SB_MOD_BITS);
-       first = xfs_sb_info[f].offset;
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_sb, 0, 0,
+                       XFS_TRANS_NO_WRITECOUNT, &tp);
+       if (error)
+               return error;
 
-       xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SB_BUF);
-       xfs_trans_log_buf(tp, bp, first, last);
+       xfs_log_sb(tp);
+       if (wait)
+               xfs_trans_set_sync(tp);
+       return xfs_trans_commit(tp);
 }