mkfs: allow overridable option values

If an option value ends with the string '?', treat it as a value that
can be respecified later.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
diff --git a/man/man8/mkfs.xfs.8 b/man/man8/mkfs.xfs.8
index 3548c5c..da9ebff 100644
--- a/man/man8/mkfs.xfs.8
+++ b/man/man8/mkfs.xfs.8
@@ -121,6 +121,10 @@
 .PP
 Many feature options allow an optional argument of 0 or 1, to explicitly
 disable or enable the functionality.
+.PP
+Normally, options cannot be specified more than once.
+If, however, an option has not yet been specified and its argument ends with
+a question mark ('?'), the option can be specified one additional time.
 .SH OPTIONS
 .TP
 .BI \-b " block_size_options"
@@ -218,6 +222,8 @@
 .BI uuid= value
 Use the given value as the filesystem UUID for the newly created filesystem.
 The default is to generate a random UUID.
+
+This option does not require '?' to respecify the argument value.
 .TP
 .BI rmapbt= value
 This option enables the creation of a reverse-mapping btree index in each
@@ -889,6 +895,8 @@
 always terminated with the dollar (
 .B $
 ) token.
+
+This option cannot be respecified.
 .TP
 .B \-q
 Quiet option. Normally
@@ -963,6 +971,8 @@
 will not proceed with creating the filesystem.  Refer to the
 .BR mount "(8) and " xfs_admin (8)
 manual entries for additional information.
+
+This option does not require '?' to respecify the argument value.
 .TP
 .B \-N
 Causes the file system parameters to be printed out without really
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index bad92f2..5475aef 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -161,6 +161,10 @@
  *     Do not set. It is used internally for respecification, when some options
  *     has to be parsed twice - at first as a string, then later as a number.
  *
+ *   seen_can_respect INTERNAL
+ *     Do not set. It is used internally to indicate that an option has been
+ *     seen but is allowed to be respecified later on in the command line.
+ *
  *   convert OPTIONAL
  *     A flag signalling whether the user-given value can use suffixes.
  *     If you want to allow the use of user-friendly values like 13k, 42G,
@@ -203,6 +207,7 @@
 		int		index;
 		bool		seen;
 		bool		str_seen;
+		bool		seen_can_respec;
 		bool		convert;
 		bool		is_power_2;
 		struct _conflict {
@@ -716,6 +721,7 @@
 	int			subopt)
 {
 	return opts->subopt_params[subopt].seen ||
+	       opts->subopt_params[subopt].seen_can_respec ||
 	       opts->subopt_params[subopt].str_seen;
 }
 
@@ -1266,7 +1272,8 @@
 check_opt(
 	struct opt_params	*opts,
 	int			index,
-	bool			str_seen)
+	bool			str_seen,
+	bool			allow_respec)
 {
 	struct subopt_param	*sp = &opts->subopt_params[index];
 	int			i;
@@ -1286,14 +1293,18 @@
 	 * used to track the different uses based on the @str parameter passed
 	 * to us.
 	 */
+	if (allow_respec)
+		sp->seen_can_respec = true;
 	if (!str_seen) {
 		if (sp->seen)
 			respec(opts->name, opts->subopts, index);
-		sp->seen = true;
+		if (!allow_respec)
+			sp->seen = true;
 	} else {
 		if (sp->str_seen)
 			respec(opts->name, opts->subopts, index);
-		sp->str_seen = true;
+		if (!allow_respec)
+			sp->str_seen = true;
 	}
 
 	/* check for conflicts with the option */
@@ -1302,22 +1313,45 @@
 
 		if (con->subopt == LAST_CONFLICT)
 			break;
-		if (con->opts->subopt_params[con->subopt].seen ||
-		    con->opts->subopt_params[con->subopt].str_seen)
+		if (cli_opt_set(con->opts, con->subopt))
 			conflict(opts, index, con->opts, con->subopt);
 	}
 }
 
+/*
+ * If an option value has a '?' at the end, we treat that as an option that
+ * can be respecified later, and truncate the end of @str.
+ */
+static inline bool
+check_allow_respec(
+	char		*str)
+{
+	char		*s_end;
+
+	if (!str)
+		return false;
+
+	s_end = str + strlen(str) - 1;
+	if (*s_end != '?')
+		return false;
+
+	*s_end = 0;
+	return true;
+}
+
 static long long
 getnum(
-	const char		*str,
+	char			*str,
 	struct opt_params	*opts,
 	int			index)
 {
 	struct subopt_param	*sp = &opts->subopt_params[index];
 	long long		c;
+	bool			allow_respec;
 
-	check_opt(opts, index, false);
+	allow_respec = check_allow_respec(str);
+	check_opt(opts, index, false, allow_respec);
+
 	/* empty strings might just return a default value */
 	if (!str || *str == '\0') {
 		if (sp->defaultval == SUBOPT_NEEDS_VAL)
@@ -1379,7 +1413,10 @@
 	struct opt_params	*opts,
 	int			index)
 {
-	check_opt(opts, index, true);
+	bool			allow_respec;
+
+	allow_respec = check_allow_respec(str);
+	check_opt(opts, index, true, allow_respec);
 
 	/* empty strings for string options are not valid */
 	if (!str || *str == '\0')