diff mbox

[16/22] mkfs: Change all value fields in opt structures into unions

Message ID 1481117249-21273-17-git-send-email-jtulak@redhat.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Jan Tulak Dec. 7, 2016, 1:27 p.m. UTC
Trying to cover all possible values in a single data type is impossible,
so convert the field from long long type to union. This requires
also some small changes in supporting code, otherwise it would not compile.

Signed-off-by: Jan Tulak <jtulak@redhat.com>
---
 mkfs/xfs_mkfs.c | 812 +++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 504 insertions(+), 308 deletions(-)

Comments

Bill O'Donnell Jan. 13, 2017, 5:36 p.m. UTC | #1
On Wed, Dec 07, 2016 at 02:27:23PM +0100, Jan Tulak wrote:
> Trying to cover all possible values in a single data type is impossible,
> so convert the field from long long type to union. This requires
> also some small changes in supporting code, otherwise it would not compile.
> 
> Signed-off-by: Jan Tulak <jtulak@redhat.com>
> ---
>  mkfs/xfs_mkfs.c | 812 +++++++++++++++++++++++++++++++++++---------------------
>  1 file changed, 504 insertions(+), 308 deletions(-)
> 
> diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
> index 7ffe8ff..afc63d1 100644
> --- a/mkfs/xfs_mkfs.c
> +++ b/mkfs/xfs_mkfs.c
> @@ -121,6 +121,231 @@ unsigned int		sectorsize;
>  #define M_RMAPBT	3
>  #define M_REFLINK	4
>  
> +enum e_type {
> +	TYPE_UNDEF,
> +	LONGLONG,
> +	BOOL,
> +	UINT64,
> +	INT,
> +	UINT,
> +	STRING
> +};
> +union u_value {
> +	long long 	ll;
> +	bool		b;
> +	__uint64_t	uint64;
> +	int		i;
> +	unsigned int	u;
> +	char		*s;
> +};
> +static bool
> +cmp_uvalues_gt(enum e_type a_type, union u_value a, enum e_type b_type, union u_value b) {
> +	if (a_type == STRING || b_type == STRING) {
> +		if (a_type == b_type)
> +			return strcmp(a.s, b.s);
> +		return false;
> +	}	switch(a_type){
> +	case LONGLONG:
> +		switch(b_type){
> +		case LONGLONG:
> +			return a.ll > b.ll;
> +		case BOOL:
> +			return a.ll > b.b;
> +		case UINT64:
> +			return a.ll > b.uint64;
> +		case INT:
> +			return a.ll > b.i;
> +		case UINT:
> +			return a.ll > b.u;
> +		default:
> +			return false;
> +		};
> +		break;
> +	case BOOL:
> +		switch(b_type){
> +		case LONGLONG:
> +			return a.b > b.ll;
> +		case BOOL:
> +			return a.b > b.b;
> +		case UINT64:
> +			return a.b > b.uint64;
> +		case INT:
> +			return a.b > b.i;
> +		case UINT:
> +			return a.b > b.u;
> +		default:
> +			return false;
> +		};
> +		break;
> +	case UINT64:
> +		switch(b_type){
> +		case LONGLONG:
> +			return a.uint64 > b.ll;
> +		case BOOL:
> +			return a.uint64 > b.b;
> +		case UINT64:
> +			return a.uint64 > b.uint64;
> +		case INT:
> +			return a.uint64 > b.i;
> +		case UINT:
> +			return a.uint64 > b.u;
> +		default:
> +			return false;
> +		};
> +		break;
> +	case INT:
> +		switch(b_type){
> +		case LONGLONG:
> +			return a.i > b.ll;
> +		case BOOL:
> +			return a.i > b.b;
> +		case UINT64:
> +			return a.i > b.uint64;
> +		case INT:
> +			return a.i > b.i;
> +		case UINT:
> +			return a.i > b.u;
> +		default:
> +			return false;
> +		};
> +		break;
> +	case UINT:
> +		switch(b_type){
> +		case LONGLONG:
> +			return a.u > b.ll;
> +		case BOOL:
> +			return a.u > b.b;
> +		case UINT64:
> +			return a.u > b.uint64;
> +		case INT:
> +			return a.u > b.i;
> +		case UINT:
> +			return a.u > b.u;
> +		default:
> +			return false;
> +		};
> +		break;
> +	default:
> +		return false;
> +	};
> +
> +	return false;
> +}
> +static bool
> +cmp_uvalue_gt_num(enum e_type a_type, union u_value a, long long b) {
> +	union u_value u;
> +	u.ll = b;
> +	return cmp_uvalues_gt(a_type, a, LONGLONG, u);
> +}
> +static bool
> +cmp_uvalue_lt_num(enum e_type a_type, union u_value a, long long b) {
> +	union u_value u;
> +	u.ll = b;
> +	return cmp_uvalues_gt(LONGLONG, u, a_type, a);
> +}
> +
> +static bool
> +test_uvalues(enum e_type a_type, union u_value a, enum e_type b_type, union u_value b) {
> +	if (a_type == STRING || b_type == STRING) {
> +		if (a_type == b_type)
> +			return strcmp(a.s, b.s) == 0;
> +		return false;
> +	}
> +	switch(a_type){
> +	case LONGLONG:
> +		switch(b_type){
> +		case LONGLONG:
> +			return a.ll == b.ll;
> +		case BOOL:
> +			return a.ll == b.b;
> +		case UINT64:
> +			return a.ll == b.uint64;
> +		case INT:
> +			return a.ll == b.i;
> +		case UINT:
> +			return a.ll == b.u;
> +		default:
> +			return false;
> +		};
> +		break;
> +	case BOOL:
> +		switch(b_type){
> +		case LONGLONG:
> +			return a.b == b.ll;
> +		case BOOL:
> +			return a.b == b.b;
> +		case UINT64:
> +			return a.b == b.uint64;
> +		case INT:
> +			return a.b == b.i;
> +		case UINT:
> +			return a.b == b.u;
> +		default:
> +			return false;
> +		};
> +		break;
> +	case UINT64:
> +		switch(b_type){
> +		case LONGLONG:
> +			return a.uint64 == b.ll;
> +		case BOOL:
> +			return a.uint64 == b.b;
> +		case UINT64:
> +			return a.uint64 == b.uint64;
> +		case INT:
> +			return a.uint64 == b.i;
> +		case UINT:
> +			return a.uint64 == b.u;
> +		default:
> +			return false;
> +		};
> +		break;
> +	case INT:
> +		switch(b_type){
> +		case LONGLONG:
> +			return a.i == b.ll;
> +		case BOOL:
> +			return a.i == b.b;
> +		case UINT64:
> +			return a.i == b.uint64;
> +		case INT:
> +			return a.i == b.i;
> +		case UINT:
> +			return a.i == b.u;
> +		default:
> +			return false;
> +		};
> +		break;
> +	case UINT:
> +		switch(b_type){
> +		case LONGLONG:
> +			return a.u == b.ll;
> +		case BOOL:
> +			return a.u == b.b;
> +		case UINT64:
> +			return a.u == b.uint64;
> +		case INT:
> +			return a.u == b.i;
> +		case UINT:
> +			return a.u == b.u;
> +		default:
> +			return false;
> +		};
> +		break;
> +	default:
> +		return false;
> +	};
> +
> +	return false;
> +}
> +
> +static bool
> +test_uvalue_num(enum e_type a_type, union u_value a, long long b) {
> +	union u_value u;
> +	u.ll = b;
> +	return test_uvalues(a_type, a, LONGLONG, u);
> +}
> +
>  /*
>   * Table for parsing mkfs parameters.
>   *
> @@ -193,11 +418,15 @@ unsigned int		sectorsize;
>   *     If the subopt accepts some values (-d file=[1|0]), then this
>   *     sets what is used with simple specifying the subopt (-d file).
>   *
> - *   value INTERNAL
> - *     Do not set this on initialization. Use flagval for what you want
> - *     to do. This is filled with user input and anything you write here now
> - *     is overwritten. (If the user input is a string and not a number, this
> - *     value is set to a positive non-zero number.)
> + *   value MANDATORY
> + *     A value that is used for given field as a default if user doesn't specify
> + *     any change. This is filled with user input and anything you write here
> + *     now is overwritten if the user specifies given option.
> + *
> + *   type MANDATORY
> + *     An enum of what type the values are. Affects every u_value field within
> + *     the suboption except conflict's .invalid_value - this one uses the
> + *     "remote" type.
>   *
>   *   needs_val OPTIONAL
>   *     Set to true if, when user specifies the option, she has to specify
> @@ -219,14 +448,15 @@ struct opt_params {
>  			int		subopt;
>  			bool		test_values;
>  			bool		test_default_value;
> -			long long	invalid_value;
> -			long long	at_value;
> +			union u_value	invalid_value;
> +			union u_value	at_value;
>  			const char	*message;
>  		}		conflicts [MAX_CONFLICTS];
> -		long long	minval;
> -		long long	maxval;
> -		long long	flagval;
> -		long long	value;
> +		union u_value	minval;
> +		union u_value	maxval;
> +		union u_value	flagval;
> +		union u_value value;
> +		enum e_type 	type;
>  		bool		needs_val;
>  	}		subopt_params[MAX_SUBOPTS];
>  } opts[MAX_OPTS] = {
> @@ -244,9 +474,10 @@ struct opt_params {
>  					  .subopt = B_SIZE,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = XFS_MIN_BLOCKSIZE_LOG,
> -			  .maxval = XFS_MAX_BLOCKSIZE_LOG,
> +			  .minval.i = XFS_MIN_BLOCKSIZE_LOG,
> +			  .maxval.i = XFS_MAX_BLOCKSIZE_LOG,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = B_SIZE,
>  			  .convert = true,
> @@ -255,9 +486,10 @@ struct opt_params {
>  					  .subopt = B_LOG,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = XFS_MIN_BLOCKSIZE,
> -			  .maxval = XFS_MAX_BLOCKSIZE,
> +			  .minval.u = XFS_MIN_BLOCKSIZE,
> +			  .maxval.u = XFS_MAX_BLOCKSIZE,
>  			  .needs_val = true,
> +			  .type = UINT,
>  			},
>  		},
>  	},
> @@ -288,26 +520,30 @@ struct opt_params {
>  					  .subopt = D_AGSIZE,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = 1,
> -			  .maxval = XFS_MAX_AGNUMBER,
> +			  .minval.uint64 = 1,
> +			  .maxval.uint64 = XFS_MAX_AGNUMBER,
>  			  .needs_val = true,
> +			  .type = UINT64,
>  			},
>  			{ .index = D_FILE,
>  			  .conflicts = { {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.i = 0,
> +			  .maxval.i = 1,
> +			  .flagval.i = 1,
> +			  .type = INT,
>  			},
>  			{ .index = D_NAME,
>  			  .conflicts = { {LAST_CONFLICT} },
>  			  .needs_val = true,
> +			  .type = STRING,
>  			},
>  			{ .index = D_SIZE,
>  			  .conflicts = { {LAST_CONFLICT} },
>  			  .convert = true,
> -			  .minval = XFS_AG_MIN_BYTES,
> -			  .maxval = LLONG_MAX,
> +			  .minval.uint64 = XFS_AG_MIN_BYTES,
> +			  .maxval.uint64 = LLONG_MAX,
>  			  .needs_val = true,
> +			  .type = UINT64,
>  			},
>  			{ .index = D_SUNIT,
>  			  .conflicts = { {.opt = OPT_D,
> @@ -320,9 +556,10 @@ struct opt_params {
>  					  .subopt = D_SW,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = UINT_MAX,
> +			  .minval.i = 0,
> +			  .maxval.i = UINT_MAX,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = D_SWIDTH,
>  			  .conflicts = { {.opt = OPT_D,
> @@ -335,9 +572,10 @@ struct opt_params {
>  					  .subopt = D_SW,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = UINT_MAX,
> +			  .minval.i = 0,
> +			  .maxval.i = UINT_MAX,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = D_AGSIZE,
>  			  .conflicts = { {.opt = OPT_D,
> @@ -345,9 +583,10 @@ struct opt_params {
>  					 },
>  					 {LAST_CONFLICT} },
>  			  .convert = true,
> -			  .minval = XFS_AG_MIN_BYTES,
> -			  .maxval = XFS_AG_MAX_BYTES,
> +			  .minval.uint64 = XFS_AG_MIN_BYTES,
> +			  .maxval.uint64 = XFS_AG_MAX_BYTES,
>  			  .needs_val = true,
> +			  .type = UINT64,
>  			},
>  			{ .index = D_SU,
>  			  .conflicts = { {.opt = OPT_D,
> @@ -361,9 +600,10 @@ struct opt_params {
>  					 },
>  					 {LAST_CONFLICT} },
>  			  .convert = true,
> -			  .minval = 0,
> -			  .maxval = UINT_MAX,
> +			  .minval.i = 0,
> +			  .maxval.i = UINT_MAX,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = D_SW,
>  			  .conflicts = { {.opt = OPT_D,
> @@ -376,18 +616,20 @@ struct opt_params {
>  					  .subopt = D_SWIDTH,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = UINT_MAX,
> +			  .minval.i = 0,
> +			  .maxval.i = UINT_MAX,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = D_SECTLOG,
>  			  .conflicts = { {.opt = OPT_D,
>  					  .subopt = D_SECTSIZE,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = XFS_MIN_SECTORSIZE_LOG,
> -			  .maxval = XFS_MAX_SECTORSIZE_LOG,
> +			  .minval.i = XFS_MIN_SECTORSIZE_LOG,
> +			  .maxval.i = XFS_MAX_SECTORSIZE_LOG,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = D_SECTSIZE,
>  			  .conflicts = { {.opt = OPT_D,
> @@ -396,9 +638,10 @@ struct opt_params {
>  					 {LAST_CONFLICT} },
>  			  .convert = true,
>  			  .is_power_2 = true,
> -			  .minval = XFS_MIN_SECTORSIZE,
> -			  .maxval = XFS_MAX_SECTORSIZE,
> +			  .minval.u = XFS_MIN_SECTORSIZE,
> +			  .maxval.u = XFS_MAX_SECTORSIZE,
>  			  .needs_val = true,
> +			  .type = UINT,
>  			},
>  			{ .index = D_NOALIGN,
>  			  .conflicts = { {.opt = OPT_D,
> @@ -414,27 +657,31 @@ struct opt_params {
>  					  .subopt = D_SWIDTH,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.i = 0,
> +			  .maxval.i = 1,
> +			  .flagval.i = 1,
> +			  .type = INT,
>  			},
>  			{ .index = D_RTINHERIT,
>  			  .conflicts = { {LAST_CONFLICT} },
> -			  .minval = 1,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.u = 1,
> +			  .maxval.u = 1,
> +			  .flagval.u = 1,
> +			  .type = UINT,
>  			},
>  			{ .index = D_PROJINHERIT,
>  			  .conflicts = { {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = UINT_MAX,
> +			  .minval.u = 0,
> +			  .maxval.u = UINT_MAX,
>  			  .needs_val = true,
> +			  .type = UINT,
>  			},
>  			{ .index = D_EXTSZINHERIT,
>  			  .conflicts = { {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = UINT_MAX,
> +			  .minval.u = 0,
> +			  .maxval.u = UINT_MAX,
>  			  .needs_val = true,
> +			  .type = UINT,
>  			},
>  		},
>  	},
> @@ -458,14 +705,15 @@ struct opt_params {
>  					  .subopt = M_CRC,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 1,
> -					  .at_value = 0,
> +					  .invalid_value.b = 1,
> +					  .at_value.b = 0,
>  					  .message = \
>  		"Inodes always aligned for CRC enabled filesytems."},
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.b = false,
> +			  .maxval.b = true,
> +			  .flagval.b = true,
> +			  .type = BOOL,
>  			},
>  			{ .index = I_LOG,
>  			  .conflicts = { {.opt = OPT_I,
> @@ -475,15 +723,17 @@ struct opt_params {
>  					  .subopt = I_SIZE,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = XFS_DINODE_MIN_LOG,
> -			  .maxval = XFS_DINODE_MAX_LOG,
> +			  .minval.i = XFS_DINODE_MIN_LOG,
> +			  .maxval.i = XFS_DINODE_MAX_LOG,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = I_MAXPCT,
>  			  .conflicts = { {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 100,
> +			  .minval.i = 0,
> +			  .maxval.i = 100,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = I_PERBLOCK,
>  			  .conflicts = { {.opt = OPT_I,
> @@ -494,9 +744,10 @@ struct opt_params {
>  					 },
>  					 {LAST_CONFLICT} },
>  			  .is_power_2 = true,
> -			  .minval = XFS_MIN_INODE_PERBLOCK,
> -			  .maxval = XFS_MAX_BLOCKSIZE / XFS_DINODE_MIN_SIZE,
> +			  .minval.i = XFS_MIN_INODE_PERBLOCK,
> +			  .maxval.i = XFS_MAX_BLOCKSIZE / XFS_DINODE_MIN_SIZE,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = I_SIZE,
>  			  .conflicts = { {.opt = OPT_I,
> @@ -507,52 +758,56 @@ struct opt_params {
>  					 },
>  					 {LAST_CONFLICT} },
>  			  .is_power_2 = true,
> -			  .minval = XFS_DINODE_MIN_SIZE,
> -			  .maxval = XFS_DINODE_MAX_SIZE,
> +			  .minval.i = XFS_DINODE_MIN_SIZE,
> +			  .maxval.i = XFS_DINODE_MAX_SIZE,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = I_ATTR,
>  			  .conflicts = { {.opt = OPT_M,
>  					  .subopt = M_CRC,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 1,
> -					  .at_value = 1,
> +					  .invalid_value.b = true,
> +					  .at_value.i = 1,
>  					  .message = \
>  		"V2 attribute format always enabled on CRC enabled filesytems."},
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 2,
> +			  .minval.i = 0,
> +			  .maxval.i = 2,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = I_PROJID32BIT,
>  			  .conflicts = { {.opt = OPT_M,
>  					  .subopt = M_CRC,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 1,
> -					  .at_value = 0,
> +					  .invalid_value.b = true,
> +					  .at_value.b = 0,
>  					  .message = \
>  		"32 bit Project IDs always enabled on CRC enabled filesytems."},
>  					 {LAST_CONFLICT} },
>  
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.b = false,
> +			  .maxval.b = true,
> +			  .flagval.b = true,
> +			  .type = BOOL,
>  			},
>  			{ .index = I_SPINODES,
>  			  .conflicts = { {.opt = OPT_M,
>  					  .subopt = M_CRC,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 0,
> -					  .at_value = 1,
> +					  .invalid_value.b = 0,
> +					  .at_value.i = 1,
>  					  .message = \
>  		"Sparse inodes not supported without CRC support."},
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.i = 0,
> +			  .maxval.i = 1,
> +			  .flagval.i = 1,
> +			  .type = INT,
>  			},
>  		},
>  	},
> @@ -576,13 +831,15 @@ struct opt_params {
>  		},
>  		.subopt_params = {
>  			{ .index = L_AGNUM,
> +			  /* FIXME custom type xfs_agnumber_t? */

