]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - mkfs/xfs_mkfs.c
xfsprogs: make static things static
[thirdparty/xfsprogs-dev.git] / mkfs / xfs_mkfs.c
index a7dd4479ba5fad990cafa2258fc261bb91b1f4a3..d1387ddf0539173606ed60d03a6a0354a8e2b09c 100644 (file)
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2000-2005 Silicon Graphics, Inc.
  * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms 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.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
-
+#include "libfrog.h"
 #include "libxfs.h"
 #include <ctype.h>
-#ifdef ENABLE_BLKID
-#  include <blkid/blkid.h>
-#endif /* ENABLE_BLKID */
 #include "xfs_multidisk.h"
 #include "libxcmd.h"
+#include "fsgeom.h"
+
+
+#define TERABYTES(count, blog) ((uint64_t)(count) << (40 - (blog)))
+#define GIGABYTES(count, blog) ((uint64_t)(count) << (30 - (blog)))
+#define MEGABYTES(count, blog) ((uint64_t)(count) << (20 - (blog)))
+
+/*
+ * Use this macro before we have superblock and mount structure to
+ * convert from basic blocks to filesystem blocks.
+ */
+#define        DTOBT(d, bl)    ((xfs_rfsblock_t)((d) >> ((bl) - BBSHIFT)))
+
+/*
+ * amount (in bytes) we zero at the beginning and end of the device to
+ * remove traces of other filesystems, raid superblocks, etc.
+ */
+#define WHACK_SIZE (128 * 1024)
 
 /*
- * Prototypes for internal functions.
+ * XXX: The configured block and sector sizes are defined as global variables so
+ * that they don't need to be passed to getnum/cvtnum().
  */
-static void conflict(char opt, char *tab[], int oldidx, int newidx);
-static void illegal(const char *value, const char *opt);
-static __attribute__((noreturn)) void usage (void);
-static __attribute__((noreturn)) void reqval(char opt, char *tab[], int idx);
-static void respec(char opt, char *tab[], int idx);
-static void unknown(char opt, char *s);
-static int  ispow2(unsigned int i);
+static unsigned int            blocksize;
+static unsigned int            sectorsize;
 
 /*
- * The configured block and sector sizes are defined as global variables so
- * that they don't need to be passed to functions that require them.
+ * Enums for each CLI parameter type are declared first so we can calculate the
+ * maximum array size needed to hold them automatically.
  */
-unsigned int           blocksize;
-unsigned int           sectorsize;
+enum {
+       B_SIZE = 0,
+       B_MAX_OPTS,
+};
+
+enum {
+       D_AGCOUNT = 0,
+       D_FILE,
+       D_NAME,
+       D_SIZE,
+       D_SUNIT,
+       D_SWIDTH,
+       D_AGSIZE,
+       D_SU,
+       D_SW,
+       D_SECTSIZE,
+       D_NOALIGN,
+       D_RTINHERIT,
+       D_PROJINHERIT,
+       D_EXTSZINHERIT,
+       D_COWEXTSIZE,
+       D_MAX_OPTS,
+};
+
+enum {
+       I_ALIGN = 0,
+       I_MAXPCT,
+       I_PERBLOCK,
+       I_SIZE,
+       I_ATTR,
+       I_PROJID32BIT,
+       I_SPINODES,
+       I_MAX_OPTS,
+};
+
+enum {
+       L_AGNUM = 0,
+       L_INTERNAL,
+       L_SIZE,
+       L_VERSION,
+       L_SUNIT,
+       L_SU,
+       L_DEV,
+       L_SECTSIZE,
+       L_FILE,
+       L_NAME,
+       L_LAZYSBCNTR,
+       L_MAX_OPTS,
+};
+
+enum {
+       N_SIZE = 0,
+       N_VERSION,
+       N_FTYPE,
+       N_MAX_OPTS,
+};
+
+enum {
+       R_EXTSIZE = 0,
+       R_SIZE,
+       R_DEV,
+       R_FILE,
+       R_NAME,
+       R_NOALIGN,
+       R_MAX_OPTS,
+};
+
+enum {
+       S_SIZE = 0,
+       S_SECTSIZE,
+       S_MAX_OPTS,
+};
+
+enum {
+       M_CRC = 0,
+       M_FINOBT,
+       M_UUID,
+       M_RMAPBT,
+       M_REFLINK,
+       M_MAX_OPTS,
+};
+
+/* Just define the max options array size manually right now */
+#define MAX_SUBOPTS    D_MAX_OPTS
 
-#define MAX_SUBOPTS    16
 #define SUBOPT_NEEDS_VAL       (-1LL)
 #define MAX_CONFLICTS  8
 #define LAST_CONFLICT  (-1)
@@ -121,35 +201,33 @@ struct opt_params {
                bool            str_seen;
                bool            convert;
                bool            is_power_2;
-               int             conflicts[MAX_CONFLICTS];
+               struct _conflict {
+                       struct opt_params       *opts;
+                       int                     subopt;
+               }               conflicts[MAX_CONFLICTS];
                long long       minval;
                long long       maxval;
                long long       defaultval;
        }               subopt_params[MAX_SUBOPTS];
 };
 
-struct opt_params bopts = {
+/*
+ * The two dimensional conflict array requires some initialisations to know
+ * about tables that haven't yet been defined. Work around this ordering
+ * issue with extern definitions here.
+ */
+static struct opt_params sopts;
+
+static struct opt_params bopts = {
        .name = 'b',
        .subopts = {
-#define        B_LOG           0
-               "log",
-#define        B_SIZE          1
-               "size",
-               NULL
+               [B_SIZE] = "size",
        },
        .subopt_params = {
-               { .index = B_LOG,
-                 .conflicts = { B_SIZE,
-                                LAST_CONFLICT },
-                 .minval = XFS_MIN_BLOCKSIZE_LOG,
-                 .maxval = XFS_MAX_BLOCKSIZE_LOG,
-                 .defaultval = SUBOPT_NEEDS_VAL,
-               },
                { .index = B_SIZE,
                  .convert = true,
                  .is_power_2 = true,
-                 .conflicts = { B_LOG,
-                                LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = XFS_MIN_BLOCKSIZE,
                  .maxval = XFS_MAX_BLOCKSIZE,
                  .defaultval = SUBOPT_NEEDS_VAL,
@@ -157,121 +235,99 @@ struct opt_params bopts = {
        },
 };
 
-struct opt_params dopts = {
+static struct opt_params dopts = {
        .name = 'd',
        .subopts = {
-#define        D_AGCOUNT       0
-               "agcount",
-#define        D_FILE          1
-               "file",
-#define        D_NAME          2
-               "name",
-#define        D_SIZE          3
-               "size",
-#define D_SUNIT                4
-               "sunit",
-#define D_SWIDTH       5
-               "swidth",
-#define D_AGSIZE       6
-               "agsize",
-#define D_SU           7
-               "su",
-#define D_SW           8
-               "sw",
-#define D_SECTLOG      9
-               "sectlog",
-#define D_SECTSIZE     10
-               "sectsize",
-#define D_NOALIGN      11
-               "noalign",
-#define D_RTINHERIT    12
-               "rtinherit",
-#define D_PROJINHERIT  13
-               "projinherit",
-#define D_EXTSZINHERIT 14
-               "extszinherit",
-               NULL
+               [D_AGCOUNT] = "agcount",
+               [D_FILE] = "file",
+               [D_NAME] = "name",
+               [D_SIZE] = "size",
+               [D_SUNIT] = "sunit",
+               [D_SWIDTH] = "swidth",
+               [D_AGSIZE] = "agsize",
+               [D_SU] = "su",
+               [D_SW] = "sw",
+               [D_SECTSIZE] = "sectsize",
+               [D_NOALIGN] = "noalign",
+               [D_RTINHERIT] = "rtinherit",
+               [D_PROJINHERIT] = "projinherit",
+               [D_EXTSZINHERIT] = "extszinherit",
+               [D_COWEXTSIZE] = "cowextsize",
        },
        .subopt_params = {
                { .index = D_AGCOUNT,
-                 .conflicts = { D_AGSIZE,
-                                LAST_CONFLICT },
+                 .conflicts = { { &dopts, D_AGSIZE },
+                                { NULL, LAST_CONFLICT } },
                  .minval = 1,
                  .maxval = XFS_MAX_AGNUMBER,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = D_FILE,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
                },
                { .index = D_NAME,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = D_SIZE,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .minval = XFS_AG_MIN_BYTES,
                  .maxval = LLONG_MAX,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = D_SUNIT,
-                 .conflicts = { D_NOALIGN,
-                                D_SU,
-                                D_SW,
-                                LAST_CONFLICT },
+                 .conflicts = { { &dopts, D_NOALIGN },
+                                { &dopts, D_SU },
+                                { &dopts, D_SW },
+                                { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = UINT_MAX,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = D_SWIDTH,
-                 .conflicts = { D_NOALIGN,
-                                D_SU,
-                                D_SW,
-                                LAST_CONFLICT },
+                 .conflicts = { { &dopts, D_NOALIGN },
+                                { &dopts, D_SU },
+                                { &dopts, D_SW },
+                                { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = UINT_MAX,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = D_AGSIZE,
-                 .conflicts = { D_AGCOUNT,
-                                LAST_CONFLICT },
+                 .conflicts = { { &dopts, D_AGCOUNT },
+                                { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .minval = XFS_AG_MIN_BYTES,
                  .maxval = XFS_AG_MAX_BYTES,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = D_SU,
-                 .conflicts = { D_NOALIGN,
-                                D_SUNIT,
-                                D_SWIDTH,
-                                LAST_CONFLICT },
+                 .conflicts = { { &dopts, D_NOALIGN },
+                                { &dopts, D_SUNIT },
+                                { &dopts, D_SWIDTH },
+                                { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .minval = 0,
                  .maxval = UINT_MAX,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = D_SW,
-                 .conflicts = { D_NOALIGN,
-                                D_SUNIT,
-                                D_SWIDTH,
-                                LAST_CONFLICT },
+                 .conflicts = { { &dopts, D_NOALIGN },
+                                { &dopts, D_SUNIT },
+                                { &dopts, D_SWIDTH },
+                                { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = UINT_MAX,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
-               { .index = D_SECTLOG,
-                 .conflicts = { D_SECTSIZE,
-                                LAST_CONFLICT },
-                 .minval = XFS_MIN_SECTORSIZE_LOG,
-                 .maxval = XFS_MAX_SECTORSIZE_LOG,
-                 .defaultval = SUBOPT_NEEDS_VAL,
-               },
                { .index = D_SECTSIZE,
-                 .conflicts = { D_SECTLOG,
-                                LAST_CONFLICT },
+                 .conflicts = { { &sopts, S_SIZE },
+                                { &sopts, S_SECTSIZE },
+                                { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .is_power_2 = true,
                  .minval = XFS_MIN_SECTORSIZE,
@@ -279,29 +335,35 @@ struct opt_params dopts = {
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = D_NOALIGN,
-                 .conflicts = { D_SU,
-                                D_SW,
-                                D_SUNIT,
-                                D_SWIDTH,
-                                LAST_CONFLICT },
+                 .conflicts = { { &dopts, D_SU },
+                                { &dopts, D_SW },
+                                { &dopts, D_SUNIT },
+                                { &dopts, D_SWIDTH },
+                                { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
                },
                { .index = D_RTINHERIT,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 1,
                  .maxval = 1,
                  .defaultval = 1,
                },
                { .index = D_PROJINHERIT,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = UINT_MAX,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = D_EXTSZINHERIT,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
+                 .minval = 0,
+                 .maxval = UINT_MAX,
+                 .defaultval = SUBOPT_NEEDS_VAL,
+               },
+               { .index = D_COWEXTSIZE,
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = UINT_MAX,
                  .defaultval = SUBOPT_NEEDS_VAL,
@@ -310,80 +372,60 @@ struct opt_params dopts = {
 };
 
 
-struct opt_params iopts = {
+static struct opt_params iopts = {
        .name = 'i',
        .subopts = {
-#define        I_ALIGN         0
-               "align",
-#define        I_LOG           1
-               "log",
-#define        I_MAXPCT        2
-               "maxpct",
-#define        I_PERBLOCK      3
-               "perblock",
-#define        I_SIZE          4
-               "size",
-#define        I_ATTR          5
-               "attr",
-#define        I_PROJID32BIT   6
-               "projid32bit",
-#define I_SPINODES     7
-               "sparse",
-               NULL
+               [I_ALIGN] = "align",
+               [I_MAXPCT] = "maxpct",
+               [I_PERBLOCK] = "perblock",
+               [I_SIZE] = "size",
+               [I_ATTR] = "attr",
+               [I_PROJID32BIT] = "projid32bit",
+               [I_SPINODES] = "sparse",
        },
        .subopt_params = {
                { .index = I_ALIGN,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
                },
-               { .index = I_LOG,
-                 .conflicts = { I_PERBLOCK,
-                                I_SIZE,
-                                LAST_CONFLICT },
-                 .minval = XFS_DINODE_MIN_LOG,
-                 .maxval = XFS_DINODE_MAX_LOG,
-                 .defaultval = SUBOPT_NEEDS_VAL,
-               },
                { .index = I_MAXPCT,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 100,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = I_PERBLOCK,
-                 .conflicts = { I_LOG,
-                                I_SIZE,
-                                LAST_CONFLICT },
+                 .conflicts = { { &iopts, I_SIZE },
+                                { NULL, LAST_CONFLICT } },
                  .is_power_2 = true,
                  .minval = XFS_MIN_INODE_PERBLOCK,
                  .maxval = XFS_MAX_BLOCKSIZE / XFS_DINODE_MIN_SIZE,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = I_SIZE,
-                 .conflicts = { I_PERBLOCK,
-                                I_LOG,
-                                LAST_CONFLICT },
+                 .conflicts = { { &iopts, I_PERBLOCK },
+                                { NULL, LAST_CONFLICT } },
                  .is_power_2 = true,
                  .minval = XFS_DINODE_MIN_SIZE,
                  .maxval = XFS_DINODE_MAX_SIZE,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = I_ATTR,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 2,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = I_PROJID32BIT,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
                },
                { .index = I_SPINODES,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
@@ -391,95 +433,76 @@ struct opt_params iopts = {
        },
 };
 
-struct opt_params lopts = {
+static struct opt_params lopts = {
        .name = 'l',
        .subopts = {
-#define        L_AGNUM         0
-               "agnum",
-#define        L_INTERNAL      1
-               "internal",
-#define        L_SIZE          2
-               "size",
-#define L_VERSION      3
-               "version",
-#define L_SUNIT                4
-               "sunit",
-#define L_SU           5
-               "su",
-#define L_DEV          6
-               "logdev",
-#define        L_SECTLOG       7
-               "sectlog",
-#define        L_SECTSIZE      8
-               "sectsize",
-#define        L_FILE          9
-               "file",
-#define        L_NAME          10
-               "name",
-#define        L_LAZYSBCNTR    11
-               "lazy-count",
-               NULL
+               [L_AGNUM] = "agnum",
+               [L_INTERNAL] = "internal",
+               [L_SIZE] = "size",
+               [L_VERSION] = "version",
+               [L_SUNIT] = "sunit",
+               [L_SU] = "su",
+               [L_DEV] = "logdev",
+               [L_SECTSIZE] = "sectsize",
+               [L_FILE] = "file",
+               [L_NAME] = "name",
+               [L_LAZYSBCNTR] = "lazy-count",
        },
        .subopt_params = {
                { .index = L_AGNUM,
-                 .conflicts = { L_DEV,
-                                LAST_CONFLICT },
+                 .conflicts = { { &lopts, L_DEV },
+                                { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = UINT_MAX,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = L_INTERNAL,
-                 .conflicts = { L_FILE,
-                                L_DEV,
-                                LAST_CONFLICT },
+                 .conflicts = { { &lopts, L_FILE },
+                                { &lopts, L_DEV },
+                                { &lopts, L_SECTSIZE },
+                                { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
                },
                { .index = L_SIZE,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .minval = 2 * 1024 * 1024LL,  /* XXX: XFS_MIN_LOG_BYTES */
                  .maxval = XFS_MAX_LOG_BYTES,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = L_VERSION,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 1,
                  .maxval = 2,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = L_SUNIT,
-                 .conflicts = { L_SU,
-                                LAST_CONFLICT },
+                 .conflicts = { { &lopts, L_SU },
+                                { NULL, LAST_CONFLICT } },
                  .minval = 1,
                  .maxval = BTOBB(XLOG_MAX_RECORD_BSIZE),
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = L_SU,
-                 .conflicts = { L_SUNIT,
-                                LAST_CONFLICT },
+                 .conflicts = { { &lopts, L_SUNIT },
+                                { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .minval = BBTOB(1),
                  .maxval = XLOG_MAX_RECORD_BSIZE,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = L_DEV,
-                 .conflicts = { L_AGNUM,
-                                L_INTERNAL,
-                                LAST_CONFLICT },
-                 .defaultval = SUBOPT_NEEDS_VAL,
-               },
-               { .index = L_SECTLOG,
-                 .conflicts = { L_SECTSIZE,
-                                LAST_CONFLICT },
-                 .minval = XFS_MIN_SECTORSIZE_LOG,
-                 .maxval = XFS_MAX_SECTORSIZE_LOG,
+                 .conflicts = { { &lopts, L_AGNUM },
+                                { &lopts, L_NAME },
+                                { &lopts, L_INTERNAL },
+                                { NULL, LAST_CONFLICT } },
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = L_SECTSIZE,
-                 .conflicts = { L_SECTLOG,
-                                LAST_CONFLICT },
+                 .conflicts = { { &lopts, L_INTERNAL },
+                                { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .is_power_2 = true,
                  .minval = XFS_MIN_SECTORSIZE,
@@ -487,20 +510,21 @@ struct opt_params lopts = {
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = L_FILE,
-                 .conflicts = { L_INTERNAL,
-                                LAST_CONFLICT },
+                 .conflicts = { { &lopts, L_INTERNAL },
+                                { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
                },
                { .index = L_NAME,
-                 .conflicts = { L_AGNUM,
-                                L_INTERNAL,
-                                LAST_CONFLICT },
+                 .conflicts = { { &lopts, L_AGNUM },
+                                { &lopts, L_DEV },
+                                { &lopts, L_INTERNAL },
+                                { NULL, LAST_CONFLICT } },
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = L_LAZYSBCNTR,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
@@ -508,30 +532,16 @@ struct opt_params lopts = {
        },
 };
 
-struct opt_params nopts = {
+static struct opt_params nopts = {
        .name = 'n',
        .subopts = {
-#define        N_LOG           0
-               "log",
-#define        N_SIZE          1
-               "size",
-#define        N_VERSION       2
-               "version",
-#define        N_FTYPE         3
-               "ftype",
-       NULL,
+               [N_SIZE] = "size",
+               [N_VERSION] = "version",
+               [N_FTYPE] = "ftype",
        },
        .subopt_params = {
-               { .index = N_LOG,
-                 .conflicts = { N_SIZE,
-                                LAST_CONFLICT },
-                 .minval = XFS_MIN_REC_DIRSIZE,
-                 .maxval = XFS_MAX_BLOCKSIZE_LOG,
-                 .defaultval = SUBOPT_NEEDS_VAL,
-               },
                { .index = N_SIZE,
-                 .conflicts = { N_LOG,
-                                LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .is_power_2 = true,
                  .minval = 1 << XFS_MIN_REC_DIRSIZE,
@@ -539,13 +549,13 @@ struct opt_params nopts = {
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = N_VERSION,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 2,
                  .maxval = 2,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = N_FTYPE,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
@@ -553,95 +563,67 @@ struct opt_params nopts = {
        },
 };
 
-struct opt_params ropts = {
+static struct opt_params ropts = {
        .name = 'r',
        .subopts = {
-#define        R_EXTSIZE       0
-               "extsize",
-#define        R_SIZE          1
-               "size",
-#define        R_DEV           2
-               "rtdev",
-#define        R_FILE          3
-               "file",
-#define        R_NAME          4
-               "name",
-#define R_NOALIGN      5
-               "noalign",
-               NULL
+               [R_EXTSIZE] = "extsize",
+               [R_SIZE] = "size",
+               [R_DEV] = "rtdev",
+               [R_FILE] = "file",
+               [R_NAME] = "name",
+               [R_NOALIGN] = "noalign",
        },
        .subopt_params = {
                { .index = R_EXTSIZE,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .minval = XFS_MIN_RTEXTSIZE,
                  .maxval = XFS_MAX_RTEXTSIZE,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = R_SIZE,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .minval = 0,
                  .maxval = LLONG_MAX,
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = R_DEV,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { &ropts, R_NAME },
+                                { NULL, LAST_CONFLICT } },
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = R_FILE,
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                },
                { .index = R_NAME,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { &ropts, R_DEV },
+                                { NULL, LAST_CONFLICT } },
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = R_NOALIGN,
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                },
        },
 };
 
-struct opt_params sopts = {
+static struct opt_params sopts = {
        .name = 's',
        .subopts = {
-#define        S_LOG           0
-               "log",
-#define        S_SECTLOG       1
-               "sectlog",
-#define        S_SIZE          2
-               "size",
-#define        S_SECTSIZE      3
-               "sectsize",
-               NULL
+               [S_SIZE] = "size",
+               [S_SECTSIZE] = "sectsize",
        },
        .subopt_params = {
-               { .index = S_LOG,
-                 .conflicts = { S_SIZE,
-                                S_SECTSIZE,
-                                LAST_CONFLICT },
-                 .minval = XFS_MIN_SECTORSIZE_LOG,
-                 .maxval = XFS_MAX_SECTORSIZE_LOG,
-                 .defaultval = SUBOPT_NEEDS_VAL,
-               },
-               { .index = S_SECTLOG,
-                 .conflicts = { S_SIZE,
-                                S_SECTSIZE,
-                                LAST_CONFLICT },
-                 .minval = XFS_MIN_SECTORSIZE_LOG,
-                 .maxval = XFS_MAX_SECTORSIZE_LOG,
-                 .defaultval = SUBOPT_NEEDS_VAL,
-               },
                { .index = S_SIZE,
-                 .conflicts = { S_LOG,
-                                S_SECTLOG,
-                                LAST_CONFLICT },
+                 .conflicts = { { &sopts, S_SECTSIZE },
+                                { &dopts, D_SECTSIZE },
+                                { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .is_power_2 = true,
                  .minval = XFS_MIN_SECTORSIZE,
@@ -649,9 +631,9 @@ struct opt_params sopts = {
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = S_SECTSIZE,
-                 .conflicts = { S_LOG,
-                                S_SECTLOG,
-                                LAST_CONFLICT },
+                 .conflicts = { { &sopts, S_SIZE },
+                                { &dopts, D_SECTSIZE },
+                                { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .is_power_2 = true,
                  .minval = XFS_MIN_SECTORSIZE,
@@ -661,143 +643,366 @@ struct opt_params sopts = {
        },
 };
 
-struct opt_params mopts = {
+static struct opt_params mopts = {
        .name = 'm',
        .subopts = {
-#define        M_CRC           0
-               "crc",
-#define M_FINOBT       1
-               "finobt",
-#define M_UUID         2
-               "uuid",
-#define M_RMAPBT       3
-               "rmapbt",
-#define M_REFLINK      4
-               "reflink",
-               NULL
+               [M_CRC] = "crc",
+               [M_FINOBT] = "finobt",
+               [M_UUID] = "uuid",
+               [M_RMAPBT] = "rmapbt",
+               [M_REFLINK] = "reflink",
        },
        .subopt_params = {
                { .index = M_CRC,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
                },
                { .index = M_FINOBT,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
                  .defaultval = 1,
                },
                { .index = M_UUID,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
                { .index = M_RMAPBT,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
-                 .defaultval = 0,
+                 .defaultval = 1,
                },
                { .index = M_REFLINK,
-                 .conflicts = { LAST_CONFLICT },
+                 .conflicts = { { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
-                 .defaultval = 0,
+                 .defaultval = 1,
                },
        },
 };
 
-#define TERABYTES(count, blog) ((__uint64_t)(count) << (40 - (blog)))
-#define GIGABYTES(count, blog) ((__uint64_t)(count) << (30 - (blog)))
-#define MEGABYTES(count, blog) ((__uint64_t)(count) << (20 - (blog)))
+/* quick way of checking if a parameter was set on the CLI */
+static bool
+cli_opt_set(
+       struct opt_params       *opts,
+       int                     subopt)
+{
+       return opts->subopt_params[subopt].seen ||
+              opts->subopt_params[subopt].str_seen;
+}
 
 /*
- * Use this macro before we have superblock and mount structure
+ * Options configured on the command line.
+ *
+ * This stores all the specific config parameters the user sets on the command
+ * line. We do not use these values directly - they are inputs to the mkfs
+ * geometry validation and override any default configuration value we have.
+ *
+ * We don't keep flags to indicate what parameters are set - if we need to check
+ * if an option was set on the command line, we check the relevant entry in the
+ * option table which records whether it was specified in the .seen and
+ * .str_seen variables in the table.
+ *
+ * Some parameters are stored as strings for post-parsing after their dependent
+ * options have been resolved (e.g. block size and sector size have been parsed
+ * and validated).
+ *
+ * This allows us to check that values have been set without needing separate
+ * flags for each value, and hence avoids needing to record and check for each
+ * specific option that can set the value later on in the code. In the cases
+ * where we don't have a cli_params structure around, the above cli_opt_set()
+ * function can be used.
  */
-#define        DTOBT(d)        ((xfs_rfsblock_t)((d) >> (blocklog - BBSHIFT)))
+struct sb_feat_args {
+       int     log_version;
+       int     attr_version;
+       int     dir_version;
+       bool    inode_align;            /* XFS_SB_VERSION_ALIGNBIT */
+       bool    nci;                    /* XFS_SB_VERSION_BORGBIT */
+       bool    lazy_sb_counters;       /* XFS_SB_VERSION2_LAZYSBCOUNTBIT */
+       bool    parent_pointers;        /* XFS_SB_VERSION2_PARENTBIT */
+       bool    projid32bit;            /* XFS_SB_VERSION2_PROJID32BIT */
+       bool    crcs_enabled;           /* XFS_SB_VERSION2_CRCBIT */
+       bool    dirftype;               /* XFS_SB_VERSION2_FTYPE */
+       bool    finobt;                 /* XFS_SB_FEAT_RO_COMPAT_FINOBT */
+       bool    spinodes;               /* XFS_SB_FEAT_INCOMPAT_SPINODES */
+       bool    rmapbt;                 /* XFS_SB_FEAT_RO_COMPAT_RMAPBT */
+       bool    reflink;                /* XFS_SB_FEAT_RO_COMPAT_REFLINK */
+       bool    nodalign;
+       bool    nortalign;
+};
 
-/*
- * Use this for block reservations needed for mkfs's conditions
- * (basically no fragmentation).
- */
-#define        MKFS_BLOCKRES_INODE     \
-       ((uint)(mp->m_ialloc_blks + (mp->m_in_maxlevels - 1)))
-#define        MKFS_BLOCKRES(rb)       \
-       ((uint)(MKFS_BLOCKRES_INODE + XFS_DA_NODE_MAXDEPTH + \
-       (XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) - 1) + (rb)))
+struct cli_params {
+       int     sectorsize;
+       int     blocksize;
+
+       /* parameters that depend on sector/block size being validated. */
+       char    *dsize;
+       char    *agsize;
+       char    *dsu;
+       char    *dirblocksize;
+       char    *logsize;
+       char    *lsu;
+       char    *rtextsize;
+       char    *rtsize;
+
+       /* parameters where 0 is a valid CLI value */
+       int     dsunit;
+       int     dswidth;
+       int     dsw;
+       int64_t logagno;
+       int     loginternal;
+       int     lsunit;
+
+       /* parameters where 0 is not a valid value */
+       int64_t agcount;
+       int     inodesize;
+       int     inopblock;
+       int     imaxpct;
+       int     lsectorsize;
+       uuid_t  uuid;
+
+       /* feature flags that are set */
+       struct sb_feat_args     sb_feat;
+
+       /* root inode characteristics */
+       struct fsxattr          fsx;
 
-/* amount (in bytes) we zero at the beginning and end of the device to
- * remove traces of other filesystems, raid superblocks, etc.
+       /* libxfs device setup */
+       struct libxfs_xinit     *xi;
+};
+
+/*
+ * Calculated filesystem feature and geometry information.
+ *
+ * This structure contains the information we will use to create the on-disk
+ * filesystem from. The validation and calculation code uses it to store all the
+ * temporary and final config state for the filesystem.
+ *
+ * The information in this structure will contain a mix of validated CLI input
+ * variables, default feature state and calculated values that are needed to
+ * construct the superblock and other on disk features. These are all in one
+ * place so that we don't have to pass handfuls of seemingly arbitrary variables
+ * around to different functions to do the work we need to do.
  */
-#define WHACK_SIZE (128 * 1024)
+struct mkfs_params {
+       int             blocksize;
+       int             blocklog;
+       int             sectorsize;
+       int             sectorlog;
+       int             lsectorsize;
+       int             lsectorlog;
+       int             dirblocksize;
+       int             dirblocklog;
+       int             inodesize;
+       int             inodelog;
+       int             inopblock;
+
+       uint64_t        dblocks;
+       uint64_t        logblocks;
+       uint64_t        rtblocks;
+       uint64_t        rtextblocks;
+       uint64_t        rtextents;
+       uint64_t        rtbmblocks;     /* rt bitmap blocks */
+
+       int             dsunit;         /* in FSBs */
+       int             dswidth;        /* in FSBs */
+       int             lsunit;         /* in FSBs */
+
+       uint64_t        agsize;
+       uint64_t        agcount;
+
+       int             imaxpct;
+
+       bool            loginternal;
+       uint64_t        logstart;
+       uint64_t        logagno;
+
+       uuid_t          uuid;
+       char            *label;
+
+       struct sb_feat_args     sb_feat;
+};
 
 /*
- * Convert lsu to lsunit for 512 bytes blocks and check validity of the values.
+ * Default filesystem features and configuration values
+ *
+ * This structure contains the default mkfs values that are to be used when
+ * a user does not specify the option on the command line. We do not use these
+ * values directly - they are inputs to the mkfs geometry validation and
+ * calculations.
  */
+struct mkfs_default_params {
+       char    *source;        /* where the defaults came from */
+
+       int     sectorsize;
+       int     blocksize;
+
+       /* feature flags that are set */
+       struct sb_feat_args     sb_feat;
+
+       /* root inode characteristics */
+       struct fsxattr          fsx;
+};
+
+static void __attribute__((noreturn))
+usage( void )
+{
+       fprintf(stderr, _("Usage: %s\n\
+/* blocksize */                [-b size=num]\n\
+/* metadata */         [-m crc=0|1,finobt=0|1,uuid=xxx,rmapbt=0|1,reflink=0|1]\n\
+/* data subvol */      [-d agcount=n,agsize=n,file,name=xxx,size=num,\n\
+                           (sunit=value,swidth=value|su=num,sw=num|noalign),\n\
+                           sectsize=num\n\
+/* force overwrite */  [-f]\n\
+/* inode size */       [-i log=n|perblock=n|size=num,maxpct=n,attr=0|1|2,\n\
+                           projid32bit=0|1,sparse=0|1]\n\
+/* no discard */       [-K]\n\
+/* log subvol */       [-l agnum=n,internal,size=num,logdev=xxx,version=n\n\
+                           sunit=value|su=num,sectsize=num,lazy-count=0|1]\n\
+/* label */            [-L label (maximum 12 characters)]\n\
+/* naming */           [-n size=num,version=2|ci,ftype=0|1]\n\
+/* no-op info only */  [-N]\n\
+/* prototype file */   [-p fname]\n\
+/* quiet */            [-q]\n\
+/* realtime subvol */  [-r extsize=num,size=num,rtdev=xxx]\n\
+/* sectorsize */       [-s size=num]\n\
+/* version */          [-V]\n\
+                       devicename\n\
+<devicename> is required unless -d name=xxx is given.\n\
+<num> is xxx (bytes), xxxs (sectors), xxxb (fs blocks), xxxk (xxx KiB),\n\
+      xxxm (xxx MiB), xxxg (xxx GiB), xxxt (xxx TiB) or xxxp (xxx PiB).\n\
+<value> is xxx (512 byte blocks).\n"),
+               progname);
+       exit(1);
+}
+
 static void
-calc_stripe_factors(
-       int             dsu,
-       int             dsw,
-       int             dsectsz,
-       int             lsu,
-       int             lsectsz,
-       int             *dsunit,
-       int             *dswidth,
-       int             *lsunit)
+conflict(
+       struct opt_params       *opts,
+       int                     option,
+       struct opt_params       *con_opts,
+       int                     conflict)
 {
-       /* Handle data sunit/swidth options */
-       if ((*dsunit && !*dswidth) || (!*dsunit && *dswidth)) {
-               fprintf(stderr,
-                       _("both data sunit and data swidth options "
-                       "must be specified\n"));
-               usage();
-       }
+       fprintf(stderr, _("Cannot specify both -%c %s and -%c %s\n"),
+                       con_opts->name, con_opts->subopts[conflict],
+                       opts->name, opts->subopts[option]);
+       usage();
+}
 
-       if (dsu || dsw) {
-               if ((dsu && !dsw) || (!dsu && dsw)) {
+
+static void
+illegal(
+       const char      *value,
+       const char      *opt)
+{
+       fprintf(stderr, _("Invalid value %s for -%s option\n"), value, opt);
+       usage();
+}
+
+static int
+ispow2(
+       unsigned int    i)
+{
+       return (i & (i - 1)) == 0;
+}
+
+static void __attribute__((noreturn))
+reqval(
+       char            opt,
+       const char      *tab[],
+       int             idx)
+{
+       fprintf(stderr, _("-%c %s option requires a value\n"), opt, tab[idx]);
+       usage();
+}
+
+static void
+respec(
+       char            opt,
+       const char      *tab[],
+       int             idx)
+{
+       fprintf(stderr, "-%c ", opt);
+       if (tab)
+               fprintf(stderr, "%s ", tab[idx]);
+       fprintf(stderr, _("option respecified\n"));
+       usage();
+}
+
+static void
+unknown(
+       char            opt,
+       char            *s)
+{
+       fprintf(stderr, _("unknown option -%c %s\n"), opt, s);
+       usage();
+}
+
+long long
+cvtnum(
+       unsigned int    blksize,
+       unsigned int    sectsize,
+       const char      *s)
+{
+       long long       i;
+       char            *sp;
+       int             c;
+
+       i = strtoll(s, &sp, 0);
+       if (i == 0 && sp == s)
+               return -1LL;
+       if (*sp == '\0')
+               return i;
+
+       if (sp[1] != '\0')
+               return -1LL;
+
+       if (*sp == 'b') {
+               if (!blksize) {
                        fprintf(stderr,
-                               _("both data su and data sw options "
-                               "must be specified\n"));
+_("Blocksize must be provided prior to using 'b' suffix.\n"));
                        usage();
+               } else {
+                       return i * blksize;
                }
-
-               if (dsu % dsectsz) {
+       }
+       if (*sp == 's') {
+               if (!sectsize) {
                        fprintf(stderr,
-                               _("data su must be a multiple of the "
-                               "sector size (%d)\n"), dsectsz);
+_("Sectorsize must be specified prior to using 's' suffix.\n"));
                        usage();
+               } else {
+                       return i * sectsize;
                }
-
-               *dsunit  = (int)BTOBBT(dsu);
-               *dswidth = *dsunit * dsw;
-       }
-
-       if (*dsunit && (*dswidth % *dsunit != 0)) {
-               fprintf(stderr,
-                       _("data stripe width (%d) must be a multiple of the "
-                       "data stripe unit (%d)\n"), *dswidth, *dsunit);
-               usage();
        }
 
-       /* Handle log sunit options */
-
-       if (lsu)
-               *lsunit = (int)BTOBBT(lsu);
-
-       /* verify if lsu/lsunit is a multiple block size */
-       if (lsu % blocksize != 0) {
-               fprintf(stderr,
-_("log stripe unit (%d) must be a multiple of the block size (%d)\n"),
-               lsu, blocksize);
-               exit(1);
-       }
-       if ((BBTOB(*lsunit) % blocksize != 0)) {
-               fprintf(stderr,
-_("log stripe unit (%d) must be a multiple of the block size (%d)\n"),
-               BBTOB(*lsunit), blocksize);
-               exit(1);
+       c = tolower(*sp);
+       switch (c) {
+       case 'e':
+               i *= 1024LL;
+               /* fall through */
+       case 'p':
+               i *= 1024LL;
+               /* fall through */
+       case 't':
+               i *= 1024LL;
+               /* fall through */
+       case 'g':
+               i *= 1024LL;
+               /* fall through */
+       case 'm':
+               i *= 1024LL;
+               /* fall through */
+       case 'k':
+               return i * 1024LL;
+       default:
+               break;
        }
+       return -1LL;
 }
 
 static void
@@ -807,7 +1012,6 @@ check_device_type(
        bool            no_size,
        bool            no_name,
        int             *create,
-       bool            force_overwrite,
        const char      *optname)
 {
        struct stat statbuf;
@@ -838,13 +1042,6 @@ check_device_type(
                return;
        }
 
-       if (!force_overwrite && check_overwrite(name)) {
-               fprintf(stderr,
-       _("%s: Use the -f option to force overwrite.\n"),
-                       progname);
-               exit(1);
-       }
-
        /*
         * We only want to completely truncate and recreate an existing file if
         * we were specifically told it was a file. Set the create flag only in
@@ -875,118 +1072,25 @@ check_device_type(
 }
 
 static void
-fixup_log_stripe_unit(
-       int             lsflag,
-       int             sunit,
-       xfs_rfsblock_t  *logblocks,
-       int             blocklog)
+validate_overwrite(
+       const char      *name,
+       bool            force_overwrite)
 {
-       __uint64_t      tmp_logblocks;
-
-       /*
-        * Make sure that the log size is a multiple of the stripe unit
-        */
-       if ((*logblocks % sunit) != 0) {
-               if (!lsflag) {
-                       tmp_logblocks = ((*logblocks + (sunit - 1))
-                                               / sunit) * sunit;
-                       /*
-                        * If the log is too large, round down
-                        * instead of round up
-                        */
-                       if ((tmp_logblocks > XFS_MAX_LOG_BLOCKS) ||
-                           ((tmp_logblocks << blocklog) > XFS_MAX_LOG_BYTES)) {
-                               tmp_logblocks = (*logblocks / sunit) * sunit;
-                       }
-                       *logblocks = tmp_logblocks;
-               } else {
-                       fprintf(stderr, _("log size %lld is not a multiple "
-                                         "of the log stripe unit %d\n"),
-                               (long long) *logblocks, sunit);
-                       usage();
-               }
+       if (!force_overwrite && check_overwrite(name)) {
+               fprintf(stderr,
+       _("%s: Use the -f option to force overwrite.\n"),
+                       progname);
+               exit(1);
        }
-}
-
-static xfs_fsblock_t
-fixup_internal_log_stripe(
-       xfs_mount_t     *mp,
-       int             lsflag,
-       xfs_fsblock_t   logstart,
-       __uint64_t      agsize,
-       int             sunit,
-       xfs_rfsblock_t  *logblocks,
-       int             blocklog,
-       int             *lalign)
-{
-       if ((logstart % sunit) != 0) {
-               logstart = ((logstart + (sunit - 1))/sunit) * sunit;
-               *lalign = 1;
-       }
-
-       fixup_log_stripe_unit(lsflag, sunit, logblocks, blocklog);
-
-       if (*logblocks > agsize - XFS_FSB_TO_AGBNO(mp, logstart)) {
-               fprintf(stderr,
-                       _("Due to stripe alignment, the internal log size "
-                       "(%lld) is too large.\n"), (long long) *logblocks);
-               fprintf(stderr, _("Must fit within an allocation group.\n"));
-               usage();
-       }
-       return logstart;
-}
-
-void
-validate_log_size(__uint64_t logblocks, int blocklog, int min_logblocks)
-{
-       if (logblocks < min_logblocks) {
-               fprintf(stderr,
-       _("log size %lld blocks too small, minimum size is %d blocks\n"),
-                       (long long)logblocks, min_logblocks);
-               usage();
-       }
-       if (logblocks > XFS_MAX_LOG_BLOCKS) {
-               fprintf(stderr,
-       _("log size %lld blocks too large, maximum size is %lld blocks\n"),
-                       (long long)logblocks, XFS_MAX_LOG_BLOCKS);
-               usage();
-       }
-       if ((logblocks << blocklog) > XFS_MAX_LOG_BYTES) {
-               fprintf(stderr,
-       _("log size %lld bytes too large, maximum size is %lld bytes\n"),
-                       (long long)(logblocks << blocklog), XFS_MAX_LOG_BYTES);
-               usage();
-       }
-}
-
-static int
-calc_default_imaxpct(
-       int             blocklog,
-       __uint64_t      dblocks)
-{
-       /*
-        * This returns the % of the disk space that is used for
-        * inodes, it changes relatively to the FS size:
-        *  - over  50 TB, use 1%,
-        *  - 1TB - 50 TB, use 5%,
-        *  - under  1 TB, use XFS_DFL_IMAXIMUM_PCT (25%).
-        */
-
-       if (dblocks < TERABYTES(1, blocklog)) {
-               return XFS_DFL_IMAXIMUM_PCT;
-       } else if (dblocks < TERABYTES(50, blocklog)) {
-               return 5;
-       }
-
-       return 1;
+
 }
 
 static void
 validate_ag_geometry(
        int             blocklog,
-       __uint64_t      dblocks,
-       __uint64_t      agsize,
-       __uint64_t      agcount)
+       uint64_t        dblocks,
+       uint64_t        agsize,
+       uint64_t        agcount)
 {
        if (agsize < XFS_AG_MIN_BLOCKS(blocklog)) {
                fprintf(stderr,
@@ -1062,7 +1166,7 @@ zero_old_xfs_structures(
 {
        void                    *buf;
        xfs_sb_t                sb;
-       __uint32_t              bsize;
+       uint32_t                bsize;
        int                     i;
        xfs_off_t               off;
 
@@ -1115,8 +1219,8 @@ zero_old_xfs_structures(
                        i != sb.sb_blocklog)
                goto done;
 
-       if (sb.sb_dblocks > ((__uint64_t)sb.sb_agcount * sb.sb_agblocks) ||
-                       sb.sb_dblocks < ((__uint64_t)(sb.sb_agcount - 1) *
+       if (sb.sb_dblocks > ((uint64_t)sb.sb_agcount * sb.sb_agblocks) ||
+                       sb.sb_dblocks < ((uint64_t)(sb.sb_agcount - 1) *
                                         sb.sb_agblocks + XFS_MIN_AG_BLOCKS))
                goto done;
 
@@ -1136,7 +1240,7 @@ done:
 }
 
 static void
-discard_blocks(dev_t dev, __uint64_t nsectors)
+discard_blocks(dev_t dev, uint64_t nsectors)
 {
        int fd;
 
@@ -1149,113 +1253,6 @@ discard_blocks(dev_t dev, __uint64_t nsectors)
                platform_discard_blocks(fd, 0, nsectors << 9);
 }
 
-struct sb_feat_args {
-       int     log_version;
-       int     attr_version;
-       int     dir_version;
-       int     spinodes;
-       int     finobt;
-       bool    inode_align;
-       bool    nci;
-       bool    lazy_sb_counters;
-       bool    projid16bit;
-       bool    crcs_enabled;
-       bool    dirftype;
-       bool    parent_pointers;
-       bool    rmapbt;
-       bool    reflink;
-};
-
-static void
-sb_set_features(
-       struct xfs_sb           *sbp,
-       struct sb_feat_args     *fp,
-       int                     sectsize,
-       int                     lsectsize,
-       int                     dsunit)
-{
-
-       sbp->sb_versionnum = XFS_DFL_SB_VERSION_BITS;
-       if (fp->crcs_enabled)
-               sbp->sb_versionnum |= XFS_SB_VERSION_5;
-       else
-               sbp->sb_versionnum |= XFS_SB_VERSION_4;
-
-       if (fp->inode_align)
-               sbp->sb_versionnum |= XFS_SB_VERSION_ALIGNBIT;
-       if (dsunit)
-               sbp->sb_versionnum |= XFS_SB_VERSION_DALIGNBIT;
-       if (fp->log_version == 2)
-               sbp->sb_versionnum |= XFS_SB_VERSION_LOGV2BIT;
-       if (fp->attr_version == 1)
-               sbp->sb_versionnum |= XFS_SB_VERSION_ATTRBIT;
-       if (sectsize > BBSIZE || lsectsize > BBSIZE)
-               sbp->sb_versionnum |= XFS_SB_VERSION_SECTORBIT;
-       if (fp->nci)
-               sbp->sb_versionnum |= XFS_SB_VERSION_BORGBIT;
-
-
-       sbp->sb_features2 = 0;
-       if (fp->lazy_sb_counters)
-               sbp->sb_features2 |= XFS_SB_VERSION2_LAZYSBCOUNTBIT;
-       if (!fp->projid16bit)
-               sbp->sb_features2 |= XFS_SB_VERSION2_PROJID32BIT;
-       if (fp->parent_pointers)
-               sbp->sb_features2 |= XFS_SB_VERSION2_PARENTBIT;
-       if (fp->crcs_enabled)
-               sbp->sb_features2 |= XFS_SB_VERSION2_CRCBIT;
-       if (fp->attr_version == 2)
-               sbp->sb_features2 |= XFS_SB_VERSION2_ATTR2BIT;
-
-       /* v5 superblocks have their own feature bit for dirftype */
-       if (fp->dirftype && !fp->crcs_enabled)
-               sbp->sb_features2 |= XFS_SB_VERSION2_FTYPE;
-
-       /* update whether extended features are in use */
-       if (sbp->sb_features2 != 0)
-               sbp->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT;
-
-       /*
-        * Due to a structure alignment issue, sb_features2 ended up in one
-        * of two locations, the second "incorrect" location represented by
-        * the sb_bad_features2 field. To avoid older kernels mounting
-        * filesystems they shouldn't, set both field to the same value.
-        */
-       sbp->sb_bad_features2 = sbp->sb_features2;
-
-       if (!fp->crcs_enabled)
-               return;
-
-       /* default features for v5 filesystems */
-       sbp->sb_features_compat = 0;
-       sbp->sb_features_ro_compat = 0;
-       sbp->sb_features_incompat = XFS_SB_FEAT_INCOMPAT_FTYPE;
-       sbp->sb_features_log_incompat = 0;
-
-       if (fp->finobt)
-               sbp->sb_features_ro_compat = XFS_SB_FEAT_RO_COMPAT_FINOBT;
-       if (fp->rmapbt)
-               sbp->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_RMAPBT;
-       if (fp->reflink)
-               sbp->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_REFLINK;
-
-       /*
-        * Sparse inode chunk support has two main inode alignment requirements.
-        * First, sparse chunk alignment must match the cluster size. Second,
-        * full chunk alignment must match the inode chunk size.
-        *
-        * Copy the already calculated/scaled inoalignmt to spino_align and
-        * update the former to the full inode chunk size.
-        */
-       if (fp->spinodes) {
-               sbp->sb_spino_align = sbp->sb_inoalignmt;
-               sbp->sb_inoalignmt = XFS_INODES_PER_CHUNK *
-                       sbp->sb_inodesize >> sbp->sb_blocklog;
-               sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_SPINODES;
-       }
-
-}
-
 static __attribute__((noreturn)) void
 illegal_option(
        const char              *value,
@@ -1264,9 +1261,9 @@ illegal_option(
        const char              *reason)
 {
        fprintf(stderr,
-               _("Illegal value %s for -%c %s option. %s\n"),
+               _("Invalid value %s for -%c %s option. %s\n"),
                value, opts->name, opts->subopts[index],
-               reason ? reason : "");
+               reason);
        usage();
 }
 
@@ -1284,9 +1281,9 @@ check_opt(
 
        if (sp->index != index) {
                fprintf(stderr,
-       ("Developer screwed up option parsing (%d/%d)! Please report!\n"),
+       _("Developer screwed up option parsing (%d/%d)! Please report!\n"),
                        sp->index, index);
-               reqval(opts->name, (char **)opts->subopts, index);
+               reqval(opts->name, opts->subopts, index);
        }
 
        /*
@@ -1299,24 +1296,23 @@ check_opt(
         */
        if (!str_seen) {
                if (sp->seen)
-                       respec(opts->name, (char **)opts->subopts, index);
+                       respec(opts->name, opts->subopts, index);
                sp->seen = true;
        } else {
                if (sp->str_seen)
-                       respec(opts->name, (char **)opts->subopts, index);
+                       respec(opts->name, opts->subopts, index);
                sp->str_seen = true;
        }
 
        /* check for conflicts with the option */
        for (i = 0; i < MAX_CONFLICTS; i++) {
-               int conflict_opt = sp->conflicts[i];
+               struct _conflict *con = &sp->conflicts[i];
 
-               if (conflict_opt == LAST_CONFLICT)
+               if (con->subopt == LAST_CONFLICT)
                        break;
-               if (opts->subopt_params[conflict_opt].seen ||
-                   opts->subopt_params[conflict_opt].str_seen)
-                       conflict(opts->name, (char **)opts->subopts,
-                                conflict_opt, index);
+               if (con->opts->subopt_params[con->subopt].seen ||
+                   con->opts->subopt_params[con->subopt].str_seen)
+                       conflict(opts, index, con->opts, con->subopt);
        }
 }
 
@@ -1333,7 +1329,7 @@ getnum(
        /* empty strings might just return a default value */
        if (!str || *str == '\0') {
                if (sp->defaultval == SUBOPT_NEEDS_VAL)
-                       reqval(opts->name, (char **)opts->subopts, index);
+                       reqval(opts->name, opts->subopts, index);
                return sp->defaultval;
        }
 
@@ -1358,18 +1354,20 @@ getnum(
 
                c = strtoll(str, &str_end, 0);
                if (c == 0 && str_end == str)
-                       illegal_option(str, opts, index, NULL);
+                       illegal_option(str, opts, index,
+                                       _("Value not recognized as number."));
                if (*str_end != '\0')
-                       illegal_option(str, opts, index, NULL);
+                       illegal_option(str, opts, index,
+                                       _("Unit suffixes are not allowed."));
        }
 
        /* Validity check the result. */
        if (c < sp->minval)
-               illegal_option(str, opts, index, _("value is too small"));
+               illegal_option(str, opts, index, _("Value is too small."));
        else if (c > sp->maxval)
-               illegal_option(str, opts, index, _("value is too large"));
+               illegal_option(str, opts, index, _("Value is too large."));
        if (sp->is_power_2 && !ispow2(c))
-               illegal_option(str, opts, index, _("value must be a power of 2"));
+               illegal_option(str, opts, index, _("Value must be a power of 2."));
        return c;
 }
 
@@ -1389,677 +1387,543 @@ getstr(
 
        /* empty strings for string options are not valid */
        if (!str || *str == '\0')
-               reqval(opts->name, (char **)opts->subopts, index);
+               reqval(opts->name, opts->subopts, index);
        return str;
 }
 
-int
-main(
-       int                     argc,
-       char                    **argv)
+static int
+block_opts_parser(
+       struct opt_params       *opts,
+       int                     subopt,
+       char                    *value,
+       struct cli_params       *cli)
 {
-       __uint64_t              agcount;
-       xfs_agf_t               *agf;
-       xfs_agi_t               *agi;
-       xfs_agnumber_t          agno;
-       __uint64_t              agsize;
-       xfs_alloc_rec_t         *arec;
-       struct xfs_btree_block  *block;
-       int                     blflag;
-       int                     blocklog;
-       int                     bsflag;
-       int                     bsize;
-       xfs_buf_t               *buf;
-       int                     c;
-       int                     daflag;
-       int                     dasize;
-       xfs_rfsblock_t          dblocks;
-       char                    *dfile;
-       int                     dirblocklog;
-       int                     dirblocksize;
-       char                    *dsize;
-       int                     dsu;
-       int                     dsw;
-       int                     dsunit;
-       int                     dswidth;
-       int                     force_overwrite;
-       struct fsxattr          fsx;
-       int                     ilflag;
-       int                     imaxpct;
-       int                     imflag;
-       int                     inodelog;
-       int                     inopblock;
-       int                     ipflag;
-       int                     isflag;
-       int                     isize;
-       char                    *label = NULL;
-       int                     laflag;
-       int                     lalign;
-       int                     ldflag;
-       int                     liflag;
-       xfs_agnumber_t          logagno;
-       xfs_rfsblock_t          logblocks;
-       char                    *logfile;
-       int                     loginternal;
-       char                    *logsize;
-       xfs_fsblock_t           logstart;
-       int                     lvflag;
-       int                     lsflag;
-       int                     lsuflag;
-       int                     lsunitflag;
-       int                     lsectorlog;
-       int                     lsectorsize;
-       int                     lslflag;
-       int                     lssflag;
-       int                     lsu;
-       int                     lsunit;
-       int                     min_logblocks;
-       xfs_mount_t             *mp;
-       xfs_mount_t             mbuf;
-       xfs_extlen_t            nbmblocks;
-       int                     nlflag;
-       int                     nodsflag;
-       int                     norsflag;
-       xfs_alloc_rec_t         *nrec;
-       int                     nsflag;
-       int                     nvflag;
-       int                     Nflag;
-       int                     discard = 1;
-       char                    *p;
-       char                    *protofile;
-       char                    *protostring;
-       int                     qflag;
-       xfs_rfsblock_t          rtblocks;
-       xfs_extlen_t            rtextblocks;
-       xfs_rtblock_t           rtextents;
-       char                    *rtextsize;
-       char                    *rtfile;
-       char                    *rtsize;
-       xfs_sb_t                *sbp;
-       int                     sectorlog;
-       __uint64_t              sector_mask;
-       int                     slflag;
-       int                     ssflag;
-       __uint64_t              tmp_agsize;
-       uuid_t                  uuid;
-       int                     worst_freelist;
-       libxfs_init_t           xi;
-       struct fs_topology      ft;
-       struct sb_feat_args     sb_feat = {
-               .finobt = 1,
-               .spinodes = 0,
-               .log_version = 2,
-               .attr_version = 2,
-               .dir_version = XFS_DFL_DIR_VERSION,
-               .inode_align = XFS_IFLAG_ALIGN,
-               .nci = false,
-               .lazy_sb_counters = true,
-               .projid16bit = false,
-               .crcs_enabled = true,
-               .dirftype = true,
-               .parent_pointers = false,
-               .rmapbt = false,
-               .reflink = false,
-       };
-
-       platform_uuid_generate(&uuid);
-       progname = basename(argv[0]);
-       setlocale(LC_ALL, "");
-       bindtextdomain(PACKAGE, LOCALEDIR);
-       textdomain(PACKAGE);
-
-       blflag = bsflag = slflag = ssflag = lslflag = lssflag = 0;
-       blocklog = blocksize = 0;
-       sectorlog = lsectorlog = 0;
-       sectorsize = lsectorsize = 0;
-       agsize = daflag = dasize = dblocks = 0;
-       ilflag = imflag = ipflag = isflag = 0;
-       liflag = laflag = lsflag = lsuflag = lsunitflag = ldflag = lvflag = 0;
-       loginternal = 1;
-       logagno = logblocks = rtblocks = rtextblocks = 0;
-       Nflag = nlflag = nsflag = nvflag = 0;
-       dirblocklog = dirblocksize = 0;
-       qflag = 0;
-       imaxpct = inodelog = inopblock = isize = 0;
-       dfile = logfile = rtfile = NULL;
-       dsize = logsize = rtsize = rtextsize = protofile = NULL;
-       dsu = dsw = dsunit = dswidth = lalign = lsu = lsunit = 0;
-       nodsflag = norsflag = 0;
-       force_overwrite = 0;
-       worst_freelist = 0;
-       memset(&fsx, 0, sizeof(fsx));
-
-       memset(&xi, 0, sizeof(xi));
-       xi.isdirect = LIBXFS_DIRECT;
-       xi.isreadonly = LIBXFS_EXCLUSIVELY;
-
-       while ((c = getopt(argc, argv, "b:d:i:l:L:m:n:KNp:qr:s:CfV")) != EOF) {
-               switch (c) {
-               case 'C':
-               case 'f':
-                       force_overwrite = 1;
-                       break;
-               case 'b':
-                       p = optarg;
-                       while (*p != '\0') {
-                               char    **subopts = (char **)bopts.subopts;
-                               char    *value;
-
-                               switch (getsubopt(&p, subopts, &value)) {
-                               case B_LOG:
-                                       blocklog = getnum(value, &bopts, B_LOG);
-                                       blocksize = 1 << blocklog;
-                                       blflag = 1;
-                                       break;
-                               case B_SIZE:
-                                       blocksize = getnum(value, &bopts,
-                                                          B_SIZE);
-                                       blocklog = libxfs_highbit32(blocksize);
-                                       bsflag = 1;
-                                       break;
-                               default:
-                                       unknown('b', value);
-                               }
-                       }
-                       break;
-               case 'd':
-                       p = optarg;
-                       while (*p != '\0') {
-                               char    **subopts = (char **)dopts.subopts;
-                               char    *value;
-
-                               switch (getsubopt(&p, subopts, &value)) {
-                               case D_AGCOUNT:
-                                       agcount = getnum(value, &dopts,
-                                                        D_AGCOUNT);
-                                       daflag = 1;
-                                       break;
-                               case D_AGSIZE:
-                                       agsize = getnum(value, &dopts, D_AGSIZE);
-                                       dasize = 1;
-                                       break;
-                               case D_FILE:
-                                       xi.disfile = getnum(value, &dopts,
-                                                           D_FILE);
-                                       break;
-                               case D_NAME:
-                                       xi.dname = getstr(value, &dopts, D_NAME);
-                                       break;
-                               case D_SIZE:
-                                       dsize = getstr(value, &dopts, D_SIZE);
-                                       break;
-                               case D_SUNIT:
-                                       dsunit = getnum(value, &dopts, D_SUNIT);
-                                       break;
-                               case D_SWIDTH:
-                                       dswidth = getnum(value, &dopts,
-                                                        D_SWIDTH);
-                                       break;
-                               case D_SU:
-                                       dsu = getnum(value, &dopts, D_SU);
-                                       break;
-                               case D_SW:
-                                       dsw = getnum(value, &dopts, D_SW);
-                                       break;
-                               case D_NOALIGN:
-                                       nodsflag = getnum(value, &dopts,
-                                                               D_NOALIGN);
-                                       break;
-                               case D_SECTLOG:
-                                       sectorlog = getnum(value, &dopts,
-                                                          D_SECTLOG);
-                                       sectorsize = 1 << sectorlog;
-                                       slflag = 1;
-                                       break;
-                               case D_SECTSIZE:
-                                       sectorsize = getnum(value, &dopts,
-                                                           D_SECTSIZE);
-                                       sectorlog =
-                                               libxfs_highbit32(sectorsize);
-                                       ssflag = 1;
-                                       break;
-                               case D_RTINHERIT:
-                                       c = getnum(value, &dopts, D_RTINHERIT);
-                                       if (c)
-                                               fsx.fsx_xflags |=
-                                                       XFS_DIFLAG_RTINHERIT;
-                                       break;
-                               case D_PROJINHERIT:
-                                       fsx.fsx_projid = getnum(value, &dopts,
-                                                               D_PROJINHERIT);
-                                       fsx.fsx_xflags |=
-                                               XFS_DIFLAG_PROJINHERIT;
-                                       break;
-                               case D_EXTSZINHERIT:
-                                       fsx.fsx_extsize = getnum(value, &dopts,
-                                                                D_EXTSZINHERIT);
-                                       fsx.fsx_xflags |=
-                                               XFS_DIFLAG_EXTSZINHERIT;
-                                       break;
-                               default:
-                                       unknown('d', value);
-                               }
-                       }
-                       break;
-               case 'i':
-                       p = optarg;
-                       while (*p != '\0') {
-                               char    **subopts = (char **)iopts.subopts;
-                               char    *value;
-
-                               switch (getsubopt(&p, subopts, &value)) {
-                               case I_ALIGN:
-                                       sb_feat.inode_align = getnum(value,
-                                                               &iopts, I_ALIGN);
-                                       break;
-                               case I_LOG:
-                                       inodelog = getnum(value, &iopts, I_LOG);
-                                       isize = 1 << inodelog;
-                                       ilflag = 1;
-                                       break;
-                               case I_MAXPCT:
-                                       imaxpct = getnum(value, &iopts,
-                                                        I_MAXPCT);
-                                       imflag = 1;
-                                       break;
-                               case I_PERBLOCK:
-                                       inopblock = getnum(value, &iopts,
-                                                          I_PERBLOCK);
-                                       ipflag = 1;
-                                       break;
-                               case I_SIZE:
-                                       isize = getnum(value, &iopts, I_SIZE);
-                                       inodelog = libxfs_highbit32(isize);
-                                       isflag = 1;
-                                       break;
-                               case I_ATTR:
-                                       sb_feat.attr_version =
-                                               getnum(value, &iopts, I_ATTR);
-                                       break;
-                               case I_PROJID32BIT:
-                                       sb_feat.projid16bit =
-                                               !getnum(value, &iopts,
-                                                       I_PROJID32BIT);
-                                       break;
-                               case I_SPINODES:
-                                       sb_feat.spinodes = getnum(value,
-                                                       &iopts, I_SPINODES);
-                                       break;
-                               default:
-                                       unknown('i', value);
-                               }
-                       }
-                       break;
-               case 'l':
-                       p = optarg;
-                       while (*p != '\0') {
-                               char    **subopts = (char **)lopts.subopts;
-                               char    *value;
-
-                               switch (getsubopt(&p, subopts, &value)) {
-                               case L_AGNUM:
-                                       logagno = getnum(value, &lopts, L_AGNUM);
-                                       laflag = 1;
-                                       break;
-                               case L_FILE:
-                                       xi.lisfile = getnum(value, &lopts,
-                                                           L_FILE);
-                                       break;
-                               case L_INTERNAL:
-                                       loginternal = getnum(value, &lopts,
-                                                            L_INTERNAL);
-                                       liflag = 1;
-                                       break;
-                               case L_SU:
-                                       lsu = getnum(value, &lopts, L_SU);
-                                       lsuflag = 1;
-                                       break;
-                               case L_SUNIT:
-                                       lsunit = getnum(value, &lopts, L_SUNIT);
-                                       lsunitflag = 1;
-                                       break;
-                               case L_NAME:
-                               case L_DEV:
-                                       logfile = getstr(value, &lopts, L_NAME);
-                                       xi.logname = logfile;
-                                       ldflag = 1;
-                                       loginternal = 0;
-                                       break;
-                               case L_VERSION:
-                                       sb_feat.log_version =
-                                               getnum(value, &lopts, L_VERSION);
-                                       lvflag = 1;
-                                       break;
-                               case L_SIZE:
-                                       logsize = getstr(value, &lopts, L_SIZE);
-                                       break;
-                               case L_SECTLOG:
-                                       lsectorlog = getnum(value, &lopts,
-                                                           L_SECTLOG);
-                                       lsectorsize = 1 << lsectorlog;
-                                       lslflag = 1;
-                                       break;
-                               case L_SECTSIZE:
-                                       lsectorsize = getnum(value, &lopts,
-                                                            L_SECTSIZE);
-                                       lsectorlog =
-                                               libxfs_highbit32(lsectorsize);
-                                       lssflag = 1;
-                                       break;
-                               case L_LAZYSBCNTR:
-                                       sb_feat.lazy_sb_counters =
-                                                       getnum(value, &lopts,
-                                                              L_LAZYSBCNTR);
-                                       break;
-                               default:
-                                       unknown('l', value);
-                               }
-                       }
-                       break;
-               case 'L':
-                       if (strlen(optarg) > sizeof(sbp->sb_fname))
-                               illegal(optarg, "L");
-                       label = optarg;
-                       break;
-               case 'm':
-                       p = optarg;
-                       while (*p != '\0') {
-                               char    **subopts = (char **)mopts.subopts;
-                               char    *value;
-
-                               switch (getsubopt(&p, subopts, &value)) {
-                               case M_CRC:
-                                       sb_feat.crcs_enabled =
-                                               getnum(value, &mopts, M_CRC);
-                                       if (sb_feat.crcs_enabled)
-                                               sb_feat.dirftype = true;
-                                       break;
-                               case M_FINOBT:
-                                       sb_feat.finobt = getnum(
-                                               value, &mopts, M_FINOBT);
-                                       break;
-                               case M_UUID:
-                                       if (!value || *value == '\0')
-                                               reqval('m', subopts, M_UUID);
-                                       if (platform_uuid_parse(value, &uuid))
-                                               illegal(optarg, "m uuid");
-                                       break;
-                               case M_RMAPBT:
-                                       sb_feat.rmapbt = getnum(
-                                               value, &mopts, M_RMAPBT);
-                                       break;
-                               case M_REFLINK:
-                                       sb_feat.reflink = getnum(
-                                               value, &mopts, M_REFLINK);
-                                       break;
-                               default:
-                                       unknown('m', value);
-                               }
-                       }
-                       break;
-               case 'n':
-                       p = optarg;
-                       while (*p != '\0') {
-                               char    **subopts = (char **)nopts.subopts;
-                               char    *value;
-
-                               switch (getsubopt(&p, subopts, &value)) {
-                               case N_LOG:
-                                       dirblocklog = getnum(value, &nopts,
-                                                            N_LOG);
-                                       dirblocksize = 1 << dirblocklog;
-                                       nlflag = 1;
-                                       break;
-                               case N_SIZE:
-                                       dirblocksize = getnum(value, &nopts,
-                                                             N_SIZE);
-                                       dirblocklog =
-                                               libxfs_highbit32(dirblocksize);
-                                       nsflag = 1;
-                                       break;
-                               case N_VERSION:
-                                       value = getstr(value, &nopts, N_VERSION);
-                                       if (!strcasecmp(value, "ci")) {
-                                               /* ASCII CI mode */
-                                               sb_feat.nci = true;
-                                       } else {
-                                               sb_feat.dir_version =
-                                                       getnum(value, &nopts,
-                                                              N_VERSION);
-                                       }
-                                       nvflag = 1;
-                                       break;
-                               case N_FTYPE:
-                                       sb_feat.dirftype = getnum(value, &nopts,
-                                                                 N_FTYPE);
-                                       break;
-                               default:
-                                       unknown('n', value);
-                               }
-                       }
-                       break;
-               case 'N':
-                       Nflag = 1;
-                       break;
-               case 'K':
-                       discard = 0;
-                       break;
-               case 'p':
-                       if (protofile)
-                               respec('p', NULL, 0);
-                       protofile = optarg;
-                       break;
-               case 'q':
-                       qflag = 1;
-                       break;
-               case 'r':
-                       p = optarg;
-                       while (*p != '\0') {
-                               char    **subopts = (char **)ropts.subopts;
-                               char    *value;
-
-                               switch (getsubopt(&p, subopts, &value)) {
-                               case R_EXTSIZE:
-                                       rtextsize = getstr(value, &ropts,
-                                                          R_EXTSIZE);
-                                       break;
-                               case R_FILE:
-                                       xi.risfile = getnum(value, &ropts,
-                                                           R_FILE);
-                                       break;
-                               case R_NAME:
-                               case R_DEV:
-                                       xi.rtname = getstr(value, &ropts,
-                                                          R_NAME);
-                                       break;
-                               case R_SIZE:
-                                       rtsize = getstr(value, &ropts, R_SIZE);
-                                       break;
-                               case R_NOALIGN:
-                                       norsflag = getnum(value, &ropts,
-                                                               R_NOALIGN);
-                                       break;
-                               default:
-                                       unknown('r', value);
-                               }
-                       }
-                       break;
-               case 's':
-                       p = optarg;
-                       while (*p != '\0') {
-                               char    **subopts = (char **)sopts.subopts;
-                               char    *value;
-
-                               switch (getsubopt(&p, subopts, &value)) {
-                               case S_LOG:
-                               case S_SECTLOG:
-                                       if (lssflag)
-                                               conflict('s', subopts,
-                                                        S_SECTSIZE, S_SECTLOG);
-                                       sectorlog = getnum(value, &sopts,
-                                                          S_SECTLOG);
-                                       lsectorlog = sectorlog;
-                                       sectorsize = 1 << sectorlog;
-                                       lsectorsize = sectorsize;
-                                       lslflag = slflag = 1;
-                                       break;
-                               case S_SIZE:
-                               case S_SECTSIZE:
-                                       if (lslflag)
-                                               conflict('s', subopts, S_SECTLOG,
-                                                        S_SECTSIZE);
-                                       sectorsize = getnum(value, &sopts,
-                                                           S_SECTSIZE);
-                                       lsectorsize = sectorsize;
-                                       sectorlog =
-                                               libxfs_highbit32(sectorsize);
-                                       lsectorlog = sectorlog;
-                                       lssflag = ssflag = 1;
-                                       break;
-                               default:
-                                       unknown('s', value);
-                               }
-                       }
-                       break;
-               case 'V':
-                       printf(_("%s version %s\n"), progname, VERSION);
-                       exit(0);
-               case '?':
-                       unknown(optopt, "");
-               }
-       }
-       if (argc - optind > 1) {
-               fprintf(stderr, _("extra arguments\n"));
-               usage();
-       } else if (argc - optind == 1) {
-               dfile = xi.volname = getstr(argv[optind], &dopts, D_NAME);
-       } else
-               dfile = xi.dname;
-
-       /*
-        * Blocksize and sectorsize first, other things depend on them
-        * For RAID4/5/6 we want to align sector size and block size,
-        * so we need to start with the device geometry extraction too.
-        */
-       if (!blflag && !bsflag) {
-               blocklog = XFS_DFL_BLOCKSIZE_LOG;
-               blocksize = 1 << XFS_DFL_BLOCKSIZE_LOG;
-       }
-       if (blocksize < XFS_MIN_BLOCKSIZE || blocksize > XFS_MAX_BLOCKSIZE) {
-               fprintf(stderr, _("illegal block size %d\n"), blocksize);
-               usage();
-       }
-       if (sb_feat.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();
-       }
-       if (sb_feat.crcs_enabled && !sb_feat.dirftype) {
-               fprintf(stderr, _("cannot disable ftype with crcs enabled\n"));
-               usage();
+       switch (subopt) {
+       case B_SIZE:
+               cli->blocksize = getnum(value, opts, subopt);
+               break;
+       default:
+               return -EINVAL;
        }
+       return 0;
+}
 
-       if (!slflag && !ssflag) {
-               sectorlog = XFS_MIN_SECTORSIZE_LOG;
-               sectorsize = XFS_MIN_SECTORSIZE;
-       }
-       if (!lslflag && !lssflag) {
-               lsectorlog = sectorlog;
-               lsectorsize = sectorsize;
+static int
+data_opts_parser(
+       struct opt_params       *opts,
+       int                     subopt,
+       char                    *value,
+       struct cli_params       *cli)
+{
+       switch (subopt) {
+       case D_AGCOUNT:
+               cli->agcount = getnum(value, opts, subopt);
+               break;
+       case D_AGSIZE:
+               cli->agsize = getstr(value, opts, subopt);
+               break;
+       case D_FILE:
+               cli->xi->disfile = getnum(value, opts, subopt);
+               break;
+       case D_NAME:
+               cli->xi->dname = getstr(value, opts, subopt);
+               break;
+       case D_SIZE:
+               cli->dsize = getstr(value, opts, subopt);
+               break;
+       case D_SUNIT:
+               cli->dsunit = getnum(value, opts, subopt);
+               break;
+       case D_SWIDTH:
+               cli->dswidth = getnum(value, opts, subopt);
+               break;
+       case D_SU:
+               cli->dsu = getstr(value, opts, subopt);
+               break;
+       case D_SW:
+               cli->dsw = getnum(value, opts, subopt);
+               break;
+       case D_NOALIGN:
+               cli->sb_feat.nodalign = getnum(value, opts, subopt);
+               break;
+       case D_SECTSIZE:
+               cli->sectorsize = getnum(value, opts, subopt);
+               break;
+       case D_RTINHERIT:
+               if (getnum(value, opts, subopt))
+                       cli->fsx.fsx_xflags |= XFS_DIFLAG_RTINHERIT;
+               break;
+       case D_PROJINHERIT:
+               cli->fsx.fsx_projid = getnum(value, opts, subopt);
+               cli->fsx.fsx_xflags |= XFS_DIFLAG_PROJINHERIT;
+               break;
+       case D_EXTSZINHERIT:
+               cli->fsx.fsx_extsize = getnum(value, opts, subopt);
+               cli->fsx.fsx_xflags |= XFS_DIFLAG_EXTSZINHERIT;
+               break;
+       case D_COWEXTSIZE:
+               cli->fsx.fsx_cowextsize = getnum(value, opts, subopt);
+               cli->fsx.fsx_xflags |= FS_XFLAG_COWEXTSIZE;
+               break;
+       default:
+               return -EINVAL;
        }
+       return 0;
+}
+
+static int
+inode_opts_parser(
+       struct opt_params       *opts,
+       int                     subopt,
+       char                    *value,
+       struct cli_params       *cli)
+{
+       switch (subopt) {
+       case I_ALIGN:
+               cli->sb_feat.inode_align = getnum(value, opts, subopt);
+               break;
+       case I_MAXPCT:
+               cli->imaxpct = getnum(value, opts, subopt);
+               break;
+       case I_PERBLOCK:
+               cli->inopblock = getnum(value, opts, subopt);
+               break;
+       case I_SIZE:
+               cli->inodesize = getnum(value, opts, subopt);
+               break;
+       case I_ATTR:
+               cli->sb_feat.attr_version = getnum(value, opts, subopt);
+               break;
+       case I_PROJID32BIT:
+               cli->sb_feat.projid32bit = getnum(value, opts, subopt);
+               break;
+       case I_SPINODES:
+               cli->sb_feat.spinodes = getnum(value, opts, subopt);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int
+log_opts_parser(
+       struct opt_params       *opts,
+       int                     subopt,
+       char                    *value,
+       struct cli_params       *cli)
+{
+       switch (subopt) {
+       case L_AGNUM:
+               cli->logagno = getnum(value, opts, subopt);
+               break;
+       case L_FILE:
+               cli->xi->lisfile = getnum(value, opts, subopt);
+               break;
+       case L_INTERNAL:
+               cli->loginternal = getnum(value, opts, subopt);
+               break;
+       case L_SU:
+               cli->lsu = getstr(value, opts, subopt);
+               break;
+       case L_SUNIT:
+               cli->lsunit = getnum(value, opts, subopt);
+               break;
+       case L_NAME:
+       case L_DEV:
+               cli->xi->logname = getstr(value, opts, subopt);
+               cli->loginternal = 0;
+               break;
+       case L_VERSION:
+               cli->sb_feat.log_version = getnum(value, opts, subopt);
+               break;
+       case L_SIZE:
+               cli->logsize = getstr(value, opts, subopt);
+               break;
+       case L_SECTSIZE:
+               cli->lsectorsize = getnum(value, opts, subopt);
+               break;
+       case L_LAZYSBCNTR:
+               cli->sb_feat.lazy_sb_counters = getnum(value, opts, subopt);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int
+meta_opts_parser(
+       struct opt_params       *opts,
+       int                     subopt,
+       char                    *value,
+       struct cli_params       *cli)
+{
+       switch (subopt) {
+       case M_CRC:
+               cli->sb_feat.crcs_enabled = getnum(value, opts, subopt);
+               if (cli->sb_feat.crcs_enabled)
+                       cli->sb_feat.dirftype = true;
+               break;
+       case M_FINOBT:
+               cli->sb_feat.finobt = getnum(value, opts, subopt);
+               break;
+       case M_UUID:
+               if (!value || *value == '\0')
+                       reqval('m', opts->subopts, subopt);
+               if (platform_uuid_parse(value, &cli->uuid))
+                       illegal(value, "m uuid");
+               break;
+       case M_RMAPBT:
+               cli->sb_feat.rmapbt = getnum(value, opts, subopt);
+               break;
+       case M_REFLINK:
+               cli->sb_feat.reflink = getnum(value, opts, subopt);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int
+naming_opts_parser(
+       struct opt_params       *opts,
+       int                     subopt,
+       char                    *value,
+       struct cli_params       *cli)
+{
+       switch (subopt) {
+       case N_SIZE:
+               cli->dirblocksize = getstr(value, opts, subopt);
+               break;
+       case N_VERSION:
+               value = getstr(value, &nopts, subopt);
+               if (!strcasecmp(value, "ci")) {
+                       /* ASCII CI mode */
+                       cli->sb_feat.nci = true;
+               } else {
+                       cli->sb_feat.dir_version = getnum(value, opts, subopt);
+               }
+               break;
+       case N_FTYPE:
+               cli->sb_feat.dirftype = getnum(value, opts, subopt);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int
+rtdev_opts_parser(
+       struct opt_params       *opts,
+       int                     subopt,
+       char                    *value,
+       struct cli_params       *cli)
+{
+       switch (subopt) {
+       case R_EXTSIZE:
+               cli->rtextsize = getstr(value, opts, subopt);
+               break;
+       case R_FILE:
+               cli->xi->risfile = getnum(value, opts, subopt);
+               break;
+       case R_NAME:
+       case R_DEV:
+               cli->xi->rtname = getstr(value, opts, subopt);
+               break;
+       case R_SIZE:
+               cli->rtsize = getstr(value, opts, subopt);
+               break;
+       case R_NOALIGN:
+               cli->sb_feat.nortalign = getnum(value, opts, subopt);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int
+sector_opts_parser(
+       struct opt_params       *opts,
+       int                     subopt,
+       char                    *value,
+       struct cli_params       *cli)
+{
+       switch (subopt) {
+       case S_SIZE:
+       case S_SECTSIZE:
+               cli->sectorsize = getnum(value, opts, subopt);
+               cli->lsectorsize = cli->sectorsize;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static struct subopts {
+       char            opt;
+       struct opt_params *opts;
+       int             (*parser)(struct opt_params     *opts,
+                                 int                   subopt,
+                                 char                  *value,
+                                 struct cli_params     *cli);
+} subopt_tab[] = {
+       { 'b', &bopts, block_opts_parser },
+       { 'd', &dopts, data_opts_parser },
+       { 'i', &iopts, inode_opts_parser },
+       { 'l', &lopts, log_opts_parser },
+       { 'm', &mopts, meta_opts_parser },
+       { 'n', &nopts, naming_opts_parser },
+       { 'r', &ropts, rtdev_opts_parser },
+       { 's', &sopts, sector_opts_parser },
+       { '\0', NULL, NULL },
+};
+
+static void
+parse_subopts(
+       char            opt,
+       char            *arg,
+       struct cli_params *cli)
+{
+       struct subopts  *sop = &subopt_tab[0];
+       char            *p;
+       int             ret = 0;
+
+       while (sop->opts) {
+               if (sop->opt == opt)
+                       break;
+               sop++;
+       }
+
+       /* should never happen */
+       if (!sop->opts)
+               return;
+
+       p = arg;
+       while (*p != '\0') {
+               char    **subopts = (char **)sop->opts->subopts;
+               char    *value;
+               int     subopt;
+
+               subopt = getsubopt(&p, subopts, &value);
+
+               ret = (sop->parser)(sop->opts, subopt, value, cli);
+               if (ret)
+                       unknown(opt, value);
+       }
+}
+
+static void
+validate_sectorsize(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli,
+       struct mkfs_default_params *dft,
+       struct fs_topology      *ft,
+       char                    *dfile,
+       int                     dry_run,
+       int                     force_overwrite)
+{
+       /* set configured sector sizes in preparation for checks */
+       if (!cli->sectorsize) {
+               cfg->sectorsize = dft->sectorsize;
+       } else {
+               cfg->sectorsize = cli->sectorsize;
+       }
+       cfg->sectorlog = libxfs_highbit32(cfg->sectorsize);
 
        /*
         * Before anything else, verify that we are correctly operating on
         * files or block devices and set the control parameters correctly.
+        */
+       check_device_type(dfile, &cli->xi->disfile, !cli->dsize, !dfile,
+                         dry_run ? NULL : &cli->xi->dcreat, "d");
+       if (!cli->loginternal)
+               check_device_type(cli->xi->logname, &cli->xi->lisfile,
+                                 !cli->logsize, !cli->xi->logname,
+                                 dry_run ? NULL : &cli->xi->lcreat, "l");
+       if (cli->xi->rtname)
+               check_device_type(cli->xi->rtname, &cli->xi->risfile,
+                                 !cli->rtsize, !cli->xi->rtname,
+                                 dry_run ? NULL : &cli->xi->rcreat, "r");
+
+       /*
         * Explicitly disable direct IO for image files so we don't error out on
         * sector size mismatches between the new filesystem and the underlying
         * host filesystem.
         */
-       check_device_type(dfile, &xi.disfile, !dsize, !dfile,
-                         Nflag ? NULL : &xi.dcreat, force_overwrite, "d");
-       if (!loginternal)
-               check_device_type(xi.logname, &xi.lisfile, !logsize, !xi.logname,
-                                 Nflag ? NULL : &xi.lcreat,
-                                 force_overwrite, "l");
-       if (xi.rtname)
-               check_device_type(xi.rtname, &xi.risfile, !rtsize, !xi.rtname,
-                                 Nflag ? NULL : &xi.rcreat,
-                                 force_overwrite, "r");
-       if (xi.disfile || xi.lisfile || xi.risfile)
-               xi.isdirect = 0;
-
-       memset(&ft, 0, sizeof(ft));
-       get_topology(&xi, &ft, force_overwrite);
-
-       if (!ssflag) {
+       if (cli->xi->disfile || cli->xi->lisfile || cli->xi->risfile)
+               cli->xi->isdirect = 0;
+
+       memset(ft, 0, sizeof(*ft));
+       get_topology(cli->xi, ft, force_overwrite);
+
+       if (!cli->sectorsize) {
                /*
                 * Unless specified manually on the command line use the
                 * advertised sector size of the device.  We use the physical
                 * sector size unless the requested block size is smaller
                 * than that, then we can use logical, but warn about the
                 * inefficiency.
+                *
+                * Set the topology sectors if they were not probed to the
+                * minimum supported sector size.
                 */
 
-               /* Older kernels may not have physical/logical distinction */
-               if (!ft.psectorsize)
-                       ft.psectorsize = ft.lsectorsize;
+               if (!ft->lsectorsize)
+                       ft->lsectorsize = XFS_MIN_SECTORSIZE;
 
-               sectorsize = ft.psectorsize ? ft.psectorsize :
-                                             XFS_MIN_SECTORSIZE;
+               /* Older kernels may not have physical/logical distinction */
+               if (!ft->psectorsize)
+                       ft->psectorsize = ft->lsectorsize;
 
-               if ((blocksize < sectorsize) && (blocksize >= ft.lsectorsize)) {
-                       fprintf(stderr,
-_("specified blocksize %d is less than device physical sector size %d\n"),
-                               blocksize, ft.psectorsize);
+               cfg->sectorsize = ft->psectorsize;
+               if (cfg->blocksize < cfg->sectorsize &&
+                   cfg->blocksize >= ft->lsectorsize) {
                        fprintf(stderr,
-_("switching to logical sector size %d\n"),
-                               ft.lsectorsize);
-                       sectorsize = ft.lsectorsize ? ft.lsectorsize :
-                                                     XFS_MIN_SECTORSIZE;
+_("specified blocksize %d is less than device physical sector size %d\n"
+  "switching to logical sector size %d\n"),
+                               cfg->blocksize, ft->psectorsize,
+                               ft->lsectorsize);
+                       cfg->sectorsize = ft->lsectorsize;
                }
+
+               cfg->sectorlog = libxfs_highbit32(cfg->sectorsize);
        }
 
-       if (!ssflag) {
-               sectorlog = libxfs_highbit32(sectorsize);
-               if (loginternal) {
-                       lsectorsize = sectorsize;
-                       lsectorlog = sectorlog;
-               }
+       /* validate specified/probed sector size */
+       if (cfg->sectorsize < XFS_MIN_SECTORSIZE ||
+           cfg->sectorsize > XFS_MAX_SECTORSIZE) {
+               fprintf(stderr, _("illegal sector size %d\n"), cfg->sectorsize);
+               usage();
        }
 
-       if (sectorsize < XFS_MIN_SECTORSIZE ||
-           sectorsize > XFS_MAX_SECTORSIZE || sectorsize > blocksize) {
-               if (ssflag)
-                       fprintf(stderr, _("illegal sector size %d\n"), sectorsize);
-               else
-                       fprintf(stderr,
-_("block size %d cannot be smaller than logical sector size %d\n"),
-                               blocksize, ft.lsectorsize);
+       if (cfg->blocksize < cfg->sectorsize) {
+               fprintf(stderr,
+_("block size %d cannot be smaller than sector size %d\n"),
+                       cfg->blocksize, cfg->sectorsize);
                usage();
        }
-       if (sectorsize < ft.lsectorsize) {
+
+       if (cfg->sectorsize < ft->lsectorsize) {
                fprintf(stderr, _("illegal sector size %d; hw sector is %d\n"),
-                       sectorsize, ft.lsectorsize);
+                       cfg->sectorsize, ft->lsectorsize);
+               usage();
+       }
+}
+
+static void
+validate_blocksize(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli,
+       struct mkfs_default_params *dft)
+{
+       /*
+        * Blocksize and sectorsize first, other things depend on them
+        * For RAID4/5/6 we want to align sector size and block size,
+        * so we need to start with the device geometry extraction too.
+        */
+       if (!cli->blocksize)
+               cfg->blocksize = dft->blocksize;
+       else
+               cfg->blocksize = cli->blocksize;
+       cfg->blocklog = libxfs_highbit32(cfg->blocksize);
+
+       /* validate block sizes are in range */
+       if (cfg->blocksize < XFS_MIN_BLOCKSIZE ||
+           cfg->blocksize > XFS_MAX_BLOCKSIZE) {
+               fprintf(stderr, _("illegal block size %d\n"), cfg->blocksize);
+               usage();
+       }
+
+       if (cli->sb_feat.crcs_enabled &&
+           cfg->blocksize < XFS_MIN_CRC_BLOCKSIZE) {
+               fprintf(stderr,
+_("Minimum block size for CRC enabled filesystems is %d bytes.\n"),
+                       XFS_MIN_CRC_BLOCKSIZE);
+               usage();
+       }
+
+}
+
+/*
+ * Grab log sector size and validate.
+ *
+ * XXX: should we probe sector size on external log device rather than using
+ * the data device sector size?
+ */
+static void
+validate_log_sectorsize(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli,
+       struct mkfs_default_params *dft)
+{
+
+       if (cli->loginternal && cli->lsectorsize &&
+           cli->lsectorsize != cfg->sectorsize) {
+               fprintf(stderr,
+_("Can't change sector size on internal log!\n"));
                usage();
        }
-       if (lsectorsize < XFS_MIN_SECTORSIZE ||
-           lsectorsize > XFS_MAX_SECTORSIZE || lsectorsize > blocksize) {
-               fprintf(stderr, _("illegal log sector size %d\n"), lsectorsize);
+
+       if (cli->lsectorsize)
+               cfg->lsectorsize = cli->lsectorsize;
+       else if (cli->loginternal)
+               cfg->lsectorsize = cfg->sectorsize;
+       else
+               cfg->lsectorsize = dft->sectorsize;
+       cfg->lsectorlog = libxfs_highbit32(cfg->lsectorsize);
+
+       if (cfg->lsectorsize < XFS_MIN_SECTORSIZE ||
+           cfg->lsectorsize > XFS_MAX_SECTORSIZE ||
+           cfg->lsectorsize > cfg->blocksize) {
+               fprintf(stderr, _("illegal log sector size %d\n"),
+                       cfg->lsectorsize);
                usage();
-       } else if (lsectorsize > XFS_MIN_SECTORSIZE && !lsu && !lsunit) {
-               lsu = blocksize;
-               sb_feat.log_version = 2;
        }
+       if (cfg->lsectorsize > XFS_MIN_SECTORSIZE) {
+               if (cli->sb_feat.log_version < 2) {
+                       /* user specified non-default log version */
+                       fprintf(stderr,
+_("Version 1 logs do not support sector size %d\n"),
+                               cfg->lsectorsize);
+                       usage();
+               }
+       }
+
+       /* if lsu or lsunit was specified, automatically use v2 logs */
+       if ((cli_opt_set(&lopts, L_SU) || cli_opt_set(&lopts, L_SUNIT)) &&
+           cli->sb_feat.log_version == 1) {
+               fprintf(stderr,
+_("log stripe unit specified, using v2 logs\n"));
+               cli->sb_feat.log_version = 2;
+       }
+
+}
 
+/*
+ * Check that the incoming features make sense. The CLI structure was
+ * initialised with the default values before parsing, so we can just
+ * check it and copy it straight across to the cfg structure if it
+ * checks out.
+ */
+static void
+validate_sb_features(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli)
+{
        /*
         * Now we have blocks and sector sizes set up, check parameters that are
         * no longer optional for CRC enabled filesystems.  Catch them up front
         * here before doing anything else.
         */
-       if (sb_feat.crcs_enabled) {
-               /* minimum inode size is 512 bytes, ipflag checked later */
-               if ((isflag || ilflag) && inodelog < XFS_DINODE_DFL_CRC_LOG) {
+       if (cli->sb_feat.crcs_enabled) {
+               /* minimum inode size is 512 bytes, rest checked later */
+               if (cli->inodesize &&
+                   cli->inodesize < (1 << XFS_DINODE_DFL_CRC_LOG)) {
                        fprintf(stderr,
 _("Minimum inode size for CRCs is %d bytes\n"),
                                1 << XFS_DINODE_DFL_CRC_LOG);
@@ -2067,40 +1931,48 @@ _("Minimum inode size for CRCs is %d bytes\n"),
                }
 
                /* inodes always aligned */
-               if (!sb_feat.inode_align) {
+               if (!cli->sb_feat.inode_align) {
                        fprintf(stderr,
-_("Inodes always aligned for CRC enabled filesytems\n"));
+_("Inodes always aligned for CRC enabled filesystems\n"));
                        usage();
                }
 
                /* lazy sb counters always on */
-               if (!sb_feat.lazy_sb_counters) {
+               if (!cli->sb_feat.lazy_sb_counters) {
                        fprintf(stderr,
-_("Lazy superblock counted always enabled for CRC enabled filesytems\n"));
+_("Lazy superblock counters always enabled for CRC enabled filesystems\n"));
                        usage();
                }
 
                /* version 2 logs always on */
-               if (sb_feat.log_version != 2) {
+               if (cli->sb_feat.log_version != 2) {
                        fprintf(stderr,
-_("V2 logs always enabled for CRC enabled filesytems\n"));
+_("V2 logs always enabled for CRC enabled filesystems\n"));
                        usage();
                }
 
                /* attr2 always on */
-               if (sb_feat.attr_version != 2) {
+               if (cli->sb_feat.attr_version != 2) {
                        fprintf(stderr,
-_("V2 attribute format always enabled on CRC enabled filesytems\n"));
+_("V2 attribute format always enabled on CRC enabled filesystems\n"));
                        usage();
                }
 
                /* 32 bit project quota always on */
                /* attr2 always on */
-               if (sb_feat.projid16bit) {
+               if (!cli->sb_feat.projid32bit) {
+                       fprintf(stderr,
+_("32 bit Project IDs always enabled on CRC enabled filesystems\n"));
+                       usage();
+               }
+
+               /* ftype always on */
+               if (!cli->sb_feat.dirftype) {
                        fprintf(stderr,
-_("32 bit Project IDs always enabled on CRC enabled filesytems\n"));
+_("Directory ftype field always enabled on CRC enabled filesystems\n"));
                        usage();
                }
+
        } else {
                /*
                 * The kernel doesn't currently support crc=0,finobt=1
@@ -2111,209 +1983,429 @@ _("32 bit Project IDs always enabled on CRC enabled filesytems\n"));
                 * then issue an error.
                 * The same is also for sparse inodes.
                 */
-               if (sb_feat.finobt && mopts.subopt_params[M_FINOBT].seen) {
+               if (cli->sb_feat.finobt && cli_opt_set(&mopts, M_FINOBT)) {
                        fprintf(stderr,
 _("finobt not supported without CRC support\n"));
                        usage();
                }
-               sb_feat.finobt = 0;
+               cli->sb_feat.finobt = false;
 
-               if (sb_feat.spinodes) {
+               if (cli->sb_feat.spinodes && cli_opt_set(&iopts, I_SPINODES)) {
                        fprintf(stderr,
 _("sparse inodes not supported without CRC support\n"));
                        usage();
                }
-               sb_feat.spinodes = 0;
+               cli->sb_feat.spinodes = false;
 
-               if (sb_feat.rmapbt) {
+               if (cli->sb_feat.rmapbt) {
                        fprintf(stderr,
 _("rmapbt not supported without CRC support\n"));
                        usage();
                }
-               sb_feat.rmapbt = false;
+               cli->sb_feat.rmapbt = false;
 
-               if (sb_feat.reflink) {
+               if (cli->sb_feat.reflink) {
                        fprintf(stderr,
 _("reflink not supported without CRC support\n"));
                        usage();
                }
-               sb_feat.reflink = false;
+               cli->sb_feat.reflink = false;
+       }
+
+       if ((cli->fsx.fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
+           !cli->sb_feat.reflink) {
+               fprintf(stderr,
+_("cowextsize not supported without reflink support\n"));
+               usage();
        }
 
+       if (cli->sb_feat.reflink && cli->xi->rtname) {
+               fprintf(stderr,
+_("reflink not supported with realtime devices\n"));
+               usage();
+               cli->sb_feat.reflink = false;
+       }
 
-       if (sb_feat.rmapbt && xi.rtname) {
+       if (cli->sb_feat.rmapbt && cli->xi->rtname) {
                fprintf(stderr,
 _("rmapbt not supported with realtime devices\n"));
                usage();
-               sb_feat.rmapbt = false;
+               cli->sb_feat.rmapbt = false;
        }
 
-       if (nsflag || nlflag) {
-               if (dirblocksize < blocksize ||
-                                       dirblocksize > XFS_MAX_BLOCKSIZE) {
+       /*
+        * Copy features across to config structure now.
+        */
+       cfg->sb_feat = cli->sb_feat;
+       if (!platform_uuid_is_null(&cli->uuid))
+               platform_uuid_copy(&cfg->uuid, &cli->uuid);
+}
+
+static void
+validate_dirblocksize(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli)
+{
+
+       if (cli->dirblocksize)
+               cfg->dirblocksize = getnum(cli->dirblocksize, &nopts, N_SIZE);
+
+       if (cfg->dirblocksize) {
+               if (cfg->dirblocksize < cfg->blocksize ||
+                   cfg->dirblocksize > XFS_MAX_BLOCKSIZE) {
                        fprintf(stderr, _("illegal directory block size %d\n"),
-                               dirblocksize);
+                               cfg->dirblocksize);
                        usage();
                }
-       } else {
-               if (blocksize < (1 << XFS_MIN_REC_DIRSIZE))
-                       dirblocklog = XFS_MIN_REC_DIRSIZE;
-               else
-                       dirblocklog = blocklog;
-               dirblocksize = 1 << dirblocklog;
+               cfg->dirblocklog = libxfs_highbit32(cfg->dirblocksize);
+               return;
        }
 
+       /* use default size based on current block size */
+       if (cfg->blocksize < (1 << XFS_MIN_REC_DIRSIZE))
+               cfg->dirblocklog = XFS_MIN_REC_DIRSIZE;
+       else
+               cfg->dirblocklog = cfg->blocklog;
+       cfg->dirblocksize = 1 << cfg->dirblocklog;
+}
 
-       if (dsize) {
-               __uint64_t dbytes;
+static void
+validate_inodesize(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli)
+{
 
-               dbytes = getnum(dsize, &dopts, D_SIZE);
-               if (dbytes % XFS_MIN_BLOCKSIZE) {
-                       fprintf(stderr,
-                       _("illegal data length %lld, not a multiple of %d\n"),
-                               (long long)dbytes, XFS_MIN_BLOCKSIZE);
-                       usage();
-               }
-               dblocks = (xfs_rfsblock_t)(dbytes >> blocklog);
-               if (dbytes % blocksize)
-                       fprintf(stderr, _("warning: "
-       "data length %lld not a multiple of %d, truncated to %lld\n"),
-                               (long long)dbytes, blocksize,
-                               (long long)(dblocks << blocklog));
-       }
-       if (ipflag) {
-               inodelog = blocklog - libxfs_highbit32(inopblock);
-               isize = 1 << inodelog;
-       } else if (!ilflag && !isflag) {
-               inodelog = sb_feat.crcs_enabled ? XFS_DINODE_DFL_CRC_LOG
-                                               : XFS_DINODE_DFL_LOG;
-               isize = 1 << inodelog;
-       }
-       if (sb_feat.crcs_enabled && inodelog < XFS_DINODE_DFL_CRC_LOG) {
+       if (cli->inopblock)
+               cfg->inodelog = cfg->blocklog - libxfs_highbit32(cli->inopblock);
+       else if (cli->inodesize)
+               cfg->inodelog = libxfs_highbit32(cli->inodesize);
+       else if (cfg->sb_feat.crcs_enabled)
+               cfg->inodelog = XFS_DINODE_DFL_CRC_LOG;
+       else
+               cfg->inodelog = XFS_DINODE_DFL_LOG;
+
+       cfg->inodesize = 1 << cfg->inodelog;
+       cfg->inopblock = cfg->blocksize / cfg->inodesize;
+
+       /* input parsing has already validated non-crc inode size range */
+       if (cfg->sb_feat.crcs_enabled &&
+           cfg->inodelog < XFS_DINODE_DFL_CRC_LOG) {
                fprintf(stderr,
                _("Minimum inode size for CRCs is %d bytes\n"),
                        1 << XFS_DINODE_DFL_CRC_LOG);
                usage();
        }
 
-       if (logsize) {
-               __uint64_t logbytes;
+       if (cfg->inodesize > cfg->blocksize / XFS_MIN_INODE_PERBLOCK ||
+           cfg->inopblock < XFS_MIN_INODE_PERBLOCK ||
+           cfg->inodesize < XFS_DINODE_MIN_SIZE ||
+           cfg->inodesize > XFS_DINODE_MAX_SIZE) {
+               int     maxsz;
 
-               logbytes = getnum(logsize, &lopts, L_SIZE);
-               if (logbytes % XFS_MIN_BLOCKSIZE) {
+               fprintf(stderr, _("illegal inode size %d\n"), cfg->inodesize);
+               maxsz = min(cfg->blocksize / XFS_MIN_INODE_PERBLOCK,
+                           XFS_DINODE_MAX_SIZE);
+               if (XFS_DINODE_MIN_SIZE == maxsz)
                        fprintf(stderr,
-                       _("illegal log length %lld, not a multiple of %d\n"),
-                               (long long)logbytes, XFS_MIN_BLOCKSIZE);
-                       usage();
-               }
-               logblocks = (xfs_rfsblock_t)(logbytes >> blocklog);
-               if (logbytes % blocksize)
+                       _("allowable inode size with %d byte blocks is %d\n"),
+                               cfg->blocksize, XFS_DINODE_MIN_SIZE);
+               else
                        fprintf(stderr,
-       _("warning: log length %lld not a multiple of %d, truncated to %lld\n"),
-                               (long long)logbytes, blocksize,
-                               (long long)(logblocks << blocklog));
+       _("allowable inode size with %d byte blocks is between %d and %d\n"),
+                               cfg->blocksize, XFS_DINODE_MIN_SIZE, maxsz);
+               exit(1);
        }
-       if (rtsize) {
-               __uint64_t rtbytes;
+}
 
-               rtbytes = getnum(rtsize, &ropts, R_SIZE);
-               if (rtbytes % XFS_MIN_BLOCKSIZE) {
-                       fprintf(stderr,
-                       _("illegal rt length %lld, not a multiple of %d\n"),
-                               (long long)rtbytes, XFS_MIN_BLOCKSIZE);
-                       usage();
-               }
-               rtblocks = (xfs_rfsblock_t)(rtbytes >> blocklog);
-               if (rtbytes % blocksize)
-                       fprintf(stderr,
-       _("warning: rt length %lld not a multiple of %d, truncated to %lld\n"),
-                               (long long)rtbytes, blocksize,
-                               (long long)(rtblocks << blocklog));
+static xfs_rfsblock_t
+calc_dev_size(
+       char                    *size,
+       struct mkfs_params      *cfg,
+       struct opt_params       *opts,
+       int                     sizeopt,
+       char                    *type)
+{
+       uint64_t                dbytes;
+       xfs_rfsblock_t          dblocks;
+
+       if (!size)
+               return 0;
+
+       dbytes = getnum(size, opts, sizeopt);
+       if (dbytes % XFS_MIN_BLOCKSIZE) {
+               fprintf(stderr,
+               _("illegal %s length %lld, not a multiple of %d\n"),
+                       type, (long long)dbytes, XFS_MIN_BLOCKSIZE);
+               usage();
+       }
+       dblocks = (xfs_rfsblock_t)(dbytes >> cfg->blocklog);
+       if (dbytes % cfg->blocksize) {
+               fprintf(stderr,
+_("warning: %s length %lld not a multiple of %d, truncated to %lld\n"),
+                       type, (long long)dbytes, cfg->blocksize,
+                       (long long)(dblocks << cfg->blocklog));
        }
+       return dblocks;
+}
+
+static void
+validate_rtextsize(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli,
+       struct fs_topology      *ft)
+{
+       uint64_t                rtextbytes;
+
        /*
         * If specified, check rt extent size against its constraints.
         */
-       if (rtextsize) {
-               __uint64_t rtextbytes;
+       if (cli->rtextsize) {
 
-               rtextbytes = getnum(rtextsize, &ropts, R_EXTSIZE);
-               if (rtextbytes % blocksize) {
+               rtextbytes = getnum(cli->rtextsize, &ropts, R_EXTSIZE);
+               if (rtextbytes % cfg->blocksize) {
                        fprintf(stderr,
                _("illegal rt extent size %lld, not a multiple of %d\n"),
-                               (long long)rtextbytes, blocksize);
+                               (long long)rtextbytes, cfg->blocksize);
                        usage();
                }
-               rtextblocks = (xfs_extlen_t)(rtextbytes >> blocklog);
+               cfg->rtextblocks = (xfs_extlen_t)(rtextbytes >> cfg->blocklog);
        } else {
                /*
                 * If realtime extsize has not been specified by the user,
                 * and the underlying volume is striped, then set rtextblocks
                 * to the stripe width.
                 */
-               __uint64_t      rswidth;
-               __uint64_t      rtextbytes;
+               uint64_t        rswidth;
 
-               if (!norsflag && !xi.risfile && !(!rtsize && xi.disfile))
-                       rswidth = ft.rtswidth;
+               if (!cfg->sb_feat.nortalign && !cli->xi->risfile &&
+                   !(!cli->rtsize && cli->xi->disfile))
+                       rswidth = ft->rtswidth;
                else
                        rswidth = 0;
 
                /* check that rswidth is a multiple of fs blocksize */
-               if (!norsflag && rswidth && !(BBTOB(rswidth) % blocksize)) {
-                       rswidth = DTOBT(rswidth);
-                       rtextbytes = rswidth << blocklog;
-                       if (XFS_MIN_RTEXTSIZE <= rtextbytes &&
-                           (rtextbytes <= XFS_MAX_RTEXTSIZE)) {
-                               rtextblocks = rswidth;
+               if (!cfg->sb_feat.nortalign && rswidth &&
+                   !(BBTOB(rswidth) % cfg->blocksize)) {
+                       rswidth = DTOBT(rswidth, cfg->blocklog);
+                       rtextbytes = rswidth << cfg->blocklog;
+                       if (rtextbytes > XFS_MIN_RTEXTSIZE &&
+                           rtextbytes <= XFS_MAX_RTEXTSIZE) {
+                               cfg->rtextblocks = rswidth;
                        }
                }
-               if (!rtextblocks) {
-                       rtextblocks = (blocksize < XFS_MIN_RTEXTSIZE) ?
-                                       XFS_MIN_RTEXTSIZE >> blocklog : 1;
+               if (!cfg->rtextblocks) {
+                       cfg->rtextblocks = (cfg->blocksize < XFS_MIN_RTEXTSIZE)
+                                       ? XFS_MIN_RTEXTSIZE >> cfg->blocklog
+                                       : 1;
                }
        }
-       ASSERT(rtextblocks);
+       ASSERT(cfg->rtextblocks);
+}
 
-       /*
-        * Check some argument sizes against mins, maxes.
-        */
-       if (isize > blocksize / XFS_MIN_INODE_PERBLOCK ||
-           isize < XFS_DINODE_MIN_SIZE ||
-           isize > XFS_DINODE_MAX_SIZE) {
-               int     maxsz;
+/*
+ * Validate the configured stripe geometry, or is none is specified, pull
+ * the configuration from the underlying device.
+ *
+ * CLI parameters come in as different units, go out as filesystem blocks.
+ */
+static void
+calc_stripe_factors(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli,
+       struct fs_topology      *ft)
+{
+       long long int   big_dswidth;
+       int             dsunit = 0;
+       int             dswidth = 0;
+       int             lsunit = 0;
+       int             dsu = 0;
+       int             dsw = 0;
+       int             lsu = 0;
+       bool            use_dev = false;
+
+       if (cli_opt_set(&dopts, D_SUNIT))
+               dsunit = cli->dsunit;
+       if (cli_opt_set(&dopts, D_SWIDTH))
+               dswidth = cli->dswidth;
+
+       if (cli_opt_set(&dopts, D_SU))
+               dsu = getnum(cli->dsu, &dopts, D_SU);
+       if (cli_opt_set(&dopts, D_SW))
+               dsw = cli->dsw;
+
+       /* data sunit/swidth options */
+       if (cli_opt_set(&dopts, D_SUNIT) != cli_opt_set(&dopts, D_SWIDTH)) {
+               fprintf(stderr,
+_("both data sunit and data swidth options must be specified\n"));
+               usage();
+       }
 
-               fprintf(stderr, _("illegal inode size %d\n"), isize);
-               maxsz = MIN(blocksize / XFS_MIN_INODE_PERBLOCK,
-                           XFS_DINODE_MAX_SIZE);
-               if (XFS_DINODE_MIN_SIZE == maxsz)
+       /* convert dsu/dsw to dsunit/dswidth and use them from now on */
+       if (dsu || dsw) {
+               if (cli_opt_set(&dopts, D_SU) != cli_opt_set(&dopts, D_SW)) {
                        fprintf(stderr,
-                       _("allowable inode size with %d byte blocks is %d\n"),
-                               blocksize, XFS_DINODE_MIN_SIZE);
-               else
+_("both data su and data sw options must be specified\n"));
+                       usage();
+               }
+
+               if (dsu % cfg->sectorsize) {
                        fprintf(stderr,
-       _("allowable inode size with %d byte blocks is between %d and %d\n"),
-                               blocksize, XFS_DINODE_MIN_SIZE, maxsz);
-               exit(1);
+_("data su must be a multiple of the sector size (%d)\n"), cfg->sectorsize);
+                       usage();
+               }
+
+               dsunit  = (int)BTOBBT(dsu);
+               big_dswidth = (long long int)dsunit * dsw;
+               if (big_dswidth > INT_MAX) {
+                       fprintf(stderr,
+_("data stripe width (%lld) is too large of a multiple of the data stripe unit (%d)\n"),
+                               big_dswidth, dsunit);
+                       usage();
+               }
+               dswidth = big_dswidth;
        }
 
-       /* if lsu or lsunit was specified, automatically use v2 logs */
-       if ((lsu || lsunit) && sb_feat.log_version == 1) {
+       if ((dsunit && !dswidth) || (!dsunit && dswidth) ||
+           (dsunit && (dswidth % dsunit != 0))) {
                fprintf(stderr,
-                       _("log stripe unit specified, using v2 logs\n"));
-               sb_feat.log_version = 2;
+_("data stripe width (%d) must be a multiple of the data stripe unit (%d)\n"),
+                       dswidth, dsunit);
+               usage();
        }
 
-       calc_stripe_factors(dsu, dsw, sectorsize, lsu, lsectorsize,
-                               &dsunit, &dswidth, &lsunit);
+       /* If sunit & swidth were manually specified as 0, same as noalign */
+       if ((cli_opt_set(&dopts, D_SUNIT) || cli_opt_set(&dopts, D_SU)) &&
+           !dsunit && !dswidth)
+               cfg->sb_feat.nodalign = true;
 
-       xi.setblksize = sectorsize;
+       /* if we are not using alignment, don't apply device defaults */
+       if (cfg->sb_feat.nodalign) {
+               cfg->dsunit = 0;
+               cfg->dswidth = 0;
+               goto check_lsunit;
+       }
+
+       /* if no stripe config set, use the device default */
+       if (!dsunit) {
+               /* Ignore nonsense from device.  XXX add more validation */
+               if (ft->dsunit && ft->dswidth == 0) {
+                       fprintf(stderr,
+_("%s: Volume reports stripe unit of %d bytes and stripe width of 0, ignoring.\n"),
+                               progname, BBTOB(ft->dsunit));
+                       ft->dsunit = 0;
+                       ft->dswidth = 0;
+               } else {
+                       dsunit = ft->dsunit;
+                       dswidth = ft->dswidth;
+                       use_dev = true;
+               }
+       } else {
+               /* check and warn if user-specified alignment is sub-optimal */
+               if (ft->dsunit && ft->dsunit != dsunit) {
+                       fprintf(stderr,
+_("%s: Specified data stripe unit %d is not the same as the volume stripe unit %d\n"),
+                               progname, dsunit, ft->dsunit);
+               }
+               if (ft->dswidth && ft->dswidth != dswidth) {
+                       fprintf(stderr,
+_("%s: Specified data stripe width %d is not the same as the volume stripe width %d\n"),
+                               progname, dswidth, ft->dswidth);
+               }
+       }
+
+       /*
+        * now we have our stripe config, check it's a multiple of block
+        * size.
+        */
+       if ((BBTOB(dsunit) % cfg->blocksize) ||
+           (BBTOB(dswidth) % cfg->blocksize)) {
+               /*
+                * If we are using device defaults, just clear them and we're
+                * good to go. Otherwise bail out with an error.
+                */
+               if (!use_dev) {
+                       fprintf(stderr,
+_("%s: Stripe unit(%d) or stripe width(%d) is not a multiple of the block size(%d)\n"),
+                               progname, BBTOB(dsunit), BBTOB(dswidth),
+                               cfg->blocksize);
+                       exit(1);
+               }
+               dsunit = 0;
+               dswidth = 0;
+               cfg->sb_feat.nodalign = true;
+       }
+
+       /* convert from 512 byte blocks to fs blocksize */
+       cfg->dsunit = DTOBT(dsunit, cfg->blocklog);
+       cfg->dswidth = DTOBT(dswidth, cfg->blocklog);
+
+check_lsunit:
+       /* log sunit options */
+       if (cli_opt_set(&lopts, L_SUNIT))
+               lsunit = cli->lsunit;
+       else if (cli_opt_set(&lopts, L_SU))
+               lsu = getnum(cli->lsu, &lopts, L_SU);
+       else if (cfg->lsectorsize > XLOG_HEADER_SIZE)
+               lsu = cfg->blocksize; /* lsunit matches filesystem block size */
+
+       if (lsu) {
+               /* verify if lsu is a multiple block size */
+               if (lsu % cfg->blocksize != 0) {
+                       fprintf(stderr,
+       _("log stripe unit (%d) must be a multiple of the block size (%d)\n"),
+                               lsu, cfg->blocksize);
+                       usage();
+               }
+               lsunit = (int)BTOBBT(lsu);
+       }
+       if (BBTOB(lsunit) % cfg->blocksize != 0) {
+               fprintf(stderr,
+_("log stripe unit (%d) must be a multiple of the block size (%d)\n"),
+                       BBTOB(lsunit), cfg->blocksize);
+               usage();
+       }
+
+       /*
+        * check that log sunit is modulo fsblksize or default it to dsunit.
+        */
+       if (lsunit) {
+               /* convert from 512 byte blocks to fs blocks */
+               cfg->lsunit = DTOBT(lsunit, cfg->blocklog);
+       } else if (cfg->sb_feat.log_version == 2 &&
+                  cfg->loginternal && cfg->dsunit) {
+               /* lsunit and dsunit now in fs blocks */
+               cfg->lsunit = cfg->dsunit;
+       }
+
+       if (cfg->sb_feat.log_version == 2 &&
+           cfg->lsunit * cfg->blocksize > 256 * 1024) {
+               /* Warn only if specified on commandline */
+               if (cli->lsu || cli->lsunit != -1) {
+                       fprintf(stderr,
+_("log stripe unit (%d bytes) is too large (maximum is 256KiB)\n"
+  "log stripe unit adjusted to 32KiB\n"),
+                               (cfg->lsunit * cfg->blocksize));
+               }
+               /* XXX: 64k block size? */
+               cfg->lsunit = (32 * 1024) / cfg->blocksize;
+       }
+
+}
+
+static void
+open_devices(
+       struct mkfs_params      *cfg,
+       struct libxfs_xinit     *xi)
+{
+       uint64_t                sector_mask;
 
        /*
         * Initialize.  This will open the log and rt devices as well.
         */
-       if (!libxfs_init(&xi))
+       xi->setblksize = cfg->sectorsize;
+       if (!libxfs_init(xi))
                usage();
-       if (!xi.ddev) {
+       if (!xi->ddev) {
                fprintf(stderr, _("no device name given in argument list\n"));
                usage();
        }
@@ -2328,988 +2420,1590 @@ _("rmapbt not supported with realtime devices\n"));
         * So, we reduce the size (in basic blocks) to a perfect
         * multiple of the sector size, or 1024, whichever is larger.
         */
+       sector_mask = (uint64_t)-1 << (max(cfg->sectorlog, 10) - BBSHIFT);
+       xi->dsize &= sector_mask;
+       xi->rtsize &= sector_mask;
+       xi->logBBsize &= (uint64_t)-1 << (max(cfg->lsectorlog, 10) - BBSHIFT);
+}
 
-       sector_mask = (__uint64_t)-1 << (MAX(sectorlog, 10) - BBSHIFT);
-       xi.dsize &= sector_mask;
-       xi.rtsize &= sector_mask;
-       xi.logBBsize &= (__uint64_t)-1 << (MAX(lsectorlog, 10) - BBSHIFT);
-
-
-       /* don't do discards on print-only runs or on files */
-       if (discard && !Nflag) {
-               if (!xi.disfile)
-                       discard_blocks(xi.ddev, xi.dsize);
-               if (xi.rtdev && !xi.risfile)
-                       discard_blocks(xi.rtdev, xi.rtsize);
-               if (xi.logdev && xi.logdev != xi.ddev && !xi.lisfile)
-                       discard_blocks(xi.logdev, xi.logBBsize);
-       }
-
-       if (!liflag && !ldflag)
-               loginternal = xi.logdev == 0;
-       if (xi.logname)
-               logfile = xi.logname;
-       else if (loginternal)
-               logfile = _("internal log");
-       else if (xi.volname && xi.logdev)
-               logfile = _("volume log");
-       else if (!ldflag) {
-               fprintf(stderr, _("no log subvolume or internal log\n"));
-               usage();
+static void
+discard_devices(
+       struct libxfs_xinit     *xi)
+{
+       /*
+        * This function has to be called after libxfs has been initialized.
+        */
+
+       if (!xi->disfile)
+               discard_blocks(xi->ddev, xi->dsize);
+       if (xi->rtdev && !xi->risfile)
+               discard_blocks(xi->rtdev, xi->rtsize);
+       if (xi->logdev && xi->logdev != xi->ddev && !xi->lisfile)
+               discard_blocks(xi->logdev, xi->logBBsize);
+}
+
+static void
+validate_datadev(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli)
+{
+       struct libxfs_xinit     *xi = cli->xi;
+
+       if (!xi->dsize) {
+               /*
+                * if the device is a file, we can't validate the size here.
+                * Instead, the file will be truncated to the correct length
+                * later on. if it's not a file, we've got a dud device.
+                */
+               if (!xi->disfile) {
+                       fprintf(stderr, _("can't get size of data subvolume\n"));
+                       usage();
+               }
+               ASSERT(cfg->dblocks);
+       } else if (cfg->dblocks) {
+               /* check the size fits into the underlying device */
+               if (cfg->dblocks > DTOBT(xi->dsize, cfg->blocklog)) {
+                       fprintf(stderr,
+_("size %s specified for data subvolume is too large, maximum is %lld blocks\n"),
+                               cli->dsize,
+                               (long long)DTOBT(xi->dsize, cfg->blocklog));
+                       usage();
+               }
+       } else {
+               /* no user size, so use the full block device */
+               cfg->dblocks = DTOBT(xi->dsize, cfg->blocklog);
        }
-       if (xi.rtname)
-               rtfile = xi.rtname;
-       else
-       if (xi.volname && xi.rtdev)
-               rtfile = _("volume rt");
-       else if (!xi.rtdev)
-               rtfile = _("none");
-       if (dsize && xi.dsize > 0 && dblocks > DTOBT(xi.dsize)) {
+
+       if (cfg->dblocks < XFS_MIN_DATA_BLOCKS) {
                fprintf(stderr,
-                       _("size %s specified for data subvolume is too large, "
-                       "maximum is %lld blocks\n"),
-                       dsize, (long long)DTOBT(xi.dsize));
-               usage();
-       } else if (!dsize && xi.dsize > 0)
-               dblocks = DTOBT(xi.dsize);
-       else if (!dsize) {
-               fprintf(stderr, _("can't get size of data subvolume\n"));
+_("size %lld of data subvolume is too small, minimum %d blocks\n"),
+                       (long long)cfg->dblocks, XFS_MIN_DATA_BLOCKS);
                usage();
        }
-       if (dblocks < XFS_MIN_DATA_BLOCKS) {
-               fprintf(stderr,
-       _("size %lld of data subvolume is too small, minimum %d blocks\n"),
-                       (long long)dblocks, XFS_MIN_DATA_BLOCKS);
-               usage();
+
+       if (xi->dbsize > cfg->sectorsize) {
+               fprintf(stderr, _(
+"Warning: the data subvolume sector size %u is less than the sector size \n\
+reported by the device (%u).\n"),
+                       cfg->sectorsize, xi->dbsize);
        }
+}
 
-       if (loginternal && xi.logdev) {
-               fprintf(stderr,
-                       _("can't have both external and internal logs\n"));
+/*
+ * This is more complex than it needs to be because we still support volume
+ * based external logs. They are only discovered *after* the devices have been
+ * opened, hence the crazy "is this really an internal log" checks here.
+ */
+static void
+validate_logdev(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli,
+       char                    **devname)
+{
+       struct libxfs_xinit     *xi = cli->xi;
+
+       *devname = NULL;
+
+       /* check for volume log first */
+       if (cli->loginternal && xi->volname && xi->logdev) {
+               *devname = _("volume log");
+               cfg->loginternal = false;
+       } else
+               cfg->loginternal = cli->loginternal;
+
+       /* now run device checks */
+       if (cfg->loginternal) {
+               if (xi->logdev) {
+                       fprintf(stderr,
+_("can't have both external and internal logs\n"));
+                       usage();
+               }
+
+               /*
+                * if no sector size has been specified on the command line,
+                * use what has been configured and validated for the data
+                * device.
+                */
+               if (!cli->lsectorsize) {
+                       cfg->lsectorsize = cfg->sectorsize;
+                       cfg->lsectorlog = cfg->sectorlog;
+               }
+
+               if (cfg->sectorsize != cfg->lsectorsize) {
+                       fprintf(stderr,
+_("data and log sector sizes must be equal for internal logs\n"));
+                       usage();
+               }
+               if (cli->logsize && cfg->logblocks >= cfg->dblocks) {
+                       fprintf(stderr,
+_("log size %lld too large for internal log\n"),
+                               (long long)cfg->logblocks);
+                       usage();
+               }
+               *devname = _("internal log");
+               return;
+       }
+
+       /* External/log subvolume checks */
+       if (xi->logname)
+               *devname = xi->logname;
+       if (!*devname || !xi->logdev) {
+               fprintf(stderr, _("no log subvolume or external log.\n"));
                usage();
-       } else if (loginternal && sectorsize != lsectorsize) {
+       }
+
+       if (!cfg->logblocks) {
+               if (xi->logBBsize == 0) {
+                       fprintf(stderr,
+_("unable to get size of the log subvolume.\n"));
+                       usage();
+               }
+               cfg->logblocks = DTOBT(xi->logBBsize, cfg->blocklog);
+       } else if (cfg->logblocks > DTOBT(xi->logBBsize, cfg->blocklog)) {
                fprintf(stderr,
-       _("data and log sector sizes must be equal for internal logs\n"));
+_("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
+                       cli->logsize,
+                       (long long)DTOBT(xi->logBBsize, cfg->blocklog));
                usage();
        }
 
-       if (xi.dbsize > sectorsize) {
-               fprintf(stderr, _(
-"Warning: the data subvolume sector size %u is less than the sector size \n\
-reported by the device (%u).\n"),
-                       sectorsize, xi.dbsize);
-       }
-       if (!loginternal && xi.lbsize > lsectorsize) {
+       if (xi->lbsize > cfg->lsectorsize) {
                fprintf(stderr, _(
 "Warning: the log subvolume sector size %u is less than the sector size\n\
 reported by the device (%u).\n"),
-                       lsectorsize, xi.lbsize);
-       }
-       if (rtsize && xi.rtsize > 0 && xi.rtbsize > sectorsize) {
-               fprintf(stderr, _(
-"Warning: the realtime subvolume sector size %u is less than the sector size\n\
-reported by the device (%u).\n"),
-                       sectorsize, xi.rtbsize);
+                       cfg->lsectorsize, xi->lbsize);
        }
+}
 
-       if (rtsize && xi.rtsize > 0 && rtblocks > DTOBT(xi.rtsize)) {
-               fprintf(stderr,
-                       _("size %s specified for rt subvolume is too large, "
-                       "maximum is %lld blocks\n"),
-                       rtsize, (long long)DTOBT(xi.rtsize));
-               usage();
-       } else if (!rtsize && xi.rtsize > 0)
-               rtblocks = DTOBT(xi.rtsize);
-       else if (rtsize && !xi.rtdev) {
-               fprintf(stderr,
-                       _("size specified for non-existent rt subvolume\n"));
+static void
+validate_rtdev(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli,
+       char                    **devname)
+{
+       struct libxfs_xinit     *xi = cli->xi;
+
+       *devname = NULL;
+
+       if (!xi->rtdev) {
+               if (cli->rtsize) {
+                       fprintf(stderr,
+_("size specified for non-existent rt subvolume\n"));
+                       usage();
+               }
+
+               *devname = _("none");
+               cfg->rtblocks = 0;
+               cfg->rtextents = 0;
+               cfg->rtbmblocks = 0;
+               return;
+       }
+       if (!xi->rtsize) {
+               fprintf(stderr, _("Invalid zero length rt subvolume found\n"));
                usage();
        }
-       if (xi.rtdev) {
-               rtextents = rtblocks / rtextblocks;
-               nbmblocks = (xfs_extlen_t)howmany(rtextents, NBBY * blocksize);
-       } else {
-               rtextents = rtblocks = 0;
-               nbmblocks = 0;
-       }
-
-       if (!nodsflag) {
-               if (dsunit) {
-                       if (ft.dsunit && ft.dsunit != dsunit) {
-                               fprintf(stderr,
-                                       _("%s: Specified data stripe unit %d "
-                                       "is not the same as the volume stripe "
-                                       "unit %d\n"),
-                                       progname, dsunit, ft.dsunit);
-                       }
-                       if (ft.dswidth && ft.dswidth != dswidth) {
-                               fprintf(stderr,
-                                       _("%s: Specified data stripe width %d "
-                                       "is not the same as the volume stripe "
-                                       "width %d\n"),
-                                       progname, dswidth, ft.dswidth);
-                       }
-               } else {
-                       dsunit = ft.dsunit;
-                       dswidth = ft.dswidth;
-                       nodsflag = 1;
+
+       /* volume rtdev */
+       if (xi->volname)
+               *devname = _("volume rt");
+       else
+               *devname = xi->rtname;
+
+       if (cli->rtsize) {
+               if (cfg->rtblocks > DTOBT(xi->rtsize, cfg->blocklog)) {
+                       fprintf(stderr,
+_("size %s specified for rt subvolume is too large, maxi->um is %lld blocks\n"),
+                               cli->rtsize,
+                               (long long)DTOBT(xi->rtsize, cfg->blocklog));
+                       usage();
+               }
+               if (xi->rtbsize > cfg->sectorsize) {
+                       fprintf(stderr, _(
+"Warning: the realtime subvolume sector size %u is less than the sector size\n\
+reported by the device (%u).\n"),
+                               cfg->sectorsize, xi->rtbsize);
                }
-       } /* else dsunit & dswidth can't be set if nodsflag is set */
+       } else {
+               /* grab volume size */
+               cfg->rtblocks = DTOBT(xi->rtsize, cfg->blocklog);
+       }
+
+       cfg->rtextents = cfg->rtblocks / cfg->rtextblocks;
+       cfg->rtbmblocks = (xfs_extlen_t)howmany(cfg->rtextents,
+                                               NBBY * cfg->blocksize);
+}
+
+static void
+calculate_initial_ag_geometry(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli)
+{
+       if (cli->agsize) {              /* User-specified AG size */
+               cfg->agsize = getnum(cli->agsize, &dopts, D_AGSIZE);
 
-       if (dasize) {           /* User-specified AG size */
                /*
                 * Check specified agsize is a multiple of blocksize.
                 */
-               if (agsize % blocksize) {
+               if (cfg->agsize % cfg->blocksize) {
                        fprintf(stderr,
-               _("agsize (%lld) not a multiple of fs blk size (%d)\n"),
-                               (long long)agsize, blocksize);
+_("agsize (%s) not a multiple of fs blk size (%d)\n"),
+                               cli->agsize, cfg->blocksize);
                        usage();
                }
-               agsize /= blocksize;
-               agcount = dblocks / agsize + (dblocks % agsize != 0);
-
-       } else if (daflag) {    /* User-specified AG count */
-               agsize = dblocks / agcount + (dblocks % agcount != 0);
+               cfg->agsize /= cfg->blocksize;
+               cfg->agcount = cfg->dblocks / cfg->agsize +
+                               (cfg->dblocks % cfg->agsize != 0);
+
+       } else if (cli->agcount) {      /* User-specified AG count */
+               cfg->agcount = cli->agcount;
+               cfg->agsize = cfg->dblocks / cfg->agcount +
+                               (cfg->dblocks % cfg->agcount != 0);
        } else {
-               calc_default_ag_geometry(blocklog, dblocks,
-                               dsunit | dswidth, &agsize, &agcount);
+               calc_default_ag_geometry(cfg->blocklog, cfg->dblocks,
+                                        cfg->dsunit, &cfg->agsize,
+                                        &cfg->agcount);
        }
+}
 
-       /*
-        * If dsunit is a multiple of fs blocksize, then check that is a
-        * multiple of the agsize too
-        */
-       if (dsunit && !(BBTOB(dsunit) % blocksize) &&
-           dswidth && !(BBTOB(dswidth) % blocksize)) {
+/*
+ * Align the AG size to stripe geometry. If this fails and we are using
+ * discovered stripe geometry, tell the caller to clear the stripe geometry.
+ * Otherwise, set the aligned geometry (valid or invalid!) so that the
+ * validation call will fail and exit.
+ */
+static void
+align_ag_geometry(
+       struct mkfs_params      *cfg)
+{
+       uint64_t        tmp_agsize;
+       int             dsunit = cfg->dsunit;
 
-               /* convert from 512 byte blocks to fs blocksize */
-               dsunit = DTOBT(dsunit);
-               dswidth = DTOBT(dswidth);
+       if (!dsunit)
+               goto validate;
 
+       /*
+        * agsize is not a multiple of dsunit
+        */
+       if ((cfg->agsize % dsunit) != 0) {
                /*
-                * agsize is not a multiple of dsunit
+                * Round up to stripe unit boundary. Also make sure
+                * that agsize is still larger than
+                * XFS_AG_MIN_BLOCKS(blocklog)
                 */
-               if ((agsize % dsunit) != 0) {
-                       /*
-                        * Round up to stripe unit boundary. Also make sure
-                        * that agsize is still larger than
-                        * XFS_AG_MIN_BLOCKS(blocklog)
-                        */
-                       tmp_agsize = ((agsize + (dsunit - 1))/ dsunit) * dsunit;
+               tmp_agsize = ((cfg->agsize + dsunit - 1) / dsunit) * dsunit;
+               /*
+                * Round down to stripe unit boundary if rounding up
+                * created an AG size that is larger than the AG max.
+                */
+               if (tmp_agsize > XFS_AG_MAX_BLOCKS(cfg->blocklog))
+                       tmp_agsize = (cfg->agsize / dsunit) * dsunit;
+
+               if (tmp_agsize < XFS_AG_MIN_BLOCKS(cfg->blocklog) &&
+                   tmp_agsize > XFS_AG_MAX_BLOCKS(cfg->blocklog)) {
+
                        /*
-                        * Round down to stripe unit boundary if rounding up
-                        * created an AG size that is larger than the AG max.
+                        * If the AG size is invalid and we are using device
+                        * probed stripe alignment, just clear the alignment
+                        * and continue on.
                         */
-                       if (tmp_agsize > XFS_AG_MAX_BLOCKS(blocklog))
-                               tmp_agsize = ((agsize) / dsunit) * dsunit;
-
-                       if ((tmp_agsize >= XFS_AG_MIN_BLOCKS(blocklog)) &&
-                           (tmp_agsize <= XFS_AG_MAX_BLOCKS(blocklog))) {
-                               agsize = tmp_agsize;
-                               if (!daflag)
-                                       agcount = dblocks/agsize +
-                                               (dblocks % agsize != 0);
-                               if (dasize)
-                                       fprintf(stderr,
-                               _("agsize rounded to %lld, swidth = %d\n"),
-                                               (long long)agsize, dswidth);
-                       } else {
-                               if (nodsflag) {
-                                       dsunit = dswidth = 0;
-                               } else {
-                                       /*
-                                        * agsize is out of bounds, this will
-                                        * print nice details & exit.
-                                        */
-                                       validate_ag_geometry(blocklog, dblocks,
-                                                           agsize, agcount);
-                                       exit(1);
-                               }
+                       if (!cli_opt_set(&dopts, D_SUNIT) &&
+                           !cli_opt_set(&dopts, D_SU)) {
+                               cfg->dsunit = 0;
+                               cfg->dswidth = 0;
+                               goto validate;
                        }
-               }
-               if (dswidth && ((agsize % dswidth) == 0) && (agcount > 1)) {
-                       /* This is a non-optimal configuration because all AGs
-                        * start on the same disk in the stripe.  Changing
-                        * the AG size by one sunit will guarantee that this
-                        * does not happen.
+                       /*
+                        * set the agsize to the invalid value so the following
+                        * validation of the ag will fail and print a nice error
+                        * and exit.
                         */
-                       tmp_agsize = agsize - dsunit;
-                       if (tmp_agsize < XFS_AG_MIN_BLOCKS(blocklog)) {
-                               tmp_agsize = agsize + dsunit;
-                               if (dblocks < agsize) {
-                                       /* oh well, nothing to do */
-                                       tmp_agsize = agsize;
-                               }
-                       }
-                       if (daflag || dasize) {
-                               fprintf(stderr, _(
+                       cfg->agsize = tmp_agsize;
+                       goto validate;
+               }
+
+               /* update geometry to be stripe unit aligned */
+               cfg->agsize = tmp_agsize;
+               if (!cli_opt_set(&dopts, D_AGCOUNT))
+                       cfg->agcount = cfg->dblocks / cfg->agsize +
+                                       (cfg->dblocks % cfg->agsize != 0);
+               if (cli_opt_set(&dopts, D_AGSIZE))
+                       fprintf(stderr,
+_("agsize rounded to %lld, sunit = %d\n"),
+                               (long long)cfg->agsize, dsunit);
+       }
+
+       if ((cfg->agsize % cfg->dswidth) == 0 &&
+           cfg->dswidth != cfg->dsunit &&
+           cfg->agcount > 1) {
+
+               if (cli_opt_set(&dopts, D_AGCOUNT) ||
+                   cli_opt_set(&dopts, D_AGSIZE)) {
+                       fprintf(stderr, _(
 "Warning: AG size is a multiple of stripe width.  This can cause performance\n\
 problems by aligning all AGs on the same disk.  To avoid this, run mkfs with\n\
-an AG size that is one stripe unit smaller, for example %llu.\n"),
-                                       (unsigned long long)tmp_agsize);
-                       } else {
-                               agsize = tmp_agsize;
-                               agcount = dblocks/agsize + (dblocks % agsize != 0);
-                               /*
-                                * If the last AG is too small, reduce the
-                                * filesystem size and drop the blocks.
-                                */
-                               if ( dblocks % agsize != 0 &&
-                                   (dblocks % agsize <
-                                   XFS_AG_MIN_BLOCKS(blocklog))) {
-                                       dblocks = (xfs_rfsblock_t)((agcount - 1) * agsize);
-                                       agcount--;
-                                       ASSERT(agcount != 0);
-                               }
-                       }
+an AG size that is one stripe unit smaller or larger, for example %llu.\n"),
+                               (unsigned long long)cfg->agsize - dsunit);
+                       goto validate;
                }
-       } else {
-               if (nodsflag)
-                       dsunit = dswidth = 0;
-               else {
-                       fprintf(stderr,
-                               _("%s: Stripe unit(%d) or stripe width(%d) is "
-                               "not a multiple of the block size(%d)\n"),
-                               progname, BBTOB(dsunit), BBTOB(dswidth),
-                               blocksize);
-                       exit(1);
+
+               /*
+                * This is a non-optimal configuration because all AGs start on
+                * the same disk in the stripe.  Changing the AG size by one
+                * sunit will guarantee that this does not happen.
+                */
+               tmp_agsize = cfg->agsize - dsunit;
+               if (tmp_agsize < XFS_AG_MIN_BLOCKS(cfg->blocklog)) {
+                       tmp_agsize = cfg->agsize + dsunit;
+                       if (cfg->dblocks < cfg->agsize) {
+                               /* oh well, nothing to do */
+                               tmp_agsize = cfg->agsize;
+                       }
                }
+
+               cfg->agsize = tmp_agsize;
+               cfg->agcount = cfg->dblocks / cfg->agsize +
+                               (cfg->dblocks % cfg->agsize != 0);
        }
 
+validate:
        /*
         * If the last AG is too small, reduce the filesystem size
         * and drop the blocks.
         */
-       if ( dblocks % agsize != 0 &&
-            (dblocks % agsize < XFS_AG_MIN_BLOCKS(blocklog))) {
-               ASSERT(!daflag);
-               dblocks = (xfs_rfsblock_t)((agcount - 1) * agsize);
-               agcount--;
-               ASSERT(agcount != 0);
+       if (cfg->dblocks % cfg->agsize != 0 &&
+            (cfg->dblocks % cfg->agsize < XFS_AG_MIN_BLOCKS(cfg->blocklog))) {
+               ASSERT(!cli_opt_set(&dopts, D_AGCOUNT));
+               cfg->dblocks = (xfs_rfsblock_t)((cfg->agcount - 1) * cfg->agsize);
+               cfg->agcount--;
+               ASSERT(cfg->agcount != 0);
        }
 
-       validate_ag_geometry(blocklog, dblocks, agsize, agcount);
+       validate_ag_geometry(cfg->blocklog, cfg->dblocks,
+                            cfg->agsize, cfg->agcount);
+}
 
-       if (!imflag)
-               imaxpct = calc_default_imaxpct(blocklog, dblocks);
+static void
+calculate_imaxpct(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli)
+{
+       cfg->imaxpct = cli->imaxpct;
+       if (cfg->imaxpct)
+               return;
 
        /*
-        * check that log sunit is modulo fsblksize or default it to dsunit.
+        * This returns the % of the disk space that is used for
+        * inodes, it changes relatively to the FS size:
+        *  - over  50 TB, use 1%,
+        *  - 1TB - 50 TB, use 5%,
+        *  - under  1 TB, use XFS_DFL_IMAXIMUM_PCT (25%).
         */
 
-       if (lsunit) {
-               /* convert from 512 byte blocks to fs blocks */
-               lsunit = DTOBT(lsunit);
-       } else if (sb_feat.log_version == 2 && loginternal && dsunit) {
-               /* lsunit and dsunit now in fs blocks */
-               lsunit = dsunit;
-       }
-
-       if (sb_feat.log_version == 2 && (lsunit * blocksize) > 256 * 1024) {
-               /* Warn only if specified on commandline */
-               if (lsuflag || lsunitflag) {
-                       fprintf(stderr,
-       _("log stripe unit (%d bytes) is too large (maximum is 256KiB)\n"),
-                               (lsunit * blocksize));
-                       fprintf(stderr,
-       _("log stripe unit adjusted to 32KiB\n"));
-               }
-               lsunit = (32 * 1024) >> blocklog;
-       }
-
-       min_logblocks = max_trans_res(agsize,
-                                  sb_feat.crcs_enabled, sb_feat.dir_version,
-                                  sectorlog, blocklog, inodelog, dirblocklog,
-                                  sb_feat.log_version, lsunit, sb_feat.finobt,
-                                  sb_feat.rmapbt, sb_feat.reflink);
-       ASSERT(min_logblocks);
-       min_logblocks = MAX(XFS_MIN_LOG_BLOCKS, min_logblocks);
-       if (!logsize && dblocks >= (1024*1024*1024) >> blocklog)
-               min_logblocks = MAX(min_logblocks, XFS_MIN_LOG_BYTES>>blocklog);
-       if (logsize && xi.logBBsize > 0 && logblocks > DTOBT(xi.logBBsize)) {
-               fprintf(stderr,
-_("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
-                       logsize, (long long)DTOBT(xi.logBBsize));
-               usage();
-       } else if (!logsize && xi.logBBsize > 0) {
-               logblocks = DTOBT(xi.logBBsize);
-       } else if (logsize && !xi.logdev && !loginternal) {
-               fprintf(stderr,
-                       _("size specified for non-existent log subvolume\n"));
-               usage();
-       } else if (loginternal && logsize && logblocks >= dblocks) {
-               fprintf(stderr, _("size %lld too large for internal log\n"),
-                       (long long)logblocks);
-               usage();
-       } else if (!loginternal && !xi.logdev) {
-               logblocks = 0;
-       } else if (loginternal && !logsize) {
+       if (cfg->dblocks < TERABYTES(1, cfg->blocklog))
+               cfg->imaxpct = XFS_DFL_IMAXIMUM_PCT;
+       else if (cfg->dblocks < TERABYTES(50, cfg->blocklog))
+               cfg->imaxpct = 5;
+       else
+               cfg->imaxpct = 1;
+}
 
-               if (dblocks < GIGABYTES(1, blocklog)) {
-                       /* tiny filesystems get minimum sized logs. */
-                       logblocks = min_logblocks;
-               } else if (dblocks < GIGABYTES(16, blocklog)) {
+/*
+ * Set up the initial state of the superblock so we can start using the
+ * libxfs geometry macros.
+ */
+static void
+sb_set_features(
+       struct mkfs_params      *cfg,
+       struct xfs_sb           *sbp)
+{
+       struct sb_feat_args     *fp = &cfg->sb_feat;
 
-                       /*
-                        * For small filesystems, we want to use the
-                        * XFS_MIN_LOG_BYTES for filesystems smaller than 16G if
-                        * at all possible, ramping up to 128MB at 256GB.
-                        */
-                       logblocks = MIN(XFS_MIN_LOG_BYTES >> blocklog,
-                                       min_logblocks * XFS_DFL_LOG_FACTOR);
-               } else {
-                       /*
-                        * With a 2GB max log size, default to maximum size
-                        * at 4TB. This keeps the same ratio from the older
-                        * max log size of 128M at 256GB fs size. IOWs,
-                        * the ratio of fs size to log size is 2048:1.
-                        */
-                       logblocks = (dblocks << blocklog) / 2048;
-                       logblocks = logblocks >> blocklog;
-               }
+       sbp->sb_versionnum = XFS_DFL_SB_VERSION_BITS;
+       if (fp->crcs_enabled)
+               sbp->sb_versionnum |= XFS_SB_VERSION_5;
+       else
+               sbp->sb_versionnum |= XFS_SB_VERSION_4;
 
-               /* Ensure the chosen size meets minimum log size requirements */
-               logblocks = MAX(min_logblocks, logblocks);
+       if (fp->inode_align) {
+               int     cluster_size = XFS_INODE_BIG_CLUSTER_SIZE;
 
-               /* make sure the log fits wholly within an AG */
-               if (logblocks >= agsize)
-                       logblocks = min_logblocks;
+               sbp->sb_versionnum |= XFS_SB_VERSION_ALIGNBIT;
+               if (cfg->sb_feat.crcs_enabled)
+                       cluster_size *= cfg->inodesize / XFS_DINODE_MIN_SIZE;
+               sbp->sb_inoalignmt = cluster_size >> cfg->blocklog;
+       } else
+               sbp->sb_inoalignmt = 0;
 
-               /* and now clamp the size to the maximum supported size */
-               logblocks = MIN(logblocks, XFS_MAX_LOG_BLOCKS);
-               if ((logblocks << blocklog) > XFS_MAX_LOG_BYTES)
-                       logblocks = XFS_MAX_LOG_BYTES >> blocklog;
+       if (cfg->dsunit)
+               sbp->sb_versionnum |= XFS_SB_VERSION_DALIGNBIT;
+       if (fp->log_version == 2)
+               sbp->sb_versionnum |= XFS_SB_VERSION_LOGV2BIT;
+       if (fp->attr_version == 1)
+               sbp->sb_versionnum |= XFS_SB_VERSION_ATTRBIT;
+       if (fp->nci)
+               sbp->sb_versionnum |= XFS_SB_VERSION_BORGBIT;
 
+       if (cfg->sectorsize > BBSIZE || cfg->lsectorsize > BBSIZE) {
+               sbp->sb_versionnum |= XFS_SB_VERSION_SECTORBIT;
+               sbp->sb_logsectlog = (uint8_t)cfg->lsectorlog;
+               sbp->sb_logsectsize = (uint16_t)cfg->lsectorsize;
+       } else {
+               sbp->sb_logsectlog = 0;
+               sbp->sb_logsectsize = 0;
        }
-       validate_log_size(logblocks, blocklog, min_logblocks);
 
-       protostring = setup_proto(protofile);
-       bsize = 1 << (blocklog - BBSHIFT);
-       mp = &mbuf;
-       sbp = &mp->m_sb;
-       memset(mp, 0, sizeof(xfs_mount_t));
-       sbp->sb_blocklog = (__uint8_t)blocklog;
-       sbp->sb_sectlog = (__uint8_t)sectorlog;
-       sbp->sb_agblklog = (__uint8_t)libxfs_log2_roundup((unsigned int)agsize);
-       sbp->sb_agblocks = (xfs_agblock_t)agsize;
-       mp->m_blkbb_log = sbp->sb_blocklog - BBSHIFT;
-       mp->m_sectbb_log = sbp->sb_sectlog - BBSHIFT;
+       sbp->sb_features2 = 0;
+       if (fp->lazy_sb_counters)
+               sbp->sb_features2 |= XFS_SB_VERSION2_LAZYSBCOUNTBIT;
+       if (fp->projid32bit)
+               sbp->sb_features2 |= XFS_SB_VERSION2_PROJID32BIT;
+       if (fp->parent_pointers)
+               sbp->sb_features2 |= XFS_SB_VERSION2_PARENTBIT;
+       if (fp->crcs_enabled)
+               sbp->sb_features2 |= XFS_SB_VERSION2_CRCBIT;
+       if (fp->attr_version == 2)
+               sbp->sb_features2 |= XFS_SB_VERSION2_ATTR2BIT;
+
+       /* v5 superblocks have their own feature bit for dirftype */
+       if (fp->dirftype && !fp->crcs_enabled)
+               sbp->sb_features2 |= XFS_SB_VERSION2_FTYPE;
+
+       /* update whether extended features are in use */
+       if (sbp->sb_features2 != 0)
+               sbp->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT;
 
        /*
-        * sb_versionnum, finobt and rmapbt flags must be set before we use
-        * libxfs_prealloc_blocks().
+        * Due to a structure alignment issue, sb_features2 ended up in one
+        * of two locations, the second "incorrect" location represented by
+        * the sb_bad_features2 field. To avoid older kernels mounting
+        * filesystems they shouldn't, set both field to the same value.
         */
-       sb_set_features(&mp->m_sb, &sb_feat, sectorsize, lsectorsize, dsunit);
+       sbp->sb_bad_features2 = sbp->sb_features2;
 
+       if (!fp->crcs_enabled)
+               return;
 
-       if (loginternal) {
-               /*
-                * Readjust the log size to fit within an AG if it was sized
-                * automatically.
-                */
-               if (!logsize) {
-                       logblocks = MIN(logblocks,
-                                       libxfs_alloc_ag_max_usable(mp));
+       /* default features for v5 filesystems */
+       sbp->sb_features_compat = 0;
+       sbp->sb_features_ro_compat = 0;
+       sbp->sb_features_incompat = XFS_SB_FEAT_INCOMPAT_FTYPE;
+       sbp->sb_features_log_incompat = 0;
 
-                       /* revalidate the log size is valid if we changed it */
-                       validate_log_size(logblocks, blocklog, min_logblocks);
-               }
-               if (logblocks > agsize - libxfs_prealloc_blocks(mp)) {
+       if (fp->finobt)
+               sbp->sb_features_ro_compat = XFS_SB_FEAT_RO_COMPAT_FINOBT;
+       if (fp->rmapbt)
+               sbp->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_RMAPBT;
+       if (fp->reflink)
+               sbp->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_REFLINK;
+
+       /*
+        * Sparse inode chunk support has two main inode alignment requirements.
+        * First, sparse chunk alignment must match the cluster size. Second,
+        * full chunk alignment must match the inode chunk size.
+        *
+        * Copy the already calculated/scaled inoalignmt to spino_align and
+        * update the former to the full inode chunk size.
+        */
+       if (fp->spinodes) {
+               sbp->sb_spino_align = sbp->sb_inoalignmt;
+               sbp->sb_inoalignmt = XFS_INODES_PER_CHUNK *
+                               cfg->inodesize >> cfg->blocklog;
+               sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_SPINODES;
+       }
+
+}
+
+/*
+ * Make sure that the log size is a multiple of the stripe unit
+ */
+static void
+align_log_size(
+       struct mkfs_params      *cfg,
+       int                     sunit)
+{
+       uint64_t        tmp_logblocks;
+
+       /* nothing to do if it's already aligned. */
+       if ((cfg->logblocks % sunit) == 0)
+               return;
+
+       if (cli_opt_set(&lopts, L_SIZE)) {
+               fprintf(stderr,
+_("log size %lld is not a multiple of the log stripe unit %d\n"),
+                       (long long) cfg->logblocks, sunit);
+               usage();
+       }
+
+       tmp_logblocks = ((cfg->logblocks + (sunit - 1)) / sunit) * sunit;
+
+       /* If the log is too large, round down instead of round up */
+       if ((tmp_logblocks > XFS_MAX_LOG_BLOCKS) ||
+           ((tmp_logblocks << cfg->blocklog) > XFS_MAX_LOG_BYTES)) {
+               tmp_logblocks = (cfg->logblocks / sunit) * sunit;
+       }
+       cfg->logblocks = tmp_logblocks;
+}
+
+/*
+ * Make sure that the internal log is correctly aligned to the specified
+ * stripe unit.
+ */
+static void
+align_internal_log(
+       struct mkfs_params      *cfg,
+       struct xfs_mount        *mp,
+       int                     sunit)
+{
+       /* round up log start if necessary */
+       if ((cfg->logstart % sunit) != 0)
+               cfg->logstart = ((cfg->logstart + (sunit - 1)) / sunit) * sunit;
+
+       /* round up/down the log size now */
+       align_log_size(cfg, sunit);
+
+       /* check the aligned log still fits in an AG. */
+       if (cfg->logblocks > cfg->agsize - XFS_FSB_TO_AGBNO(mp, cfg->logstart)) {
+               fprintf(stderr,
+_("Due to stripe alignment, the internal log size (%lld) is too large.\n"
+  "Must fit within an allocation group.\n"),
+                       (long long) cfg->logblocks);
+               usage();
+       }
+}
+
+static void
+validate_log_size(uint64_t logblocks, int blocklog, int min_logblocks)
+{
+       if (logblocks < min_logblocks) {
+               fprintf(stderr,
+       _("log size %lld blocks too small, minimum size is %d blocks\n"),
+                       (long long)logblocks, min_logblocks);
+               usage();
+       }
+       if (logblocks > XFS_MAX_LOG_BLOCKS) {
+               fprintf(stderr,
+       _("log size %lld blocks too large, maximum size is %lld blocks\n"),
+                       (long long)logblocks, XFS_MAX_LOG_BLOCKS);
+               usage();
+       }
+       if ((logblocks << blocklog) > XFS_MAX_LOG_BYTES) {
+               fprintf(stderr,
+       _("log size %lld bytes too large, maximum size is %lld bytes\n"),
+                       (long long)(logblocks << blocklog), XFS_MAX_LOG_BYTES);
+               usage();
+       }
+}
+
+static void
+calculate_log_size(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli,
+       struct xfs_mount        *mp)
+{
+       struct xfs_sb           *sbp = &mp->m_sb;
+       int                     min_logblocks;
+       struct xfs_mount        mount;
+
+       /* we need a temporary mount to calculate the minimum log size. */
+       memset(&mount, 0, sizeof(mount));
+       mount.m_sb = *sbp;
+       libxfs_mount(&mount, &mp->m_sb, 0, 0, 0, 0);
+       min_logblocks = libxfs_log_calc_minimum_size(&mount);
+       libxfs_umount(&mount);
+
+       ASSERT(min_logblocks);
+       min_logblocks = max(XFS_MIN_LOG_BLOCKS, min_logblocks);
+
+       /* if we have lots of blocks, check against XFS_MIN_LOG_BYTES, too */
+       if (!cli->logsize &&
+           cfg->dblocks >= (1024*1024*1024) >> cfg->blocklog)
+               min_logblocks = max(min_logblocks,
+                                   XFS_MIN_LOG_BYTES >> cfg->blocklog);
+
+       /*
+        * external logs will have a device and size by now, so all we have
+        * to do is validate it against minimum size and align it.
+        */
+       if (!cfg->loginternal) {
+               if (min_logblocks > cfg->logblocks) {
                        fprintf(stderr,
-       _("internal log size %lld too large, must fit in allocation group\n"),
-                               (long long)logblocks);
+_("external log device %lld too small, must be at least %lld blocks\n"),
+                               (long long)cfg->logblocks,
+                               (long long)min_logblocks);
                        usage();
                }
+               cfg->logstart = 0;
+               cfg->logagno = 0;
+               if (cfg->lsunit)
+                       align_log_size(cfg, cfg->lsunit);
 
-               if (laflag) {
-                       if (logagno >= agcount) {
-                               fprintf(stderr,
-               _("log ag number %d too large, must be less than %lld\n"),
-                                       logagno, (long long)agcount);
-                               usage();
-                       }
-               } else
-                       logagno = (xfs_agnumber_t)(agcount / 2);
+               validate_log_size(cfg->logblocks, cfg->blocklog, min_logblocks);
+               return;
+       }
+
+       /* internal log - if no size specified, calculate automatically */
+       if (!cfg->logblocks) {
+               if (cfg->dblocks < GIGABYTES(1, cfg->blocklog)) {
+                       /* tiny filesystems get minimum sized logs. */
+                       cfg->logblocks = min_logblocks;
+               } else if (cfg->dblocks < GIGABYTES(16, cfg->blocklog)) {
+
+                       /*
+                        * For small filesystems, we want to use the
+                        * XFS_MIN_LOG_BYTES for filesystems smaller than 16G if
+                        * at all possible, ramping up to 128MB at 256GB.
+                        */
+                       cfg->logblocks = min(XFS_MIN_LOG_BYTES >> cfg->blocklog,
+                                       min_logblocks * XFS_DFL_LOG_FACTOR);
+               } else {
+                       /*
+                        * With a 2GB max log size, default to maximum size
+                        * at 4TB. This keeps the same ratio from the older
+                        * max log size of 128M at 256GB fs size. IOWs,
+                        * the ratio of fs size to log size is 2048:1.
+                        */
+                       cfg->logblocks = (cfg->dblocks << cfg->blocklog) / 2048;
+                       cfg->logblocks = cfg->logblocks >> cfg->blocklog;
+               }
+
+               /* Ensure the chosen size meets minimum log size requirements */
+               cfg->logblocks = max(min_logblocks, cfg->logblocks);
 
-               logstart = XFS_AGB_TO_FSB(mp, logagno, libxfs_prealloc_blocks(mp));
                /*
-                * Align the logstart at stripe unit boundary.
+                * Make sure the log fits wholly within an AG
+                *
+                * XXX: If agf->freeblks ends up as 0 because the log uses all
+                * the free space, it causes the kernel all sorts of problems
+                * with per-ag reservations. Right now just back it off one
+                * block, but there's a whole can of worms here that needs to be
+                * opened to decide what is the valid maximum size of a log in
+                * an AG.
                 */
-               if (lsunit) {
-                       logstart = fixup_internal_log_stripe(mp,
-                                       lsflag, logstart, agsize, lsunit,
-                                       &logblocks, blocklog, &lalign);
-               } else if (dsunit) {
-                       logstart = fixup_internal_log_stripe(mp,
-                                       lsflag, logstart, agsize, dsunit,
-                                       &logblocks, blocklog, &lalign);
+               cfg->logblocks = min(cfg->logblocks,
+                                    libxfs_alloc_ag_max_usable(mp) - 1);
+
+               /* and now clamp the size to the maximum supported size */
+               cfg->logblocks = min(cfg->logblocks, XFS_MAX_LOG_BLOCKS);
+               if ((cfg->logblocks << cfg->blocklog) > XFS_MAX_LOG_BYTES)
+                       cfg->logblocks = XFS_MAX_LOG_BYTES >> cfg->blocklog;
+
+               validate_log_size(cfg->logblocks, cfg->blocklog, min_logblocks);
+       }
+
+       if (cfg->logblocks > sbp->sb_agblocks - libxfs_prealloc_blocks(mp)) {
+               fprintf(stderr,
+_("internal log size %lld too large, must fit in allocation group\n"),
+                       (long long)cfg->logblocks);
+               usage();
+       }
+
+       if (cli_opt_set(&lopts, L_AGNUM)) {
+               if (cli->logagno >= sbp->sb_agcount) {
+                       fprintf(stderr,
+_("log ag number %lld too large, must be less than %lld\n"),
+                               (long long)cli->logagno,
+                               (long long)sbp->sb_agcount);
+                       usage();
                }
-       } else {
-               logstart = 0;
-               if (lsunit)
-                       fixup_log_stripe_unit(lsflag, lsunit,
-                                       &logblocks, blocklog);
-       }
-       validate_log_size(logblocks, blocklog, min_logblocks);
-
-       if (!qflag || Nflag) {
-               printf(_(
-                  "meta-data=%-22s isize=%-6d agcount=%lld, agsize=%lld blks\n"
-                  "         =%-22s sectsz=%-5u attr=%u, projid32bit=%u\n"
-                  "         =%-22s crc=%-8u finobt=%u, sparse=%u, rmapbt=%u, reflink=%u\n"
-                  "data     =%-22s bsize=%-6u blocks=%llu, imaxpct=%u\n"
-                  "         =%-22s sunit=%-6u swidth=%u blks\n"
-                  "naming   =version %-14u bsize=%-6u ascii-ci=%d ftype=%d\n"
-                  "log      =%-22s bsize=%-6d blocks=%lld, version=%d\n"
-                  "         =%-22s sectsz=%-5u sunit=%d blks, lazy-count=%d\n"
-                  "realtime =%-22s extsz=%-6d blocks=%lld, rtextents=%lld\n"),
-                       dfile, isize, (long long)agcount, (long long)agsize,
-                       "", sectorsize, sb_feat.attr_version,
-                                   !sb_feat.projid16bit,
-                       "", sb_feat.crcs_enabled, sb_feat.finobt, sb_feat.spinodes,
-                       sb_feat.rmapbt, sb_feat.reflink,
-                       "", blocksize, (long long)dblocks, imaxpct,
-                       "", dsunit, dswidth,
-                       sb_feat.dir_version, dirblocksize, sb_feat.nci,
-                               sb_feat.dirftype,
-                       logfile, 1 << blocklog, (long long)logblocks,
-                       sb_feat.log_version, "", lsectorsize, lsunit,
-                               sb_feat.lazy_sb_counters,
-                       rtfile, rtextblocks << blocklog,
-                       (long long)rtblocks, (long long)rtextents);
-               if (Nflag)
-                       exit(0);
+               cfg->logagno = cli->logagno;
+       } else
+               cfg->logagno = (xfs_agnumber_t)(sbp->sb_agcount / 2);
+
+       cfg->logstart = XFS_AGB_TO_FSB(mp, cfg->logagno,
+                                      libxfs_prealloc_blocks(mp));
+
+       /*
+        * Align the logstart at stripe unit boundary.
+        */
+       if (cfg->lsunit) {
+               align_internal_log(cfg, mp, cfg->lsunit);
+       } else if (cfg->dsunit) {
+               align_internal_log(cfg, mp, cfg->dsunit);
        }
+       validate_log_size(cfg->logblocks, cfg->blocklog, min_logblocks);
+}
 
-       if (label)
-               strncpy(sbp->sb_fname, label, sizeof(sbp->sb_fname));
+/*
+ * Set up superblock with the minimum parameters required for
+ * the libxfs macros needed by the log sizing code to run successfully.
+ * This includes a minimum log size calculation, so we need everything
+ * that goes into that calculation to be setup here including feature
+ * flags.
+ */
+static void
+start_superblock_setup(
+       struct mkfs_params      *cfg,
+       struct xfs_mount        *mp,
+       struct xfs_sb           *sbp)
+{
        sbp->sb_magicnum = XFS_SB_MAGIC;
-       sbp->sb_blocksize = blocksize;
-       sbp->sb_dblocks = dblocks;
-       sbp->sb_rblocks = rtblocks;
-       sbp->sb_rextents = rtextents;
-       platform_uuid_copy(&sbp->sb_uuid, &uuid);
+       sbp->sb_sectsize = (uint16_t)cfg->sectorsize;
+       sbp->sb_sectlog = (uint8_t)cfg->sectorlog;
+       sbp->sb_blocksize = cfg->blocksize;
+       sbp->sb_blocklog = (uint8_t)cfg->blocklog;
+
+       sbp->sb_agblocks = (xfs_agblock_t)cfg->agsize;
+       sbp->sb_agblklog = (uint8_t)log2_roundup(cfg->agsize);
+       sbp->sb_agcount = (xfs_agnumber_t)cfg->agcount;
+
+       sbp->sb_inodesize = (uint16_t)cfg->inodesize;
+       sbp->sb_inodelog = (uint8_t)cfg->inodelog;
+       sbp->sb_inopblock = (uint16_t)(cfg->blocksize / cfg->inodesize);
+       sbp->sb_inopblog = (uint8_t)(cfg->blocklog - cfg->inodelog);
+
+       sbp->sb_dirblklog = cfg->dirblocklog - cfg->blocklog;
+
+       sb_set_features(cfg, sbp);
+
+       /*
+        * log stripe unit is stored in bytes on disk and cannot be zero
+        * for v2 logs.
+        */
+       if (cfg->sb_feat.log_version == 2) {
+               if (cfg->lsunit)
+                       sbp->sb_logsunit = XFS_FSB_TO_B(mp, cfg->lsunit);
+               else
+                       sbp->sb_logsunit = 1;
+       } else
+               sbp->sb_logsunit = 0;
+
+}
+
+static void
+initialise_mount(
+       struct mkfs_params      *cfg,
+       struct xfs_mount        *mp,
+       struct xfs_sb           *sbp)
+{
+       /* Minimum needed for libxfs_prealloc_blocks() */
+       mp->m_blkbb_log = sbp->sb_blocklog - BBSHIFT;
+       mp->m_sectbb_log = sbp->sb_sectlog - BBSHIFT;
+}
+
+/*
+ * Format everything from the generated config into the superblock that
+ * will be used to initialise the on-disk superblock. This is the in-memory
+ * copy, so no need to care about endian swapping here.
+ */
+static void
+finish_superblock_setup(
+       struct mkfs_params      *cfg,
+       struct xfs_mount        *mp,
+       struct xfs_sb           *sbp)
+{
+       if (cfg->label)
+               strncpy(sbp->sb_fname, cfg->label, sizeof(sbp->sb_fname));
+
+       sbp->sb_dblocks = cfg->dblocks;
+       sbp->sb_rblocks = cfg->rtblocks;
+       sbp->sb_rextents = cfg->rtextents;
+       platform_uuid_copy(&sbp->sb_uuid, &cfg->uuid);
        /* Only in memory; libxfs expects this as if read from disk */
-       platform_uuid_copy(&sbp->sb_meta_uuid, &uuid);
-       sbp->sb_logstart = logstart;
+       platform_uuid_copy(&sbp->sb_meta_uuid, &cfg->uuid);
+       sbp->sb_logstart = cfg->logstart;
        sbp->sb_rootino = sbp->sb_rbmino = sbp->sb_rsumino = NULLFSINO;
-       sbp->sb_rextsize = rtextblocks;
-       sbp->sb_agcount = (xfs_agnumber_t)agcount;
-       sbp->sb_rbmblocks = nbmblocks;
-       sbp->sb_logblocks = (xfs_extlen_t)logblocks;
-       sbp->sb_sectsize = (__uint16_t)sectorsize;
-       sbp->sb_inodesize = (__uint16_t)isize;
-       sbp->sb_inopblock = (__uint16_t)(blocksize / isize);
-       sbp->sb_sectlog = (__uint8_t)sectorlog;
-       sbp->sb_inodelog = (__uint8_t)inodelog;
-       sbp->sb_inopblog = (__uint8_t)(blocklog - inodelog);
-       sbp->sb_rextslog =
-               (__uint8_t)(rtextents ?
-                       libxfs_highbit32((unsigned int)rtextents) : 0);
+       sbp->sb_rextsize = cfg->rtextblocks;
+       sbp->sb_agcount = (xfs_agnumber_t)cfg->agcount;
+       sbp->sb_rbmblocks = cfg->rtbmblocks;
+       sbp->sb_logblocks = (xfs_extlen_t)cfg->logblocks;
+       sbp->sb_rextslog = (uint8_t)(cfg->rtextents ?
+                       libxfs_highbit32((unsigned int)cfg->rtextents) : 0);
        sbp->sb_inprogress = 1; /* mkfs is in progress */
-       sbp->sb_imax_pct = imaxpct;
+       sbp->sb_imax_pct = cfg->imaxpct;
        sbp->sb_icount = 0;
        sbp->sb_ifree = 0;
-       sbp->sb_fdblocks = dblocks - agcount * libxfs_prealloc_blocks(mp) -
-               (loginternal ? logblocks : 0);
+       sbp->sb_fdblocks = cfg->dblocks -
+                          cfg->agcount * libxfs_prealloc_blocks(mp) -
+                          (cfg->loginternal ? cfg->logblocks : 0);
        sbp->sb_frextents = 0;  /* will do a free later */
        sbp->sb_uquotino = sbp->sb_gquotino = sbp->sb_pquotino = 0;
        sbp->sb_qflags = 0;
-       sbp->sb_unit = dsunit;
-       sbp->sb_width = dswidth;
-       sbp->sb_dirblklog = dirblocklog - blocklog;
-       if (sb_feat.log_version == 2) { /* This is stored in bytes */
-               lsunit = (lsunit == 0) ? 1 : XFS_FSB_TO_B(mp, lsunit);
-               sbp->sb_logsunit = lsunit;
-       } else
-               sbp->sb_logsunit = 0;
-       if (sb_feat.inode_align) {
-               int     cluster_size = XFS_INODE_BIG_CLUSTER_SIZE;
-               if (sb_feat.crcs_enabled)
-                       cluster_size *= isize / XFS_DINODE_MIN_SIZE;
-               sbp->sb_inoalignmt = cluster_size >> blocklog;
-               sb_feat.inode_align = sbp->sb_inoalignmt != 0;
-       } else
-               sbp->sb_inoalignmt = 0;
-       if (lsectorsize != BBSIZE || sectorsize != BBSIZE) {
-               sbp->sb_logsectlog = (__uint8_t)lsectorlog;
-               sbp->sb_logsectsize = (__uint16_t)lsectorsize;
-       } else {
-               sbp->sb_logsectlog = 0;
-               sbp->sb_logsectsize = 0;
-       }
+       sbp->sb_unit = cfg->dsunit;
+       sbp->sb_width = cfg->dswidth;
 
-       sb_set_features(&mp->m_sb, &sb_feat, sectorsize, lsectorsize, dsunit);
+}
 
-       if (force_overwrite)
-               zero_old_xfs_structures(&xi, sbp);
+/*
+ * Sanitise the data and log devices and prepare them so libxfs can mount the
+ * device successfully. Also check we can access the rt device if configured.
+ */
+static void
+prepare_devices(
+       struct mkfs_params      *cfg,
+       struct libxfs_xinit     *xi,
+       struct xfs_mount        *mp,
+       struct xfs_sb           *sbp,
+       bool                    clear_stale)
+{
+       struct xfs_buf          *buf;
+       int                     whack_blks = BTOBB(WHACK_SIZE);
+       int                     lsunit;
 
        /*
-        * Zero out the beginning of the device, to obliterate any old
-        * filesystem signatures out there.  This should take care of
-        * swap (somewhere around the page size), jfs (32k),
-        * ext[2,3] and reiserfs (64k) - and hopefully all else.
+        * If there's an old XFS filesystem on the device with enough intact
+        * information that we can parse the superblock, there's enough
+        * information on disk to confuse a future xfs_repair call. To avoid
+        * this, whack all the old secondary superblocks that we can find.
         */
-       libxfs_buftarg_init(mp, xi.ddev, xi.logdev, xi.rtdev);
-       buf = libxfs_getbuf(mp->m_ddev_targp, 0, BTOBB(WHACK_SIZE));
-       memset(XFS_BUF_PTR(buf), 0, WHACK_SIZE);
-       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
-       libxfs_purgebuf(buf);
-
-       /* OK, now write the superblock */
-       buf = libxfs_getbuf(mp->m_ddev_targp, XFS_SB_DADDR, XFS_FSS_TO_BB(mp, 1));
-       buf->b_ops = &xfs_sb_buf_ops;
-       memset(XFS_BUF_PTR(buf), 0, sectorsize);
-       libxfs_sb_to_disk((void *)XFS_BUF_PTR(buf), sbp);
-       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
-       libxfs_purgebuf(buf);
+       if (clear_stale)
+               zero_old_xfs_structures(xi, sbp);
 
        /*
-        * If the data area is a file, then grow it out to its final size
-        * if needed so that the reads for the end of the device in the mount
-        * code will succeed.
+        * If the data device is a file, grow out the file to its final size if
+        * needed so that the reads for the end of the device in the mount code
+        * will succeed.
         */
-       if (xi.disfile && xi.dsize * xi.dbsize < dblocks * blocksize) {
-               if (ftruncate(xi.dfd, dblocks * blocksize) < 0) {
+       if (xi->disfile &&
+           xi->dsize * xi->dbsize < cfg->dblocks * cfg->blocksize) {
+               if (ftruncate(xi->dfd, cfg->dblocks * cfg->blocksize) < 0) {
                        fprintf(stderr,
                                _("%s: Growing the data section failed\n"),
                                progname);
                        exit(1);
                }
+
+               /* update size to be able to whack blocks correctly */
+               xi->dsize = BTOBB(cfg->dblocks * cfg->blocksize);
        }
 
        /*
-        * Zero out the end of the device, to obliterate any
-        * old MD RAID (or other) metadata at the end of the device.
-        * (MD sb is ~64k from the end, take out a wider swath to be sure)
+        * Zero out the end to obliterate any old MD RAID (or other) metadata at
+        * the end of the device.  (MD sb is ~64k from the end, take out a wider
+        * swath to be sure)
         */
-       if (!xi.disfile) {
-               buf = libxfs_getbuf(mp->m_ddev_targp,
-                                   (xi.dsize - BTOBB(WHACK_SIZE)),
-                                   BTOBB(WHACK_SIZE));
-               memset(XFS_BUF_PTR(buf), 0, WHACK_SIZE);
+       buf = libxfs_getbuf(mp->m_ddev_targp, (xi->dsize - whack_blks),
+                           whack_blks);
+       memset(buf->b_addr, 0, WHACK_SIZE);
+       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+       libxfs_purgebuf(buf);
+
+       /*
+        * Now zero out the beginning of the device, to obliterate any old
+        * filesystem signatures out there.  This should take care of
+        * swap (somewhere around the page size), jfs (32k),
+        * ext[2,3] and reiserfs (64k) - and hopefully all else.
+        */
+       buf = libxfs_getbuf(mp->m_ddev_targp, 0, whack_blks);
+       memset(buf->b_addr, 0, WHACK_SIZE);
+       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+       libxfs_purgebuf(buf);
+
+       /* OK, now write the superblock... */
+       buf = libxfs_getbuf(mp->m_ddev_targp, XFS_SB_DADDR, XFS_FSS_TO_BB(mp, 1));
+       buf->b_ops = &xfs_sb_buf_ops;
+       memset(buf->b_addr, 0, cfg->sectorsize);
+       libxfs_sb_to_disk(buf->b_addr, sbp);
+       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+       libxfs_purgebuf(buf);
+
+       /* ...and zero the log.... */
+       lsunit = sbp->sb_logsunit;
+       if (lsunit == 1)
+               lsunit = sbp->sb_logsectsize;
+
+       libxfs_log_clear(mp->m_logdev_targp, NULL,
+                        XFS_FSB_TO_DADDR(mp, cfg->logstart),
+                        (xfs_extlen_t)XFS_FSB_TO_BB(mp, cfg->logblocks),
+                        &sbp->sb_uuid, cfg->sb_feat.log_version,
+                        lsunit, XLOG_FMT, XLOG_INIT_CYCLE, false);
+
+       /* finally, check we can write the last block in the realtime area */
+       if (mp->m_rtdev_targp->dev && cfg->rtblocks > 0) {
+               buf = libxfs_getbuf(mp->m_rtdev_targp,
+                                   XFS_FSB_TO_BB(mp, cfg->rtblocks - 1LL),
+                                   BTOBB(cfg->blocksize));
+               memset(buf->b_addr, 0, cfg->blocksize);
                libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
                libxfs_purgebuf(buf);
        }
 
+}
+
+/*
+ * XXX: this code is mostly common with the kernel growfs code.
+ * These initialisations should be pulled into libxfs to keep the
+ * kernel/userspace header initialisation code the same.
+ */
+static void
+initialise_ag_headers(
+       struct mkfs_params      *cfg,
+       struct xfs_mount        *mp,
+       struct xfs_sb           *sbp,
+       xfs_agnumber_t          agno,
+       int                     *worst_freelist)
+{
+       struct xfs_perag        *pag = libxfs_perag_get(mp, agno);
+       struct xfs_agfl         *agfl;
+       struct xfs_agf          *agf;
+       struct xfs_agi          *agi;
+       struct xfs_buf          *buf;
+       struct xfs_btree_block  *block;
+       struct xfs_alloc_rec    *arec;
+       struct xfs_alloc_rec    *nrec;
+       int                     bucket;
+       uint64_t                agsize = cfg->agsize;
+       xfs_agblock_t           agblocks;
+       bool                    is_log_ag = false;
+       int                     c;
+
+       if (cfg->loginternal && agno == cfg->logagno)
+               is_log_ag = true;
+
        /*
-        * Zero the log....
+        * Superblock.
         */
-       libxfs_log_clear(mp->m_logdev_targp, NULL,
-               XFS_FSB_TO_DADDR(mp, logstart),
-               (xfs_extlen_t)XFS_FSB_TO_BB(mp, logblocks),
-               &sbp->sb_uuid, sb_feat.log_version, lsunit, XLOG_FMT, XLOG_INIT_CYCLE, false);
+       buf = libxfs_getbuf(mp->m_ddev_targp,
+                       XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
+                       XFS_FSS_TO_BB(mp, 1));
+       buf->b_ops = &xfs_sb_buf_ops;
+       memset(buf->b_addr, 0, cfg->sectorsize);
+       libxfs_sb_to_disk(buf->b_addr, sbp);
+       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
 
-       mp = libxfs_mount(mp, sbp, xi.ddev, xi.logdev, xi.rtdev, 0);
-       if (mp == NULL) {
-               fprintf(stderr, _("%s: filesystem failed to initialize\n"),
-                       progname);
-               exit(1);
+       /*
+        * AG header block: freespace
+        */
+       buf = libxfs_getbuf(mp->m_ddev_targp,
+                       XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
+                       XFS_FSS_TO_BB(mp, 1));
+       buf->b_ops = &xfs_agf_buf_ops;
+       agf = XFS_BUF_TO_AGF(buf);
+       memset(agf, 0, cfg->sectorsize);
+       if (agno == cfg->agcount - 1)
+               agsize = cfg->dblocks - (xfs_rfsblock_t)(agno * agsize);
+       agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
+       agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
+       agf->agf_seqno = cpu_to_be32(agno);
+       agf->agf_length = cpu_to_be32(agsize);
+       agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
+       agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
+       agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
+       agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
+       pag->pagf_levels[XFS_BTNUM_BNOi] = 1;
+       pag->pagf_levels[XFS_BTNUM_CNTi] = 1;
+
+       if (xfs_sb_version_hasrmapbt(sbp)) {
+               agf->agf_roots[XFS_BTNUM_RMAPi] = cpu_to_be32(XFS_RMAP_BLOCK(mp));
+               agf->agf_levels[XFS_BTNUM_RMAPi] = cpu_to_be32(1);
+               agf->agf_rmap_blocks = cpu_to_be32(1);
+       }
+
+       if (xfs_sb_version_hasreflink(sbp)) {
+               agf->agf_refcount_root = cpu_to_be32(libxfs_refc_block(mp));
+               agf->agf_refcount_level = cpu_to_be32(1);
+               agf->agf_refcount_blocks = cpu_to_be32(1);
+       }
+
+       agf->agf_flfirst = 0;
+       agf->agf_fllast = cpu_to_be32(libxfs_agfl_size(mp) - 1);
+       agf->agf_flcount = 0;
+       agblocks = (xfs_agblock_t)(agsize - libxfs_prealloc_blocks(mp));
+       agf->agf_freeblks = cpu_to_be32(agblocks);
+       agf->agf_longest = cpu_to_be32(agblocks);
+
+       if (xfs_sb_version_hascrc(sbp))
+               platform_uuid_copy(&agf->agf_uuid, &sbp->sb_uuid);
+
+       if (is_log_ag) {
+               be32_add_cpu(&agf->agf_freeblks, -(int64_t)cfg->logblocks);
+               agf->agf_longest = cpu_to_be32(agsize -
+                       XFS_FSB_TO_AGBNO(mp, cfg->logstart) - cfg->logblocks);
+       }
+       if (libxfs_alloc_min_freelist(mp, pag) > *worst_freelist)
+               *worst_freelist = libxfs_alloc_min_freelist(mp, pag);
+       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+       /*
+        * AG freelist header block
+        */
+       buf = libxfs_getbuf(mp->m_ddev_targp,
+                       XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
+                       XFS_FSS_TO_BB(mp, 1));
+       buf->b_ops = &xfs_agfl_buf_ops;
+       agfl = XFS_BUF_TO_AGFL(buf);
+       /* setting to 0xff results in initialisation to NULLAGBLOCK */
+       memset(agfl, 0xff, cfg->sectorsize);
+       if (xfs_sb_version_hascrc(sbp)) {
+               agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
+               agfl->agfl_seqno = cpu_to_be32(agno);
+               platform_uuid_copy(&agfl->agfl_uuid, &sbp->sb_uuid);
+               for (bucket = 0; bucket < libxfs_agfl_size(mp); bucket++)
+                       agfl->agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
        }
 
+       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
        /*
-        * XXX: this code is effectively shared with the kernel growfs code.
-        * These initialisations should be pulled into libxfs to keep the
-        * kernel/userspace header initialisation code the same.
+        * AG header block: inodes
         */
-       for (agno = 0; agno < agcount; agno++) {
-               struct xfs_agfl *agfl;
-               int             bucket;
-               struct xfs_perag *pag = libxfs_perag_get(mp, agno);
+       buf = libxfs_getbuf(mp->m_ddev_targp,
+                       XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
+                       XFS_FSS_TO_BB(mp, 1));
+       agi = XFS_BUF_TO_AGI(buf);
+       buf->b_ops = &xfs_agi_buf_ops;
+       memset(agi, 0, cfg->sectorsize);
+       agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
+       agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
+       agi->agi_seqno = cpu_to_be32(agno);
+       agi->agi_length = cpu_to_be32(agsize);
+       agi->agi_count = 0;
+       agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
+       agi->agi_level = cpu_to_be32(1);
+       if (xfs_sb_version_hasfinobt(sbp)) {
+               agi->agi_free_root = cpu_to_be32(XFS_FIBT_BLOCK(mp));
+               agi->agi_free_level = cpu_to_be32(1);
+       }
+       agi->agi_freecount = 0;
+       agi->agi_newino = cpu_to_be32(NULLAGINO);
+       agi->agi_dirino = cpu_to_be32(NULLAGINO);
+       if (xfs_sb_version_hascrc(sbp))
+               platform_uuid_copy(&agi->agi_uuid, &sbp->sb_uuid);
+       for (c = 0; c < XFS_AGI_UNLINKED_BUCKETS; c++)
+               agi->agi_unlinked[c] = cpu_to_be32(NULLAGINO);
+       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
 
+       /*
+        * BNO btree root block
+        */
+       buf = libxfs_getbuf(mp->m_ddev_targp,
+                       XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
+                       BTOBB(cfg->blocksize));
+       buf->b_ops = &xfs_allocbt_buf_ops;
+       block = XFS_BUF_TO_BLOCK(buf);
+       memset(block, 0, cfg->blocksize);
+       libxfs_btree_init_block(mp, buf, XFS_BTNUM_BNO, 0, 1, agno, 0);
+
+       arec = XFS_ALLOC_REC_ADDR(mp, block, 1);
+       arec->ar_startblock = cpu_to_be32(libxfs_prealloc_blocks(mp));
+       if (is_log_ag) {
+               xfs_agblock_t   start = XFS_FSB_TO_AGBNO(mp, cfg->logstart);
+
+               ASSERT(start >= libxfs_prealloc_blocks(mp));
+               if (start != libxfs_prealloc_blocks(mp)) {
+                       /*
+                        * Modify first record to pad stripe align of log
+                        */
+                       arec->ar_blockcount = cpu_to_be32(start -
+                                               libxfs_prealloc_blocks(mp));
+                       nrec = arec + 1;
+                       /*
+                        * Insert second record at start of internal log
+                        * which then gets trimmed.
+                        */
+                       nrec->ar_startblock = cpu_to_be32(
+                                       be32_to_cpu(arec->ar_startblock) +
+                                       be32_to_cpu(arec->ar_blockcount));
+                       arec = nrec;
+                       be16_add_cpu(&block->bb_numrecs, 1);
+               }
                /*
-                * Superblock.
+                * Change record start to after the internal log
                 */
+               be32_add_cpu(&arec->ar_startblock, cfg->logblocks);
+       }
+       /*
+        * Calculate the record block count and check for the case where
+        * the log might have consumed all available space in the AG. If
+        * so, reset the record count to 0 to avoid exposure of an invalid
+        * record start block.
+        */
+       arec->ar_blockcount = cpu_to_be32(agsize -
+                                         be32_to_cpu(arec->ar_startblock));
+       if (!arec->ar_blockcount)
+               block->bb_numrecs = 0;
+
+       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+       /*
+        * CNT btree root block
+        */
+       buf = libxfs_getbuf(mp->m_ddev_targp,
+                       XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
+                       BTOBB(cfg->blocksize));
+       buf->b_ops = &xfs_allocbt_buf_ops;
+       block = XFS_BUF_TO_BLOCK(buf);
+       memset(block, 0, cfg->blocksize);
+       libxfs_btree_init_block(mp, buf, XFS_BTNUM_CNT, 0, 1, agno, 0);
+
+       arec = XFS_ALLOC_REC_ADDR(mp, block, 1);
+       arec->ar_startblock = cpu_to_be32(libxfs_prealloc_blocks(mp));
+       if (is_log_ag) {
+               xfs_agblock_t   start = XFS_FSB_TO_AGBNO(mp, cfg->logstart);
+
+               ASSERT(start >= libxfs_prealloc_blocks(mp));
+               if (start != libxfs_prealloc_blocks(mp)) {
+                       arec->ar_blockcount = cpu_to_be32(start -
+                                       libxfs_prealloc_blocks(mp));
+                       nrec = arec + 1;
+                       nrec->ar_startblock = cpu_to_be32(
+                                       be32_to_cpu(arec->ar_startblock) +
+                                       be32_to_cpu(arec->ar_blockcount));
+                       arec = nrec;
+                       be16_add_cpu(&block->bb_numrecs, 1);
+               }
+               be32_add_cpu(&arec->ar_startblock, cfg->logblocks);
+       }
+       /*
+        * Calculate the record block count and check for the case where
+        * the log might have consumed all available space in the AG. If
+        * so, reset the record count to 0 to avoid exposure of an invalid
+        * record start block.
+        */
+       arec->ar_blockcount = cpu_to_be32(agsize -
+                                         be32_to_cpu(arec->ar_startblock));
+       if (!arec->ar_blockcount)
+               block->bb_numrecs = 0;
+
+       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+       /*
+        * refcount btree root block
+        */
+       if (xfs_sb_version_hasreflink(sbp)) {
                buf = libxfs_getbuf(mp->m_ddev_targp,
-                               XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
-                               XFS_FSS_TO_BB(mp, 1));
-               buf->b_ops = &xfs_sb_buf_ops;
-               memset(XFS_BUF_PTR(buf), 0, sectorsize);
-               libxfs_sb_to_disk((void *)XFS_BUF_PTR(buf), sbp);
+                       XFS_AGB_TO_DADDR(mp, agno, libxfs_refc_block(mp)),
+                       BTOBB(cfg->blocksize));
+               buf->b_ops = &xfs_refcountbt_buf_ops;
+
+               block = XFS_BUF_TO_BLOCK(buf);
+               memset(block, 0, cfg->blocksize);
+               libxfs_btree_init_block(mp, buf, XFS_BTNUM_REFC, 0, 0, agno, 0);
                libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+       }
 
-               /*
-                * AG header block: freespace
-                */
+       /*
+        * INO btree root block
+        */
+       buf = libxfs_getbuf(mp->m_ddev_targp,
+                       XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
+                       BTOBB(cfg->blocksize));
+       buf->b_ops = &xfs_inobt_buf_ops;
+       block = XFS_BUF_TO_BLOCK(buf);
+       memset(block, 0, cfg->blocksize);
+       libxfs_btree_init_block(mp, buf, XFS_BTNUM_INO, 0, 0, agno, 0);
+       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+       /*
+        * Free INO btree root block
+        */
+       if (xfs_sb_version_hasfinobt(sbp)) {
                buf = libxfs_getbuf(mp->m_ddev_targp,
-                               XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
-                               XFS_FSS_TO_BB(mp, 1));
-               buf->b_ops = &xfs_agf_buf_ops;
-               agf = XFS_BUF_TO_AGF(buf);
-               memset(agf, 0, sectorsize);
-               if (agno == agcount - 1)
-                       agsize = dblocks - (xfs_rfsblock_t)(agno * agsize);
-               agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
-               agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
-               agf->agf_seqno = cpu_to_be32(agno);
-               agf->agf_length = cpu_to_be32(agsize);
-               agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
-               agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
-               agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
-               agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
-               pag->pagf_levels[XFS_BTNUM_BNOi] = 1;
-               pag->pagf_levels[XFS_BTNUM_CNTi] = 1;
-               if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
-                       agf->agf_roots[XFS_BTNUM_RMAPi] =
-                                               cpu_to_be32(XFS_RMAP_BLOCK(mp));
-                       agf->agf_levels[XFS_BTNUM_RMAPi] = cpu_to_be32(1);
-                       agf->agf_rmap_blocks = cpu_to_be32(1);
-               }
-               if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-                       agf->agf_refcount_root = cpu_to_be32(
-                                       libxfs_refc_block(mp));
-                       agf->agf_refcount_level = cpu_to_be32(1);
-                       agf->agf_refcount_blocks = cpu_to_be32(1);
-               }
-               agf->agf_flfirst = 0;
-               agf->agf_fllast = cpu_to_be32(XFS_AGFL_SIZE(mp) - 1);
-               agf->agf_flcount = 0;
-               nbmblocks = (xfs_extlen_t)(agsize - libxfs_prealloc_blocks(mp));
-               agf->agf_freeblks = cpu_to_be32(nbmblocks);
-               agf->agf_longest = cpu_to_be32(nbmblocks);
-               if (xfs_sb_version_hascrc(&mp->m_sb))
-                       platform_uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_uuid);
-
-               if (loginternal && agno == logagno) {
-                       be32_add_cpu(&agf->agf_freeblks, -logblocks);
-                       agf->agf_longest = cpu_to_be32(agsize -
-                               XFS_FSB_TO_AGBNO(mp, logstart) - logblocks);
-               }
-               if (libxfs_alloc_min_freelist(mp, pag) > worst_freelist)
-                       worst_freelist = libxfs_alloc_min_freelist(mp, pag);
+                               XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
+                               BTOBB(cfg->blocksize));
+               buf->b_ops = &xfs_inobt_buf_ops;
+               block = XFS_BUF_TO_BLOCK(buf);
+               memset(block, 0, cfg->blocksize);
+               libxfs_btree_init_block(mp, buf, XFS_BTNUM_FINO, 0, 0, agno, 0);
                libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+       }
+
+       /* RMAP btree root block */
+       if (xfs_sb_version_hasrmapbt(sbp)) {
+               struct xfs_rmap_rec     *rrec;
 
-               /*
-                * AG freelist header block
-                */
                buf = libxfs_getbuf(mp->m_ddev_targp,
-                               XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
-                               XFS_FSS_TO_BB(mp, 1));
-               buf->b_ops = &xfs_agfl_buf_ops;
-               agfl = XFS_BUF_TO_AGFL(buf);
-               /* setting to 0xff results in initialisation to NULLAGBLOCK */
-               memset(agfl, 0xff, sectorsize);
-               if (xfs_sb_version_hascrc(&mp->m_sb)) {
-                       agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
-                       agfl->agfl_seqno = cpu_to_be32(agno);
-                       platform_uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_uuid);
-                       for (bucket = 0; bucket < XFS_AGFL_SIZE(mp); bucket++)
-                               agfl->agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
-               }
+                       XFS_AGB_TO_DADDR(mp, agno, XFS_RMAP_BLOCK(mp)),
+                       BTOBB(cfg->blocksize));
+               buf->b_ops = &xfs_rmapbt_buf_ops;
+               block = XFS_BUF_TO_BLOCK(buf);
+               memset(block, 0, cfg->blocksize);
 
-               libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+               libxfs_btree_init_block(mp, buf, XFS_BTNUM_RMAP, 0, 0, agno, 0);
 
                /*
-                * AG header block: inodes
+                * mark the AG header regions as static metadata
+                * The BNO btree block is the first block after the
+                * headers, so it's location defines the size of region
+                * the static metadata consumes.
                 */
-               buf = libxfs_getbuf(mp->m_ddev_targp,
-                               XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
-                               XFS_FSS_TO_BB(mp, 1));
-               agi = XFS_BUF_TO_AGI(buf);
-               buf->b_ops = &xfs_agi_buf_ops;
-               memset(agi, 0, sectorsize);
-               agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
-               agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
-               agi->agi_seqno = cpu_to_be32(agno);
-               agi->agi_length = cpu_to_be32((xfs_agblock_t)agsize);
-               agi->agi_count = 0;
-               agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
-               agi->agi_level = cpu_to_be32(1);
-               if (sb_feat.finobt) {
-                       agi->agi_free_root = cpu_to_be32(XFS_FIBT_BLOCK(mp));
-                       agi->agi_free_level = cpu_to_be32(1);
+               rrec = XFS_RMAP_REC_ADDR(block, 1);
+               rrec->rm_startblock = 0;
+               rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
+               rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
+               rrec->rm_offset = 0;
+               be16_add_cpu(&block->bb_numrecs, 1);
+
+               /* account freespace btree root blocks */
+               rrec = XFS_RMAP_REC_ADDR(block, 2);
+               rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
+               rrec->rm_blockcount = cpu_to_be32(2);
+               rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
+               rrec->rm_offset = 0;
+               be16_add_cpu(&block->bb_numrecs, 1);
+
+               /* account inode btree root blocks */
+               rrec = XFS_RMAP_REC_ADDR(block, 3);
+               rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
+               rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
+                                               XFS_IBT_BLOCK(mp));
+               rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
+               rrec->rm_offset = 0;
+               be16_add_cpu(&block->bb_numrecs, 1);
+
+               /* account for rmap btree root */
+               rrec = XFS_RMAP_REC_ADDR(block, 4);
+               rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
+               rrec->rm_blockcount = cpu_to_be32(1);
+               rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
+               rrec->rm_offset = 0;
+               be16_add_cpu(&block->bb_numrecs, 1);
+
+               /* account for refcount btree root */
+               if (xfs_sb_version_hasreflink(sbp)) {
+                       rrec = XFS_RMAP_REC_ADDR(block, 5);
+                       rrec->rm_startblock = cpu_to_be32(libxfs_refc_block(mp));
+                       rrec->rm_blockcount = cpu_to_be32(1);
+                       rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
+                       rrec->rm_offset = 0;
+                       be16_add_cpu(&block->bb_numrecs, 1);
                }
-               agi->agi_freecount = 0;
-               agi->agi_newino = cpu_to_be32(NULLAGINO);
-               agi->agi_dirino = cpu_to_be32(NULLAGINO);
-               if (xfs_sb_version_hascrc(&mp->m_sb))
-                       platform_uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_uuid);
-               for (c = 0; c < XFS_AGI_UNLINKED_BUCKETS; c++)
-                       agi->agi_unlinked[c] = cpu_to_be32(NULLAGINO);
+
+               /* account for the log space */
+               if (is_log_ag) {
+                       rrec = XFS_RMAP_REC_ADDR(block,
+                                       be16_to_cpu(block->bb_numrecs) + 1);
+                       rrec->rm_startblock = cpu_to_be32(
+                                       XFS_FSB_TO_AGBNO(mp, cfg->logstart));
+                       rrec->rm_blockcount = cpu_to_be32(cfg->logblocks);
+                       rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_LOG);
+                       rrec->rm_offset = 0;
+                       be16_add_cpu(&block->bb_numrecs, 1);
+               }
+
                libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+       }
 
-               /*
-                * BNO btree root block
-                */
-               buf = libxfs_getbuf(mp->m_ddev_targp,
-                               XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
-                               bsize);
-               buf->b_ops = &xfs_allocbt_buf_ops;
-               block = XFS_BUF_TO_BLOCK(buf);
-               memset(block, 0, blocksize);
-               if (xfs_sb_version_hascrc(&mp->m_sb))
-                       libxfs_btree_init_block(mp, buf, XFS_ABTB_CRC_MAGIC, 0, 1,
-                                               agno, 0);
-               else
-                       libxfs_btree_init_block(mp, buf, XFS_ABTB_MAGIC, 0, 1,
-                                               agno, 0);
-
-               arec = XFS_ALLOC_REC_ADDR(mp, block, 1);
-               arec->ar_startblock = cpu_to_be32(libxfs_prealloc_blocks(mp));
-               if (loginternal && agno == logagno) {
-                       if (lalign) {
-                               /*
-                                * Have to insert two records
-                                * Insert pad record for stripe align of log
-                                */
-                               arec->ar_blockcount = cpu_to_be32(
-                                       XFS_FSB_TO_AGBNO(mp, logstart) -
-                                       be32_to_cpu(arec->ar_startblock));
-                               nrec = arec + 1;
-                               /*
-                                * Insert record at start of internal log
-                                */
-                               nrec->ar_startblock = cpu_to_be32(
-                                       be32_to_cpu(arec->ar_startblock) +
-                                       be32_to_cpu(arec->ar_blockcount));
-                               arec = nrec;
-                               be16_add_cpu(&block->bb_numrecs, 1);
-                       }
-                       /*
-                        * Change record start to after the internal log
-                        */
-                       be32_add_cpu(&arec->ar_startblock, logblocks);
+       libxfs_perag_put(pag);
+}
+
+static void
+initialise_ag_freespace(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno,
+       int                     worst_freelist)
+{
+       struct xfs_alloc_arg    args;
+       struct xfs_trans        *tp;
+       int                     c;
+
+       c = -libxfs_trans_alloc_rollable(mp, worst_freelist, &tp);
+       if (c)
+               res_failed(c);
+
+       memset(&args, 0, sizeof(args));
+       args.tp = tp;
+       args.mp = mp;
+       args.agno = agno;
+       args.alignment = 1;
+       args.pag = libxfs_perag_get(mp, agno);
+
+       libxfs_alloc_fix_freelist(&args, 0);
+       libxfs_perag_put(args.pag);
+       c = -libxfs_trans_commit(tp);
+       if (c)
+               res_failed(c);
+}
+
+/*
+ * rewrite several secondary superblocks with the root inode number filled out.
+ * This can help repair recovery from a trashed primary superblock without
+ * losing the root inode.
+ */
+static void
+rewrite_secondary_superblocks(
+       struct xfs_mount        *mp)
+{
+       struct xfs_buf          *buf;
+
+       /* rewrite the last superblock */
+       buf = libxfs_readbuf(mp->m_dev,
+                       XFS_AGB_TO_DADDR(mp, mp->m_sb.sb_agcount - 1,
+                               XFS_SB_DADDR),
+                       XFS_FSS_TO_BB(mp, 1),
+                       LIBXFS_EXIT_ON_FAILURE, &xfs_sb_buf_ops);
+       XFS_BUF_TO_SBP(buf)->sb_rootino = cpu_to_be64(mp->m_sb.sb_rootino);
+       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+       /* and one in the middle for luck if there's enough AGs for that */
+       if (mp->m_sb.sb_agcount <= 2)
+               return;
+
+       buf = libxfs_readbuf(mp->m_dev,
+                       XFS_AGB_TO_DADDR(mp, (mp->m_sb.sb_agcount - 1) / 2,
+                               XFS_SB_DADDR),
+                       XFS_FSS_TO_BB(mp, 1),
+                       LIBXFS_EXIT_ON_FAILURE, &xfs_sb_buf_ops);
+       XFS_BUF_TO_SBP(buf)->sb_rootino = cpu_to_be64(mp->m_sb.sb_rootino);
+       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+}
+
+int
+main(
+       int                     argc,
+       char                    **argv)
+{
+       xfs_agnumber_t          agno;
+       xfs_buf_t               *buf;
+       int                     c;
+       char                    *dfile = NULL;
+       char                    *logfile = NULL;
+       char                    *rtfile = NULL;
+       int                     dry_run = 0;
+       int                     discard = 1;
+       int                     force_overwrite = 0;
+       int                     quiet = 0;
+       char                    *protofile = NULL;
+       char                    *protostring = NULL;
+       int                     worst_freelist = 0;
+
+       struct libxfs_xinit     xi = {
+               .isdirect = LIBXFS_DIRECT,
+               .isreadonly = LIBXFS_EXCLUSIVELY,
+       };
+       struct xfs_mount        mbuf = {};
+       struct xfs_mount        *mp = &mbuf;
+       struct xfs_sb           *sbp = &mp->m_sb;
+       struct fs_topology      ft = {};
+       struct cli_params       cli = {
+               .xi = &xi,
+               .loginternal = 1,
+       };
+       struct mkfs_params      cfg = {};
+
+       /* build time defaults */
+       struct mkfs_default_params      dft = {
+               .source = _("package build definitions"),
+               .sectorsize = XFS_MIN_SECTORSIZE,
+               .blocksize = 1 << XFS_DFL_BLOCKSIZE_LOG,
+               .sb_feat = {
+                       .log_version = 2,
+                       .attr_version = 2,
+                       .dir_version = 2,
+                       .inode_align = true,
+                       .nci = false,
+                       .lazy_sb_counters = true,
+                       .projid32bit = true,
+                       .crcs_enabled = true,
+                       .dirftype = true,
+                       .finobt = true,
+                       .spinodes = true,
+                       .rmapbt = false,
+                       .reflink = false,
+                       .parent_pointers = false,
+                       .nodalign = false,
+                       .nortalign = false,
+               },
+       };
+
+       platform_uuid_generate(&cli.uuid);
+       progname = basename(argv[0]);
+       setlocale(LC_ALL, "");
+       bindtextdomain(PACKAGE, LOCALEDIR);
+       textdomain(PACKAGE);
+
+       /*
+        * TODO: Sourcing defaults from a config file
+        *
+        * Before anything else, see if there's a config file with different
+        * defaults. If a file exists in <package location>, read in the new
+        * default values and overwrite them in the &dft structure. This way the
+        * new defaults will apply before we parse the CLI, and the CLI will
+        * still be able to override them. When more than one source is
+        * implemented, emit a message to indicate where the defaults being
+        * used came from.
+        *
+        * printf(_("Default configuration sourced from %s\n"), dft.source);
+        */
+
+       /* copy new defaults into CLI parsing structure */
+       memcpy(&cli.sb_feat, &dft.sb_feat, sizeof(cli.sb_feat));
+       memcpy(&cli.fsx, &dft.fsx, sizeof(cli.fsx));
+
+       while ((c = getopt(argc, argv, "b:d:i:l:L:m:n:KNp:qr:s:CfV")) != EOF) {
+               switch (c) {
+               case 'C':
+               case 'f':
+                       force_overwrite = 1;
+                       break;
+               case 'b':
+               case 'd':
+               case 'i':
+               case 'l':
+               case 'm':
+               case 'n':
+               case 'r':
+               case 's':
+                       parse_subopts(c, optarg, &cli);
+                       break;
+               case 'L':
+                       if (strlen(optarg) > sizeof(sbp->sb_fname))
+                               illegal(optarg, "L");
+                       cfg.label = optarg;
+                       break;
+               case 'N':
+                       dry_run = 1;
+                       break;
+               case 'K':
+                       discard = 0;
+                       break;
+               case 'p':
+                       if (protofile)
+                               respec('p', NULL, 0);
+                       protofile = optarg;
+                       break;
+               case 'q':
+                       quiet = 1;
+                       break;
+               case 'V':
+                       printf(_("%s version %s\n"), progname, VERSION);
+                       exit(0);
+               case '?':
+                       unknown(optopt, "");
                }
-               /*
-                * Calculate the record block count and check for the case where
-                * the log might have consumed all available space in the AG. If
-                * so, reset the record count to 0 to avoid exposure of an invalid
-                * record start block.
-                */
-               arec->ar_blockcount = cpu_to_be32(agsize -
-                                       be32_to_cpu(arec->ar_startblock));
-               if (!arec->ar_blockcount)
-                       block->bb_numrecs = 0;
-
-               libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+       }
+       if (argc - optind > 1) {
+               fprintf(stderr, _("extra arguments\n"));
+               usage();
+       } else if (argc - optind == 1) {
+               dfile = xi.volname = getstr(argv[optind], &dopts, D_NAME);
+       } else
+               dfile = xi.dname;
 
-               /*
-                * CNT btree root block
-                */
-               buf = libxfs_getbuf(mp->m_ddev_targp,
-                               XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
-                               bsize);
-               buf->b_ops = &xfs_allocbt_buf_ops;
-               block = XFS_BUF_TO_BLOCK(buf);
-               memset(block, 0, blocksize);
-               if (xfs_sb_version_hascrc(&mp->m_sb))
-                       libxfs_btree_init_block(mp, buf, XFS_ABTC_CRC_MAGIC, 0, 1,
-                                               agno, 0);
-               else
-                       libxfs_btree_init_block(mp, buf, XFS_ABTC_MAGIC, 0, 1,
-                                               agno, 0);
-
-               arec = XFS_ALLOC_REC_ADDR(mp, block, 1);
-               arec->ar_startblock = cpu_to_be32(libxfs_prealloc_blocks(mp));
-               if (loginternal && agno == logagno) {
-                       if (lalign) {
-                               arec->ar_blockcount = cpu_to_be32(
-                                       XFS_FSB_TO_AGBNO(mp, logstart) -
-                                       be32_to_cpu(arec->ar_startblock));
-                               nrec = arec + 1;
-                               nrec->ar_startblock = cpu_to_be32(
-                                       be32_to_cpu(arec->ar_startblock) +
-                                       be32_to_cpu(arec->ar_blockcount));
-                               arec = nrec;
-                               be16_add_cpu(&block->bb_numrecs, 1);
-                       }
-                       be32_add_cpu(&arec->ar_startblock, logblocks);
-               }
-               /*
-                * Calculate the record block count and check for the case where
-                * the log might have consumed all available space in the AG. If
-                * so, reset the record count to 0 to avoid exposure of an invalid
-                * record start block.
-                */
-               arec->ar_blockcount = cpu_to_be32(agsize -
-                                       be32_to_cpu(arec->ar_startblock));
-               if (!arec->ar_blockcount)
-                       block->bb_numrecs = 0;
+       protostring = setup_proto(protofile);
 
-               libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+       /*
+        * Extract as much of the valid config as we can from the CLI input
+        * before opening the libxfs devices.
+        */
+       validate_blocksize(&cfg, &cli, &dft);
+       validate_sectorsize(&cfg, &cli, &dft, &ft, dfile, dry_run,
+                           force_overwrite);
 
-               /*
-                * refcount btree root block
-                */
-               if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-                       buf = libxfs_getbuf(mp->m_ddev_targp,
-                                       XFS_AGB_TO_DADDR(mp, agno,
-                                               libxfs_refc_block(mp)),
-                                       bsize);
-                       buf->b_ops = &xfs_refcountbt_buf_ops;
-
-                       block = XFS_BUF_TO_BLOCK(buf);
-                       memset(block, 0, blocksize);
-                       libxfs_btree_init_block(mp, buf, XFS_REFC_CRC_MAGIC, 0,
-                                               0, agno, 0);
-
-                       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
-               }
+       /*
+        * XXX: we still need to set block size and sector size global variables
+        * so that getnum/cvtnum works correctly
+        */
+       blocksize = cfg.blocksize;
+       sectorsize = cfg.sectorsize;
 
-               /*
-                * INO btree root block
-                */
-               buf = libxfs_getbuf(mp->m_ddev_targp,
-                               XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
-                               bsize);
-               buf->b_ops = &xfs_inobt_buf_ops;
-               block = XFS_BUF_TO_BLOCK(buf);
-               memset(block, 0, blocksize);
-               if (xfs_sb_version_hascrc(&mp->m_sb))
-                       libxfs_btree_init_block(mp, buf, XFS_IBT_CRC_MAGIC, 0, 0,
-                                               agno, 0);
-               else
-                       libxfs_btree_init_block(mp, buf, XFS_IBT_MAGIC, 0, 0,
-                                               agno, 0);
-               libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+       validate_log_sectorsize(&cfg, &cli, &dft);
+       validate_sb_features(&cfg, &cli);
 
-               /*
-                * Free INO btree root block
-                */
-               if (sb_feat.finobt) {
-                       buf = libxfs_getbuf(mp->m_ddev_targp,
-                                       XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
-                                       bsize);
-                       buf->b_ops = &xfs_inobt_buf_ops;
-                       block = XFS_BUF_TO_BLOCK(buf);
-                       memset(block, 0, blocksize);
-                       if (xfs_sb_version_hascrc(&mp->m_sb))
-                               libxfs_btree_init_block(mp, buf, XFS_FIBT_CRC_MAGIC, 0, 0,
-                                                       agno, 0);
-                       else
-                               libxfs_btree_init_block(mp, buf, XFS_FIBT_MAGIC, 0, 0,
-                                                       agno, 0);
-                       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
-               }
+       /*
+        * we've now completed basic validation of the features, sector and
+        * block sizes, so from this point onwards we use the values found in
+        * the cfg structure for them, not the command line structure.
+        */
+       validate_dirblocksize(&cfg, &cli);
+       validate_inodesize(&cfg, &cli);
 
-               /* RMAP btree root block */
-               if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
-                       struct xfs_rmap_rec     *rrec;
+       /*
+        * if the device size was specified convert it to a block count
+        * now we have a valid block size. These will be set to zero if
+        * nothing was specified, indicating we should use the full device.
+        */
+       cfg.dblocks = calc_dev_size(cli.dsize, &cfg, &dopts, D_SIZE, "data");
+       cfg.logblocks = calc_dev_size(cli.logsize, &cfg, &lopts, L_SIZE, "log");
+       cfg.rtblocks = calc_dev_size(cli.rtsize, &cfg, &ropts, R_SIZE, "rt");
 
-                       buf = libxfs_getbuf(mp->m_ddev_targp,
-                               XFS_AGB_TO_DADDR(mp, agno, XFS_RMAP_BLOCK(mp)),
-                               bsize);
-                       buf->b_ops = &xfs_rmapbt_buf_ops;
-                       block = XFS_BUF_TO_BLOCK(buf);
-                       memset(block, 0, blocksize);
+       validate_rtextsize(&cfg, &cli, &ft);
+       calc_stripe_factors(&cfg, &cli, &ft);
 
-                       libxfs_btree_init_block(mp, buf, XFS_RMAP_CRC_MAGIC, 0, 0,
-                                               agno, 0);
+       /*
+        * Open and validate the device configurations
+        */
+       open_devices(&cfg, &xi);
+       validate_overwrite(dfile, force_overwrite);
+       validate_datadev(&cfg, &cli);
+       validate_logdev(&cfg, &cli, &logfile);
+       validate_rtdev(&cfg, &cli, &rtfile);
 
-                       /*
-                        * mark the AG header regions as static metadata
-                        * The BNO btree block is the first block after the
-                        * headers, so it's location defines the size of region
-                        * the static metadata consumes.
-                        */
-                       rrec = XFS_RMAP_REC_ADDR(block, 1);
-                       rrec->rm_startblock = 0;
-                       rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
-                       rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
-                       rrec->rm_offset = 0;
-                       be16_add_cpu(&block->bb_numrecs, 1);
+       /*
+        * At this point when know exactly what size all the devices are,
+        * so we can start validating and calculating layout options that are
+        * dependent on device sizes. Once calculated, make sure everything
+        * aligns to device geometry correctly.
+        */
+       calculate_initial_ag_geometry(&cfg, &cli);
+       align_ag_geometry(&cfg);
 
-                       /* account freespace btree root blocks */
-                       rrec = XFS_RMAP_REC_ADDR(block, 2);
-                       rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
-                       rrec->rm_blockcount = cpu_to_be32(2);
-                       rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
-                       rrec->rm_offset = 0;
-                       be16_add_cpu(&block->bb_numrecs, 1);
+       calculate_imaxpct(&cfg, &cli);
 
-                       /* account inode btree root blocks */
-                       rrec = XFS_RMAP_REC_ADDR(block, 3);
-                       rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
-                       rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
-                                                       XFS_IBT_BLOCK(mp));
-                       rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
-                       rrec->rm_offset = 0;
-                       be16_add_cpu(&block->bb_numrecs, 1);
+       /*
+        * Set up the basic superblock parameters now so that we can use
+        * the geometry information we've already validated in libxfs
+        * provided functions to determine on-disk format information.
+        */
+       start_superblock_setup(&cfg, mp, sbp);
+       initialise_mount(&cfg, mp, sbp);
 
-                       /* account for rmap btree root */
-                       rrec = XFS_RMAP_REC_ADDR(block, 4);
-                       rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
-                       rrec->rm_blockcount = cpu_to_be32(1);
-                       rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
-                       rrec->rm_offset = 0;
-                       be16_add_cpu(&block->bb_numrecs, 1);
+       /*
+        * With the mount set up, we can finally calculate the log size
+        * constraints and do default size calculations and final validation
+        */
+       calculate_log_size(&cfg, &cli, mp);
 
-                       /* account for refcount btree root */
-                       if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-                               rrec = XFS_RMAP_REC_ADDR(block, 5);
-                               rrec->rm_startblock = cpu_to_be32(
-                                                       libxfs_refc_block(mp));
-                               rrec->rm_blockcount = cpu_to_be32(1);
-                               rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
-                               rrec->rm_offset = 0;
-                               be16_add_cpu(&block->bb_numrecs, 1);
-                       }
+       finish_superblock_setup(&cfg, mp, sbp);
 
-                       /* account for the log space */
-                       if (loginternal && agno == logagno) {
-                               rrec = XFS_RMAP_REC_ADDR(block,
-                                       be16_to_cpu(block->bb_numrecs) + 1);
-                               rrec->rm_startblock = cpu_to_be32(
-                                               XFS_FSB_TO_AGBNO(mp, logstart));
-                               rrec->rm_blockcount = cpu_to_be32(logblocks);
-                               rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_LOG);
-                               rrec->rm_offset = 0;
-                               be16_add_cpu(&block->bb_numrecs, 1);
-                       }
+       /* Print the intended geometry of the fs. */
+       if (!quiet || dry_run) {
+               struct xfs_fsop_geom    geo;
+               int                     error;
 
-                       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+               error = -libxfs_fs_geometry(sbp, &geo,
+                               XFS_FS_GEOM_MAX_STRUCT_VER);
+               if (error) {
+                       fprintf(stderr,
+       _("%s: failed to generate filesystem geometry\n"),
+                               progname);
+                       exit(1);
                }
 
-               libxfs_perag_put(pag);
+               xfs_report_geom(&geo, dfile, logfile, rtfile);
+               if (dry_run)
+                       exit(0);
        }
 
        /*
-        * Touch last block, make fs the right size if it's a file.
+        * All values have been validated, discard the old device layout.
         */
-       buf = libxfs_getbuf(mp->m_ddev_targp,
-               (xfs_daddr_t)XFS_FSB_TO_BB(mp, dblocks - 1LL), bsize);
-       memset(XFS_BUF_PTR(buf), 0, blocksize);
-       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+       if (discard && !dry_run)
+               discard_devices(&xi);
 
        /*
-        * Make sure we can write the last block in the realtime area.
+        * we need the libxfs buffer cache from here on in.
         */
-       if (mp->m_rtdev_targp->dev && rtblocks > 0) {
-               buf = libxfs_getbuf(mp->m_rtdev_targp,
-                               XFS_FSB_TO_BB(mp, rtblocks - 1LL), bsize);
-               memset(XFS_BUF_PTR(buf), 0, blocksize);
-               libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
-       }
+       libxfs_buftarg_init(mp, xi.ddev, xi.logdev, xi.rtdev);
 
        /*
-        * BNO, CNT free block list
+        * Before we mount the filesystem we need to make sure the devices have
+        * enough of the filesystem structure on them that allows libxfs to
+        * mount.
         */
-       for (agno = 0; agno < agcount; agno++) {
-               xfs_alloc_arg_t args;
-               xfs_trans_t     *tp;
-               struct xfs_trans_res tres = {0};
-
-               c = libxfs_trans_alloc(mp, &tres, worst_freelist, 0, 0, &tp);
-               if (c)
-                       res_failed(c);
+       prepare_devices(&cfg, &xi, mp, sbp, force_overwrite);
+       mp = libxfs_mount(mp, sbp, xi.ddev, xi.logdev, xi.rtdev, 0);
+       if (mp == NULL) {
+               fprintf(stderr, _("%s: filesystem failed to initialize\n"),
+                       progname);
+               exit(1);
+       }
 
-               memset(&args, 0, sizeof(args));
-               args.tp = tp;
-               args.mp = mp;
-               args.agno = agno;
-               args.alignment = 1;
-               args.pag = libxfs_perag_get(mp,agno);
+       /*
+        * Initialise all the static on disk metadata.
+        */
+       for (agno = 0; agno < cfg.agcount; agno++)
+               initialise_ag_headers(&cfg, mp, sbp, agno, &worst_freelist);
 
-               libxfs_alloc_fix_freelist(&args, 0);
-               libxfs_perag_put(args.pag);
-               libxfs_trans_commit(tp);
-       }
+       /*
+        * Initialise the freespace freelists (i.e. AGFLs) in each AG.
+        */
+       for (agno = 0; agno < cfg.agcount; agno++)
+               initialise_ag_freespace(mp, agno, worst_freelist);
 
        /*
         * Allocate the root inode and anything else in the proto file.
         */
-       parse_proto(mp, &fsx, &protostring);
+       parse_proto(mp, &cli.fsx, &protostring);
 
        /*
         * Protect ourselves against possible stupidity
@@ -3322,34 +4016,10 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
        }
 
        /*
-        * Write out multiple secondary superblocks with rootinode field set
+        * Re-write multiple secondary superblocks with rootinode field set
         */
-       if (mp->m_sb.sb_agcount > 1) {
-               /*
-                * the last superblock
-                */
-               buf = libxfs_readbuf(mp->m_dev,
-                               XFS_AGB_TO_DADDR(mp, mp->m_sb.sb_agcount-1,
-                                       XFS_SB_DADDR),
-                               XFS_FSS_TO_BB(mp, 1),
-                               LIBXFS_EXIT_ON_FAILURE, &xfs_sb_buf_ops);
-               XFS_BUF_TO_SBP(buf)->sb_rootino = cpu_to_be64(
-                                                       mp->m_sb.sb_rootino);
-               libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
-               /*
-                * and one in the middle for luck
-                */
-               if (mp->m_sb.sb_agcount > 2) {
-                       buf = libxfs_readbuf(mp->m_dev,
-                               XFS_AGB_TO_DADDR(mp, (mp->m_sb.sb_agcount-1)/2,
-                                       XFS_SB_DADDR),
-                               XFS_FSS_TO_BB(mp, 1),
-                               LIBXFS_EXIT_ON_FAILURE, &xfs_sb_buf_ops);
-                       XFS_BUF_TO_SBP(buf)->sb_rootino = cpu_to_be64(
-                                                       mp->m_sb.sb_rootino);
-                       libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
-               }
-       }
+       if (mp->m_sb.sb_agcount > 1)
+               rewrite_secondary_superblocks(mp);
 
        /*
         * Dump all inodes and buffers before marking us all done.
@@ -3371,163 +4041,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
        if (xi.logdev && xi.logdev != xi.ddev)
                libxfs_device_close(xi.logdev);
        libxfs_device_close(xi.ddev);
+       libxfs_destroy();
 
        return 0;
 }
-
-static void
-conflict(
-       char            opt,
-       char            *tab[],
-       int             oldidx,
-       int             newidx)
-{
-       fprintf(stderr, _("Cannot specify both -%c %s and -%c %s\n"),
-               opt, tab[oldidx], opt, tab[newidx]);
-       usage();
-}
-
-
-static void
-illegal(
-       const char      *value,
-       const char      *opt)
-{
-       fprintf(stderr, _("Illegal value %s for -%s option\n"), value, opt);
-       usage();
-}
-
-static int
-ispow2(
-       unsigned int    i)
-{
-       return (i & (i - 1)) == 0;
-}
-
-static void __attribute__((noreturn))
-reqval(
-       char            opt,
-       char            *tab[],
-       int             idx)
-{
-       fprintf(stderr, _("-%c %s option requires a value\n"), opt, tab[idx]);
-       usage();
-}
-
-static void
-respec(
-       char            opt,
-       char            *tab[],
-       int             idx)
-{
-       fprintf(stderr, "-%c ", opt);
-       if (tab)
-               fprintf(stderr, "%s ", tab[idx]);
-       fprintf(stderr, _("option respecified\n"));
-       usage();
-}
-
-static void
-unknown(
-       char            opt,
-       char            *s)
-{
-       fprintf(stderr, _("unknown option -%c %s\n"), opt, s);
-       usage();
-}
-
-long long
-cvtnum(
-       unsigned int    blksize,
-       unsigned int    sectsize,
-       const char      *s)
-{
-       long long       i;
-       char            *sp;
-       int             c;
-
-       i = strtoll(s, &sp, 0);
-       if (i == 0 && sp == s)
-               return -1LL;
-       if (*sp == '\0')
-               return i;
-
-       if (sp[1] != '\0')
-               return -1LL;
-
-       if (*sp == 'b') {
-               if (!blksize) {
-                       fprintf(stderr,
-_("Blocksize must be provided prior to using 'b' suffix.\n"));
-                       usage();
-               } else {
-                       return i * blksize;
-               }
-       }
-       if (*sp == 's') {
-               if (!sectsize) {
-                       fprintf(stderr,
-_("Sectorsize must be specified prior to using 's' suffix.\n"));
-                       usage();
-               } else {
-                       return i * sectsize;
-               }
-       }
-
-       c = tolower(*sp);
-       switch (c) {
-       case 'e':
-               i *= 1024LL;
-               /* fall through */
-       case 'p':
-               i *= 1024LL;
-               /* fall through */
-       case 't':
-               i *= 1024LL;
-               /* fall through */
-       case 'g':
-               i *= 1024LL;
-               /* fall through */
-       case 'm':
-               i *= 1024LL;
-               /* fall through */
-       case 'k':
-               return i * 1024LL;
-       default:
-               break;
-       }
-       return -1LL;
-}
-
-static void __attribute__((noreturn))
-usage( void )
-{
-       fprintf(stderr, _("Usage: %s\n\
-/* blocksize */                [-b log=n|size=num]\n\
-/* metadata */         [-m crc=0|1,finobt=0|1,uuid=xxx,rmapbt=0|1,reflink=0|1]\n\
-/* data subvol */      [-d agcount=n,agsize=n,file,name=xxx,size=num,\n\
-                           (sunit=value,swidth=value|su=num,sw=num|noalign),\n\
-                           sectlog=n|sectsize=num\n\
-/* force overwrite */  [-f]\n\
-/* inode size */       [-i log=n|perblock=n|size=num,maxpct=n,attr=0|1|2,\n\
-                           projid32bit=0|1,sparse=0|1]\n\
-/* no discard */       [-K]\n\
-/* log subvol */       [-l agnum=n,internal,size=num,logdev=xxx,version=n\n\
-                           sunit=value|su=num,sectlog=n|sectsize=num,\n\
-                           lazy-count=0|1]\n\
-/* label */            [-L label (maximum 12 characters)]\n\
-/* naming */           [-n log=n|size=num,version=2|ci,ftype=0|1]\n\
-/* no-op info only */  [-N]\n\
-/* prototype file */   [-p fname]\n\
-/* quiet */            [-q]\n\
-/* realtime subvol */  [-r extsize=num,size=num,rtdev=xxx]\n\
-/* sectorsize */       [-s log=n|size=num]\n\
-/* version */          [-V]\n\
-                       devicename\n\
-<devicename> is required unless -d name=xxx is given.\n\
-<num> is xxx (bytes), xxxs (sectors), xxxb (fs blocks), xxxk (xxx KiB),\n\
-      xxxm (xxx MiB), xxxg (xxx GiB), xxxt (xxx TiB) or xxxp (xxx PiB).\n\
-<value> is xxx (512 byte blocks).\n"),
-               progname);
-       exit(1);
-}