[PATCH] [RFC, v2] mkfs.xfs: factor the crap out of input parsing

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Dave Chinner <dchinner@xxxxxxxxxx>

The mkfs.xfs code base is a mess. I started factoring it years ago, and
the primary goal I had for that work was to make mkfs.xfs
maintainable and lay a solid foundation for being able to modify it
in future.

This patch takes up where that work stalled at - factoring the crap
out of the input parsing and parameter validation. To do this I
hacked together a quick global "cli geometry" structure that holds
the handful of values that can be set from the CLI, and pushed all
the other feature flag things into the existing sb_feat global
structure.

I then factored the sub options parsing loop using a table setup
with to call a suboption specific parser function that does the
table based option parsing. This gets *all* the suboption parsing
out of the main option parsing loop which is soooo much simpler:


                case 'b':
                case 'd':
                case 'i':
                case 'l':
                case 'm':
                case 'n':
                case 'r':
                case 's':
                        parse_subopts(c, optarg);
                        break;

Having done that, I started on the mess that followed the option
parsing loop and started separating that out into functions that
validate and set a clear set of parameters that the mkfs code then
later uses. Hence we end up with code like this in the main
function.

        /*
         * Extract as much of the valid config as we can from the CLI input
         * before opening the libxfs devices.
         */
        validate_blocksize(&blocksize, &blocklog);
        validate_sectorsize(&sectorsize, &sectorlog, &ft, dfile, Nflag,
                            force_overwrite);
        validate_log_sectorsize(&lsectorsize, &lsectorlog,
                                sectorsize, sectorlog);
        validate_sb_features();

        validate_dirblocksize(&dirblocksize, &dirblocklog, blocklog);
        validate_inodesize(&isize, &inodelog, &inopblock, blocklog);

This has greatly reduced the spaghetti code and removed all the
duplicated checks and strangely dissociated nature of all teh
validation and calculations. The stripe unit validation is now all
in one function, instead of strewn across 5 disjoint hunks that
had some amount of duplicated functionality. Same goes for device
checks, calculating AG sizes, etc.

As such, it's far, far easier to see what the overall structure of
mkfs is doing now. The factoring of all the input validation and
format calculations results in the high level code in then main()
function documenting the steps we are taking quite clearly.

It also points out that we have a /lot/ of parameters that we
calculate and pass around within these functions, indicating this is
the next area of abstraction and simplification. This also points
out that we can separate the mkfs functionality clearly into three
parts: input parsing, validation and format calculations, and
finally creating the on-disk structures.

The patch as it stands has been smoke tested, but I have not checked
that the on-disk layout for every possible option is being correctly
handled. It passes xfs/191-mkfs-input-validation with the exception
of 2 cases try to set the sector size on an internal log, which this
patch disallows.

The next steps are to:

	- replace the hacking struct cligeo with a key-value
	  structure for input parameter storage to abstract input
	  parsing from validation and calculation

	- extend the key-value structure for holding superblock
	  feature defaults to holding all default parameter values
	  to be used when no values are provided by input parsing

	- clean up the validation and calculation code to use the
	  abstracted input and default structures

	- have the calculation code output into a single output
	  structure which we can then pass to the on-disk layout
	  code.

	- add a key-value config file interface to override the
	  build in default values structure members, hence allowing
	  distros to ship different default configs without needing
	  to change the source code.

	- split it all up into reviewable pieces.


Signed-Off-By: Dave Chinner <dchinner@xxxxxxxxxx>
---
 include/libxfs.h |    2 +-
 mkfs/xfs_mkfs.c  | 2710 ++++++++++++++++++++++++++++++------------------------
 2 files changed, 1509 insertions(+), 1203 deletions(-)

diff --git a/include/libxfs.h b/include/libxfs.h
index e5e152378c2c..b77819b1adcc 100644
--- a/include/libxfs.h
+++ b/include/libxfs.h
@@ -95,7 +95,7 @@ extern uint32_t crc32c_le(uint32_t crc, unsigned char const *p, size_t len);
 /*
  * Argument structure for libxfs_init().
  */