Does something really need to be fixed here?


>  			  .conflicts = { {.opt = OPT_L,
>  					  .subopt = L_DEV,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = UINT_MAX,
> +			  .minval.u = 0,
> +			  .maxval.u = UINT_MAX,
>  			  .needs_val = true,
> +			  .type = UINT,
>  			},
>  			{ .index = L_INTERNAL,
>  			  .conflicts = { {.opt = OPT_L,
> @@ -592,39 +849,43 @@ struct opt_params {
>  					  .subopt = L_DEV,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.i = 0,
> +			  .maxval.i = 1,
> +			  .flagval.i = 1,
> +			  .type = INT,
>  			},
>  			{ .index = L_SIZE,
>  			  .conflicts = { {LAST_CONFLICT} },
>  			  .convert = true,
> -			  .minval = 2 * 1024 * 1024LL,	/* XXX: XFS_MIN_LOG_BYTES */
> -			  .maxval = XFS_MAX_LOG_BYTES,
> +			  .minval.i = 2 * 1024 * 1024LL,	/* XXX: XFS_MIN_LOG_BYTES */
> +			  .maxval.i = XFS_MAX_LOG_BYTES,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = L_VERSION,
>  			  .conflicts = {{.opt = OPT_M,
>  					  .subopt = M_CRC,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 1,
> -					  .at_value = 1,
> +					  .invalid_value.b = true,
> +					  .at_value.i = 1,
>  					  .message =
>  				"V2 logs are required for CRC enabled filesystems."},
>  					 {LAST_CONFLICT} },
> -			  .minval = 1,
> -			  .maxval = 2,
> +			  .minval.i = 1,
> +			  .maxval.i = 2,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = L_SUNIT,
>  			  .conflicts = { {.opt = OPT_L,
>  					  .subopt = L_SU,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = 1,
> -			  .maxval = BTOBB(XLOG_MAX_RECORD_BSIZE),
> +			  .minval.i = 1,
> +			  .maxval.i = BTOBB(XLOG_MAX_RECORD_BSIZE),
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = L_SU,
>  			  .conflicts = { {.opt = OPT_L,
> @@ -632,9 +893,10 @@ struct opt_params {
>  					 },
>  					 {LAST_CONFLICT} },
>  			  .convert = true,
> -			  .minval = BBTOB(1),
> -			  .maxval = XLOG_MAX_RECORD_BSIZE,
> +			  .minval.i = BBTOB(1),
> +			  .maxval.i = XLOG_MAX_RECORD_BSIZE,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = L_DEV,
>  			  .conflicts = { {.opt = OPT_L,
> @@ -645,15 +907,17 @@ struct opt_params {
>  					 },
>  					 {LAST_CONFLICT} },
>  			  .needs_val = true,
> +			  .type = STRING,
>  			},
>  			{ .index = L_SECTLOG,
>  			  .conflicts = { {.opt = OPT_L,
>  					  .subopt = L_SECTSIZE,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = XFS_MIN_SECTORSIZE_LOG,
> -			  .maxval = XFS_MAX_SECTORSIZE_LOG,
> +			  .minval.i = XFS_MIN_SECTORSIZE_LOG,
> +			  .maxval.i = XFS_MAX_SECTORSIZE_LOG,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = L_SECTSIZE,
>  			  .conflicts = { {.opt = OPT_L,
> @@ -662,18 +926,20 @@ struct opt_params {
>  					 {LAST_CONFLICT} },
>  			  .convert = true,
>  			  .is_power_2 = true,
> -			  .minval = XFS_MIN_SECTORSIZE,
> -			  .maxval = XFS_MAX_SECTORSIZE,
> +			  .minval.i = XFS_MIN_SECTORSIZE,
> +			  .maxval.i = XFS_MAX_SECTORSIZE,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = L_FILE,
>  			  .conflicts = { {.opt = OPT_L,
>  					  .subopt = L_INTERNAL,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.i = 0,
> +			  .maxval.i = 1,
> +			  .flagval.i = 1,
> +			  .type = INT,
>  			},
>  			{ .index = L_NAME,
>  			  .conflicts = { {.opt = OPT_L,
> @@ -684,20 +950,22 @@ struct opt_params {
>  					 },
>  					 {LAST_CONFLICT} },
>  			  .needs_val = true,
> +			  .type = STRING,
>  			},
>  			{ .index = L_LAZYSBCNTR,
>  			  .conflicts = { {.opt = OPT_M,
>  					  .subopt = M_CRC,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 1,
> -					  .at_value = 0,
> +					  .invalid_value.b = true,
> +					  .at_value.b = false,
>  					  .message =
>  		"Lazy superblock counted always enabled for CRC enabled filesytems."},
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.b = false,
> +			  .maxval.b = true,
> +			  .flagval.b = true,
> +			  .type = BOOL,
>  			},
>  		},
>  	},
> @@ -717,9 +985,10 @@ struct opt_params {
>  					  .subopt = N_SIZE,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = XFS_MIN_REC_DIRSIZE,
> -			  .maxval = XFS_MAX_BLOCKSIZE_LOG,
> +			  .minval.i = XFS_MIN_REC_DIRSIZE,
> +			  .maxval.i = XFS_MAX_BLOCKSIZE_LOG,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = N_SIZE,
>  			  .conflicts = { {.opt = OPT_N,
> @@ -728,29 +997,32 @@ struct opt_params {
>  					 {LAST_CONFLICT} },
>  			  .convert = true,
>  			  .is_power_2 = true,
> -			  .minval = 1 << XFS_MIN_REC_DIRSIZE,
> -			  .maxval = XFS_MAX_BLOCKSIZE,
> +			  .minval.i = 1 << XFS_MIN_REC_DIRSIZE,
> +			  .maxval.i = XFS_MAX_BLOCKSIZE,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = N_VERSION,
>  			  .conflicts = { {LAST_CONFLICT} },
> -			  .minval = 2,
> -			  .maxval = 2,
> +			  .minval.i = 2,
> +			  .maxval.i = 2,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = N_FTYPE,
>  			  .conflicts = {  {.opt = OPT_M,
>  					  .subopt = M_CRC,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 1,
> -					  .at_value = 0,
> +					  .invalid_value.b = true,
> +					  .at_value.b = false,
>  					  .message =
>  		"Cannot disable ftype with crcs enabled."},
>  					  {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.b = false,
> +			  .maxval.b = true,
> +			  .flagval.b = true,
> +			  .type = BOOL,
>  			},
>  		},
>  	},
> @@ -770,33 +1042,37 @@ struct opt_params {
>  			{ .index = R_EXTSIZE,
>  			  .conflicts = { {LAST_CONFLICT} },
>  			  .convert = true,
> -			  .minval = XFS_MIN_RTEXTSIZE,
> -			  .maxval = XFS_MAX_RTEXTSIZE,
> +			  .minval.uint64 = XFS_MIN_RTEXTSIZE,
> +			  .maxval.uint64 = XFS_MAX_RTEXTSIZE,
>  			  .needs_val = true,
> +			  .type = UINT64,
>  			},
>  			{ .index = R_SIZE,
>  			  .conflicts = { {LAST_CONFLICT} },
>  			  .convert = true,
> -			  .minval = 0,
> -			  .maxval = LLONG_MAX,
> +			  .minval.uint64 = 0,
> +			  .maxval.uint64 = LLONG_MAX,
>  			  .needs_val = true,
> +			  .type = UINT64,
>  			},
>  			{ .index = R_DEV,
>  			  .conflicts = { {.opt = OPT_M,
>  				  	  .subopt = M_RMAPBT,
>  					  .test_values = false,
>  					  .test_default_value = true,
> -					  .invalid_value = 0,
> -					  .at_value = 0,
> +					  .invalid_value.b = 0,
> +					  .at_value.b = 0,
>  					  .message =
>  		"rmapbt not supported without CRC support."},
>  					 {LAST_CONFLICT} },
>  			  .needs_val = true,
> +			  .type = STRING,
>  			},
>  			{ .index = R_FILE,
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.i = 0,
> +			  .maxval.i = 1,
> +			  .flagval.i = 1,
> +			  .type = INT,
>  			  .conflicts = { {LAST_CONFLICT} },
>  			},
>  			{ .index = R_NAME,
> @@ -804,17 +1080,19 @@ struct opt_params {
>  				  	  .subopt = M_RMAPBT,
>  					  .test_values = false,
>  					  .test_default_value = true,
> -					  .invalid_value = 0,
> -					  .at_value = 0,
> +					  .invalid_value.b = 0,
> +					  .at_value.b = 0,
>  					  .message =
>  		"rmapbt not supported without CRC support."},
>  					 {LAST_CONFLICT} },
>  			  .needs_val = true,
> +			  .type = STRING,
>  			},
>  			{ .index = R_NOALIGN,
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.i = 0,
> +			  .maxval.i = 1,
> +			  .flagval.i = 1,
> +			  .type = INT,
>  			  .conflicts = { {LAST_CONFLICT} },
>  			},
>  		},
> @@ -838,9 +1116,10 @@ struct opt_params {
>  					  .subopt = S_SECTSIZE,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = XFS_MIN_SECTORSIZE_LOG,
> -			  .maxval = XFS_MAX_SECTORSIZE_LOG,
> +			  .minval.i = XFS_MIN_SECTORSIZE_LOG,
> +			  .maxval.i = XFS_MAX_SECTORSIZE_LOG,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = S_SECTLOG,
>  			  .conflicts = { {.opt = OPT_S,
> @@ -850,9 +1129,10 @@ struct opt_params {
>  					  .subopt = S_SECTSIZE,
>  					 },
>  					 {LAST_CONFLICT} },
> -			  .minval = XFS_MIN_SECTORSIZE_LOG,
> -			  .maxval = XFS_MAX_SECTORSIZE_LOG,
> +			  .minval.i = XFS_MIN_SECTORSIZE_LOG,
> +			  .maxval.i = XFS_MAX_SECTORSIZE_LOG,
>  			  .needs_val = true,
> +			  .type = INT,
>  			},
>  			{ .index = S_SIZE,
>  			  .conflicts = { {.opt = OPT_S,
> @@ -864,9 +1144,10 @@ struct opt_params {
>  					 {LAST_CONFLICT} },
>  			  .convert = true,
>  			  .is_power_2 = true,
> -			  .minval = XFS_MIN_SECTORSIZE,
> -			  .maxval = XFS_MAX_SECTORSIZE,
> +			  .minval.u = XFS_MIN_SECTORSIZE,
> +			  .maxval.u = XFS_MAX_SECTORSIZE,
>  			  .needs_val = true,
> +			  .type = UINT,
>  			},
>  			{ .index = S_SECTSIZE,
>  			  .conflicts = { {.opt = OPT_S,
> @@ -878,9 +1159,10 @@ struct opt_params {
>  					 {LAST_CONFLICT} },
>  			  .convert = true,
>  			  .is_power_2 = true,
> -			  .minval = XFS_MIN_SECTORSIZE,
> -			  .maxval = XFS_MAX_SECTORSIZE,
> +			  .minval.u = XFS_MIN_SECTORSIZE,
> +			  .maxval.u = XFS_MAX_SECTORSIZE,
>  			  .needs_val = true,
> +			  .type = UINT,
>  			},
>  		},
>  	},
> @@ -901,148 +1183,153 @@ struct opt_params {
>  					  .subopt = L_VERSION,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 1,
> -					  .at_value = 1,
> +					  .invalid_value.i = 1,
> +					  .at_value.b = 1,
>  					  .message =
>  		"V2 logs are required for CRC enabled filesystems."},
>  					 {.opt = OPT_I,
>  					  .subopt = I_ALIGN,
>  					  .test_values = false,
>  					  .test_default_value = true,
> -					  .invalid_value = 0,
> -					  .at_value = 1,
> +					  .invalid_value.b = 0,
> +					  .at_value.b = 1,
>  					  .message =
>  		"Inodes always aligned for CRC enabled filesytems."},
>  					 {.opt = OPT_I,
>  					  .subopt = I_PROJID32BIT,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 0,
> -					  .at_value = 1,
> +					  .invalid_value.b = 0,
> +					  .at_value.b = 1,
>  					  .message =
>  		"32 bit Project IDs always enabled on CRC enabled filesytems."},
>  					 {.opt = OPT_I,
>  					  .subopt = I_ATTR,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 1,
> -					  .at_value = 1,
> +					  .invalid_value.i = 1,
> +					  .at_value.b = 1,
>  					  .message =
>  		"V2 attribute format always enabled on CRC enabled filesytems."},
>  					 {.opt = OPT_L,
>  					  .subopt = L_LAZYSBCNTR,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 0,
> -					  .at_value = 1,
> +					  .invalid_value.b = 0,
> +					  .at_value.b = 1,
>  					  .message =
>  		"Lazy superblock counted always enabled for CRC enabled filesytems."},
>  					 {.opt = OPT_M,
>  					  .subopt = M_FINOBT,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 1,
> -					  .at_value = 0,
> +					  .invalid_value.i = 1,
> +					  .at_value.b = 0,
>  					  .message =
>  		"Finobt not supported without CRC support."},
>  					 {.opt = OPT_M,
>  				  	  .subopt = M_RMAPBT,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 1,
> -					  .at_value = 0,
> +					  .invalid_value.b = 1,
> +					  .at_value.b = 0,
>  					  .message =
>  		"rmapbt not supported without CRC support."},
>  					 {.opt = OPT_M,
>  				  	  .subopt = M_REFLINK,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 1,
> -					  .at_value = 0,
> +					  .invalid_value.b = 1,
> +					  .at_value.b = 0,
>  					  .message =
>  		"reflink not supported without CRC support."},
>  					 {.opt = OPT_I,
>  					  .subopt = I_SPINODES,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 1,
> -					  .at_value = 0,
> +					  .invalid_value.i = 1,
> +					  .at_value.b = 0,
>  					  .message =
>  		"Sparse inodes not supported without CRC support."},
>  					 {.opt = OPT_N,
>  					  .subopt = N_FTYPE,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 0,
> -					  .at_value = 1,
> +					  .invalid_value.b = 0,
> +					  .at_value.b = 1,
>  					  .message =
>  		"Cannot disable ftype with crcs enabled."},
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.b = false,
> +			  .maxval.b = true,
> +			  .flagval.b = true,
> +			  .type = BOOL,
>  			},
>  			{ .index = M_FINOBT,
>  			  .conflicts = { {.opt = OPT_M,
>  					  .subopt = M_CRC,
>  					  .test_values = true,
>  					  .test_default_value = true,
> -					  .invalid_value = 0,
> -					  .at_value = 1,
> +					  .invalid_value.b = 0,
> +					  .at_value.i = 1,
>  					  .message =
>  		"Finobt not supported without CRC support."},
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 1,
> -			  .flagval = 1,
> +			  .minval.i = 0,
> +			  .maxval.i = 1,
> +			  .flagval.i = 1,
> +			  .type = INT,
>  			},
>  			{ .index = M_UUID,
>  			  .conflicts = { {LAST_CONFLICT} },
>  			  .needs_val = true,
> +			  .type = STRING,
>  			},
>  			{ .index = M_RMAPBT,
>  			.conflicts = { {.opt = OPT_M,
>  					.subopt = M_CRC,
>  					.test_values = true,
>  					.test_default_value = true,
> -					.invalid_value = 0,
> -					.at_value = 1,
> +					.invalid_value.b = 0,
> +					.at_value.b = 1,
>  					.message =
>  		"rmapbt not supported without CRC support."},
>  					{.opt = OPT_R,
>  					 .subopt = R_NAME,
>  					 .test_values = false,
>  					 .test_default_value = true,
> -					 .invalid_value = 0,
> -					 .at_value = 0,
> +					 .invalid_value.b = 0,
> +					 .at_value.b = 0,
>  					 .message =
>  		"rmapbt not supported with realtime devices."},
>  					{.opt = OPT_R,
>  					 .subopt = R_DEV,
>  					 .test_values = false,
>  					 .test_default_value = true,
> -					 .invalid_value = 0,
> -					 .at_value = 0,
> +					 .invalid_value.b = 0,
> +					 .at_value.b = 0,
>  					 .message =
>  		"rmapbt not supported with realtime devices."},
>  				       {LAST_CONFLICT} },
> -			.minval = 0,
> -			.maxval = 1,
> -			.flagval = 0,
> +			.minval.b = false,
> +			.maxval.b = true,
> +			.flagval.b = false,
> +			.type = BOOL,
>  			},
>  			{ .index = M_REFLINK,
>  			.conflicts = { {.opt = OPT_M,
>  					.subopt = M_CRC,
>  					.test_values = true,
>  					.test_default_value = true,
> -					.invalid_value = 0,
> -					.at_value = 1,
> +					.invalid_value.b = 0,
> +					.at_value.b = 1,
>  					.message =
>  		"reflink not supported without CRC support."},
>  					 {LAST_CONFLICT} },
> -			  .minval = 0,
> -			  .maxval = 1,
> +			  .minval.b = 0,
> +			  .maxval.b = 1,
>  			  .needs_val = true,
> +			 .type = BOOL,
>  			},
>  		},
>  	},
> @@ -1653,8 +1940,7 @@ check_subopt_conflicts(
>  static void
>  check_subopt_value(
>  	struct opt_params	*opt,
> -	int			index,
> -	long long 		value)
> +	int			index)
>  {
>  	struct subopt_param	*sp = &opt->subopt_params[index];
>  	int			i;
> @@ -1669,9 +1955,19 @@ check_subopt_value(
>  			break;
>  		if ( (opts[conflict_opt.opt].subopt_params[conflict_opt.subopt].seen ||
>  		      conflict_opt.test_default_value) &&
> -		    opts[conflict_opt.opt].subopt_params[conflict_opt.subopt].value
> -				== conflict_opt.invalid_value &&
> -		    value == conflict_opt.at_value) {
> +		      test_uvalues(
> +			opts[conflict_opt.opt].subopt_params[conflict_opt.subopt].type,
> +			opts[conflict_opt.opt].subopt_params[conflict_opt.subopt].value,
> +			opts[conflict_opt.opt].subopt_params[conflict_opt.subopt].type,
> +			conflict_opt.invalid_value
> +		      ) &&
> +		      test_uvalues(
> +			sp->type,
> +			sp->value,
> +			sp->type,
> +			conflict_opt.at_value
> +		      )
> +		    ) {
>  			conflict_struct(opt, sp, &conflict_opt);
>  		}
>  	}
> @@ -1695,7 +1991,7 @@ check_opt(
>  		if (!sp->seen)
>  			continue;
>  		check_subopt_conflicts(opt, index, false);
> -		check_subopt_value(opt, index, sp->value);
> +		check_subopt_value(opt, index);
>  	}
>  }
>  static void
> @@ -1721,18 +2017,42 @@ getnum(
>  		if (sp->needs_val)
>  			reqval(opts->name, (char **)opts->subopts, index);
>  		sp->seen = true;
> -		return sp->flagval;
> +		switch(sp->type){
> +		case LONGLONG:
> +			return sp->flagval.ll;
> +		case BOOL:
> +			return sp->flagval.b;
> +		case UINT64:
> +			return sp->flagval.uint64;
> +		case INT:
> +			return sp->flagval.i;
> +		case UINT:
> +			return sp->flagval.u;
> +		default:
> +			fprintf(stderr,
> +				_("Option -%c %s called getnum, but is not numeric."
> +				  " This is a bug.\n"), opts->name, opts->subopts[index]);
> +			exit(1);
> +		}
>  	}
>  
>  	sp->seen = true;
>  
> -	if (sp->minval == 0 && sp->maxval == 0) {
> +	if (test_uvalue_num(sp->type, sp->minval, 0) &&
> +		test_uvalue_num(sp->type, sp->maxval, 0)) {
>  		fprintf(stderr,
>  			_("Option -%c %s has undefined minval/maxval."
>  			  "Can't verify value range. This is a bug.\n"),
>  			opts->name, opts->subopts[index]);
>  		exit(1);
>  	}
> +	if (sp->type == TYPE_UNDEF) {
> +		fprintf(stderr,
> +			_("Option -%c %s is of undefined type."
> +			  "Can't parse value. This is a bug.\n"),
> +			opts->name, opts->subopts[index]);
> +		exit(1);
> +	}
>  
>  	/*
>  	 * Some values are pure numbers, others can have suffixes that define
> @@ -1753,9 +2073,9 @@ getnum(
>  	}
>  
>  	/* Validity check the result. */
> -	if (c < sp->minval)
> +	if (cmp_uvalue_gt_num(sp->type, sp->minval, c))
>  		illegal_option(str, opts, index, _("value is too small"));
> -	else if (c > sp->maxval)
> +	else if (cmp_uvalue_lt_num(sp->type, sp->maxval, c))
>  		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"));
> @@ -1940,20 +2260,12 @@ main(
>  								B_LOG);
>  					blocksize = 1 << blocklog;
>  					blflag = 1;
> -					opts[OPT_B].subopt_params[B_LOG].value =
> -							blocklog;
> -					opts[OPT_B].subopt_params[B_SIZE].value =
> -							blocksize;
>  					break;
>  				case B_SIZE:
>  					blocksize = getnum(value, &opts[OPT_B],
>  							   B_SIZE);
>  					blocklog = libxfs_highbit32(blocksize);
>  					bsflag = 1;
> -					opts[OPT_B].subopt_params[B_LOG].value =
> -							blocklog;
> -					opts[OPT_B].subopt_params[B_SIZE].value =
> -							blocksize;
>  					break;
>  				default:
>  					unknown('b', value);
> @@ -1971,70 +2283,47 @@ main(
>  					agcount = getnum(value, &opts[OPT_D],
>  							 D_AGCOUNT);
>  					daflag = 1;
> -					opts[OPT_D].subopt_params[D_AGCOUNT].value =
> -							agcount;
>  					break;
>  				case D_AGSIZE:
>  					agsize = getnum(value, &opts[OPT_D],
>  								D_AGSIZE);
>  					dasize = 1;
> -					opts[OPT_D].subopt_params[D_AGSIZE].value =
> -							agsize;
>  					break;
>  				case D_FILE:
>  					xi.disfile = getnum(value, &opts[OPT_D],
>  							    D_FILE);
> -					opts[OPT_D].subopt_params[D_FILE].value =
> -							xi.disfile;
>  					break;
>  				case D_NAME:
>  					xi.dname = getstr(value, &opts[OPT_D],
>  								D_NAME);
> -					opts[OPT_D].subopt_params[D_NAME].value = 1;
>  					break;
>  				case D_SIZE:
>  					dbytes = getnum(value, &opts[OPT_D],
>  								D_SIZE);
> -					opts[OPT_D].subopt_params[D_SIZE].value =
> -							dbytes;
>  					break;
>  				case D_SUNIT:
>  					dsunit = getnum(value, &opts[OPT_D],
>  								D_SUNIT);
> -					opts[OPT_D].subopt_params[D_SUNIT].value =
> -							dsunit;
>  					break;
>  				case D_SWIDTH:
>  					dswidth = getnum(value, &opts[OPT_D],
>  							 D_SWIDTH);
> -					opts[OPT_D].subopt_params[D_SWIDTH].value =
> -							dswidth;
>  					break;
>  				case D_SU:
>  					dsu = getnum(value, &opts[OPT_D], D_SU);
> -					opts[OPT_D].subopt_params[D_SU].value =
> -							dsu;
>  					break;
>  				case D_SW:
>  					dsw = getnum(value, &opts[OPT_D], D_SW);
> -					opts[OPT_D].subopt_params[D_SW].value =
> -							dsw;
>  					break;
>  				case D_NOALIGN:
>  					nodsflag = getnum(value, &opts[OPT_D],
>  								D_NOALIGN);
> -					opts[OPT_D].subopt_params[D_NOALIGN].value =
> -							nodsflag;
>  					break;
>  				case D_SECTLOG:
>  					sectorlog = getnum(value, &opts[OPT_D],
>  							   D_SECTLOG);
>  					sectorsize = 1 << sectorlog;
>  					slflag = 1;
> -					opts[OPT_D].subopt_params[D_SECTSIZE].value =
> -							sectorsize;
> -					opts[OPT_D].subopt_params[D_SECTLOG].value =
> -							sectorlog;
>  					break;
>  				case D_SECTSIZE:
>  					sectorsize = getnum(value, &opts[OPT_D],
> @@ -2042,10 +2331,6 @@ main(
>  					sectorlog =
>  						libxfs_highbit32(sectorsize);
>  					ssflag = 1;
> -					opts[OPT_D].subopt_params[D_SECTSIZE].value =
> -							sectorsize;
> -					opts[OPT_D].subopt_params[D_SECTLOG].value =
> -							sectorlog;
>  					break;
>  				case D_RTINHERIT:
>  					c = getnum(value, &opts[OPT_D],
> @@ -2053,24 +2338,18 @@ main(
>  					if (c)
>  						fsx.fsx_xflags |=
>  							XFS_DIFLAG_RTINHERIT;
> -					opts[OPT_D].subopt_params[D_RTINHERIT].value =
> -							c;
>  					break;
>  				case D_PROJINHERIT:
>  					fsx.fsx_projid = getnum(value, &opts[OPT_D],
>  								D_PROJINHERIT);
>  					fsx.fsx_xflags |=
>  						XFS_DIFLAG_PROJINHERIT;
> -					opts[OPT_D].subopt_params[D_PROJINHERIT].value =
> -							fsx.fsx_projid;
>  					break;
>  				case D_EXTSZINHERIT:
>  					fsx.fsx_extsize = getnum(value, &opts[OPT_D],
>  								 D_EXTSZINHERIT);
>  					fsx.fsx_xflags |=
>  						XFS_DIFLAG_EXTSZINHERIT;
> -					opts[OPT_D].subopt_params[D_EXTSZINHERIT].value =
> -							fsx.fsx_extsize;
>  					break;
>  				default:
>  					unknown('d', value);
> @@ -2088,64 +2367,44 @@ main(
>  					sb_feat.inode_align = getnum(value,
>  								&opts[OPT_I],
>  								I_ALIGN);
> -					opts[OPT_I].subopt_params[I_ALIGN].value =
> -							sb_feat.inode_align;
>  					break;
>  				case I_LOG:
>  					inodelog = getnum(value, &opts[OPT_I],
>  								I_LOG);
>  					isize = 1 << inodelog;
>  					ilflag = 1;
> -					opts[OPT_I].subopt_params[I_SIZE].value =
> -							isize;
> -					opts[OPT_I].subopt_params[I_LOG].value =
> -							inodelog;
>  					break;
>  				case I_MAXPCT:
>  					imaxpct = getnum(value, &opts[OPT_I],
>  							 I_MAXPCT);
>  					imflag = 1;
> -					opts[OPT_I].subopt_params[I_MAXPCT].value =
> -							imaxpct;
>  					break;
>  				case I_PERBLOCK:
>  					inopblock = getnum(value, &opts[OPT_I],
>  							   I_PERBLOCK);
>  					ipflag = 1;
> -					opts[OPT_I].subopt_params[I_PERBLOCK].value =
> -							inopblock;
>  					break;
>  				case I_SIZE:
>  					isize = getnum(value, &opts[OPT_I],
>  								I_SIZE);
>  					inodelog = libxfs_highbit32(isize);
>  					isflag = 1;
> -					opts[OPT_I].subopt_params[I_SIZE].value =
> -							isize;
> -					opts[OPT_I].subopt_params[I_LOG].value =
> -							inodelog;
>  					break;
>  				case I_ATTR:
>  					sb_feat.attr_version =
>  
>  						getnum(value, &opts[OPT_I],
>  								I_ATTR);
> -					opts[OPT_I].subopt_params[I_ATTR].value =
> -							sb_feat.attr_version;
>  					break;
>  				case I_PROJID32BIT:
>  					sb_feat.projid16bit =
>  						!getnum(value, &opts[OPT_I],
>  							I_PROJID32BIT);
> -					opts[OPT_I].subopt_params[I_PROJID32BIT].value =
> -							sb_feat.projid16bit;
>  					break;
>  				case I_SPINODES:
>  					sb_feat.spinodes = getnum(value,
>  								&opts[OPT_I],
>  								I_SPINODES);
> -					opts[OPT_I].subopt_params[I_SPINODES].value =
> -							sb_feat.spinodes;
>  					break;
>  				default:
>  					unknown('i', value);
> @@ -2163,34 +2422,24 @@ main(
>  					logagno = getnum(value, &opts[OPT_L],
>  								L_AGNUM);
>  					laflag = 1;
> -					opts[OPT_L].subopt_params[L_AGNUM].value =
> -							logagno;
>  					break;
>  				case L_FILE:
>  					xi.lisfile = getnum(value, &opts[OPT_L],
>  							    L_FILE);
> -					opts[OPT_L].subopt_params[L_FILE].value =
> -							xi.lisfile;
>  					break;
>  				case L_INTERNAL:
>  					loginternal = getnum(value, &opts[OPT_L],
>  							     L_INTERNAL);
>  					liflag = 1;
> -					opts[OPT_L].subopt_params[L_INTERNAL].value =
> -							loginternal;
>  					break;
>  				case L_SU:
>  					lsu = getnum(value, &opts[OPT_L], L_SU);
>  					lsuflag = 1;
> -					opts[OPT_L].subopt_params[L_SU].value =
> -							lsu;
>  					break;
>  				case L_SUNIT:
>  					lsunit = getnum(value, &opts[OPT_L],
>  								L_SUNIT);
>  					lsunitflag = 1;
> -					opts[OPT_L].subopt_params[L_SUNIT].value =
> -							lsunit;
>  					break;
>  				case L_NAME:
>  				case L_DEV:
> @@ -2199,32 +2448,22 @@ main(
>  					xi.logname = logfile;
>  					ldflag = 1;
>  					loginternal = 0;
> -					opts[OPT_L].subopt_params[L_NAME].value = 1;
> -					opts[OPT_L].subopt_params[L_DEV].value = 1;
>  					break;
>  				case L_VERSION:
>  					sb_feat.log_version =
>  						getnum(value, &opts[OPT_L],
>  								L_VERSION);
>  					lvflag = 1;
> -					opts[OPT_L].subopt_params[L_VERSION].value =
> -							sb_feat.log_version;
>  					break;
>  				case L_SIZE:
>  					logbytes = getnum(value, &opts[OPT_L],
>  								L_SIZE);
> -					opts[OPT_L].subopt_params[L_SIZE].value =
> -							logbytes;
>  					break;
>  				case L_SECTLOG:
>  					lsectorlog = getnum(value, &opts[OPT_L],
>  							    L_SECTLOG);
>  					lsectorsize = 1 << lsectorlog;
>  					lslflag = 1;
> -					opts[OPT_L].subopt_params[L_SECTSIZE].value =
> -							lsectorsize;
> -					opts[OPT_L].subopt_params[L_SECTLOG].value =
> -							lsectorlog;
>  					break;
>  				case L_SECTSIZE:
>  					lsectorsize = getnum(value, &opts[OPT_L],
> @@ -2232,17 +2471,11 @@ main(
>  					lsectorlog =
>  						libxfs_highbit32(lsectorsize);
>  					lssflag = 1;
> -					opts[OPT_L].subopt_params[L_SECTSIZE].value =
> -							lsectorsize;
> -					opts[OPT_L].subopt_params[L_SECTLOG].value =
> -							lsectorlog;
>  					break;
>  				case L_LAZYSBCNTR:
>  					sb_feat.lazy_sb_counters =
>  							getnum(value, &opts[OPT_L],
>  							       L_LAZYSBCNTR);
> -					opts[OPT_L].subopt_params[L_LAZYSBCNTR].value =
> -							sb_feat.lazy_sb_counters;
>  					break;
>  				default:
>  					unknown('l', value);
> @@ -2267,27 +2500,20 @@ main(
>  								M_CRC);
>  					if (sb_feat.crcs_enabled)
>  						sb_feat.dirftype = true;
> -					opts[OPT_M].subopt_params[M_CRC].value =
> -							sb_feat.crcs_enabled;
>  					break;
>  				case M_FINOBT:
>  					sb_feat.finobt = getnum(
>  						value, &opts[OPT_M], M_FINOBT);
> -					opts[OPT_M].subopt_params[M_FINOBT].value =
> -							sb_feat.finobt;
>  					break;
>  				case M_UUID:
>  					if (!value || *value == '\0')
>  						reqval('m', subopts, M_UUID);
>  					if (platform_uuid_parse(value, &uuid))
>  						illegal(optarg, "m uuid");
> -					opts[OPT_M].subopt_params[M_UUID].value = 1;
>  					break;
>  				case M_RMAPBT:
>  					sb_feat.rmapbt = getnum(
>  						value, &opts[OPT_M], M_RMAPBT);
> -					opts[OPT_M].subopt_params[M_RMAPBT].value = 
> -						sb_feat.rmapbt;
>  					break;
>  				case M_REFLINK:
>  					sb_feat.reflink = getnum(
> @@ -2310,10 +2536,6 @@ main(
>  							     N_LOG);
>  					dirblocksize = 1 << dirblocklog;
>  					nlflag = 1;
> -					opts[OPT_N].subopt_params[N_SIZE].value =
> -							dirblocksize;
> -					opts[OPT_N].subopt_params[N_LOG].value =
> -							dirblocklog;
>  					break;
>  				case N_SIZE:
>  					dirblocksize = getnum(value, &opts[OPT_N],
> @@ -2321,10 +2543,6 @@ main(
>  					dirblocklog =
>  						libxfs_highbit32(dirblocksize);
>  					nsflag = 1;
> -					opts[OPT_N].subopt_params[N_SIZE].value =
> -							dirblocksize;
> -					opts[OPT_N].subopt_params[N_LOG].value =
> -							dirblocklog;
>  					break;
>  				case N_VERSION:
>  					value = getstr(value, &opts[OPT_N],
> @@ -2338,14 +2556,10 @@ main(
>  							       N_VERSION);
>  					}
>  					nvflag = 1;
> -					opts[OPT_N].subopt_params[N_VERSION].value =
> -							sb_feat.dir_version;
>  					break;
>  				case N_FTYPE:
>  					sb_feat.dirftype = getnum(value, &opts[OPT_N],
>  								  N_FTYPE);
> -					opts[OPT_N].subopt_params[N_FTYPE].value =
> -							sb_feat.dirftype;
>  					break;
>  				default:
>  					unknown('n', value);
> @@ -2376,33 +2590,23 @@ main(
>  				case R_EXTSIZE:
>  					rtextbytes = getnum(value, &opts[OPT_R],
>  								R_EXTSIZE);
> -					opts[OPT_R].subopt_params[R_EXTSIZE].value =
> -							rtextbytes;
>  					break;
>  				case R_FILE:
>  					xi.risfile = getnum(value, &opts[OPT_R],
>  							    R_FILE);
> -					opts[OPT_R].subopt_params[R_FILE].value =
> -							xi.risfile;
>  					break;
>  				case R_NAME:
>  				case R_DEV:
>  					xi.rtname = getstr(value, &opts[OPT_R],
>  							   R_NAME);
> -					opts[OPT_R].subopt_params[R_NAME].value = 1;
> -					opts[OPT_R].subopt_params[R_DEV].value = 1;
>  					break;
>  				case R_SIZE:
>  					rtbytes = getnum(value, &opts[OPT_R],
>  								R_SIZE);
> -					opts[OPT_R].subopt_params[R_SIZE].value =
> -							rtbytes;
>  					break;
>  				case R_NOALIGN:
>  					norsflag = getnum(value, &opts[OPT_R],
>  								R_NOALIGN);
> -					opts[OPT_R].subopt_params[R_NOALIGN].value =
> -							norsflag;
>  					break;
>  				default:
>  					unknown('r', value);
> @@ -2427,10 +2631,6 @@ main(
>  					sectorsize = 1 << sectorlog;
>  					lsectorsize = sectorsize;
>  					lslflag = slflag = 1;
> -					opts[OPT_S].subopt_params[S_LOG].value =
> -							sectorsize;
> -					opts[OPT_S].subopt_params[S_SECTLOG].value =
> -							sectorsize;
>  					break;
>  				case S_SIZE:
>  				case S_SECTSIZE:
> @@ -2444,10 +2644,6 @@ main(
>  						libxfs_highbit32(sectorsize);
>  					lsectorlog = sectorlog;
>  					lssflag = ssflag = 1;
> -					opts[OPT_S].subopt_params[S_SIZE].value =
> -							sectorlog;
> -					opts[OPT_S].subopt_params[S_SECTSIZE].value =
> -							sectorlog;
>  					break;
>  				default:
>  					unknown('s', value);
> -- 
> 2.8.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jan Tulak Jan. 16, 2017, 12:44 p.m. UTC | #2
On Fri, Jan 13, 2017 at 6:36 PM, Bill O'Donnell <billodo@redhat.com> wrote:
> On Wed, Dec 07, 2016 at 02:27:23PM +0100, Jan Tulak wrote:
>> Trying to cover all possible values in a single data type is impossible,
>> so convert the field from long long type to union. This requires
>> also some small changes in supporting code, otherwise it would not compile.
>>
>> Signed-off-by: Jan Tulak <jtulak@redhat.com>
>> ---
>>  mkfs/xfs_mkfs.c | 812 +++++++++++++++++++++++++++++++++++---------------------
>>  1 file changed, 504 insertions(+), 308 deletions(-)
>>
>> diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
>> index 7ffe8ff..afc63d1 100644
>> --- a/mkfs/xfs_mkfs.c
>> +++ b/mkfs/xfs_mkfs.c
>> @@ -121,6 +121,231 @@ unsigned int            sectorsize;
>>  #define M_RMAPBT     3
>>  #define M_REFLINK    4
>>
>> [snip]
>>
>> @@ -576,13 +831,15 @@ struct opt_params {
>>               },
>>               .subopt_params = {
>>                       { .index = L_AGNUM,
>> +                       /* FIXME custom type xfs_agnumber_t? */
>
> Does something really need to be fixed here?
>

Maybe: xfs_agnumber_t is defined as __uint32_t, while the code there
is using unsigned int. Which could possibly cause some issues if a
compiler would not treat int as 32 bits. Though we have a lot of other
custom/generic types mismatches around in the code, and it seems
unlikely that GCC or Clang would ever change how big int is... so
perhaps we can let it be. Or maybe let's change the definition of the
union so instead of unsigned int it is __uint32_t?
diff mbox

Patch

diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index 7ffe8ff..afc63d1 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -121,6 +121,231 @@  unsigned int		sectorsize;
 #define M_RMAPBT	3
 #define M_REFLINK	4
 
+enum e_type {
+	TYPE_UNDEF,
+	LONGLONG,
+	BOOL,
+	UINT64,
+	INT,
+	UINT,
+	STRING
+};
+union u_value {
+	long long 	ll;
+	bool		b;
+	__uint64_t	uint64;
+	int		i;
+	unsigned int	u;
+	char		*s;
+};
+static bool
+cmp_uvalues_gt(enum e_type a_type, union u_value a, enum e_type b_type, union u_value b) {
+	if (a_type == STRING || b_type == STRING) {
+		if (a_type == b_type)
+			return strcmp(a.s, b.s);
+		return false;
+	}	switch(a_type){
+	case LONGLONG:
+		switch(b_type){
+		case LONGLONG:
+			return a.ll > b.ll;
+		case BOOL:
+			return a.ll > b.b;
+		case UINT64:
+			return a.ll > b.uint64;
+		case INT:
+			return a.ll > b.i;
+		case UINT:
+			return a.ll > b.u;
+		default:
+			return false;
+		};
+		break;
+	case BOOL:
+		switch(b_type){
+		case LONGLONG:
+			return a.b > b.ll;
+		case BOOL:
+			return a.b > b.b;
+		case UINT64:
+			return a.b > b.uint64;
+		case INT:
+			return a.b > b.i;
+		case UINT:
+			return a.b > b.u;
+		default:
+			return false;
+		};
+		break;
+	case UINT64:
+		switch(b_type){
+		case LONGLONG:
+			return a.uint64 > b.ll;
+		case BOOL:
+			return a.uint64 > b.b;
+		case UINT64:
+			return a.uint64 > b.uint64;
+		case INT:
+			return a.uint64 > b.i;
+		case UINT:
+			return a.uint64 > b.u;
+		default:
+			return false;
+		};
+		break;
+	case INT:
+		switch(b_type){
+		case LONGLONG:
+			return a.i > b.ll;
+		case BOOL:
+			return a.i > b.b;
+		case UINT64:
+			return a.i > b.uint64;
+		case INT:
+			return a.i > b.i;
+		case UINT:
+			return a.i > b.u;
+		default:
+			return false;
+		};
+		break;
+	case UINT:
+		switch(b_type){
+		case LONGLONG:
+			return a.u > b.ll;
+		case BOOL:
+			return a.u > b.b;
+		case UINT64:
+			return a.u > b.uint64;
+		case INT:
+			return a.u > b.i;
+		case UINT:
+			return a.u > b.u;
+		default:
+			return false;
+		};
+		break;
+	default:
+		return false;
+	};
+
+	return false;
+}
+static bool
+cmp_uvalue_gt_num(enum e_type a_type, union u_value a, long long b) {
+	union u_value u;
+	u.ll = b;
+	return cmp_uvalues_gt(a_type, a, LONGLONG, u);
+}
+static bool
+cmp_uvalue_lt_num(enum e_type a_type, union u_value a, long long b) {
+	union u_value u;
+	u.ll = b;
+	return cmp_uvalues_gt(LONGLONG, u, a_type, a);
+}
+
+static bool
+test_uvalues(enum e_type a_type, union u_value a, enum e_type b_type, union u_value b) {
+	if (a_type == STRING || b_type == STRING) {
+		if (a_type == b_type)
+			return strcmp(a.s, b.s) == 0;
+		return false;
+	}
+	switch(a_type){
+	case LONGLONG:
+		switch(b_type){
+		case LONGLONG:
+			return a.ll == b.ll;
+		case BOOL:
+			return a.ll == b.b;
+		case UINT64:
+			return a.ll == b.uint64;
+		case INT:
+			return a.ll == b.i;
+		case UINT:
+			return a.ll == b.u;
+		default:
+			return false;
+		};
+		break;
+	case BOOL:
+		switch(b_type){
+		case LONGLONG:
+			return a.b == b.ll;
+		case BOOL:
+			return a.b == b.b;
+		case UINT64:
+			return a.b == b.uint64;
+		case INT:
+			return a.b == b.i;
+		case UINT:
+			return a.b == b.u;
+		default:
+			return false;
+		};
+		break;
+	case UINT64:
+		switch(b_type){
+		case LONGLONG:
+			return a.uint64 == b.ll;
+		case BOOL:
+			return a.uint64 == b.b;
+		case UINT64:
+			return a.uint64 == b.uint64;
+		case INT:
+			return a.uint64 == b.i;
+		case UINT:
+			return a.uint64 == b.u;
+		default:
+			return false;
+		};
+		break;
+	case INT:
+		switch(b_type){
+		case LONGLONG:
+			return a.i == b.ll;
+		case BOOL:
+			return a.i == b.b;
+		case UINT64:
+			return a.i == b.uint64;
+		case INT:
+			return a.i == b.i;
+		case UINT:
+			return a.i == b.u;
+		default:
+			return false;
+		};
+		break;
+	case UINT:
+		switch(b_type){
+		case LONGLONG:
+			return a.u == b.ll;
+		case BOOL:
+			return a.u == b.b;
+		case UINT64:
+			return a.u == b.uint64;
+		case INT:
+			return a.u == b.i;
+		case UINT:
+			return a.u == b.u;
+		default:
+			return false;
+		};
+		break;
+	default:
+		return false;
+	};
+
+	return false;
+}
+
+static bool
+test_uvalue_num(enum e_type a_type, union u_value a, long long b) {
+	union u_value u;
+	u.ll = b;
+	return test_uvalues(a_type, a, LONGLONG, u);
+}
+
 /*
  * Table for parsing mkfs parameters.
  *
@@ -193,11 +418,15 @@  unsigned int		sectorsize;
  *     If the subopt accepts some values (-d file=[1|0]), then this
  *     sets what is used with simple specifying the subopt (-d file).
  *
- *   value INTERNAL
- *     Do not set this on initialization. Use flagval for what you want
- *     to do. This is filled with user input and anything you write here now
- *     is overwritten. (If the user input is a string and not a number, this
- *     value is set to a positive non-zero number.)
+ *   value MANDATORY
+ *     A value that is used for given field as a default if user doesn't specify
+ *     any change. This is filled with user input and anything you write here
+ *     now is overwritten if the user specifies given option.
+ *
+ *   type MANDATORY
+ *     An enum of what type the values are. Affects every u_value field within
+ *     the suboption except conflict's .invalid_value - this one uses the
+ *     "remote" type.
  *
  *   needs_val OPTIONAL
  *     Set to true if, when user specifies the option, she has to specify
@@ -219,14 +448,15 @@  struct opt_params {
 			int		subopt;
 			bool		test_values;
 			bool		test_default_value;
-			long long	invalid_value;
-			long long	at_value;
+			union u_value	invalid_value;
+			union u_value	at_value;
 			const char	*message;
 		}		conflicts [MAX_CONFLICTS];
-		long long	minval;
-		long long	maxval;
-		long long	flagval;
-		long long	value;
+		union u_value	minval;
+		union u_value	maxval;
+		union u_value	flagval;
+		union u_value value;
+		enum e_type 	type;
 		bool		needs_val;
 	}		subopt_params[MAX_SUBOPTS];
 } opts[MAX_OPTS] = {
@@ -244,9 +474,10 @@  struct opt_params {
 					  .subopt = B_SIZE,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = XFS_MIN_BLOCKSIZE_LOG,
-			  .maxval = XFS_MAX_BLOCKSIZE_LOG,
+			  .minval.i = XFS_MIN_BLOCKSIZE_LOG,
+			  .maxval.i = XFS_MAX_BLOCKSIZE_LOG,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = B_SIZE,
 			  .convert = true,
@@ -255,9 +486,10 @@  struct opt_params {
 					  .subopt = B_LOG,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = XFS_MIN_BLOCKSIZE,
-			  .maxval = XFS_MAX_BLOCKSIZE,
+			  .minval.u = XFS_MIN_BLOCKSIZE,
+			  .maxval.u = XFS_MAX_BLOCKSIZE,
 			  .needs_val = true,
+			  .type = UINT,
 			},
 		},
 	},
@@ -288,26 +520,30 @@  struct opt_params {
 					  .subopt = D_AGSIZE,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = 1,
-			  .maxval = XFS_MAX_AGNUMBER,
+			  .minval.uint64 = 1,
+			  .maxval.uint64 = XFS_MAX_AGNUMBER,
 			  .needs_val = true,
+			  .type = UINT64,
 			},
 			{ .index = D_FILE,
 			  .conflicts = { {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.i = 0,
+			  .maxval.i = 1,
+			  .flagval.i = 1,
+			  .type = INT,
 			},
 			{ .index = D_NAME,
 			  .conflicts = { {LAST_CONFLICT} },
 			  .needs_val = true,
+			  .type = STRING,
 			},
 			{ .index = D_SIZE,
 			  .conflicts = { {LAST_CONFLICT} },
 			  .convert = true,
-			  .minval = XFS_AG_MIN_BYTES,
-			  .maxval = LLONG_MAX,
+			  .minval.uint64 = XFS_AG_MIN_BYTES,
+			  .maxval.uint64 = LLONG_MAX,
 			  .needs_val = true,
+			  .type = UINT64,
 			},
 			{ .index = D_SUNIT,
 			  .conflicts = { {.opt = OPT_D,
@@ -320,9 +556,10 @@  struct opt_params {
 					  .subopt = D_SW,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = UINT_MAX,
+			  .minval.i = 0,
+			  .maxval.i = UINT_MAX,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = D_SWIDTH,
 			  .conflicts = { {.opt = OPT_D,
@@ -335,9 +572,10 @@  struct opt_params {
 					  .subopt = D_SW,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = UINT_MAX,
+			  .minval.i = 0,
+			  .maxval.i = UINT_MAX,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = D_AGSIZE,
 			  .conflicts = { {.opt = OPT_D,
@@ -345,9 +583,10 @@  struct opt_params {
 					 },
 					 {LAST_CONFLICT} },
 			  .convert = true,
-			  .minval = XFS_AG_MIN_BYTES,
-			  .maxval = XFS_AG_MAX_BYTES,
+			  .minval.uint64 = XFS_AG_MIN_BYTES,
+			  .maxval.uint64 = XFS_AG_MAX_BYTES,
 			  .needs_val = true,
+			  .type = UINT64,
 			},
 			{ .index = D_SU,
 			  .conflicts = { {.opt = OPT_D,
@@ -361,9 +600,10 @@  struct opt_params {
 					 },
 					 {LAST_CONFLICT} },
 			  .convert = true,
-			  .minval = 0,
-			  .maxval = UINT_MAX,
+			  .minval.i = 0,
+			  .maxval.i = UINT_MAX,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = D_SW,
 			  .conflicts = { {.opt = OPT_D,
@@ -376,18 +616,20 @@  struct opt_params {
 					  .subopt = D_SWIDTH,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = UINT_MAX,
+			  .minval.i = 0,
+			  .maxval.i = UINT_MAX,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = D_SECTLOG,
 			  .conflicts = { {.opt = OPT_D,
 					  .subopt = D_SECTSIZE,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = XFS_MIN_SECTORSIZE_LOG,
-			  .maxval = XFS_MAX_SECTORSIZE_LOG,
+			  .minval.i = XFS_MIN_SECTORSIZE_LOG,
+			  .maxval.i = XFS_MAX_SECTORSIZE_LOG,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = D_SECTSIZE,
 			  .conflicts = { {.opt = OPT_D,
@@ -396,9 +638,10 @@  struct opt_params {
 					 {LAST_CONFLICT} },
 			  .convert = true,
 			  .is_power_2 = true,
-			  .minval = XFS_MIN_SECTORSIZE,
-			  .maxval = XFS_MAX_SECTORSIZE,
+			  .minval.u = XFS_MIN_SECTORSIZE,
+			  .maxval.u = XFS_MAX_SECTORSIZE,
 			  .needs_val = true,
+			  .type = UINT,
 			},
 			{ .index = D_NOALIGN,
 			  .conflicts = { {.opt = OPT_D,
@@ -414,27 +657,31 @@  struct opt_params {
 					  .subopt = D_SWIDTH,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.i = 0,
+			  .maxval.i = 1,
+			  .flagval.i = 1,
+			  .type = INT,
 			},
 			{ .index = D_RTINHERIT,
 			  .conflicts = { {LAST_CONFLICT} },
-			  .minval = 1,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.u = 1,
+			  .maxval.u = 1,
+			  .flagval.u = 1,
+			  .type = UINT,
 			},
 			{ .index = D_PROJINHERIT,
 			  .conflicts = { {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = UINT_MAX,
+			  .minval.u = 0,
+			  .maxval.u = UINT_MAX,
 			  .needs_val = true,
+			  .type = UINT,
 			},
 			{ .index = D_EXTSZINHERIT,
 			  .conflicts = { {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = UINT_MAX,
+			  .minval.u = 0,
+			  .maxval.u = UINT_MAX,
 			  .needs_val = true,
+			  .type = UINT,
 			},
 		},
 	},
@@ -458,14 +705,15 @@  struct opt_params {
 					  .subopt = M_CRC,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 1,
-					  .at_value = 0,
+					  .invalid_value.b = 1,
+					  .at_value.b = 0,
 					  .message = \
 		"Inodes always aligned for CRC enabled filesytems."},
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.b = false,
+			  .maxval.b = true,
+			  .flagval.b = true,
+			  .type = BOOL,
 			},
 			{ .index = I_LOG,
 			  .conflicts = { {.opt = OPT_I,
@@ -475,15 +723,17 @@  struct opt_params {
 					  .subopt = I_SIZE,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = XFS_DINODE_MIN_LOG,
-			  .maxval = XFS_DINODE_MAX_LOG,
+			  .minval.i = XFS_DINODE_MIN_LOG,
+			  .maxval.i = XFS_DINODE_MAX_LOG,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = I_MAXPCT,
 			  .conflicts = { {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 100,
+			  .minval.i = 0,
+			  .maxval.i = 100,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = I_PERBLOCK,
 			  .conflicts = { {.opt = OPT_I,
@@ -494,9 +744,10 @@  struct opt_params {
 					 },
 					 {LAST_CONFLICT} },
 			  .is_power_2 = true,
-			  .minval = XFS_MIN_INODE_PERBLOCK,
-			  .maxval = XFS_MAX_BLOCKSIZE / XFS_DINODE_MIN_SIZE,
+			  .minval.i = XFS_MIN_INODE_PERBLOCK,
+			  .maxval.i = XFS_MAX_BLOCKSIZE / XFS_DINODE_MIN_SIZE,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = I_SIZE,
 			  .conflicts = { {.opt = OPT_I,
@@ -507,52 +758,56 @@  struct opt_params {
 					 },
 					 {LAST_CONFLICT} },
 			  .is_power_2 = true,
-			  .minval = XFS_DINODE_MIN_SIZE,
-			  .maxval = XFS_DINODE_MAX_SIZE,
+			  .minval.i = XFS_DINODE_MIN_SIZE,
+			  .maxval.i = XFS_DINODE_MAX_SIZE,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = I_ATTR,
 			  .conflicts = { {.opt = OPT_M,
 					  .subopt = M_CRC,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 1,
-					  .at_value = 1,
+					  .invalid_value.b = true,
+					  .at_value.i = 1,
 					  .message = \
 		"V2 attribute format always enabled on CRC enabled filesytems."},
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 2,
+			  .minval.i = 0,
+			  .maxval.i = 2,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = I_PROJID32BIT,
 			  .conflicts = { {.opt = OPT_M,
 					  .subopt = M_CRC,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 1,
-					  .at_value = 0,
+					  .invalid_value.b = true,
+					  .at_value.b = 0,
 					  .message = \
 		"32 bit Project IDs always enabled on CRC enabled filesytems."},
 					 {LAST_CONFLICT} },
 
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.b = false,
+			  .maxval.b = true,
+			  .flagval.b = true,
+			  .type = BOOL,
 			},
 			{ .index = I_SPINODES,
 			  .conflicts = { {.opt = OPT_M,
 					  .subopt = M_CRC,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 0,
-					  .at_value = 1,
+					  .invalid_value.b = 0,
+					  .at_value.i = 1,
 					  .message = \
 		"Sparse inodes not supported without CRC support."},
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.i = 0,
+			  .maxval.i = 1,
+			  .flagval.i = 1,
+			  .type = INT,
 			},
 		},
 	},
@@ -576,13 +831,15 @@  struct opt_params {
 		},
 		.subopt_params = {
 			{ .index = L_AGNUM,
+			  /* FIXME custom type xfs_agnumber_t? */
 			  .conflicts = { {.opt = OPT_L,
 					  .subopt = L_DEV,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = UINT_MAX,
+			  .minval.u = 0,
+			  .maxval.u = UINT_MAX,
 			  .needs_val = true,
+			  .type = UINT,
 			},
 			{ .index = L_INTERNAL,
 			  .conflicts = { {.opt = OPT_L,
@@ -592,39 +849,43 @@  struct opt_params {
 					  .subopt = L_DEV,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.i = 0,
+			  .maxval.i = 1,
+			  .flagval.i = 1,
+			  .type = INT,
 			},
 			{ .index = L_SIZE,
 			  .conflicts = { {LAST_CONFLICT} },
 			  .convert = true,
-			  .minval = 2 * 1024 * 1024LL,	/* XXX: XFS_MIN_LOG_BYTES */
-			  .maxval = XFS_MAX_LOG_BYTES,
+			  .minval.i = 2 * 1024 * 1024LL,	/* XXX: XFS_MIN_LOG_BYTES */
+			  .maxval.i = XFS_MAX_LOG_BYTES,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = L_VERSION,
 			  .conflicts = {{.opt = OPT_M,
 					  .subopt = M_CRC,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 1,
-					  .at_value = 1,
+					  .invalid_value.b = true,
+					  .at_value.i = 1,
 					  .message =
 				"V2 logs are required for CRC enabled filesystems."},
 					 {LAST_CONFLICT} },
-			  .minval = 1,
-			  .maxval = 2,
+			  .minval.i = 1,
+			  .maxval.i = 2,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = L_SUNIT,
 			  .conflicts = { {.opt = OPT_L,
 					  .subopt = L_SU,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = 1,
-			  .maxval = BTOBB(XLOG_MAX_RECORD_BSIZE),
+			  .minval.i = 1,
+			  .maxval.i = BTOBB(XLOG_MAX_RECORD_BSIZE),
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = L_SU,
 			  .conflicts = { {.opt = OPT_L,
@@ -632,9 +893,10 @@  struct opt_params {
 					 },
 					 {LAST_CONFLICT} },
 			  .convert = true,
-			  .minval = BBTOB(1),
-			  .maxval = XLOG_MAX_RECORD_BSIZE,
+			  .minval.i = BBTOB(1),
+			  .maxval.i = XLOG_MAX_RECORD_BSIZE,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = L_DEV,
 			  .conflicts = { {.opt = OPT_L,
@@ -645,15 +907,17 @@  struct opt_params {
 					 },
 					 {LAST_CONFLICT} },
 			  .needs_val = true,
+			  .type = STRING,
 			},
 			{ .index = L_SECTLOG,
 			  .conflicts = { {.opt = OPT_L,
 					  .subopt = L_SECTSIZE,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = XFS_MIN_SECTORSIZE_LOG,
-			  .maxval = XFS_MAX_SECTORSIZE_LOG,
+			  .minval.i = XFS_MIN_SECTORSIZE_LOG,
+			  .maxval.i = XFS_MAX_SECTORSIZE_LOG,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = L_SECTSIZE,
 			  .conflicts = { {.opt = OPT_L,
@@ -662,18 +926,20 @@  struct opt_params {
 					 {LAST_CONFLICT} },
 			  .convert = true,
 			  .is_power_2 = true,
-			  .minval = XFS_MIN_SECTORSIZE,
-			  .maxval = XFS_MAX_SECTORSIZE,
+			  .minval.i = XFS_MIN_SECTORSIZE,
+			  .maxval.i = XFS_MAX_SECTORSIZE,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = L_FILE,
 			  .conflicts = { {.opt = OPT_L,
 					  .subopt = L_INTERNAL,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.i = 0,
+			  .maxval.i = 1,
+			  .flagval.i = 1,
+			  .type = INT,
 			},
 			{ .index = L_NAME,
 			  .conflicts = { {.opt = OPT_L,
@@ -684,20 +950,22 @@  struct opt_params {
 					 },
 					 {LAST_CONFLICT} },
 			  .needs_val = true,
+			  .type = STRING,
 			},
 			{ .index = L_LAZYSBCNTR,
 			  .conflicts = { {.opt = OPT_M,
 					  .subopt = M_CRC,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 1,
-					  .at_value = 0,
+					  .invalid_value.b = true,
+					  .at_value.b = false,
 					  .message =
 		"Lazy superblock counted always enabled for CRC enabled filesytems."},
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.b = false,
+			  .maxval.b = true,
+			  .flagval.b = true,
+			  .type = BOOL,
 			},
 		},
 	},
@@ -717,9 +985,10 @@  struct opt_params {
 					  .subopt = N_SIZE,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = XFS_MIN_REC_DIRSIZE,
-			  .maxval = XFS_MAX_BLOCKSIZE_LOG,
+			  .minval.i = XFS_MIN_REC_DIRSIZE,
+			  .maxval.i = XFS_MAX_BLOCKSIZE_LOG,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = N_SIZE,
 			  .conflicts = { {.opt = OPT_N,
@@ -728,29 +997,32 @@  struct opt_params {
 					 {LAST_CONFLICT} },
 			  .convert = true,
 			  .is_power_2 = true,
-			  .minval = 1 << XFS_MIN_REC_DIRSIZE,
-			  .maxval = XFS_MAX_BLOCKSIZE,
+			  .minval.i = 1 << XFS_MIN_REC_DIRSIZE,
+			  .maxval.i = XFS_MAX_BLOCKSIZE,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = N_VERSION,
 			  .conflicts = { {LAST_CONFLICT} },
-			  .minval = 2,
-			  .maxval = 2,
+			  .minval.i = 2,
+			  .maxval.i = 2,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = N_FTYPE,
 			  .conflicts = {  {.opt = OPT_M,
 					  .subopt = M_CRC,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 1,
-					  .at_value = 0,
+					  .invalid_value.b = true,
+					  .at_value.b = false,
 					  .message =
 		"Cannot disable ftype with crcs enabled."},
 					  {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.b = false,
+			  .maxval.b = true,
+			  .flagval.b = true,
+			  .type = BOOL,
 			},
 		},
 	},
@@ -770,33 +1042,37 @@  struct opt_params {
 			{ .index = R_EXTSIZE,
 			  .conflicts = { {LAST_CONFLICT} },
 			  .convert = true,
-			  .minval = XFS_MIN_RTEXTSIZE,
-			  .maxval = XFS_MAX_RTEXTSIZE,
+			  .minval.uint64 = XFS_MIN_RTEXTSIZE,
+			  .maxval.uint64 = XFS_MAX_RTEXTSIZE,
 			  .needs_val = true,
+			  .type = UINT64,
 			},
 			{ .index = R_SIZE,
 			  .conflicts = { {LAST_CONFLICT} },
 			  .convert = true,
-			  .minval = 0,
-			  .maxval = LLONG_MAX,
+			  .minval.uint64 = 0,
+			  .maxval.uint64 = LLONG_MAX,
 			  .needs_val = true,
+			  .type = UINT64,
 			},
 			{ .index = R_DEV,
 			  .conflicts = { {.opt = OPT_M,
 				  	  .subopt = M_RMAPBT,
 					  .test_values = false,
 					  .test_default_value = true,
-					  .invalid_value = 0,
-					  .at_value = 0,
+					  .invalid_value.b = 0,
+					  .at_value.b = 0,
 					  .message =
 		"rmapbt not supported without CRC support."},
 					 {LAST_CONFLICT} },
 			  .needs_val = true,
+			  .type = STRING,
 			},
 			{ .index = R_FILE,
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.i = 0,
+			  .maxval.i = 1,
+			  .flagval.i = 1,
+			  .type = INT,
 			  .conflicts = { {LAST_CONFLICT} },
 			},
 			{ .index = R_NAME,
@@ -804,17 +1080,19 @@  struct opt_params {
 				  	  .subopt = M_RMAPBT,
 					  .test_values = false,
 					  .test_default_value = true,
-					  .invalid_value = 0,
-					  .at_value = 0,
+					  .invalid_value.b = 0,
+					  .at_value.b = 0,
 					  .message =
 		"rmapbt not supported without CRC support."},
 					 {LAST_CONFLICT} },
 			  .needs_val = true,
+			  .type = STRING,
 			},
 			{ .index = R_NOALIGN,
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.i = 0,
+			  .maxval.i = 1,
+			  .flagval.i = 1,
+			  .type = INT,
 			  .conflicts = { {LAST_CONFLICT} },
 			},
 		},
@@ -838,9 +1116,10 @@  struct opt_params {
 					  .subopt = S_SECTSIZE,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = XFS_MIN_SECTORSIZE_LOG,
-			  .maxval = XFS_MAX_SECTORSIZE_LOG,
+			  .minval.i = XFS_MIN_SECTORSIZE_LOG,
+			  .maxval.i = XFS_MAX_SECTORSIZE_LOG,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = S_SECTLOG,
 			  .conflicts = { {.opt = OPT_S,
@@ -850,9 +1129,10 @@  struct opt_params {
 					  .subopt = S_SECTSIZE,
 					 },
 					 {LAST_CONFLICT} },
-			  .minval = XFS_MIN_SECTORSIZE_LOG,
-			  .maxval = XFS_MAX_SECTORSIZE_LOG,
+			  .minval.i = XFS_MIN_SECTORSIZE_LOG,
+			  .maxval.i = XFS_MAX_SECTORSIZE_LOG,
 			  .needs_val = true,
+			  .type = INT,
 			},
 			{ .index = S_SIZE,
 			  .conflicts = { {.opt = OPT_S,
@@ -864,9 +1144,10 @@  struct opt_params {
 					 {LAST_CONFLICT} },
 			  .convert = true,
 			  .is_power_2 = true,
-			  .minval = XFS_MIN_SECTORSIZE,
-			  .maxval = XFS_MAX_SECTORSIZE,
+			  .minval.u = XFS_MIN_SECTORSIZE,
+			  .maxval.u = XFS_MAX_SECTORSIZE,
 			  .needs_val = true,
+			  .type = UINT,
 			},
 			{ .index = S_SECTSIZE,
 			  .conflicts = { {.opt = OPT_S,
@@ -878,9 +1159,10 @@  struct opt_params {
 					 {LAST_CONFLICT} },
 			  .convert = true,
 			  .is_power_2 = true,
-			  .minval = XFS_MIN_SECTORSIZE,
-			  .maxval = XFS_MAX_SECTORSIZE,
+			  .minval.u = XFS_MIN_SECTORSIZE,
+			  .maxval.u = XFS_MAX_SECTORSIZE,
 			  .needs_val = true,
+			  .type = UINT,
 			},
 		},
 	},
@@ -901,148 +1183,153 @@  struct opt_params {
 					  .subopt = L_VERSION,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 1,
-					  .at_value = 1,
+					  .invalid_value.i = 1,
+					  .at_value.b = 1,
 					  .message =
 		"V2 logs are required for CRC enabled filesystems."},
 					 {.opt = OPT_I,
 					  .subopt = I_ALIGN,
 					  .test_values = false,
 					  .test_default_value = true,
-					  .invalid_value = 0,
-					  .at_value = 1,
+					  .invalid_value.b = 0,
+					  .at_value.b = 1,
 					  .message =
 		"Inodes always aligned for CRC enabled filesytems."},
 					 {.opt = OPT_I,
 					  .subopt = I_PROJID32BIT,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 0,
-					  .at_value = 1,
+					  .invalid_value.b = 0,
+					  .at_value.b = 1,
 					  .message =
 		"32 bit Project IDs always enabled on CRC enabled filesytems."},
 					 {.opt = OPT_I,
 					  .subopt = I_ATTR,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 1,
-					  .at_value = 1,
+					  .invalid_value.i = 1,
+					  .at_value.b = 1,
 					  .message =
 		"V2 attribute format always enabled on CRC enabled filesytems."},
 					 {.opt = OPT_L,
 					  .subopt = L_LAZYSBCNTR,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 0,
-					  .at_value = 1,
+					  .invalid_value.b = 0,
+					  .at_value.b = 1,
 					  .message =
 		"Lazy superblock counted always enabled for CRC enabled filesytems."},
 					 {.opt = OPT_M,
 					  .subopt = M_FINOBT,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 1,
-					  .at_value = 0,
+					  .invalid_value.i = 1,
+					  .at_value.b = 0,
 					  .message =
 		"Finobt not supported without CRC support."},
 					 {.opt = OPT_M,
 				  	  .subopt = M_RMAPBT,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 1,
-					  .at_value = 0,
+					  .invalid_value.b = 1,
+					  .at_value.b = 0,
 					  .message =
 		"rmapbt not supported without CRC support."},
 					 {.opt = OPT_M,
 				  	  .subopt = M_REFLINK,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 1,
-					  .at_value = 0,
+					  .invalid_value.b = 1,
+					  .at_value.b = 0,
 					  .message =
 		"reflink not supported without CRC support."},
 					 {.opt = OPT_I,
 					  .subopt = I_SPINODES,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 1,
-					  .at_value = 0,
+					  .invalid_value.i = 1,
+					  .at_value.b = 0,
 					  .message =
 		"Sparse inodes not supported without CRC support."},
 					 {.opt = OPT_N,
 					  .subopt = N_FTYPE,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 0,
-					  .at_value = 1,
+					  .invalid_value.b = 0,
+					  .at_value.b = 1,
 					  .message =
 		"Cannot disable ftype with crcs enabled."},
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.b = false,
+			  .maxval.b = true,
+			  .flagval.b = true,
+			  .type = BOOL,
 			},
 			{ .index = M_FINOBT,
 			  .conflicts = { {.opt = OPT_M,
 					  .subopt = M_CRC,
 					  .test_values = true,
 					  .test_default_value = true,
-					  .invalid_value = 0,
-					  .at_value = 1,
+					  .invalid_value.b = 0,
+					  .at_value.i = 1,
 					  .message =
 		"Finobt not supported without CRC support."},
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 1,
-			  .flagval = 1,
+			  .minval.i = 0,
+			  .maxval.i = 1,
+			  .flagval.i = 1,
+			  .type = INT,
 			},
 			{ .index = M_UUID,
 			  .conflicts = { {LAST_CONFLICT} },
 			  .needs_val = true,
+			  .type = STRING,
 			},
 			{ .index = M_RMAPBT,
 			.conflicts = { {.opt = OPT_M,
 					.subopt = M_CRC,
 					.test_values = true,
 					.test_default_value = true,
-					.invalid_value = 0,
-					.at_value = 1,
+					.invalid_value.b = 0,
+					.at_value.b = 1,
 					.message =
 		"rmapbt not supported without CRC support."},
 					{.opt = OPT_R,
 					 .subopt = R_NAME,
 					 .test_values = false,
 					 .test_default_value = true,
-					 .invalid_value = 0,
-					 .at_value = 0,
+					 .invalid_value.b = 0,
+					 .at_value.b = 0,
 					 .message =
 		"rmapbt not supported with realtime devices."},
 					{.opt = OPT_R,
 					 .subopt = R_DEV,
 					 .test_values = false,
 					 .test_default_value = true,
-					 .invalid_value = 0,
-					 .at_value = 0,
+					 .invalid_value.b = 0,
+					 .at_value.b = 0,
 					 .message =
 		"rmapbt not supported with realtime devices."},
 				       {LAST_CONFLICT} },
-			.minval = 0,
-			.maxval = 1,
-			.flagval = 0,
+			.minval.b = false,
+			.maxval.b = true,
+			.flagval.b = false,
+			.type = BOOL,
 			},
 			{ .index = M_REFLINK,
 			.conflicts = { {.opt = OPT_M,
 					.subopt = M_CRC,
 					.test_values = true,
 					.test_default_value = true,
-					.invalid_value = 0,
-					.at_value = 1,
+					.invalid_value.b = 0,
+					.at_value.b = 1,
 					.message =
 		"reflink not supported without CRC support."},
 					 {LAST_CONFLICT} },
-			  .minval = 0,
-			  .maxval = 1,
+			  .minval.b = 0,
+			  .maxval.b = 1,
 			  .needs_val = true,
+			 .type = BOOL,
 			},
 		},
 	},
@@ -1653,8 +1940,7 @@  check_subopt_conflicts(
 static void
 check_subopt_value(
 	struct opt_params	*opt,
-	int			index,
-	long long 		value)
+	int			index)
 {
 	struct subopt_param	*sp = &opt->subopt_params[index];
 	int			i;
@@ -1669,9 +1955,19 @@  check_subopt_value(
 			break;
 		if ( (opts[conflict_opt.opt].subopt_params[conflict_opt.subopt].seen ||
 		      conflict_opt.test_default_value) &&
-		    opts[conflict_opt.opt].subopt_params[conflict_opt.subopt].value
-				== conflict_opt.invalid_value &&
-		    value == conflict_opt.at_value) {
+		      test_uvalues(
+			opts[conflict_opt.opt].subopt_params[conflict_opt.subopt].type,
+			opts[conflict_opt.opt].subopt_params[conflict_opt.subopt].value,
+			opts[conflict_opt.opt].subopt_params[conflict_opt.subopt].type,
+			conflict_opt.invalid_value
+		      ) &&
+		      test_uvalues(
+			sp->type,
+			sp->value,
+			sp->type,
+			conflict_opt.at_value
+		      )
+		    ) {
 			conflict_struct(opt, sp, &conflict_opt);
 		}
 	}
@@ -1695,7 +1991,7 @@  check_opt(
 		if (!sp->seen)
 			continue;
 		check_subopt_conflicts(opt, index, false);
-		check_subopt_value(opt, index, sp->value);
+		check_subopt_value(opt, index);
 	}
 }
 static void
@@ -1721,18 +2017,42 @@  getnum(
 		if (sp->needs_val)
 			reqval(opts->name, (char **)opts->subopts, index);
 		sp->seen = true;
-		return sp->flagval;
+		switch(sp->type){
+		case LONGLONG:
+			return sp->flagval.ll;
+		case BOOL:
+			return sp->flagval.b;
+		case UINT64:
+			return sp->flagval.uint64;
+		case INT:
+			return sp->flagval.i;
+		case UINT:
+			return sp->flagval.u;
+		default:
+			fprintf(stderr,
+				_("Option -%c %s called getnum, but is not numeric."
+				  " This is a bug.\n"), opts->name, opts->subopts[index]);
+			exit(1);
+		}
 	}
 
 	sp->seen = true;
 
-	if (sp->minval == 0 && sp->maxval == 0) {
+	if (test_uvalue_num(sp->type, sp->minval, 0) &&
+		test_uvalue_num(sp->type, sp->maxval, 0)) {
 		fprintf(stderr,
 			_("Option -%c %s has undefined minval/maxval."
 			  "Can't verify value range. This is a bug.\n"),
 			opts->name, opts->subopts[index]);
 		exit(1);
 	}
+	if (sp->type == TYPE_UNDEF) {
+		fprintf(stderr,
+			_("Option -%c %s is of undefined type."
+			  "Can't parse value. This is a bug.\n"),
+			opts->name, opts->subopts[index]);
+		exit(1);
+	}
 
 	/*
 	 * Some values are pure numbers, others can have suffixes that define
@@ -1753,9 +2073,9 @@  getnum(
 	}
 
 	/* Validity check the result. */
-	if (c < sp->minval)
+	if (cmp_uvalue_gt_num(sp->type, sp->minval, c))
 		illegal_option(str, opts, index, _("value is too small"));
-	else if (c > sp->maxval)
+	else if (cmp_uvalue_lt_num(sp->type, sp->maxval, c))
 		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"));
@@ -1940,20 +2260,12 @@  main(
 								B_LOG);
 					blocksize = 1 << blocklog;
 					blflag = 1;
-					opts[OPT_B].subopt_params[B_LOG].value =
-							blocklog;
-					opts[OPT_B].subopt_params[B_SIZE].value =
-							blocksize;
 					break;
 				case B_SIZE:
 					blocksize = getnum(value, &opts[OPT_B],
 							   B_SIZE);
 					blocklog = libxfs_highbit32(blocksize);
 					bsflag = 1;
-					opts[OPT_B].subopt_params[B_LOG].value =
-							blocklog;
-					opts[OPT_B].subopt_params[B_SIZE].value =
-							blocksize;
 					break;
 				default:
 					unknown('b', value);
@@ -1971,70 +2283,47 @@  main(
 					agcount = getnum(value, &opts[OPT_D],
 							 D_AGCOUNT);
 					daflag = 1;
-					opts[OPT_D].subopt_params[D_AGCOUNT].value =
-							agcount;
 					break;
 				case D_AGSIZE:
 					agsize = getnum(value, &opts[OPT_D],
 								D_AGSIZE);
 					dasize = 1;
-					opts[OPT_D].subopt_params[D_AGSIZE].value =
-							agsize;
 					break;
 				case D_FILE:
 					xi.disfile = getnum(value, &opts[OPT_D],
 							    D_FILE);
-					opts[OPT_D].subopt_params[D_FILE].value =
-							xi.disfile;
 					break;
 				case D_NAME:
 					xi.dname = getstr(value, &opts[OPT_D],
 								D_NAME);
-					opts[OPT_D].subopt_params[D_NAME].value = 1;
 					break;
 				case D_SIZE:
 					dbytes = getnum(value, &opts[OPT_D],
 								D_SIZE);
-					opts[OPT_D].subopt_params[D_SIZE].value =
-							dbytes;
 					break;
 				case D_SUNIT:
 					dsunit = getnum(value, &opts[OPT_D],
 								D_SUNIT);
-					opts[OPT_D].subopt_params[D_SUNIT].value =
-							dsunit;
 					break;
 				case D_SWIDTH:
 					dswidth = getnum(value, &opts[OPT_D],
 							 D_SWIDTH);
-					opts[OPT_D].subopt_params[D_SWIDTH].value =
-							dswidth;
 					break;
 				case D_SU:
 					dsu = getnum(value, &opts[OPT_D], D_SU);
-					opts[OPT_D].subopt_params[D_SU].value =
-							dsu;
 					break;
 				case D_SW:
 					dsw = getnum(value, &opts[OPT_D], D_SW);
-					opts[OPT_D].subopt_params[D_SW].value =
-							dsw;
 					break;
 				case D_NOALIGN:
 					nodsflag = getnum(value, &opts[OPT_D],
 								D_NOALIGN);
-					opts[OPT_D].subopt_params[D_NOALIGN].value =
-							nodsflag;
 					break;
 				case D_SECTLOG:
 					sectorlog = getnum(value, &opts[OPT_D],
 							   D_SECTLOG);
 					sectorsize = 1 << sectorlog;
 					slflag = 1;
-					opts[OPT_D].subopt_params[D_SECTSIZE].value =
-							sectorsize;
-					opts[OPT_D].subopt_params[D_SECTLOG].value =
-							sectorlog;
 					break;
 				case D_SECTSIZE:
 					sectorsize = getnum(value, &opts[OPT_D],
@@ -2042,10 +2331,6 @@  main(
 					sectorlog =
 						libxfs_highbit32(sectorsize);
 					ssflag = 1;
-					opts[OPT_D].subopt_params[D_SECTSIZE].value =
-							sectorsize;
-					opts[OPT_D].subopt_params[D_SECTLOG].value =
-							sectorlog;
 					break;
 				case D_RTINHERIT:
 					c = getnum(value, &opts[OPT_D],
@@ -2053,24 +2338,18 @@  main(
 					if (c)
 						fsx.fsx_xflags |=
 							XFS_DIFLAG_RTINHERIT;
-					opts[OPT_D].subopt_params[D_RTINHERIT].value =
-							c;
 					break;
 				case D_PROJINHERIT:
 					fsx.fsx_projid = getnum(value, &opts[OPT_D],
 								D_PROJINHERIT);
 					fsx.fsx_xflags |=
 						XFS_DIFLAG_PROJINHERIT;
-					opts[OPT_D].subopt_params[D_PROJINHERIT].value =
-							fsx.fsx_projid;
 					break;
 				case D_EXTSZINHERIT:
 					fsx.fsx_extsize = getnum(value, &opts[OPT_D],
 								 D_EXTSZINHERIT);
 					fsx.fsx_xflags |=
 						XFS_DIFLAG_EXTSZINHERIT;
-					opts[OPT_D].subopt_params[D_EXTSZINHERIT].value =
-							fsx.fsx_extsize;
 					break;
 				default:
 					unknown('d', value);
@@ -2088,64 +2367,44 @@  main(
 					sb_feat.inode_align = getnum(value,
 								&opts[OPT_I],
 								I_ALIGN);
-					opts[OPT_I].subopt_params[I_ALIGN].value =
-							sb_feat.inode_align;
 					break;
 				case I_LOG:
 					inodelog = getnum(value, &opts[OPT_I],
 								I_LOG);
 					isize = 1 << inodelog;
 					ilflag = 1;
-					opts[OPT_I].subopt_params[I_SIZE].value =
-							isize;
-					opts[OPT_I].subopt_params[I_LOG].value =
-							inodelog;
 					break;
 				case I_MAXPCT:
 					imaxpct = getnum(value, &opts[OPT_I],
 							 I_MAXPCT);
 					imflag = 1;
-					opts[OPT_I].subopt_params[I_MAXPCT].value =
-							imaxpct;
 					break;
 				case I_PERBLOCK:
 					inopblock = getnum(value, &opts[OPT_I],
 							   I_PERBLOCK);
 					ipflag = 1;
-					opts[OPT_I].subopt_params[I_PERBLOCK].value =
-							inopblock;
 					break;
 				case I_SIZE:
 					isize = getnum(value, &opts[OPT_I],
 								I_SIZE);
 					inodelog = libxfs_highbit32(isize);
 					isflag = 1;
-					opts[OPT_I].subopt_params[I_SIZE].value =
-							isize;
-					opts[OPT_I].subopt_params[I_LOG].value =
-							inodelog;
 					break;
 				case I_ATTR:
 					sb_feat.attr_version =
 
 						getnum(value, &opts[OPT_I],
 								I_ATTR);
-					opts[OPT_I].subopt_params[I_ATTR].value =
-							sb_feat.attr_version;
 					break;
 				case I_PROJID32BIT:
 					sb_feat.projid16bit =
 						!getnum(value, &opts[OPT_I],
 							I_PROJID32BIT);
-					opts[OPT_I].subopt_params[I_PROJID32BIT].value =
-							sb_feat.projid16bit;
 					break;
 				case I_SPINODES:
 					sb_feat.spinodes = getnum(value,
 								&opts[OPT_I],
 								I_SPINODES);
-					opts[OPT_I].subopt_params[I_SPINODES].value =
-							sb_feat.spinodes;
 					break;
 				default:
 					unknown('i', value);
@@ -2163,34 +2422,24 @@  main(
 					logagno = getnum(value, &opts[OPT_L],
 								L_AGNUM);
 					laflag = 1;
-					opts[OPT_L].subopt_params[L_AGNUM].value =
-							logagno;
 					break;
 				case L_FILE:
 					xi.lisfile = getnum(value, &opts[OPT_L],
 							    L_FILE);
-					opts[OPT_L].subopt_params[L_FILE].value =
-							xi.lisfile;
 					break;
 				case L_INTERNAL:
 					loginternal = getnum(value, &opts[OPT_L],
 							     L_INTERNAL);
 					liflag = 1;
-					opts[OPT_L].subopt_params[L_INTERNAL].value =
-							loginternal;
 					break;
 				case L_SU:
 					lsu = getnum(value, &opts[OPT_L], L_SU);
 					lsuflag = 1;
-					opts[OPT_L].subopt_params[L_SU].value =
-							lsu;
 					break;
 				case L_SUNIT:
 					lsunit = getnum(value, &opts[OPT_L],
 								L_SUNIT);
 					lsunitflag = 1;
-					opts[OPT_L].subopt_params[L_SUNIT].value =
-							lsunit;
 					break;
 				case L_NAME:
 				case L_DEV:
@@ -2199,32 +2448,22 @@  main(
 					xi.logname = logfile;
 					ldflag = 1;
 					loginternal = 0;
-					opts[OPT_L].subopt_params[L_NAME].value = 1;
-					opts[OPT_L].subopt_params[L_DEV].value = 1;
 					break;
 				case L_VERSION:
 					sb_feat.log_version =
 						getnum(value, &opts[OPT_L],
 								L_VERSION);
 					lvflag = 1;
-					opts[OPT_L].subopt_params[L_VERSION].value =
-							sb_feat.log_version;
 					break;
 				case L_SIZE:
 					logbytes = getnum(value, &opts[OPT_L],
 								L_SIZE);
-					opts[OPT_L].subopt_params[L_SIZE].value =
-							logbytes;
 					break;
 				case L_SECTLOG:
 					lsectorlog = getnum(value, &opts[OPT_L],
 							    L_SECTLOG);
 					lsectorsize = 1 << lsectorlog;
 					lslflag = 1;
-					opts[OPT_L].subopt_params[L_SECTSIZE].value =
-							lsectorsize;
-					opts[OPT_L].subopt_params[L_SECTLOG].value =
-							lsectorlog;
 					break;
 				case L_SECTSIZE:
 					lsectorsize = getnum(value, &opts[OPT_L],
@@ -2232,17 +2471,11 @@  main(
 					lsectorlog =
 						libxfs_highbit32(lsectorsize);
 					lssflag = 1;
-					opts[OPT_L].subopt_params[L_SECTSIZE].value =
-							lsectorsize;
-					opts[OPT_L].subopt_params[L_SECTLOG].value =
-							lsectorlog;
 					break;
 				case L_LAZYSBCNTR:
 					sb_feat.lazy_sb_counters =
 							getnum(value, &opts[OPT_L],
 							       L_LAZYSBCNTR);
-					opts[OPT_L].subopt_params[L_LAZYSBCNTR].value =
-							sb_feat.lazy_sb_counters;
 					break;
 				default:
 					unknown('l', value);
@@ -2267,27 +2500,20 @@  main(
 								M_CRC);
 					if (sb_feat.crcs_enabled)
 						sb_feat.dirftype = true;
-					opts[OPT_M].subopt_params[M_CRC].value =
-							sb_feat.crcs_enabled;
 					break;
 				case M_FINOBT:
 					sb_feat.finobt = getnum(
 						value, &opts[OPT_M], M_FINOBT);
-					opts[OPT_M].subopt_params[M_FINOBT].value =
-							sb_feat.finobt;
 					break;
 				case M_UUID:
 					if (!value || *value == '\0')
 						reqval('m', subopts, M_UUID);
 					if (platform_uuid_parse(value, &uuid))
 						illegal(optarg, "m uuid");
-					opts[OPT_M].subopt_params[M_UUID].value = 1;
 					break;
 				case M_RMAPBT:
 					sb_feat.rmapbt = getnum(
 						value, &opts[OPT_M], M_RMAPBT);
-					opts[OPT_M].subopt_params[M_RMAPBT].value = 
-						sb_feat.rmapbt;
 					break;
 				case M_REFLINK:
 					sb_feat.reflink = getnum(
@@ -2310,10 +2536,6 @@  main(
 							     N_LOG);
 					dirblocksize = 1 << dirblocklog;
 					nlflag = 1;
-					opts[OPT_N].subopt_params[N_SIZE].value =
-							dirblocksize;
-					opts[OPT_N].subopt_params[N_LOG].value =
-							dirblocklog;
 					break;
 				case N_SIZE:
 					dirblocksize = getnum(value, &opts[OPT_N],
@@ -2321,10 +2543,6 @@  main(
 					dirblocklog =
 						libxfs_highbit32(dirblocksize);
 					nsflag = 1;
-					opts[OPT_N].subopt_params[N_SIZE].value =
-							dirblocksize;
-					opts[OPT_N].subopt_params[N_LOG].value =
-							dirblocklog;
 					break;
 				case N_VERSION:
 					value = getstr(value, &opts[OPT_N],
@@ -2338,14 +2556,10 @@  main(
 							       N_VERSION);
 					}
 					nvflag = 1;
-					opts[OPT_N].subopt_params[N_VERSION].value =
-							sb_feat.dir_version;
 					break;
 				case N_FTYPE:
 					sb_feat.dirftype = getnum(value, &opts[OPT_N],
 								  N_FTYPE);
-					opts[OPT_N].subopt_params[N_FTYPE].value =
-							sb_feat.dirftype;
 					break;
 				default:
 					unknown('n', value);
@@ -2376,33 +2590,23 @@  main(
 				case R_EXTSIZE:
 					rtextbytes = getnum(value, &opts[OPT_R],
 								R_EXTSIZE);
-					opts[OPT_R].subopt_params[R_EXTSIZE].value =
-							rtextbytes;
 					break;
 				case R_FILE:
 					xi.risfile = getnum(value, &opts[OPT_R],
 							    R_FILE);
-					opts[OPT_R].subopt_params[R_FILE].value =
-							xi.risfile;
 					break;
 				case R_NAME:
 				case R_DEV:
 					xi.rtname = getstr(value, &opts[OPT_R],
 							   R_NAME);
-					opts[OPT_R].subopt_params[R_NAME].value = 1;
-					opts[OPT_R].subopt_params[R_DEV].value = 1;
 					break;
 				case R_SIZE:
 					rtbytes = getnum(value, &opts[OPT_R],
 								R_SIZE);
-					opts[OPT_R].subopt_params[R_SIZE].value =
-							rtbytes;
 					break;
 				case R_NOALIGN:
 					norsflag = getnum(value, &opts[OPT_R],
 								R_NOALIGN);
-					opts[OPT_R].subopt_params[R_NOALIGN].value =
-							norsflag;
 					break;
 				default:
 					unknown('r', value);
@@ -2427,10 +2631,6 @@  main(
 					sectorsize = 1 << sectorlog;
 					lsectorsize = sectorsize;
 					lslflag = slflag = 1;
-					opts[OPT_S].subopt_params[S_LOG].value =
-							sectorsize;
-					opts[OPT_S].subopt_params[S_SECTLOG].value =
-							sectorsize;
 					break;
 				case S_SIZE:
 				case S_SECTSIZE:
@@ -2444,10 +2644,6 @@  main(
 						libxfs_highbit32(sectorsize);
 					lsectorlog = sectorlog;
 					lssflag = ssflag = 1;
-					opts[OPT_S].subopt_params[S_SIZE].value =
-							sectorlog;
-					opts[OPT_S].subopt_params[S_SECTSIZE].value =
-							sectorlog;
 					break;
 				default:
 					unknown('s', value);