-typedef struct {
+typedef struct libxfs_xinit {
 				/* input parameters */
 	char            *volname;       /* pathname of volume */
 	char            *dname;         /* pathname of data "subvolume" */
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index 7bb6408f9b77..4819295d9a2f 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -24,11 +24,11 @@
 /*
  * Prototypes for internal functions.
  */
-static void conflict(char opt, char *tab[], int oldidx, int newidx);
+static void conflict(char opt, const 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 __attribute__((noreturn)) void reqval(char opt, const char *tab[], int idx);
+static void respec(char opt, const char *tab[], int idx);
 static void unknown(char opt, char *s);
 static int  ispow2(unsigned int i);
 
@@ -428,6 +428,8 @@ struct opt_params lopts = {
 		{ .index = L_INTERNAL,
 		  .conflicts = { L_FILE,
 				 L_DEV,
+				 L_SECTLOG,
+				 L_SECTSIZE,
 				 LAST_CONFLICT },
 		  .minval = 0,
 		  .maxval = 1,
@@ -469,6 +471,7 @@ struct opt_params lopts = {
 		},
 		{ .index = L_SECTLOG,
 		  .conflicts = { L_SECTSIZE,
+				 L_INTERNAL,
 				 LAST_CONFLICT },
 		  .minval = XFS_MIN_SECTORSIZE_LOG,
 		  .maxval = XFS_MAX_SECTORSIZE_LOG,
@@ -476,6 +479,7 @@ struct opt_params lopts = {
 		},
 		{ .index = L_SECTSIZE,
 		  .conflicts = { L_SECTLOG,
+				 L_INTERNAL,
 				 LAST_CONFLICT },
 		  .convert = true,
 		  .is_power_2 = true,
@@ -711,6 +715,8 @@ struct opt_params mopts = {
 
 /*
  * Use this macro before we have superblock and mount structure
+ *
+ * XXX: macros with undeclared variables are the spawn of a hateful deity
  */
 #define	DTOBT(d)	((xfs_rfsblock_t)((d) >> (blocklog - BBSHIFT)))
 
@@ -729,74 +735,6 @@ struct opt_params mopts = {
  */
 #define WHACK_SIZE (128 * 1024)
 
-/*
- * Convert lsu to lsunit for 512 bytes blocks and check validity of the values.
- */
-static void
-calc_stripe_factors(
-	int		dsu,
-	int		dsw,
-	int		dsectsz,
-	int		lsu,
-	int		lsectsz,
-	int		*dsunit,
-	int		*dswidth,
-	int		*lsunit)
-{
-	/* Handle data sunit/swidth options */
-	if ((*dsunit && !*dswidth) || (!*dsunit && *dswidth)) {
-		fprintf(stderr,
-			_("both data sunit and data swidth options "
-			"must be specified\n"));
-		usage();
-	}
-
-	if (dsu || dsw) {
-		if ((dsu && !dsw) || (!dsu && dsw)) {
-			fprintf(stderr,
-				_("both data su and data sw options "
-				"must be specified\n"));
-			usage();
-		}
-
-		if (dsu % dsectsz) {
-			fprintf(stderr,
-				_("data su must be a multiple of the "
-				"sector size (%d)\n"), dsectsz);
-			usage();
-		}
-
-		*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);
-	}
-}
-
 static void
 check_device_type(
 	const char	*name,
@@ -871,91 +809,6 @@ check_device_type(
 	usage();
 }
 
-static void
-fixup_log_stripe_unit(
-	int		lsflag,
-	int		sunit,
-	xfs_rfsblock_t	*logblocks,
-	int		blocklog)
-{
-	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();
-		}
-	}
-}
-
-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,
@@ -1054,7 +907,7 @@ validate_ag_geometry(
 
 static void
 zero_old_xfs_structures(
-	libxfs_init_t		*xi,
+	struct libxfs_xinit	*xi,
 	xfs_sb_t		*new_sb)
 {
 	void 			*buf;
@@ -1161,6 +1014,31 @@ struct sb_feat_args {
 	bool	parent_pointers;
 	bool	rmapbt;
 	bool	reflink;
+	bool	nodalign;
+	bool	nortalign;
+	uuid_t	m_uuid;
+};
+
+/*
+ * Default values for superblock features
+ */
+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,
+	.nodalign = false,
+	.nortalign = false,
 };
 
 static void
@@ -1283,7 +1161,7 @@ check_opt(
 		fprintf(stderr,
 	("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);
 	}
 
 	/*
@@ -1296,11 +1174,11 @@ 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;
 	}
 
@@ -1312,7 +1190,7 @@ check_opt(
 			break;
 		if (opts->subopt_params[conflict_opt].seen ||
 		    opts->subopt_params[conflict_opt].str_seen)
-			conflict(opts->name, (char **)opts->subopts,
+			conflict(opts->name, opts->subopts,
 				 conflict_opt, index);
 	}
 }
@@ -1330,7 +1208,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;
 	}
 
@@ -1386,615 +1264,493 @@ 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)
+/* geometry specified on input. 0 is not specified */
+struct cli_geometry {
+	int	sectorsize;
+	int	blocksize;
+	char	*dsize;
+	int64_t	agsize;
+	int	agcount;
+	int	dsunit;
+	int	dswidth;
+	int	dsu;
+	int	dsw;
+	int	inodesize;
+	int	inopblock;
+	int	imaxpct;
+	int	dirblocksize;
+	int	logagno;
+	char	*logsize;
+	int	lsectorsize;
+	int	loginternal;
+	int	lsu;
+	int	lsunit;
+	char	*rtextsize;
+	char	*rtsize;
+
+} cligeo;
+
+/* root inode characteristics */
+struct fsxattr		fsx;
+
+/* libxfs file/device setup info */
+struct libxfs_xinit	xi;
+
+static int
+block_opts_parser(
+	struct opt_params	*opts,
+	int			subopt,
+	char			*value)
 {
-	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			dsflag;
-	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;
-	dsflag = 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);
-					dsflag = 1;
-					break;
-				case D_SWIDTH:
-					dswidth = getnum(value, &dopts,
-							 D_SWIDTH);
-					dsflag = 1;
-					break;
-				case D_SU:
-					dsu = getnum(value, &dopts, D_SU);
-					dsflag = 1;
-					break;
-				case D_SW:
-					dsw = getnum(value, &dopts, D_SW);
-					dsflag = 1;
-					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, "");
-		}
+	switch (subopt) {
+	case B_LOG:
+		blocklog = getnum(value, &bopts, B_LOG);
+		cligeo.blocksize = 1 << blocklog;
+		break;
+	case B_SIZE:
+		cligeo.blocksize = getnum(value, &bopts, B_SIZE);
+		break;
+	default:
+		return -EINVAL;
 	}
-	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.
+	 * XXX: Hack: update global block size field so other number conversion
+	 * works. Should convert all the remaining fields that relay on
+	 * block/sector sizes in specifications to the two phase getstr/getnum
+	 * process already used for D_SIZE and others.
 	 */
-	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();
-	}
+	blocksize = cligeo.blocksize;
+	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)
+{
+	int			sectorlog;
+
+	switch (subopt) {
+	case D_AGCOUNT:
+		cligeo.agcount = getnum(value, opts, D_AGCOUNT);
+		break;
+	case D_AGSIZE:
+		cligeo.agsize = getnum(value, opts, D_AGSIZE);
+		break;
+	case D_FILE:
+		xi.disfile = getnum(value, opts, D_FILE);
+		break;
+	case D_NAME:
+		xi.dname = getstr(value, opts, D_NAME);
+		break;
+	case D_SIZE:
+		cligeo.dsize = getstr(value, opts, D_SIZE);
+		break;
+	case D_SUNIT:
+		cligeo.dsunit = getnum(value, opts, D_SUNIT);
+		break;
+	case D_SWIDTH:
+		cligeo.dswidth = getnum(value, opts, D_SWIDTH);
+		break;
+	case D_SU:
+		cligeo.dsu = getnum(value, opts, D_SU);
+		break;
+	case D_SW:
+		cligeo.dsw = getnum(value, opts, D_SW);
+		break;
+	case D_NOALIGN:
+		sb_feat.nodalign = getnum(value, opts, D_NOALIGN);
+		break;
+	case D_SECTLOG:
+		if (cligeo.sectorsize)
+			conflict('d', opts->subopts, D_SECTSIZE, D_SECTLOG);
+		sectorlog = getnum(value, opts, D_SECTLOG);
+		cligeo.sectorsize = 1 << sectorlog;
+		/* XXX: Hack, see S_SECTLOG */
+		sectorsize = cligeo.sectorsize;
+		break;
+	case D_SECTSIZE:
+		cligeo.sectorsize = getnum(value, opts, D_SECTSIZE);
+		/* XXX: Hack, see S_SECTSIZE */
+		sectorsize = cligeo.sectorsize;
+		break;
+	case D_RTINHERIT:
+		if (getnum(value, opts, D_RTINHERIT))
+			fsx.fsx_xflags |= XFS_DIFLAG_RTINHERIT;
+		break;
+	case D_PROJINHERIT:
+		fsx.fsx_projid = getnum(value, opts, D_PROJINHERIT);
+		fsx.fsx_xflags |= XFS_DIFLAG_PROJINHERIT;
+		break;
+	case D_EXTSZINHERIT:
+		fsx.fsx_extsize = getnum(value, opts, D_EXTSZINHERIT);
+		fsx.fsx_xflags |= XFS_DIFLAG_EXTSZINHERIT;
+		break;
+	default:
+		return -EINVAL;
 	}
+	return 0;
+}
 
-	/*
-	 * Before anything else, verify that we are correctly operating on
-	 * files or block devices and set the control parameters correctly.
-	 * 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.
+static int
+inode_opts_parser(
+	struct opt_params	*opts,
+	int			subopt,
+	char			*value)
+{
+	int			inodelog;
+
+	switch (subopt) {
+	case I_ALIGN:
+		sb_feat.inode_align = getnum(value, &iopts, I_ALIGN);
+		break;
+	case I_LOG:
+		inodelog = getnum(value, &iopts, I_LOG);
+		cligeo.inodesize = 1 << inodelog;
+		break;
+	case I_MAXPCT:
+		cligeo.imaxpct = getnum(value, &iopts, I_MAXPCT);
+		break;
+	case I_PERBLOCK:
+		cligeo.inopblock = getnum(value, &iopts, I_PERBLOCK);
+		break;
+	case I_SIZE:
+		cligeo.inodesize = getnum(value, &iopts, I_SIZE);
+		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:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+log_opts_parser(
+	struct opt_params	*opts,
+	int			subopt,
+	char			*value)
+{
+	int			lsectorlog;
+
+	switch (subopt) {
+	case L_AGNUM:
+		cligeo.logagno = getnum(value, &lopts, L_AGNUM);
+		break;
+	case L_FILE:
+		xi.lisfile = getnum(value, &lopts, L_FILE);
+		break;
+	case L_INTERNAL:
+		cligeo.loginternal = getnum(value, &lopts, L_INTERNAL);
+		break;
+	case L_SU:
+		cligeo.lsu = getnum(value, &lopts, L_SU);
+		break;
+	case L_SUNIT:
+		cligeo.lsunit = getnum(value, &lopts, L_SUNIT);
+		break;
+	case L_NAME:
+	case L_DEV:
+		xi.logname = getstr(value, &lopts, L_NAME);
+		cligeo.loginternal = 0;
+		break;
+	case L_VERSION:
+		sb_feat.log_version = getnum(value, &lopts, L_VERSION);
+		break;
+	case L_SIZE:
+		cligeo.logsize = getstr(value, &lopts, L_SIZE);
+		break;
+	case L_SECTLOG:
+		lsectorlog = getnum(value, &lopts, L_SECTLOG);
+		cligeo.lsectorsize = 1 << lsectorlog;
+		break;
+	case L_SECTSIZE:
+		cligeo.lsectorsize = getnum(value, &lopts, L_SECTSIZE);
+		break;
+	case L_LAZYSBCNTR:
+		sb_feat.lazy_sb_counters = getnum(value, &lopts, L_LAZYSBCNTR);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+meta_opts_parser(
+	struct opt_params	*opts,
+	int			subopt,
+	char			*value)
+{
+	switch (subopt) {
+	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', opts->subopts, M_UUID);
+		if (platform_uuid_parse(value, &sb_feat.m_uuid))
+			illegal(value, "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:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+naming_opts_parser(
+	struct opt_params	*opts,
+	int			subopt,
+	char			*value)
+{
+	int			dirblocklog;
+
+	switch (subopt) {
+	case N_LOG:
+		dirblocklog = getnum(value, opts, N_LOG);
+		cligeo.dirblocksize = 1 << dirblocklog;
+		break;
+	case N_SIZE:
+		cligeo.dirblocksize = getnum(value, opts, N_SIZE);
+		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, opts, N_VERSION);
+		}
+		break;
+	case N_FTYPE:
+		sb_feat.dirftype = getnum(value, opts, N_FTYPE);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+rtdev_opts_parser(
+	struct opt_params	*opts,
+	int			subopt,
+	char			*value)
+{
+	switch (subopt) {
+	case R_EXTSIZE:
+		cligeo.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:
+		cligeo.rtsize = getstr(value, &ropts, R_SIZE);
+		break;
+	case R_NOALIGN:
+		sb_feat.nortalign = getnum(value, &ropts, R_NOALIGN);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+sector_opts_parser(
+	struct opt_params	*opts,
+	int			subopt,
+	char			*value)
+{
+	int			sectorlog;
+
+	switch (subopt) {
+	case S_LOG:
+	case S_SECTLOG:
+		if (cligeo.sectorsize)
+			conflict('s', opts->subopts, S_SECTSIZE, S_SECTLOG);
+		sectorlog = getnum(value, &sopts, S_SECTLOG);
+		cligeo.sectorsize = 1 << sectorlog;
+		cligeo.lsectorsize = cligeo.sectorsize;
+		break;
+	case S_SIZE:
+	case S_SECTSIZE:
+		if (cligeo.sectorsize)
+			conflict('s', opts->subopts, S_SECTLOG, S_SECTSIZE);
+		cligeo.sectorsize = getnum(value, &sopts, S_SECTSIZE);
+		cligeo.lsectorsize = cligeo.sectorsize;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * XXX: Hack: update global sector size field so other number conversion
+	 * works. Should convert all the remaining fields that relay on
+	 * block/sector sizes in specifications to the two phase getstr/getnum
+	 * process already used for D_SIZE and others.
+	 */
+	sectorsize = cligeo.sectorsize;
+	return 0;
+}
+
+struct subopts {
+	char		opt;
+	struct opt_params *opts;
+	int		(*parser)();
+} 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 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);
+		if (ret)
+			unknown(opt, value);
+	}
+}
+
+static void
+validate_blocksize(
+	unsigned int	*size,
+	int		*size_log)
+{
+
+	/*
+	 * 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 (!cligeo.blocksize) {
+		*size_log = XFS_DFL_BLOCKSIZE_LOG;
+		*size = 1 << XFS_DFL_BLOCKSIZE_LOG;
+	} else {
+		*size = cligeo.blocksize;
+		*size_log = libxfs_highbit32(cligeo.blocksize);
+	}
+
+	/* validate block sizes are in range */
+	if (*size < XFS_MIN_BLOCKSIZE || *size > XFS_MAX_BLOCKSIZE) {
+		fprintf(stderr, _("illegal block size %d\n"), *size);
+		usage();
+	}
+	if (sb_feat.crcs_enabled && *size < XFS_MIN_CRC_BLOCKSIZE) {
+		fprintf(stderr,
+_("Minimum block size for CRC enabled filesystems is %d bytes.\n"),
+			XFS_MIN_CRC_BLOCKSIZE);
+		usage();
+	}
+}
+
+static void
+validate_sectorsize(
+	unsigned int	*size,
+	int		*size_log,
+	struct fs_topology *ft,
+	char		*dfile,
+	int		dry_run,
+	int		force_overwrite)
+{
+	/* set configured sector sizes in preparation for checks */
+	if (!cligeo.sectorsize) {
+		*size_log = XFS_MIN_SECTORSIZE_LOG;
+		*size = XFS_MIN_SECTORSIZE;
+	} else {
+		*size = cligeo.sectorsize;
+		*size_log = libxfs_highbit32(cligeo.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, &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,
+	check_device_type(dfile, &xi.disfile, !cligeo.dsize, !dfile,
+			  dry_run ? NULL : &xi.dcreat, force_overwrite, "d");
+	if (!cligeo.loginternal)
+		check_device_type(xi.logname, &xi.lisfile, !cligeo.logsize,
+				  !xi.logname, dry_run ? NULL : &xi.lcreat,
 				  force_overwrite, "l");
 	if (xi.rtname)
-		check_device_type(xi.rtname, &xi.risfile, !rtsize, !xi.rtname,
-				  Nflag ? NULL : &xi.rcreat,
+		check_device_type(xi.rtname, &xi.risfile, !cligeo.rtsize,
+				  !xi.rtname, dry_run ? NULL : &xi.rcreat,
 				  force_overwrite, "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.
+	 */
 	if (xi.disfile || xi.lisfile || xi.risfile)
 		xi.isdirect = 0;
 
-	memset(&ft, 0, sizeof(ft));
-	get_topology(&xi, &ft, force_overwrite);
+	memset(ft, 0, sizeof(*ft));
+	get_topology(&xi, ft, force_overwrite);
 
-	if (!ssflag) {
+	if (!cligeo.sectorsize) {
 		/*
 		 * Unless specified manually on the command line use the
 		 * advertised sector size of the device.  We use the physical
@@ -2004,56 +1760,100 @@ _("Minimum block size for CRC enabled filesystems is %d bytes.\n"),
 		 */
 
 		/* Older kernels may not have physical/logical distinction */
-		if (!ft.psectorsize)
-			ft.psectorsize = ft.lsectorsize;
+		if (!ft->psectorsize)
+			ft->psectorsize = ft->lsectorsize;
 
-		sectorsize = ft.psectorsize ? ft.psectorsize :
+		*size = ft->psectorsize ? ft->psectorsize :
 					      XFS_MIN_SECTORSIZE;
 
-		if ((blocksize < sectorsize) && (blocksize >= ft.lsectorsize)) {
+		if ((blocksize < *size) && (blocksize >= ft->lsectorsize)) {
 			fprintf(stderr,
 _("specified blocksize %d is less than device physical sector size %d\n"),
-				blocksize, ft.psectorsize);
+				blocksize, ft->psectorsize);
 			fprintf(stderr,
 _("switching to logical sector size %d\n"),
-				ft.lsectorsize);
-			sectorsize = ft.lsectorsize ? ft.lsectorsize :
+				ft->lsectorsize);
+			*size = ft->lsectorsize ? ft->lsectorsize :
 						      XFS_MIN_SECTORSIZE;
 		}
-	}
 
-	if (!ssflag) {
-		sectorlog = libxfs_highbit32(sectorsize);
-		if (loginternal) {
-			lsectorsize = sectorsize;
-			lsectorlog = sectorlog;
-		}
+		*size_log = libxfs_highbit32(*size);
 	}
 
-	if (sectorsize < XFS_MIN_SECTORSIZE ||
-	    sectorsize > XFS_MAX_SECTORSIZE || sectorsize > blocksize) {
-		if (ssflag)
-			fprintf(stderr, _("illegal sector size %d\n"), sectorsize);
+	/* validate specified/probed sector size */
+	if (*size < XFS_MIN_SECTORSIZE ||
+	    *size > XFS_MAX_SECTORSIZE || *size > blocksize) {
+		if (cligeo.sectorsize)
+			fprintf(stderr, _("illegal sector size %d\n"), *size);
 		else
 			fprintf(stderr,
 _("block size %d cannot be smaller than logical sector size %d\n"),
-				blocksize, ft.lsectorsize);
+				blocksize, ft->lsectorsize);
 		usage();
 	}
-	if (sectorsize < ft.lsectorsize) {
+	if (*size < ft->lsectorsize) {
 		fprintf(stderr, _("illegal sector size %d; hw sector is %d\n"),
-			sectorsize, ft.lsectorsize);
+			*size, ft->lsectorsize);
+		usage();
+	}
+}
+
+/*
+ * Grab log sector size and validate.
+ *
+ * XXX: probe sector size on external log device rather than using ssize?
+ */
+static void
+validate_log_sectorsize(
+	int	*lsize,
+	int	*lsize_log,
+	int	sectorsize,
+	int	sectorlog)
+{
+
+	if (cligeo.loginternal && cligeo.lsectorsize &&
+	    cligeo.lsectorsize != 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 (!cligeo.lsectorsize) {
+		*lsize_log = sectorlog;
+		*lsize = sectorsize;
+	} else {
+		*lsize = cligeo.lsectorsize;
+		*lsize_log = libxfs_highbit32(cligeo.lsectorsize);
+	}
+
+	if (*lsize < XFS_MIN_SECTORSIZE ||
+	    *lsize > XFS_MAX_SECTORSIZE || *lsize > blocksize) {
+		fprintf(stderr, _("illegal log sector size %d\n"), *lsize);
 		usage();
-	} else if (lsectorsize > XFS_MIN_SECTORSIZE && !lsu && !lsunit) {
-		lsu = blocksize;
+	}
+	if (*lsize > XFS_MIN_SECTORSIZE) {
+		if (sb_feat.log_version < 2) {
+			/* user specified non-default log version */
+			fprintf(stderr,
+_("Version 1 logs do not support sector size %d\n"),
+				*lsize);
+			usage();
+		}
+		if (cligeo.lsu <= 0 && cligeo.lsunit <= 0)
+			cligeo.lsu = blocksize;
+	}
+
+	/* if lsu or lsunit was specified, automatically use v2 logs */
+	if ((cligeo.lsu || cligeo.lsunit) && sb_feat.log_version == 1) {
+		fprintf(stderr,
+			_("log stripe unit specified, using v2 logs\n"));
 		sb_feat.log_version = 2;
 	}
 
+}
+
+static void
+validate_sb_features(void)
+{
+
 	/*
 	 * Now we have blocks and sector sizes set up, check parameters that are
 	 * no longer optional for CRC enabled filesystems.  Catch them up front
@@ -2061,7 +1861,8 @@ _("block size %d cannot be smaller than logical sector size %d\n"),
 	 */
 	if (sb_feat.crcs_enabled) {
 		/* minimum inode size is 512 bytes, ipflag checked later */
-		if ((isflag || ilflag) && inodelog < XFS_DINODE_DFL_CRC_LOG) {
+		if (cligeo.inodesize &&
+		    cligeo.inodesize < (1 << XFS_DINODE_DFL_CRC_LOG)) {
 			fprintf(stderr,
 _("Minimum inode size for CRCs is %d bytes\n"),
 				1 << XFS_DINODE_DFL_CRC_LOG);
@@ -2103,6 +1904,14 @@ _("V2 attribute format always enabled on CRC enabled filesytems\n"));
 _("32 bit Project IDs always enabled on CRC enabled filesytems\n"));
 			usage();
 		}
+
+		/* ftype always on */
+		if (!sb_feat.dirftype) {
+			fprintf(stderr,
+_("Directory ftype field always enabled on CRC enabled filesytems\n"));
+			usage();
+		}
+
 	} else {
 		/*
 		 * The kernel doesn't currently support crc=0,finobt=1
@@ -2149,103 +1958,137 @@ _("rmapbt not supported with realtime devices\n"));
 		usage();
 		sb_feat.rmapbt = false;
 	}
+}
 
-	if (nsflag || nlflag) {
-		if (dirblocksize < blocksize ||
-					dirblocksize > XFS_MAX_BLOCKSIZE) {
-			fprintf(stderr, _("illegal directory block size %d\n"),
-				dirblocksize);
-			usage();
-		}
-	} else {
+static void
+validate_dirblocksize(
+	int	*size,
+	int	*size_log,
+	int	blocklog)
+{
+
+	if (cligeo.dirblocksize &&
+	    (cligeo.dirblocksize < blocksize ||
+	     cligeo.dirblocksize > XFS_MAX_BLOCKSIZE)) {
+		fprintf(stderr, _("illegal directory block size %d\n"),
+			cligeo.dirblocksize);
+		usage();
+	}
+
+	if (!cligeo.dirblocksize) {
 		if (blocksize < (1 << XFS_MIN_REC_DIRSIZE))
-			dirblocklog = XFS_MIN_REC_DIRSIZE;
+			*size_log = XFS_MIN_REC_DIRSIZE;
 		else
-			dirblocklog = blocklog;
-		dirblocksize = 1 << dirblocklog;
+			*size_log = blocklog;
+		*size = 1 << *size_log;
+	} else {
+		*size = cligeo.dirblocksize;
+		*size_log = libxfs_highbit32(cligeo.dirblocksize);
 	}
+}
 
+static void
+validate_inodesize(
+	int	*size,
+	int	*size_log,
+	int	*inodes_per_block,
+	int	blocklog)
+{
 
-	if (dsize) {
-		uint64_t dbytes;
-
-		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
+	if (cligeo.inopblock) {
+		*inodes_per_block = cligeo.inopblock;
+		*size_log = blocklog - libxfs_highbit32(cligeo.inopblock);
+		*size = 1 << *size_log;
+	} else if (cligeo.inodesize) {
+		*size = cligeo.inodesize;
+		*size_log = libxfs_highbit32(*size);
+		*inodes_per_block = blocksize / *size;
+	} else {
+		*size_log = sb_feat.crcs_enabled ? XFS_DINODE_DFL_CRC_LOG
 						: XFS_DINODE_DFL_LOG;
-		isize = 1 << inodelog;
+		*size = 1 << *size_log;
+		*inodes_per_block = blocksize / *size;
 	}
-	if (sb_feat.crcs_enabled && inodelog < XFS_DINODE_DFL_CRC_LOG) {
+	if (sb_feat.crcs_enabled && *size_log < 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 (*size > blocksize / XFS_MIN_INODE_PERBLOCK ||
+	    *inodes_per_block < XFS_MIN_INODE_PERBLOCK ||
+	    *size < XFS_DINODE_MIN_SIZE ||
+	    *size > XFS_DINODE_MAX_SIZE) {
+		int	maxsz;
 
-		logbytes = getnum(logsize, &lopts, L_SIZE);
-		if (logbytes % XFS_MIN_BLOCKSIZE) {
+		fprintf(stderr, _("illegal inode size %d\n"), *size);
+		maxsz = MIN(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"),
+				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"),
+				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 opt_params *opts,
+	int		sizeopt,
+	int		blocklog,
+	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 >> blocklog);
+	if (dbytes % blocksize) {
+		fprintf(stderr,
+_("warning: %s length %lld not a multiple of %d, truncated to %lld\n"),
+			type, (long long)dbytes, blocksize,
+			(long long)(dblocks << blocklog));
 	}
+	return dblocks;
+}
+
+static void
+validate_rtextsize(
+	xfs_extlen_t		*blocks,
+	struct fs_topology	*ft,
+	int			blocklog)
+{
+	uint64_t		rtextbytes;
+
 	/*
 	 * If specified, check rt extent size against its constraints.
 	 */
-	if (rtextsize) {
-		uint64_t rtextbytes;
+	if (cligeo.rtextsize) {
 
-		rtextbytes = getnum(rtextsize, &ropts, R_EXTSIZE);
+		rtextbytes = getnum(cligeo.rtextsize, &ropts, R_EXTSIZE);
 		if (rtextbytes % blocksize) {
 			fprintf(stderr,
 		_("illegal rt extent size %lld, not a multiple of %d\n"),
 				(long long)rtextbytes, blocksize);
 			usage();
 		}
-		rtextblocks = (xfs_extlen_t)(rtextbytes >> blocklog);
+		*blocks = (xfs_extlen_t)(rtextbytes >> blocklog);
 	} else {
 		/*
 		 * If realtime extsize has not been specified by the user,
@@ -2253,73 +2096,202 @@ _("rmapbt not supported with realtime devices\n"));
 		 * to the stripe width.
 		 */
 		uint64_t	rswidth;
-		uint64_t	rtextbytes;
 
-		if (!norsflag && !xi.risfile && !(!rtsize && xi.disfile))
-			rswidth = ft.rtswidth;
+		if (!sb_feat.nortalign && !xi.risfile &&
+		    !(!cligeo.rtsize && xi.disfile))
+			rswidth = ft->rtswidth;
 		else
 			rswidth = 0;
 
 		/* check that rswidth is a multiple of fs blocksize */
-		if (!norsflag && rswidth && !(BBTOB(rswidth) % blocksize)) {
+		if (!sb_feat.nortalign && rswidth &&
+		    !(BBTOB(rswidth) % blocksize)) {
 			rswidth = DTOBT(rswidth);
 			rtextbytes = rswidth << blocklog;
 			if (XFS_MIN_RTEXTSIZE <= rtextbytes &&
 			    (rtextbytes <= XFS_MAX_RTEXTSIZE)) {
-				rtextblocks = rswidth;
+				*blocks = rswidth;
 			}
 		}
-		if (!rtextblocks) {
-			rtextblocks = (blocksize < XFS_MIN_RTEXTSIZE) ?
+		if (!*blocks) {
+			*blocks = (blocksize < XFS_MIN_RTEXTSIZE) ?
 					XFS_MIN_RTEXTSIZE >> blocklog : 1;
 		}
 	}
-	ASSERT(rtextblocks);
+	ASSERT(*blocks);
+}
 
-	/*
-	 * 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;
+/*
+ * Convert lsu to lsunit for 512 bytes blocks and check validity of the values.
+ */
+static void
+calc_stripe_factors(
+	struct fs_topology *ft,
+	int		blocklog,
+	int		dsectsz,
+	int		lsectsz,
+	int		*dsunit,
+	int		*dswidth,
+	int		*lsunit)
+{
+	int		dsu;
+	int		dsw;
+	int		lsu;
+	bool		use_dev = false;
 
-		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)
-			fprintf(stderr,
-			_("allowable inode size with %d byte blocks is %d\n"),
-				blocksize, XFS_DINODE_MIN_SIZE);
-		else
-			fprintf(stderr,
-	_("allowable inode size with %d byte blocks is between %d and %d\n"),
-				blocksize, XFS_DINODE_MIN_SIZE, maxsz);
-		exit(1);
+	*dsunit = cligeo.dsunit == -1 ? 0 : cligeo.dsunit;
+	*dswidth = cligeo.dswidth == -1 ? 0 : cligeo.dswidth;
+	*lsunit = cligeo.lsunit == -1 ? 0 : cligeo.lsunit;
+
+	dsu = cligeo.dsu == -1 ? 0 : cligeo.dsu;
+	dsw = cligeo.dsw == -1 ? 0 : cligeo.dsw;
+	lsu = cligeo.lsu == -1 ? 0 : cligeo.lsu;
+
+
+	/* Process log sunit options first */
+	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);
+		usage();
+	}
+	if ((BBTOB(*lsunit) % blocksize != 0)) {
+		fprintf(stderr,
+_("log stripe unit (%d) must be a multiple of the block size (%d)\n"),
+			BBTOB(*lsunit), blocksize);
+		usage();
 	}
 
-	/* if lsu or lsunit was specified, automatically use v2 logs */
-	if ((lsu || lsunit) && sb_feat.log_version == 1) {
+
+	/* data sunit/swidth options */
+	if ((*dsunit && !*dswidth) || (!*dsunit && *dswidth)) {
 		fprintf(stderr,
-			_("log stripe unit specified, using v2 logs\n"));
-		sb_feat.log_version = 2;
+_("both data sunit and data swidth options must be specified\n"));
+		usage();
 	}
 
-	calc_stripe_factors(dsu, dsw, sectorsize, lsu, lsectorsize,
-				&dsunit, &dswidth, &lsunit);
+	if (dsu || dsw) {
+		if ((dsu && !dsw) || (!dsu && dsw)) {
+			fprintf(stderr,
+_("both data su and data sw options must be specified\n"));
+			usage();
+		}
+
+		if (dsu % dsectsz) {
+			fprintf(stderr,
+_("data su must be a multiple of the sector size (%d)\n"), dsectsz);
+			usage();
+		}
+
+		*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();
+	}
 
 	/* If sunit & swidth were manually specified as 0, same as noalign */
-	if (dsflag && !dsunit && !dswidth)
-		nodsflag = 1;
+	if ((cligeo.dsunit != -1 || cligeo.dsu != -1) && !*dsunit && !*dswidth)
+		sb_feat.nodalign = true;
+
+	/* if we are not using alignment, don't apply device defaults */
+	if (sb_feat.nodalign)
+		goto check_lsunit;
+
+	/* if no stripe config set, use the device default */
+	if (!*dsunit) {
+		*dsunit = ft->dsunit;
+		*dswidth = ft->dswidth;
+		use_dev = true;
+	} else {
+		/* check and warn is 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) % blocksize) || (BBTOB(*dswidth) % 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) {
+			*dsunit = 0;
+			*dswidth = 0;
+			sb_feat.nodalign = true;
+		} 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);
+		}
+	}
+
+	/* convert from 512 byte blocks to fs blocksize */
+	*dsunit = DTOBT(*dsunit);
+	*dswidth = DTOBT(*dswidth);
+
+	/*
+	 * check that log sunit is modulo fsblksize or default it to dsunit.
+	 */
+check_lsunit:
+	if (*lsunit) {
+		/* convert from 512 byte blocks to fs blocks */
+		*lsunit = DTOBT(*lsunit);
+	} else if (sb_feat.log_version == 2 && cligeo.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 (cligeo.lsunit != -1) {
+			fprintf(stderr,
+_("log stripe unit (%d bytes) is too large (maximum is 256KiB)\n"
+  "log stripe unit adjusted to 32KiB\n"),
+				(*lsunit * blocksize));
+		}
+		*lsunit = (32 * 1024) / blocksize;
+	}
+
+}
 
-	xi.setblksize = sectorsize;
+static void
+open_devices(
+	struct libxfs_xinit	*xi,
+	int			sectorlog,
+	int			lsectorlog,
+	bool			discard)
+{
+	uint64_t		sector_mask;
 
 	/*
 	 * Initialize.  This will open the log and rt devices as well.
 	 */
-	if (!libxfs_init(&xi))
+	xi->setblksize = sectorsize;
+	if (!libxfs_init(xi))
 		usage();
-	if (!xi.ddev) {
+	if (!xi->ddev) {
 		fprintf(stderr, _("no device name given in argument list\n"));
 		usage();
 	}
@@ -2334,328 +2306,481 @@ _("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(sectorlog, 10) - BBSHIFT);
-	xi.dsize &= sector_mask;
-	xi.rtsize &= sector_mask;
-	xi.logBBsize &= (uint64_t)-1 << (MAX(lsectorlog, 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();
-	}
-	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)) {
-		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"));
-		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 (!discard)
+		return;
+
+	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 libxfs_xinit	*xi,
+	xfs_rfsblock_t		*dblocks,
+	int			blocklog)
+{
+
+	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();
+		}
+	} else if (!cligeo.dsize) {
+		/* no user size, so use the full block device size */
+		*dblocks = DTOBT(xi->dsize);
+	} else {
+		/* check the size fits into the underlying device */
+		if (*dblocks > DTOBT(xi->dsize)) {
+			fprintf(stderr,
+_("size %s specified for data subvolume is too large, maxi->um is %lld blocks\n"),
+				cligeo.dsize, (long long)DTOBT(xi->dsize));
+			usage();
+		}
 	}
 
-	if (loginternal && xi.logdev) {
-		fprintf(stderr,
-			_("can't have both external and internal logs\n"));
-		usage();
-	} else if (loginternal && sectorsize != lsectorsize) {
+	if (*dblocks < XFS_MIN_DATA_BLOCKS) {
 		fprintf(stderr,
-	_("data and log sector sizes must be equal for internal logs\n"));
+_("size %lld of data subvolume is too small, minimum %d blocks\n"),
+			(long long)*dblocks, XFS_MIN_DATA_BLOCKS);
 		usage();
 	}
 
-	if (xi.dbsize > sectorsize) {
+	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);
+			sectorsize, xi->dbsize);
 	}
-	if (!loginternal && xi.lbsize > 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);
+}
+
+/*
+ * 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 libxfs_xinit	*xi,
+	char			**devname,
+	xfs_rfsblock_t		*logblocks,
+	xfs_rfsblock_t		dblocks,
+	int			blocklog,
+	int			lsectorsize)
+{
+	*devname = NULL;
+
+	/* check for volume log first */
+	if (cligeo.loginternal && xi->volname && xi->logdev) {
+		*devname = _("volume log");
+		cligeo.loginternal = false;
+	}
+
+	/* now run device checks */
+	if (cligeo.loginternal) {
+		if (xi->logdev) {
+			fprintf(stderr,
+_("can't have both external and internal logs\n"));
+			usage();
+		}
+		if (sectorsize != lsectorsize) {
+			fprintf(stderr,
+_("data and log sector sizes must be equal for internal logs\n"));
+			usage();
+		}
+		if (cligeo.logsize && *logblocks >= dblocks) {
+			fprintf(stderr,
+_("log size %lld too large for internal log\n"),
+				(long long)logblocks);
+			usage();
+		}
+		*devname = _("internal log");
+		return;
 	}
-	if (rtsize && xi.rtsize > 0 && xi.rtbsize > sectorsize) {
+
+	/* External/log subvolume checks */
+	if (xi->logname)
+		*devname = xi->logname;
+	if (!*devname || !xi->logdev) {
+		fprintf(stderr, _("no log subvolume or external log.\n"));
+		usage();
+	}
+
+	if (!cligeo.logsize) {
+		if (xi->logBBsize == 0) {
+			fprintf(stderr,
+_("unable to get size of the log subvolume.\n"));
+			usage();
+		}
+		*logblocks = DTOBT(xi->logBBsize);
+	} else {
+		if (*logblocks > DTOBT(xi->logBBsize)) {
+			fprintf(stderr,
+_("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
+				cligeo.logsize, (long long)DTOBT(xi->logBBsize));
+			usage();
+		}
+	}
+
+	if (xi->lbsize > lsectorsize) {
 		fprintf(stderr, _(
-"Warning: the realtime subvolume sector size %u is less than the sector size\n\
+"Warning: the log subvolume sector size %u is less than the sector size\n\
 reported by the device (%u).\n"),
-			sectorsize, xi.rtbsize);
+			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 libxfs_xinit	*xi,
+	char			**devname,
+	xfs_rfsblock_t		*rtblocks,
+	xfs_rfsblock_t		*rtextents,
+	xfs_extlen_t		*nbmblocks,
+	int			blocklog,
+	xfs_extlen_t		rtextblocks)
+{
+	*devname = NULL;
+
+	if (!xi->rtdev) {
+		if (cligeo.rtsize) {
+			fprintf(stderr,
+_("size specified for non-exi->tent rt subvolume\n"));
+			usage();
+		}
+
+		*devname = _("none");
+		*rtblocks = 0;
+		*rtextents = 0;
+		*nbmblocks = 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 (cligeo.rtsize) {
+		if (*rtblocks > DTOBT(xi->rtsize)) {
+			fprintf(stderr,
+_("size %s specified for rt subvolume is too large, maxi->um is %lld blocks\n"),
+				cligeo.rtsize, (long long)DTOBT(xi->rtsize));
+			usage();
+		}
+		if (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);
 		}
-	} /* else dsunit & dswidth can't be set if nodsflag is set */
+	} else {
+		/* grab volume size */
+		*rtblocks = DTOBT(xi->rtsize);
+	}
+
+	*rtextents = *rtblocks / rtextblocks;
+	*nbmblocks = (xfs_extlen_t)howmany(*rtextents, NBBY * blocksize);
+}
 
-	if (dasize) {		/* User-specified AG size */
+static void
+calculate_initial_ag_geometry(
+	int		blocklog,
+	uint64_t	dblocks,
+	int		multidisk,
+	uint64_t	*agsize,
+	uint64_t	*agcount)
+{
+	if (cligeo.agsize) {		/* User-specified AG size */
 		/*
 		 * Check specified agsize is a multiple of blocksize.
 		 */
-		if (agsize % blocksize) {
+		if (cligeo.agsize % blocksize) {
 			fprintf(stderr,
 		_("agsize (%lld) not a multiple of fs blk size (%d)\n"),
-				(long long)agsize, blocksize);
+				(long long)cligeo.agsize, blocksize);
 			usage();
 		}
-		agsize /= blocksize;
-		agcount = dblocks / agsize + (dblocks % agsize != 0);
+		*agsize = cligeo.agsize / blocksize;
+		*agcount = dblocks / *agsize + (dblocks % *agsize != 0);
 
-	} else if (daflag) {	/* User-specified AG count */
-		agsize = dblocks / agcount + (dblocks % agcount != 0);
+	} else if (cligeo.agcount) {	/* User-specified AG count */
+		*agcount = cligeo.agcount;
+		*agsize = dblocks / *agcount + (dblocks % *agcount != 0);
 	} else {
 		calc_default_ag_geometry(blocklog, dblocks,
-				dsunit | dswidth, &agsize, &agcount);
+					 multidisk, agsize, 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 bool
+align_ag_geometry(
+	int		dsunit,
+	int		dswidth,
+	int		blocklog,
+	xfs_rfsblock_t	*dblocks,
+	uint64_t	*agsize,
+	uint64_t	*agcount)
+{
+	uint64_t	tmp_agsize;
+	bool		ret = false;
 
-		/* convert from 512 byte blocks to fs blocksize */
-		dsunit = DTOBT(dsunit);
-		dswidth = DTOBT(dswidth);
+	if (!dsunit)
+		return false;
 
+	/*
+	 * agsize is not a multiple of dsunit
+	 */
+	if ((*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 = ((*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(blocklog))
+			tmp_agsize = ((*agsize) / dsunit) * dsunit;
+
+		if (tmp_agsize < XFS_AG_MIN_BLOCKS(blocklog) &&
+		    tmp_agsize > XFS_AG_MAX_BLOCKS(blocklog)) {
+			if (cligeo.dsunit == -1 && cligeo.dsu == -1) {
+				ret = true;
+				goto validate;
+			}
 			/*
-			 * Round down to stripe unit boundary if rounding up
-			 * created an AG size that is larger than the AG max.
+			 * set the agsize to the invalid value so the following
+			 * validation of the ag will fail and print a nice error
+			 * and exit.
 			 */
-			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);
-				}
-			}
+			*agsize = tmp_agsize;
+			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.
-			 */
-			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, _(
+
+		/* update geometry to be stripe unit aligned */
+		*agsize = tmp_agsize;
+		if (!cligeo.agcount)
+			*agcount = *dblocks / *agsize + (*dblocks % *agsize != 0);
+		if (cligeo.agsize)
+			fprintf(stderr,
+		_("agsize rounded to %lld, sunit = %d\n"),
+				(long long)*agsize, dsunit);
+	}
+
+	if ((*agsize % dswidth) == 0 && *agcount > 1) {
+
+		if (cligeo.agcount || cligeo.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)*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 = *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;
+			}
 		}
+
+		*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))) {
-		ASSERT(!daflag);
-		dblocks = (xfs_rfsblock_t)((agcount - 1) * agsize);
-		agcount--;
-		ASSERT(agcount != 0);
+	if (*dblocks % *agsize != 0 &&
+	     (*dblocks % *agsize < XFS_AG_MIN_BLOCKS(blocklog))) {
+		ASSERT(!cligeo.agcount);
+		*dblocks = (xfs_rfsblock_t)((*agcount - 1) * *agsize);
+		(*agcount)--;
+		ASSERT(*agcount != 0);
 	}
 
-	validate_ag_geometry(blocklog, dblocks, agsize, agcount);
+validate:
+	validate_ag_geometry(blocklog, *dblocks, *agsize, *agcount);
+	return ret;
+}
 
-	if (!imflag)
-		imaxpct = calc_default_imaxpct(blocklog, dblocks);
+/*
+ * Make sure that the log size is a multiple of the stripe unit
+ */
+static void
+align_log_size(
+	int		sunit,
+	xfs_rfsblock_t	*logblocks,
+	int		blocklog)
+{
+	uint64_t	tmp_logblocks;
 
-	/*
-	 * check that log sunit is modulo fsblksize or default it to dsunit.
-	 */
+	/* nothing to do if it's already aligned. */
+	if ((*logblocks % sunit) == 0)
+		return;
 
-	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 (cligeo.logsize) {
+		fprintf(stderr,
+_("log size %lld is not a multiple of the log stripe unit %d\n"),
+			(long long) *logblocks, sunit);
+		usage();
 	}
 
-	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;
+	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;
+}
+
+/*
+ * Make sure that the internal log is correctly aligned to the specified
+ * stripe unit.
+ */
+static void
+align_internal_log(
+	xfs_mount_t	*mp,
+	xfs_fsblock_t	*logstart,
+	uint64_t	agsize,
+	int		sunit,
+	xfs_rfsblock_t	*logblocks,
+	int		blocklog)
+{
+	uint64_t	agspace;
+
+	if ((*logstart % sunit) != 0)
+		*logstart = ((*logstart + (sunit - 1))/sunit) * sunit;
+
+	align_log_size(sunit, logblocks, blocklog);
+
+	/* check the aligned log still fits in an AG. */
+	agspace = agsize - libxfs_prealloc_blocks(mp);
+	if (*logblocks > agspace - XFS_FSB_TO_AGBNO(mp, *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) *logblocks);
+		usage();
+	}
+}
+
+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 xfs_mount	*mp,
+	xfs_rfsblock_t		*logblocks,
+	xfs_fsblock_t		*logstart,
+	xfs_agnumber_t		*logagno,
+	xfs_rfsblock_t		dblocks,
+	int			lsunit,
+	int			dsunit,
+	int			sectorlog,
+	int			blocklog,
+	int			inodelog,
+	int			dirblocklog)
 
-	min_logblocks = max_trans_res(agsize,
+{
+	struct xfs_sb		*sbp = &mp->m_sb;
+	int			min_logblocks;
+
+	min_logblocks = max_trans_res(sbp->sb_agblocks,
 				   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,
 				   sb_feat.inode_align);
+
 	ASSERT(min_logblocks);
 	min_logblocks = MAX(XFS_MIN_LOG_BLOCKS, min_logblocks);
-	if (!logsize && dblocks >= (1024*1024*1024) >> blocklog)
+
+	if (!cligeo.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 (!cligeo.loginternal) {
+		if (min_logblocks > *logblocks) {
+			fprintf(stderr,
+	_("external log device %lld too small, must be at least %lld blocks\n"),
+				(long long)logblocks, (long long)min_logblocks);
+			usage();
+		}
+		*logstart = 0;
+		*logagno = 0;
+		if (lsunit)
+			align_log_size(lsunit, logblocks, blocklog);
+
+		validate_log_size(*logblocks, blocklog, min_logblocks);
+		return;
+	}
+
+	/* internal log - if no size specified, calculate automatically */
+	if (!cligeo.logsize) {
 
 		if (dblocks < GIGABYTES(1, blocklog)) {
 			/* tiny filesystems get minimum sized logs. */
-			logblocks = min_logblocks;
+			*logblocks = min_logblocks;
 		} else if (dblocks < GIGABYTES(16, blocklog)) {
 
 			/*
@@ -2663,7 +2788,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 			 * 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,
+			*logblocks = MIN(XFS_MIN_LOG_BYTES >> blocklog,
 					min_logblocks * XFS_DFL_LOG_FACTOR);
 		} else {
 			/*
@@ -2672,25 +2797,253 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 			 * 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;
+			*logblocks = (dblocks << blocklog) / 2048;
+			*logblocks = *logblocks >> blocklog;
 		}
 
 		/* Ensure the chosen size meets minimum log size requirements */
-		logblocks = MAX(min_logblocks, logblocks);
+		*logblocks = MAX(min_logblocks, *logblocks);
 
 		/* make sure the log fits wholly within an AG */
-		if (logblocks >= agsize)
-			logblocks = min_logblocks;
+		*logblocks = MIN(*logblocks,
+				libxfs_alloc_ag_max_usable(mp));
 
 		/* 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;
+		*logblocks = MIN(*logblocks, XFS_MAX_LOG_BLOCKS);
+		if ((*logblocks << blocklog) > XFS_MAX_LOG_BYTES)
+			*logblocks = XFS_MAX_LOG_BYTES >> blocklog;
+
+		validate_log_size(*logblocks, blocklog, min_logblocks);
+	}
+
+
+	if (*logblocks > sbp->sb_agblocks - libxfs_prealloc_blocks(mp)) {
+		fprintf(stderr,
+_("internal log size %lld too large, must fit in allocation group\n"),
+			(long long)*logblocks);
+		usage();
+	}
+
+	if (cligeo.logagno != -1) {
+		if (cligeo.logagno >= sbp->sb_agcount) {
+			fprintf(stderr,
+_("log ag number %d too large, must be less than %lld\n"),
+				cligeo.logagno, (long long)sbp->sb_agcount);
+			usage();
+		}
+		*logagno = cligeo.logagno;
+	} else
+		*logagno = (xfs_agnumber_t)(sbp->sb_agcount / 2);
+
+	*logstart = XFS_AGB_TO_FSB(mp, *logagno, libxfs_prealloc_blocks(mp));
+
+	/*
+	 * Align the logstart at stripe unit boundary.
+	 */
+	if (lsunit) {
+		align_internal_log(mp, logstart, sbp->sb_agblocks, lsunit,
+				   logblocks, blocklog);
+	} else if (dsunit) {
+		align_internal_log(mp, logstart, sbp->sb_agblocks, dsunit,
+				   logblocks, blocklog);
+	}
+	validate_log_size(*logblocks, blocklog, min_logblocks);
+}
+
+int
+main(
+	int			argc,
+	char			**argv)
+{
+	uint64_t		agcount = 0;
+	xfs_agf_t		*agf;
+	xfs_agi_t		*agi;
+	xfs_agnumber_t		agno;
+	uint64_t		agsize = 0;
+	xfs_alloc_rec_t		*arec;
+	struct xfs_btree_block	*block;
+	int			blocklog = 0;
+	int			bsize;
+	xfs_buf_t		*buf;
+	int			c;
+	xfs_rfsblock_t		dblocks = 0;
+	char			*dfile = NULL;
+	int			dirblocklog = 0;
+	int			dirblocksize = 0;
+	int			dsunit = 0;
+	int			dswidth = 0;
+	int			force_overwrite = 0;
+	int			imaxpct = 0;
+	int			inodelog = 0;
+	int			inopblock = 0;
+	int			isize = 0;
+	char			*label = NULL;
+	int			lalign = 0;
+	xfs_agnumber_t		logagno = 0;
+	xfs_rfsblock_t		logblocks = 0;
+	char			*logfile = NULL;
+	xfs_fsblock_t		logstart = 0;
+	int			lsectorlog = 0;
+	int			lsectorsize = 0;
+	int			lsunit = 0;
+	xfs_mount_t		*mp = NULL;
+	xfs_mount_t		mbuf;
+	xfs_extlen_t		nbmblocks = 0;
+	xfs_alloc_rec_t		*nrec = NULL;
+	int			dry_run = 0;
+	int			discard = 1;
+	char			*protofile = NULL;
+	char			*protostring = NULL;
+	int			quiet = 0;
+	xfs_rfsblock_t		rtblocks = 0;
+	xfs_extlen_t		rtextblocks = 0;
+	xfs_rtblock_t		rtextents = 0;
+	char			*rtfile = NULL;
+	xfs_sb_t		*sbp;
+	int			sectorlog = 0;
+	uuid_t			uuid;
+	int			worst_freelist = 0;
+	struct fs_topology	ft;
+	int			ret;
+
+	platform_uuid_generate(&uuid);
+	progname = basename(argv[0]);
+	setlocale(LC_ALL, "");
+	bindtextdomain(PACKAGE, LOCALEDIR);
+	textdomain(PACKAGE);
+
+	/*
+	 * set parameters that can be set to zero to -1 or a default value so we
+	 * can tell if they have been set or not This gets rid of all the "was
+	 * it specified" flags.
+	 */
+	cligeo.dsu = -1;
+	cligeo.dsw = -1;
+	cligeo.dsunit = -1;
+	cligeo.dswidth = -1;
+	cligeo.loginternal = 1;
+	cligeo.logagno = -1;
+	cligeo.lsu = -1;
+	cligeo.lsunit = -1;
+
+	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':
+		case 'd':
+		case 'i':
+		case 'l':
+		case 'm':
+		case 'n':
+		case 'r':
+		case 's':
+			parse_subopts(c, optarg);
+			break;
+		case 'L':
+			if (strlen(optarg) > sizeof(sbp->sb_fname))
+				illegal(optarg, "L");
+			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, "");
+		}
+	}
+	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;
+
+	/*
+	 * Extract as much of the valid config as we can from the CLI input
+	 * before opening the libxfs devices.
+	 */
+	validate_blocksize(&blocksize, &blocklog);
+	validate_sectorsize(&sectorsize, &sectorlog, &ft, dfile, dry_run,
+			    force_overwrite);
+	validate_log_sectorsize(&lsectorsize, &lsectorlog,
+				sectorsize, sectorlog);
+	validate_sb_features();
+
+	validate_dirblocksize(&dirblocksize, &dirblocklog, blocklog);
+	validate_inodesize(&isize, &inodelog, &inopblock, blocklog);
+
+	/*
+	 * 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.
+	 */
+	dblocks = calc_dev_size(cligeo.dsize, &dopts, D_SIZE, blocklog, "data");
+	logblocks = calc_dev_size(cligeo.logsize, &lopts, L_SIZE, blocklog, "log");
+	rtblocks = calc_dev_size(cligeo.rtsize, &ropts, R_SIZE, blocklog, "rt");
+
+	validate_rtextsize(&rtextblocks, &ft, blocklog);
+	calc_stripe_factors(&ft, blocklog, sectorsize, lsectorsize,
+			    &dsunit, &dswidth, &lsunit);
 
+	/*
+	 * Open and validate the device configurations
+	 */
+	open_devices(&xi, sectorlog, lsectorlog, (discard && !dry_run));
+
+	validate_datadev(&xi, &dblocks, blocklog);
+	validate_logdev(&xi, &logfile, &logblocks, dblocks, blocklog, lsectorsize);
+	validate_rtdev(&xi, &rtfile, &rtblocks, &rtextents,
+		       &nbmblocks, blocklog, rtextblocks);
+
+	/*
+	 * 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.
+	 */
+	calculate_initial_ag_geometry(blocklog, dblocks, dsunit | dswidth,
+				      &agsize, &agcount);
+
+	/*
+	 * Alignment may fail and tell us that we need to clear the stripe
+	 * geometry to continue.
+	 */
+	ret = align_ag_geometry(dsunit, dswidth, blocklog,
+				&dblocks, &agsize, &agcount);
+	if (ret) {
+		dsunit = 0;
+		dswidth = 0;
 	}
-	validate_log_size(logblocks, blocklog, min_logblocks);
 
+	imaxpct = cligeo.imaxpct;
+	if (!imaxpct)
+		imaxpct = calc_default_imaxpct(blocklog, dblocks);
+
+	/*
+	 * 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.
+	 */
 	protostring = setup_proto(protofile);
 	bsize = 1 << (blocklog - BBSHIFT);
 	mp = &mbuf;
@@ -2700,6 +3053,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 	sbp->sb_sectlog = (uint8_t)sectorlog;
 	sbp->sb_agblklog = (uint8_t)libxfs_log2_roundup((unsigned int)agsize);
 	sbp->sb_agblocks = (xfs_agblock_t)agsize;
+	sbp->sb_agcount = (xfs_agnumber_t)agcount;
 	mp->m_blkbb_log = sbp->sb_blocklog - BBSHIFT;
 	mp->m_sectbb_log = sbp->sb_sectlog - BBSHIFT;
 
@@ -2709,58 +3063,10 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 	 */
 	sb_set_features(&mp->m_sb, &sb_feat, sectorsize, lsectorsize, dsunit);
 
+	calculate_log_size(mp, &logblocks, &logstart, &logagno, dblocks, lsunit,
+			   dsunit, sectorlog, blocklog, inodelog, dirblocklog);
 
-	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));
-
-			/* revalidate the log size is valid if we changed it */
-			validate_log_size(logblocks, blocklog, min_logblocks);
-		}
-		if (logblocks > agsize - libxfs_prealloc_blocks(mp)) {
-			fprintf(stderr,
-	_("internal log size %lld too large, must fit in allocation group\n"),
-				(long long)logblocks);
-			usage();
-		}
-
-		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);
-
-		logstart = XFS_AGB_TO_FSB(mp, logagno, libxfs_prealloc_blocks(mp));
-		/*
-		 * Align the logstart at stripe unit boundary.
-		 */
-		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);
-		}
-	} else {
-		logstart = 0;
-		if (lsunit)
-			fixup_log_stripe_unit(lsflag, lsunit,
-					&logblocks, blocklog);
-	}
-	validate_log_size(logblocks, blocklog, min_logblocks);
-
-	if (!qflag || Nflag) {
+	if (!quiet || dry_run) {
 		printf(_(
 		   "meta-data=%-22s isize=%-6d agcount=%lld, agsize=%lld blks\n"
 		   "         =%-22s sectsz=%-5u attr=%u, projid32bit=%u\n"
@@ -2785,7 +3091,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 				sb_feat.lazy_sb_counters,
 			rtfile, rtextblocks << blocklog,
 			(long long)rtblocks, (long long)rtextents);
-		if (Nflag)
+		if (dry_run)
 			exit(0);
 	}
 
@@ -2819,7 +3125,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 	sbp->sb_icount = 0;
 	sbp->sb_ifree = 0;
 	sbp->sb_fdblocks = dblocks - agcount * libxfs_prealloc_blocks(mp) -
-		(loginternal ? logblocks : 0);
+		(cligeo.loginternal ? 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;
@@ -2978,7 +3284,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 		if (xfs_sb_version_hascrc(&mp->m_sb))
 			platform_uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_uuid);
 
-		if (loginternal && agno == logagno) {
+		if (cligeo.loginternal && agno == logagno) {
 			be32_add_cpu(&agf->agf_freeblks, -logblocks);
 			agf->agf_longest = cpu_to_be32(agsize -
 				XFS_FSB_TO_AGBNO(mp, logstart) - logblocks);
@@ -3049,7 +3355,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 
 		arec = XFS_ALLOC_REC_ADDR(mp, block, 1);
 		arec->ar_startblock = cpu_to_be32(libxfs_prealloc_blocks(mp));
-		if (loginternal && agno == logagno) {
+		if (cligeo.loginternal && agno == logagno) {
 			if (lalign) {
 				/*
 				 * Have to insert two records
@@ -3099,7 +3405,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 
 		arec = XFS_ALLOC_REC_ADDR(mp, block, 1);
 		arec->ar_startblock = cpu_to_be32(libxfs_prealloc_blocks(mp));
-		if (loginternal && agno == logagno) {
+		if (cligeo.loginternal && agno == logagno) {
 			if (lalign) {
 				arec->ar_blockcount = cpu_to_be32(
 					XFS_FSB_TO_AGBNO(mp, logstart) -
@@ -3233,7 +3539,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 			}
 
 			/* account for the log space */
-			if (loginternal && agno == logagno) {
+			if (cligeo.loginternal && agno == logagno) {
 				rrec = XFS_RMAP_REC_ADDR(block,
 					be16_to_cpu(block->bb_numrecs) + 1);
 				rrec->rm_startblock = cpu_to_be32(
@@ -3364,7 +3670,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 static void
 conflict(
 	char		opt,
-	char		*tab[],
+	const char	*tab[],
 	int		oldidx,
 	int		newidx)
 {
@@ -3393,7 +3699,7 @@ ispow2(
 static void __attribute__((noreturn))
 reqval(
 	char		opt,
-	char		*tab[],
+	const char	*tab[],
 	int		idx)
 {
 	fprintf(stderr, _("-%c %s option requires a value\n"), opt, tab[idx]);
@@ -3403,7 +3709,7 @@ reqval(
 static void
 respec(
 	char		opt,
-	char		*tab[],
+	const char	*tab[],
 	int		idx)
 {
 	fprintf(stderr, "-%c ", opt);
-- 
2.13.3

--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux