* [PATCH 1/8] mkfs: automatically upgrade autofsck earlier
2026-09-04 1:29 [PATCHSET v2] xfsprogs: generate mkfs.xfs config files Darrick J. Wong
@ 2026-09-04 1:30 ` Darrick J. Wong
2026-09-04 1:30 ` [PATCH 2/8] mkfs: print config file for a given mkfs configuration Darrick J. Wong
` (6 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Darrick J. Wong @ 2026-09-04 1:30 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Move this chunk of code earlier in mkfs.xfs because a subsequent patch
will need the final value of cli.autofsck at the point where we do the
dry run reporting stuff. Nobody changes cli.autofsck after
validate_sb_features().
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Andrey Albershteyn <aalbersh@kernel.org>
---
mkfs/xfs_mkfs.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index a4864c37a8821b..74bbb677eee2b6 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -6106,6 +6106,15 @@ main(
validate_zoned(&cfg, &cli, &dft, &zt);
validate_sb_features(&cfg, &cli);
+ /*
+ * If the filesystem has full backreferences and the user didn't
+ * express an autofsck preference, enable online repair because they
+ * might as well get some useful functionality from the extra metadata.
+ */
+ if (cli.autofsck == FSPROP_AUTOFSCK_UNSET &&
+ cli.sb_feat.rmapbt && cli.sb_feat.parent_pointers)
+ cli.autofsck = FSPROP_AUTOFSCK_REPAIR;
+
/*
* we've now completed basic validation of the features, sector and
* block sizes, so from this point onwards we use the values found in
@@ -6297,15 +6306,6 @@ main(
if (mp->m_sb.sb_agcount > 1)
rewrite_secondary_superblocks(mp);
- /*
- * If the filesystem has full backreferences and the user didn't
- * express an autofsck preference, enable online repair because they
- * might as well get some useful functionality from the extra metadata.
- */
- if (cli.autofsck == FSPROP_AUTOFSCK_UNSET &&
- cli.sb_feat.rmapbt && cli.sb_feat.parent_pointers)
- cli.autofsck = FSPROP_AUTOFSCK_REPAIR;
-
if (cli.autofsck != FSPROP_AUTOFSCK_UNSET)
set_autofsck(mp, &cli);
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 2/8] mkfs: print config file for a given mkfs configuration
2026-09-04 1:29 [PATCHSET v2] xfsprogs: generate mkfs.xfs config files Darrick J. Wong
2026-09-04 1:30 ` [PATCH 1/8] mkfs: automatically upgrade autofsck earlier Darrick J. Wong
@ 2026-09-04 1:30 ` Darrick J. Wong
2026-09-04 1:30 ` [PATCH 3/8] xfs_db: print configuration file for mounted filesystems Darrick J. Wong
` (5 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Darrick J. Wong @ 2026-09-04 1:30 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add a -c makecfg= switch to mkfs to emit a mkfs.xfs configuration file
documenting the user-visible filesystem featuresets of the configured
filesystem. This can be used to emit default configuration files as we
do once a year for each LTS.
Note that geometry information is not written to the configuration file.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Andrey Albershteyn <aalbersh@kernel.org>
---
libfrog/fsgeom.h | 3
libfrog/fsgeom.c | 376 ++++++++++++++++++++++++++++++++++++++++++++++++
man/man8/mkfs.xfs.8.in | 8 +
mkfs/xfs_mkfs.c | 44 +++++-
4 files changed, 430 insertions(+), 1 deletion(-)
diff --git a/libfrog/fsgeom.h b/libfrog/fsgeom.h
index b851b9bbf36a58..1109dd2bcdb4cc 100644
--- a/libfrog/fsgeom.h
+++ b/libfrog/fsgeom.h
@@ -217,4 +217,7 @@ bytes_per_rtgroup(
fsgeo->blocksize;
}
+int xfrog_write_mkfs_config(const struct xfs_fsop_geom *fsgeo,
+ const struct fsxattr *fsx, int autofsck, FILE *fp);
+
#endif /* __LIBFROG_FSGEOM_H__ */
diff --git a/libfrog/fsgeom.c b/libfrog/fsgeom.c
index 571d376c6b3c28..fdb7b96e06b246 100644
--- a/libfrog/fsgeom.c
+++ b/libfrog/fsgeom.c
@@ -7,6 +7,8 @@
#include "bitops.h"
#include "fsgeom.h"
#include "util.h"
+#include "list.h"
+#include "libfrog/fsproperties.h"
static inline const char *
rtdev_name(
@@ -250,3 +252,377 @@ xfrog_rtgroup_geometry(
return -errno;
return 0;
}
+
+enum {
+ M_CRC = 0,
+ M_FINOBT,
+ M_RMAPBT,
+ M_REFLINK,
+ M_INOBTCNT,
+ M_BIGTIME,
+ M_METADIR,
+ M_AUTOFSCK,
+ M_MAX_OPTS,
+};
+
+enum {
+ D_RTINHERIT = 0,
+ D_PROJINHERIT,
+ D_EXTSZINHERIT,
+ D_COWEXTSIZE,
+ D_DAXINHERIT,
+ D_MAX_OPTS,
+};
+
+enum {
+ I_SPINODES = 0,
+ I_NREXT64,
+ I_EXCHANGE,
+ I_MAX_OPTS,
+};
+
+enum {
+ N_PARENT = 0,
+ N_FTYPE,
+ N_VERSION,
+ N_MAX_OPTS,
+};
+
+enum {
+ R_EXTSIZE = 0,
+ R_MAX_OPTS,
+};
+
+struct mkfs_config_opt;
+
+struct mkfs_config_data {
+ const struct xfs_fsop_geom *fsgeo;
+ const struct fsxattr *fsx;
+ enum fsprop_autofsck autofsck;
+};
+
+typedef int (*opt_print_fn)(const struct mkfs_config_opt *opt,
+ const struct mkfs_config_data *data,
+ FILE *fp);
+
+struct mkfs_config_opt {
+ const char *name;
+ uint64_t fsgeom_flag;
+ uint64_t xflags_flag;
+ opt_print_fn print_fn;
+};
+
+struct mkfs_config_section {
+ const char *ini_section;
+ struct mkfs_config_opt subopts[16];
+};
+
+static int
+print_fsgeom(
+ const struct mkfs_config_opt *opt,
+ const struct mkfs_config_data *data,
+ FILE *fp)
+{
+ const struct xfs_fsop_geom *fsgeo = data->fsgeo;
+ int ret;
+
+ ret = fprintf(fp, "%s=%d\n", opt->name,
+ !!(fsgeo->flags & opt->fsgeom_flag));
+ if (ret <= 0)
+ return ret;
+ return 0;
+}
+
+static int
+print_xflag(
+ const struct mkfs_config_opt *opt,
+ const struct mkfs_config_data *data,
+ FILE *fp)
+{
+ const struct fsxattr *fsx = data->fsx;
+ int ret;
+
+ if (!(fsx->fsx_xflags & opt->xflags_flag))
+ return 0;
+
+ ret = fprintf(fp, "%s=%d\n", opt->name, 1);
+ if (ret <= 0)
+ return ret;
+ return 0;
+}
+
+static int
+print_projinherit(
+ const struct mkfs_config_opt *opt,
+ const struct mkfs_config_data *data,
+ FILE *fp)
+{
+ const struct fsxattr *fsx = data->fsx;
+ int ret;
+
+ if (fsx->fsx_xflags & FS_XFLAG_PROJINHERIT) {
+ ret = fprintf(fp, "%s=%u\n", opt->name, fsx->fsx_projid);
+ if (ret <= 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+print_extszinherit(
+ const struct mkfs_config_opt *opt,
+ const struct mkfs_config_data *data,
+ FILE *fp)
+{
+ const struct fsxattr *fsx = data->fsx;
+ int ret;
+
+ if (fsx->fsx_xflags & FS_XFLAG_EXTSZINHERIT) {
+ ret = fprintf(fp, "%s=%u\n", opt->name, fsx->fsx_extsize);
+ if (ret <= 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+print_cowextszinherit(
+ const struct mkfs_config_opt *opt,
+ const struct mkfs_config_data *data,
+ FILE *fp)
+{
+ const struct fsxattr *fsx = data->fsx;
+ int ret;
+
+ if (fsx->fsx_xflags & FS_XFLAG_COWEXTSIZE) {
+ ret = fprintf(fp, "%s=%u\n", opt->name, fsx->fsx_cowextsize);
+ if (ret <= 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+print_rtextsize(
+ const struct mkfs_config_opt *opt,
+ const struct mkfs_config_data *data,
+ FILE *fp)
+{
+ const struct xfs_fsop_geom *fsgeo = data->fsgeo;
+ int ret;
+
+ if (fsgeo->rtextsize > 1) {
+ ret = fprintf(fp, "%s=%u\n", opt->name,
+ fsgeo->rtextsize * fsgeo->blocksize);
+ if (ret <= 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+print_dirversion(
+ const struct mkfs_config_opt *opt,
+ const struct mkfs_config_data *data,
+ FILE *fp)
+{
+ const struct xfs_fsop_geom *fsgeo = data->fsgeo;
+ int ret;
+
+ if (fsgeo->flags & XFS_FSOP_GEOM_FLAGS_DIRV2CI)
+ ret = fprintf(fp, "%s=ci\n", opt->name);
+ else
+ ret = fprintf(fp, "%s=2\n", opt->name);
+ if (ret <= 0)
+ return ret;
+ return 0;
+}
+
+static int
+print_autofsck(
+ const struct mkfs_config_opt *opt,
+ const struct mkfs_config_data *data,
+ FILE *fp)
+{
+ int ret;
+ const char *value =
+ fsprop_autofsck_write(data->autofsck);
+
+ if (value) {
+ ret = fprintf(fp, "%s=%s\n", opt->name, value);
+ if (ret <= 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct mkfs_config_section config_sections[] = {
+ {
+ .ini_section = "metadata",
+ .subopts = {
+ [M_CRC] = {
+ .name = "crc",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_V5SB,
+ },
+ [M_FINOBT] = {
+ .name = "finobt",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_FINOBT,
+ },
+ [M_RMAPBT] = {
+ .name = "rmapbt",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_RMAPBT,
+ },
+ [M_REFLINK] = {
+ .name = "reflink",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_REFLINK,
+ },
+ [M_INOBTCNT] = {
+ .name = "inobtcount",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_INOBTCNT,
+ },
+ [M_BIGTIME] = {
+ .name = "bigtime",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_BIGTIME,
+ },
+ [M_METADIR] = {
+ .name = "metadir",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_METADIR,
+ },
+ [M_AUTOFSCK] = {
+ .name = "autofsck",
+ .print_fn = print_autofsck,
+ },
+ [M_MAX_OPTS] = { },
+ },
+ },
+ {
+ .ini_section = "data",
+ .subopts = {
+ [D_RTINHERIT] = {
+ .name = "rtinherit",
+ .xflags_flag = FS_XFLAG_RTINHERIT,
+ },
+ [D_PROJINHERIT] = {
+ .name = "projinherit",
+ .print_fn = print_projinherit,
+ },
+ [D_EXTSZINHERIT] = {
+ .name = "extszinherit",
+ .print_fn = print_extszinherit,
+ },
+ [D_COWEXTSIZE] = {
+ .name = "cowextsize",
+ .print_fn = print_cowextszinherit,
+ },
+ [D_DAXINHERIT] = {
+ .name = "daxinherit",
+ .xflags_flag = FS_XFLAG_DAX,
+ },
+ [D_MAX_OPTS] = { },
+ },
+ },
+ {
+ .ini_section = "inode",
+ .subopts = {
+ [I_SPINODES] = {
+ .name = "sparse",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_SPINODES,
+ },
+ [I_NREXT64] = {
+ .name = "nrext64",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_NREXT64,
+ },
+ [I_EXCHANGE] = {
+ .name = "exchange",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_EXCHANGE_RANGE,
+ },
+ [I_MAX_OPTS] = { },
+ },
+ },
+ {
+ .ini_section = "naming",
+ .subopts = {
+ [N_PARENT] = {
+ .name = "parent",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_PARENT,
+ },
+ [N_FTYPE] = {
+ .name = "ftype",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_FTYPE,
+ },
+ [N_VERSION] = {
+ .name = "version",
+ .print_fn = print_dirversion,
+ },
+ },
+ },
+ {
+ .ini_section = "realtime",
+ .subopts = {
+ [R_EXTSIZE] = {
+ .name = "extsize",
+ .print_fn = print_rtextsize,
+ },
+ [R_MAX_OPTS] = { },
+ },
+ },
+};
+
+/*
+ * Write a mkfs.xfs configuration file for the user-visible filesystem features
+ * enabled in the corresponding fs geometry and root directory file attribute
+ * structures.
+ *
+ * Note: The regular and COW extent size hints must be in units of fsblocks,
+ * not bytes.
+ */
+int
+xfrog_write_mkfs_config(
+ const struct xfs_fsop_geom *fsgeo,
+ const struct fsxattr *fsx,
+ int autofsck,
+ FILE *fp)
+{
+ struct mkfs_config_data d = {
+ .fsgeo = fsgeo,
+ .fsx = fsx,
+ .autofsck = autofsck,
+ };
+ const struct mkfs_config_section *section = config_sections;
+ const struct mkfs_config_opt *opt;
+ int i, j;
+ int error;
+
+ for (i = 0; i < ARRAY_SIZE(config_sections); i++, section++) {
+ if (i > 0) {
+ error = fprintf(fp, "\n");
+ if (error <= 0)
+ return error;
+ }
+
+ error = fprintf(fp, "[%s]\n", section->ini_section);
+ if (error <= 0)
+ return error;
+
+ opt = section->subopts;
+ for (j = 0;
+ j < ARRAY_SIZE(section->subopts) && opt->name;
+ j++, opt++) {
+ if (opt->print_fn)
+ error = opt->print_fn(opt, &d, fp);
+ else if (opt->xflags_flag)
+ error = print_xflag(opt, &d, fp);
+ else if (opt->fsgeom_flag)
+ error = print_fsgeom(opt, &d, fp);
+ if (error)
+ return error;
+ }
+ }
+
+ return fflush(fp);
+}
diff --git a/man/man8/mkfs.xfs.8.in b/man/man8/mkfs.xfs.8.in
index c4dee4fe07ae83..f28f668a1e7e68 100644
--- a/man/man8/mkfs.xfs.8.in
+++ b/man/man8/mkfs.xfs.8.in
@@ -156,6 +156,14 @@ .SH OPTIONS
is:
.RS 1.2i
.TP
+.BI makecfg= path
+Write a configuration file to the file specified by the
+.I path
+option string.
+This file can be re-read by mkfs.xfs for a future filesystem format attempt.
+This file will contain only user-visible features.
+If no path is given, the configuration will be printed on standard output.
+.TP
.BI options= name
The configuration options will be sourced from the file specified by the
.I name
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index 74bbb677eee2b6..5b6dabb76fabfe 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -61,6 +61,7 @@ enum {
enum {
C_OPTFILE = 0,
+ C_MAKECFG,
C_MAX_OPTS,
};
@@ -312,6 +313,7 @@ static struct opt_params copts = {
.name = 'c',
.subopts = {
[C_OPTFILE] = "options",
+ [C_MAKECFG] = "makecfg",
[C_MAX_OPTS] = NULL,
},
.subopt_params = {
@@ -319,6 +321,10 @@ static struct opt_params copts = {
.conflicts = { { NULL, LAST_CONFLICT } },
.defaultval = SUBOPT_NEEDS_VAL,
},
+ { .index = C_MAKECFG,
+ .conflicts = { { NULL, LAST_CONFLICT } },
+ .defaultval = 1,
+ },
},
};
@@ -1072,6 +1078,7 @@ struct cli_params {
char *cfgfile;
char *protofile;
+ char *makecfg;
enum fsprop_autofsck autofsck;
@@ -1207,7 +1214,7 @@ usage( void )
{
fprintf(stderr, _("Usage: %s\n\
/* blocksize */ [-b size=num]\n\
-/* config file */ [-c options=xxx]\n\
+/* config file */ [-c options=path,makecfg=path\n\
/* metadata */ [-m crc=0|1,finobt=0|1,uuid=xxx,rmapbt=0|1,reflink=0|1,\n\
inobtcount=0|1,bigtime=0|1,autofsck=xxx,\n\
metadir=0|1]\n\
@@ -1787,6 +1794,12 @@ cfgfile_opts_parser(
case C_OPTFILE:
cli->cfgfile = getstr(value, opts, subopt);
break;
+ case C_MAKECFG:
+ if (!value)
+ cli->makecfg = "-";
+ else
+ cli->makecfg = getstr(value, opts, subopt);
+ break;
default:
return -EINVAL;
}
@@ -6088,6 +6101,10 @@ main(
*/
cfgfile_parse(&cli);
+ /* Don't create anything if we're merely generating a config file */
+ if (cli.makecfg)
+ dry_run = 1;
+
/*
* Extract as much of the valid config as we can from the CLI input
* before opening the libxfs devices.
@@ -6197,6 +6214,31 @@ main(
validate_supported(mp, &cli);
+ if (cli.makecfg) {
+ struct xfs_fsop_geom geo;
+ FILE *fp;
+
+ if (!strcmp(cli.makecfg, "-")) {
+ fp = stdout;
+ } else {
+ fp = fopen(cli.makecfg, "w+");
+ if (!fp) {
+ perror(cli.makecfg);
+ exit(1);
+ }
+ }
+
+ libxfs_fs_geometry(mp, &geo, XFS_FS_GEOM_MAX_STRUCT_VER);
+ error = xfrog_write_mkfs_config(&geo, &cli.fsx, cli.autofsck,
+ fp);
+ if (error) {
+ perror(cli.makecfg);
+ exit(1);
+ }
+
+ exit(0);
+ }
+
/* Print the intended geometry of the fs. */
if (!quiet || dry_run) {
struct xfs_fsop_geom geo;
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 3/8] xfs_db: print configuration file for mounted filesystems
2026-09-04 1:29 [PATCHSET v2] xfsprogs: generate mkfs.xfs config files Darrick J. Wong
2026-09-04 1:30 ` [PATCH 1/8] mkfs: automatically upgrade autofsck earlier Darrick J. Wong
2026-09-04 1:30 ` [PATCH 2/8] mkfs: print config file for a given mkfs configuration Darrick J. Wong
@ 2026-09-04 1:30 ` Darrick J. Wong
2026-09-04 1:30 ` [PATCH 4/8] xfs_spaceman: " Darrick J. Wong
` (4 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Darrick J. Wong @ 2026-09-04 1:30 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Teach xfs_db to emit a mkfs.xfs config file for an unmounted filesystem.
This can be used to create a new filesystem based on an existing image.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Andrey Albershteyn <aalbersh@kernel.org>
---
db/info.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++
man/man8/xfs_db.8 | 6 ++
2 files changed, 197 insertions(+)
diff --git a/db/info.c b/db/info.c
index 9c233c9c0e6602..3621f0b53e031b 100644
--- a/db/info.c
+++ b/db/info.c
@@ -9,6 +9,7 @@
#include "output.h"
#include "libfrog/fsgeom.h"
#include "libfrog/logging.h"
+#include "libfrog/fsproperties.h"
static void
info_help(void)
@@ -276,10 +277,200 @@ static const struct cmdinfo rgresv_cmd = {
.help = rgresv_help,
};
+static void
+makecfg_help(void)
+{
+ dbprintf(_(
+"\n"
+" Print a mkfs.xfs configuration file for user-visible filesystem features\n"
+" of the current filesystem. Geometry information are not printed.\n"
+"\n"
+));
+
+}
+static void
+fill_fsxattr(
+ struct xfs_inode *ip,
+ struct fsxattr *fa)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
+
+ fa->fsx_xflags = xfs_ip2xflags(ip);
+
+ if (ip->i_diflags & XFS_DIFLAG_EXTSIZE) {
+ fa->fsx_extsize = ip->i_extsize;
+ } else if (ip->i_diflags & XFS_DIFLAG_EXTSZINHERIT) {
+ /*
+ * Don't let a misaligned extent size hint on a directory
+ * escape to userspace if it won't pass the setattr checks
+ * later.
+ */
+ if ((ip->i_diflags & XFS_DIFLAG_RTINHERIT) &&
+ xfs_extlen_to_rtxmod(mp, ip->i_extsize) > 0) {
+ fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE |
+ FS_XFLAG_EXTSZINHERIT);
+ fa->fsx_extsize = 0;
+ } else {
+ fa->fsx_extsize = ip->i_extsize;
+ }
+ }
+
+ if (ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE) {
+ /*
+ * Don't let a misaligned CoW extent size hint on a directory
+ * escape to userspace if it won't pass the setattr checks
+ * later.
+ */
+ if ((ip->i_diflags & XFS_DIFLAG_RTINHERIT) &&
+ ip->i_cowextsize % mp->m_sb.sb_rextsize > 0) {
+ fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
+ fa->fsx_cowextsize = 0;
+ } else {
+ fa->fsx_cowextsize = ip->i_cowextsize;
+ }
+ }
+
+ fa->fsx_projid = ip->i_projid;
+ if (ifp && !xfs_need_iread_extents(ifp))
+ fa->fsx_nextents = xfs_iext_count(ifp);
+ else
+ fa->fsx_nextents = xfs_ifork_nextents(ifp);
+}
+
+static int
+get_autofsck(
+ struct xfs_inode *ip,
+ enum fsprop_autofsck *autofsck)
+{
+ char value[FSPROP_MAX_VALUELEN + 1];
+ struct xfs_da_args args = {
+ .dp = ip,
+ .geo = mp->m_attr_geo,
+ .whichfork = XFS_ATTR_FORK,
+ .op_flags = XFS_DA_OP_OKNOENT,
+ .attr_filter = LIBXFS_ATTR_ROOT,
+ .owner = mp->m_sb.sb_rootino,
+ .value = value,
+ .valuelen = sizeof(value),
+ };
+ char *p;
+ int error;
+
+ *autofsck = FSPROP_AUTOFSCK_UNSET;
+
+ error = fsprop_name_to_attr_name(FSPROP_AUTOFSCK_NAME, &p);
+ if (error < 0)
+ return ENOMEM;
+
+ args.namelen = error;
+ args.name = (const uint8_t *)p;
+
+ libxfs_attr_sethash(&args);
+
+ error = -libxfs_attr_get(&args);
+ if (error == ENODATA) {
+ error = 0;
+ goto out_p;
+ }
+ if (error || !args.valuelen)
+ goto out_p;
+
+ /* raw xattr value is not terminated */
+ value[args.valuelen] = 0;
+ *autofsck = fsprop_autofsck_read(value);
+
+out_p:
+ free(p);
+ return error;
+}
+
+static int
+makecfg_f(
+ int argc,
+ char **argv)
+{
+ struct xfs_fsop_geom geo;
+ struct fsxattr fsx = { };
+ struct xfs_inode *ip;
+ FILE *fp;
+ bool close_fp = false;
+ enum fsprop_autofsck autofsck;
+ int c;
+ int error;
+
+ while ((c = getopt(argc, argv, "")) != EOF) {
+ switch (c) {
+ default:
+ dbprintf(_("bad option for makecfg command\n"));
+ return 0;
+ }
+ }
+
+ if (optind != argc && optind != argc - 1) {
+ dbprintf(_("bad option for makecfg command\n"));
+ return 0;
+ }
+
+ error = -libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &ip);
+ if (error) {
+ fprintf(stderr, "root: %s\n", strerror(error));
+ return 1;
+ }
+
+ error = get_autofsck(ip, &autofsck);
+ if (error) {
+ fprintf(stderr, "autofsck: %s\n", strerror(error));
+ error = 1;
+ goto out_ip;
+ }
+
+ fill_fsxattr(ip, &fsx);
+ libxfs_fs_geometry(mp, &geo, XFS_FS_GEOM_MAX_STRUCT_VER);
+
+ if (optind == argc) {
+ fp = stdout;
+ } else {
+ fp = fopen(argv[optind], "w");
+ if (!fp) {
+ perror(argv[optind]);
+ error = 1;
+ goto out_ip;
+ }
+ close_fp = true;
+ }
+
+ error = xfrog_write_mkfs_config(&geo, &fsx, autofsck, fp);
+ if (error) {
+ if (close_fp)
+ perror(argv[optind]);
+ else
+ perror("makecfg");
+ }
+
+ if (close_fp)
+ fclose(fp);
+out_ip:
+ libxfs_irele(ip);
+ return error;
+}
+
+static const struct cmdinfo makecfg_cmd = {
+ .name = "makecfg",
+ .cfunc = makecfg_f,
+ .argmin = 0,
+ .argmax = 1,
+ .canpush = 0,
+ .args = NULL,
+ .oneline = N_("print mkfs.xfs configuration file"),
+ .help = makecfg_help,
+};
+
void
info_init(void)
{
add_command(&info_cmd);
add_command(&agresv_cmd);
add_command(&rgresv_cmd);
+ add_command(&makecfg_cmd);
}
diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
index ba2a68211ef21f..b602c9d399ece7 100644
--- a/man/man8/xfs_db.8
+++ b/man/man8/xfs_db.8
@@ -959,6 +959,12 @@ .SH COMMANDS
.BI "help [" command ]
Print help for one or all commands.
.TP
+.BI "makecfg [" path ]
+Write a configuration file to the given path that can be re-read by mkfs.xfs
+for a future filesystem format attempt.
+This file will contain only user-visible features.
+If no path is given, the configuration will be printed on standard output.
+.TP
.B info
Displays selected geometry information about the filesystem.
The output will have the same format that
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 4/8] xfs_spaceman: print configuration file for mounted filesystems
2026-09-04 1:29 [PATCHSET v2] xfsprogs: generate mkfs.xfs config files Darrick J. Wong
` (2 preceding siblings ...)
2026-09-04 1:30 ` [PATCH 3/8] xfs_db: print configuration file for mounted filesystems Darrick J. Wong
@ 2026-09-04 1:30 ` Darrick J. Wong
2026-09-04 1:31 ` [PATCH 5/8] makecfg: handle metadir quota options Darrick J. Wong
` (3 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Darrick J. Wong @ 2026-09-04 1:30 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Teach xfs_spaceman to emit a mkfs.xfs config file for a mounted
filesystem.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Andrey Albershteyn <aalbersh@kernel.org>
---
man/man8/xfs_spaceman.8 | 6 ++
spaceman/info.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 142 insertions(+)
diff --git a/man/man8/xfs_spaceman.8 b/man/man8/xfs_spaceman.8
index 7d2d1ff94eeb55..26ff35ab59f2ec 100644
--- a/man/man8/xfs_spaceman.8
+++ b/man/man8/xfs_spaceman.8
@@ -84,6 +84,12 @@ .SH COMMANDS
.PD
.RE
.TP
+.BI "makecfg [" path ]
+Write a configuration file to the given path that can be re-read by mkfs.xfs
+for a future filesystem format attempt.
+This file will contain only user-visible features.
+If no path is given, the configuration will be printed on standard output.
+.TP
.B info
Displays selected geometry information about the filesystem.
The opened file must be a mount point of a XFS filesystem.
diff --git a/spaceman/info.c b/spaceman/info.c
index f6234c4c67aa19..653b8ff98f5c3f 100644
--- a/spaceman/info.c
+++ b/spaceman/info.c
@@ -8,6 +8,8 @@
#include "init.h"
#include "libfrog/paths.h"
#include "libfrog/fsgeom.h"
+#include "libfrog/fsproperties.h"
+#include "libfrog/fsprops.h"
#include "space.h"
static void
@@ -51,8 +53,142 @@ static const struct cmdinfo info_cmd = {
.help = info_help,
};
+static void
+makecfg_help(void)
+{
+ printf(_(
+"\n"
+" Print a mkfs.xfs configuration file for user-visible filesystem features\n"
+" of the current filesystem. Geometry information are not printed.\n"
+"\n"
+));
+
+}
+
+static int
+get_autofsck(
+ struct fileio *f,
+ enum fsprop_autofsck *autofsck)
+{
+ struct fsprops_handle fph = { };
+ char valuebuf[FSPROP_MAX_VALUELEN + 1] = { 0 };
+ size_t valuelen = FSPROP_MAX_VALUELEN;
+ int ret;
+
+ *autofsck = FSPROP_AUTOFSCK_UNSET;
+
+ ret = fsprops_open_handle(&f->xfd, &f->fs_path, &fph);
+ if (ret == -1 && errno == EOPNOTSUPP)
+ return 0;
+ if (ret)
+ return ret;
+
+ ret = fsprops_get(&fph, FSPROP_AUTOFSCK_NAME, valuebuf, &valuelen);
+ if (ret == -1 && errno == ENODATA) {
+ ret = 0;
+ goto out_fph;
+ }
+ if (ret)
+ goto out_fph;
+
+ *autofsck = fsprop_autofsck_read(valuebuf);
+
+out_fph:
+ fsprops_free_handle(&fph);
+ return ret;
+}
+
+static int makecfg_usage(void);
+
+static int
+makecfg_f(
+ int argc,
+ char **argv)
+{
+ struct fsxattr fsx = { };
+ FILE *fp;
+ bool close_fp = false;
+ enum fsprop_autofsck autofsck;
+ int c;
+ int ret;
+
+ while ((c = getopt(argc, argv, "")) != EOF) {
+ switch (c) {
+ default:
+ fprintf(stderr, _("bad option for makecfg command\n"));
+ return makecfg_usage();
+ }
+ }
+
+ if (optind != argc && optind != argc - 1) {
+ fprintf(stderr, _("bad option for makecfg command\n"));
+ return makecfg_usage();
+ }
+
+ if (fs_table_lookup_mount(file->name) == NULL) {
+ fprintf(stderr, _("%s: Not a XFS mount point.\n"), file->name);
+ return 1;
+ }
+
+ /* ioctl reports bytes, not fsblocks */
+ ret = ioctl(file->xfd.fd, FS_IOC_FSGETXATTR, &fsx);
+ if (ret) {
+ perror(file->name);
+ return 1;
+ }
+ fsx.fsx_extsize /= file->xfd.fsgeom.blocksize;
+ fsx.fsx_cowextsize /= file->xfd.fsgeom.blocksize;
+
+ ret = get_autofsck(file, &autofsck);
+ if (ret) {
+ perror("autofsck");
+ return 1;
+ }
+
+ if (optind == argc) {
+ fp = stdout;
+ } else {
+ fp = fopen(argv[optind], "w");
+ if (!fp) {
+ perror(argv[optind]);
+ return 1;
+ }
+ close_fp = true;
+ }
+
+ ret = xfrog_write_mkfs_config(&file->xfd.fsgeom, &fsx, autofsck, fp);
+ if (ret) {
+ if (close_fp)
+ perror(argv[optind]);
+ else
+ perror("makecfg");
+ }
+
+ if (close_fp)
+ fclose(fp);
+ return ret;
+}
+
+static const struct cmdinfo makecfg_cmd = {
+ .name = "makecfg",
+ .cfunc = makecfg_f,
+ .argmin = 0,
+ .argmax = 1,
+ .canpush = 0,
+ .args = NULL,
+ .flags = CMD_FLAG_ONESHOT,
+ .oneline = N_("print mkfs.xfs configuration file"),
+ .help = makecfg_help,
+};
+
+static int makecfg_usage(void)
+{
+ return command_usage(&makecfg_cmd);
+}
+
void
info_init(void)
{
add_command(&info_cmd);
+ add_command(&makecfg_cmd);
}
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 5/8] makecfg: handle metadir quota options
2026-09-04 1:29 [PATCHSET v2] xfsprogs: generate mkfs.xfs config files Darrick J. Wong
` (3 preceding siblings ...)
2026-09-04 1:30 ` [PATCH 4/8] xfs_spaceman: " Darrick J. Wong
@ 2026-09-04 1:31 ` Darrick J. Wong
2026-09-04 3:29 ` Darrick J. Wong
2026-09-04 1:31 ` [PATCH 6/8] makecfg: handle old V4 options that are almost always on by default Darrick J. Wong
` (2 subsequent siblings)
7 siblings, 1 reply; 10+ messages in thread
From: Darrick J. Wong @ 2026-09-04 1:31 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
A different LOLLM pointed out that the mkfs configuration file generator
should generate config file lines for the quota accounting/enforcement
mkfs options for metadir filesystems, because those quota flags persist
across mounts. Add the necessary pieces to do that.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
libfrog/fsgeom.h | 11 ++++++++-
db/info.c | 3 ++
libfrog/fsgeom.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
mkfs/xfs_mkfs.c | 4 ++-
spaceman/info.c | 51 ++++++++++++++++++++++++++++++++++++++--
5 files changed, 131 insertions(+), 7 deletions(-)
diff --git a/libfrog/fsgeom.h b/libfrog/fsgeom.h
index 1109dd2bcdb4cc..c3d865f0783e52 100644
--- a/libfrog/fsgeom.h
+++ b/libfrog/fsgeom.h
@@ -217,7 +217,16 @@ bytes_per_rtgroup(
fsgeo->blocksize;
}
+/* These should correspond to XFS_[UGP]UOTA_{ACCT,ENFD} */
+#define MAKECFG_UQUOTA_ACCT 0x0001 /* user quota accounting ON */
+#define MAKECFG_UQUOTA_ENFD 0x0002 /* user quota limits enforced */
+#define MAKECFG_GQUOTA_ACCT 0x0040 /* group quota accounting ON */
+#define MAKECFG_GQUOTA_ENFD 0x0080 /* group quota limits enforced */
+#define MAKECFG_PQUOTA_ACCT 0x0008 /* project quota accounting ON */
+#define MAKECFG_PQUOTA_ENFD 0x0200 /* project quota limits enforced */
+
int xfrog_write_mkfs_config(const struct xfs_fsop_geom *fsgeo,
- const struct fsxattr *fsx, int autofsck, FILE *fp);
+ unsigned int qflags, const struct fsxattr *fsx, int autofsck,
+ FILE *fp);
#endif /* __LIBFROG_FSGEOM_H__ */
diff --git a/db/info.c b/db/info.c
index 3621f0b53e031b..ad1933a2e7dedb 100644
--- a/db/info.c
+++ b/db/info.c
@@ -440,7 +440,8 @@ makecfg_f(
close_fp = true;
}
- error = xfrog_write_mkfs_config(&geo, &fsx, autofsck, fp);
+ error = xfrog_write_mkfs_config(&geo, mp->m_sb.sb_qflags, &fsx,
+ autofsck, fp);
if (error) {
if (close_fp)
perror(argv[optind]);
diff --git a/libfrog/fsgeom.c b/libfrog/fsgeom.c
index fdb7b96e06b246..f1ca7e73df638a 100644
--- a/libfrog/fsgeom.c
+++ b/libfrog/fsgeom.c
@@ -262,6 +262,12 @@ enum {
M_BIGTIME,
M_METADIR,
M_AUTOFSCK,
+ M_UQUOTA,
+ M_GQUOTA,
+ M_PQUOTA,
+ M_UQNOENFORCE,
+ M_GQNOENFORCE,
+ M_PQNOENFORCE,
M_MAX_OPTS,
};
@@ -298,6 +304,7 @@ struct mkfs_config_opt;
struct mkfs_config_data {
const struct xfs_fsop_geom *fsgeo;
const struct fsxattr *fsx;
+ unsigned int qflags;
enum fsprop_autofsck autofsck;
};
@@ -307,9 +314,11 @@ typedef int (*opt_print_fn)(const struct mkfs_config_opt *opt,
struct mkfs_config_opt {
const char *name;
+ opt_print_fn print_fn;
uint64_t fsgeom_flag;
uint64_t xflags_flag;
- opt_print_fn print_fn;
+ unsigned int qflags_mask;
+ unsigned int qflags;
};
struct mkfs_config_section {
@@ -351,6 +360,26 @@ print_xflag(
return 0;
}
+static int
+print_qflags(
+ const struct mkfs_config_opt *opt,
+ const struct mkfs_config_data *data,
+ FILE *fp)
+{
+ int ret;
+
+ /* quota flags are only persisted on metadir filesystems */
+ if (!(data->fsgeo->flags & XFS_FSOP_GEOM_FLAGS_METADIR))
+ return 0;
+ if ((data->qflags & opt->qflags_mask) != opt->qflags)
+ return 0;
+
+ ret = fprintf(fp, "%s=1\n", opt->name);
+ if (ret <= 0)
+ return ret;
+ return 0;
+}
+
static int
print_projinherit(
const struct mkfs_config_opt *opt,
@@ -497,6 +526,42 @@ static const struct mkfs_config_section config_sections[] = {
.name = "autofsck",
.print_fn = print_autofsck,
},
+ [M_UQUOTA] = {
+ .name = "uquota",
+ .qflags = MAKECFG_UQUOTA_ACCT | MAKECFG_UQUOTA_ENFD,
+ .qflags_mask = MAKECFG_UQUOTA_ACCT | MAKECFG_UQUOTA_ENFD,
+ .print_fn = print_qflags,
+ },
+ [M_GQUOTA] = {
+ .name = "gquota",
+ .qflags = MAKECFG_GQUOTA_ACCT | MAKECFG_GQUOTA_ENFD,
+ .qflags_mask = MAKECFG_GQUOTA_ACCT | MAKECFG_GQUOTA_ENFD,
+ .print_fn = print_qflags,
+ },
+ [M_PQUOTA] = {
+ .name = "pquota",
+ .qflags = MAKECFG_PQUOTA_ACCT | MAKECFG_PQUOTA_ENFD,
+ .qflags_mask = MAKECFG_PQUOTA_ACCT | MAKECFG_PQUOTA_ENFD,
+ .print_fn = print_qflags,
+ },
+ [M_UQNOENFORCE] = {
+ .name = "uqnoenforce",
+ .qflags = MAKECFG_UQUOTA_ACCT,
+ .qflags_mask = MAKECFG_UQUOTA_ACCT | MAKECFG_UQUOTA_ENFD,
+ .print_fn = print_qflags,
+ },
+ [M_GQNOENFORCE] = {
+ .name = "gqnoenforce",
+ .qflags = MAKECFG_GQUOTA_ACCT,
+ .qflags_mask = MAKECFG_GQUOTA_ACCT | MAKECFG_GQUOTA_ENFD,
+ .print_fn = print_qflags,
+ },
+ [M_PQNOENFORCE] = {
+ .name = "pqnoenforce",
+ .qflags = MAKECFG_PQUOTA_ACCT,
+ .qflags_mask = MAKECFG_PQUOTA_ACCT | MAKECFG_PQUOTA_ENFD,
+ .print_fn = print_qflags,
+ },
[M_MAX_OPTS] = { },
},
},
@@ -584,6 +649,7 @@ static const struct mkfs_config_section config_sections[] = {
int
xfrog_write_mkfs_config(
const struct xfs_fsop_geom *fsgeo,
+ unsigned int qflags,
const struct fsxattr *fsx,
int autofsck,
FILE *fp)
@@ -592,6 +658,7 @@ xfrog_write_mkfs_config(
.fsgeo = fsgeo,
.fsx = fsx,
.autofsck = autofsck,
+ .qflags = qflags,
};
const struct mkfs_config_section *section = config_sections;
const struct mkfs_config_opt *opt;
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index 5b6dabb76fabfe..5c45e0248d1de6 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -6229,8 +6229,8 @@ main(
}
libxfs_fs_geometry(mp, &geo, XFS_FS_GEOM_MAX_STRUCT_VER);
- error = xfrog_write_mkfs_config(&geo, &cli.fsx, cli.autofsck,
- fp);
+ error = xfrog_write_mkfs_config(&geo, cli.sb_feat.qflags,
+ &cli.fsx, cli.autofsck, fp);
if (error) {
perror(cli.makecfg);
exit(1);
diff --git a/spaceman/info.c b/spaceman/info.c
index 653b8ff98f5c3f..121556e6bd9909 100644
--- a/spaceman/info.c
+++ b/spaceman/info.c
@@ -3,6 +3,7 @@
* Copyright (C) 2018 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <darrick.wong@oracle.com>
*/
+#include <sys/quota.h>
#include "libxfs.h"
#include "command.h"
#include "init.h"
@@ -11,6 +12,7 @@
#include "libfrog/fsproperties.h"
#include "libfrog/fsprops.h"
#include "space.h"
+#include "include/xqm.h"
static void
info_help(void)
@@ -88,7 +90,7 @@ get_autofsck(
ret = 0;
goto out_fph;
}
- if (ret)
+ if (ret || !valuelen)
goto out_fph;
*autofsck = fsprop_autofsck_read(valuebuf);
@@ -98,6 +100,43 @@ get_autofsck(
return ret;
}
+struct qflags_xlate {
+ unsigned int qs_flag;
+ unsigned int mkcfg_qflag;
+};
+
+static const struct qflags_xlate qsflags_xlate[] = {
+ { .qs_flag = XFS_QUOTA_UDQ_ACCT, .mkcfg_qflag = MAKECFG_UQUOTA_ACCT },
+ { .qs_flag = XFS_QUOTA_UDQ_ENFD, .mkcfg_qflag = MAKECFG_UQUOTA_ENFD },
+ { .qs_flag = XFS_QUOTA_GDQ_ACCT, .mkcfg_qflag = MAKECFG_GQUOTA_ACCT },
+ { .qs_flag = XFS_QUOTA_GDQ_ENFD, .mkcfg_qflag = MAKECFG_GQUOTA_ENFD },
+ { .qs_flag = XFS_QUOTA_PDQ_ACCT, .mkcfg_qflag = MAKECFG_PQUOTA_ACCT },
+ { .qs_flag = XFS_QUOTA_PDQ_ENFD, .mkcfg_qflag = MAKECFG_PQUOTA_ENFD },
+};
+
+static int
+get_qflags(
+ struct fileio *f,
+ unsigned int *qflags)
+{
+ struct fs_quota_stat qstat;
+ int i;
+ int ret;
+
+ *qflags = 0;
+
+ ret = quotactl(QCMD(Q_XGETQSTAT, 0), f->fs_path.fs_name, 0,
+ (void *)&qstat);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(qsflags_xlate); i++)
+ if (qstat.qs_flags & qsflags_xlate[i].qs_flag)
+ *qflags |= qsflags_xlate[i].mkcfg_qflag;
+
+ return 0;
+}
+
static int makecfg_usage(void);
static int
@@ -109,6 +148,7 @@ makecfg_f(
FILE *fp;
bool close_fp = false;
enum fsprop_autofsck autofsck;
+ unsigned int qflags;
int c;
int ret;
@@ -145,6 +185,12 @@ makecfg_f(
return 1;
}
+ ret = get_qflags(file, &qflags);
+ if (ret) {
+ perror("quotactl");
+ return 1;
+ }
+
if (optind == argc) {
fp = stdout;
} else {
@@ -156,7 +202,8 @@ makecfg_f(
close_fp = true;
}
- ret = xfrog_write_mkfs_config(&file->xfd.fsgeom, &fsx, autofsck, fp);
+ ret = xfrog_write_mkfs_config(&file->xfd.fsgeom, qflags, &fsx,
+ autofsck, fp);
if (ret) {
if (close_fp)
perror(argv[optind]);
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH 5/8] makecfg: handle metadir quota options
2026-09-04 1:31 ` [PATCH 5/8] makecfg: handle metadir quota options Darrick J. Wong
@ 2026-09-04 3:29 ` Darrick J. Wong
0 siblings, 0 replies; 10+ messages in thread
From: Darrick J. Wong @ 2026-09-04 3:29 UTC (permalink / raw)
To: aalbersh; +Cc: linux-xfs
On Thu, Sep 03, 2026 at 06:31:04PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> A different LOLLM pointed out that the mkfs configuration file generator
> should generate config file lines for the quota accounting/enforcement
> mkfs options for metadir filesystems, because those quota flags persist
> across mounts. Add the necessary pieces to do that.
>
> Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
> ---
> libfrog/fsgeom.h | 11 ++++++++-
> db/info.c | 3 ++
> libfrog/fsgeom.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> mkfs/xfs_mkfs.c | 4 ++-
> spaceman/info.c | 51 ++++++++++++++++++++++++++++++++++++++--
> 5 files changed, 131 insertions(+), 7 deletions(-)
>
>
> diff --git a/libfrog/fsgeom.h b/libfrog/fsgeom.h
> index 1109dd2bcdb4cc..c3d865f0783e52 100644
> --- a/libfrog/fsgeom.h
> +++ b/libfrog/fsgeom.h
> @@ -217,7 +217,16 @@ bytes_per_rtgroup(
> fsgeo->blocksize;
> }
>
> +/* These should correspond to XFS_[UGP]UOTA_{ACCT,ENFD} */
> +#define MAKECFG_UQUOTA_ACCT 0x0001 /* user quota accounting ON */
> +#define MAKECFG_UQUOTA_ENFD 0x0002 /* user quota limits enforced */
> +#define MAKECFG_GQUOTA_ACCT 0x0040 /* group quota accounting ON */
> +#define MAKECFG_GQUOTA_ENFD 0x0080 /* group quota limits enforced */
> +#define MAKECFG_PQUOTA_ACCT 0x0008 /* project quota accounting ON */
> +#define MAKECFG_PQUOTA_ENFD 0x0200 /* project quota limits enforced */
> +
> int xfrog_write_mkfs_config(const struct xfs_fsop_geom *fsgeo,
> - const struct fsxattr *fsx, int autofsck, FILE *fp);
> + unsigned int qflags, const struct fsxattr *fsx, int autofsck,
> + FILE *fp);
>
> #endif /* __LIBFROG_FSGEOM_H__ */
> diff --git a/db/info.c b/db/info.c
> index 3621f0b53e031b..ad1933a2e7dedb 100644
> --- a/db/info.c
> +++ b/db/info.c
> @@ -440,7 +440,8 @@ makecfg_f(
> close_fp = true;
> }
>
> - error = xfrog_write_mkfs_config(&geo, &fsx, autofsck, fp);
> + error = xfrog_write_mkfs_config(&geo, mp->m_sb.sb_qflags, &fsx,
> + autofsck, fp);
> if (error) {
> if (close_fp)
> perror(argv[optind]);
> diff --git a/libfrog/fsgeom.c b/libfrog/fsgeom.c
> index fdb7b96e06b246..f1ca7e73df638a 100644
> --- a/libfrog/fsgeom.c
> +++ b/libfrog/fsgeom.c
> @@ -262,6 +262,12 @@ enum {
> M_BIGTIME,
> M_METADIR,
> M_AUTOFSCK,
> + M_UQUOTA,
> + M_GQUOTA,
> + M_PQUOTA,
> + M_UQNOENFORCE,
> + M_GQNOENFORCE,
> + M_PQNOENFORCE,
> M_MAX_OPTS,
> };
>
> @@ -298,6 +304,7 @@ struct mkfs_config_opt;
> struct mkfs_config_data {
> const struct xfs_fsop_geom *fsgeo;
> const struct fsxattr *fsx;
> + unsigned int qflags;
> enum fsprop_autofsck autofsck;
> };
>
> @@ -307,9 +314,11 @@ typedef int (*opt_print_fn)(const struct mkfs_config_opt *opt,
>
> struct mkfs_config_opt {
> const char *name;
> + opt_print_fn print_fn;
> uint64_t fsgeom_flag;
> uint64_t xflags_flag;
> - opt_print_fn print_fn;
> + unsigned int qflags_mask;
> + unsigned int qflags;
> };
>
> struct mkfs_config_section {
> @@ -351,6 +360,26 @@ print_xflag(
> return 0;
> }
>
> +static int
> +print_qflags(
> + const struct mkfs_config_opt *opt,
> + const struct mkfs_config_data *data,
> + FILE *fp)
> +{
> + int ret;
> +
> + /* quota flags are only persisted on metadir filesystems */
> + if (!(data->fsgeo->flags & XFS_FSOP_GEOM_FLAGS_METADIR))
> + return 0;
> + if ((data->qflags & opt->qflags_mask) != opt->qflags)
> + return 0;
> +
> + ret = fprintf(fp, "%s=1\n", opt->name);
> + if (ret <= 0)
> + return ret;
> + return 0;
> +}
> +
> static int
> print_projinherit(
> const struct mkfs_config_opt *opt,
> @@ -497,6 +526,42 @@ static const struct mkfs_config_section config_sections[] = {
> .name = "autofsck",
> .print_fn = print_autofsck,
> },
> + [M_UQUOTA] = {
> + .name = "uquota",
> + .qflags = MAKECFG_UQUOTA_ACCT | MAKECFG_UQUOTA_ENFD,
> + .qflags_mask = MAKECFG_UQUOTA_ACCT | MAKECFG_UQUOTA_ENFD,
> + .print_fn = print_qflags,
> + },
> + [M_GQUOTA] = {
> + .name = "gquota",
> + .qflags = MAKECFG_GQUOTA_ACCT | MAKECFG_GQUOTA_ENFD,
> + .qflags_mask = MAKECFG_GQUOTA_ACCT | MAKECFG_GQUOTA_ENFD,
> + .print_fn = print_qflags,
> + },
> + [M_PQUOTA] = {
> + .name = "pquota",
> + .qflags = MAKECFG_PQUOTA_ACCT | MAKECFG_PQUOTA_ENFD,
> + .qflags_mask = MAKECFG_PQUOTA_ACCT | MAKECFG_PQUOTA_ENFD,
> + .print_fn = print_qflags,
> + },
> + [M_UQNOENFORCE] = {
> + .name = "uqnoenforce",
> + .qflags = MAKECFG_UQUOTA_ACCT,
> + .qflags_mask = MAKECFG_UQUOTA_ACCT | MAKECFG_UQUOTA_ENFD,
> + .print_fn = print_qflags,
> + },
> + [M_GQNOENFORCE] = {
> + .name = "gqnoenforce",
> + .qflags = MAKECFG_GQUOTA_ACCT,
> + .qflags_mask = MAKECFG_GQUOTA_ACCT | MAKECFG_GQUOTA_ENFD,
> + .print_fn = print_qflags,
> + },
> + [M_PQNOENFORCE] = {
> + .name = "pqnoenforce",
> + .qflags = MAKECFG_PQUOTA_ACCT,
> + .qflags_mask = MAKECFG_PQUOTA_ACCT | MAKECFG_PQUOTA_ENFD,
> + .print_fn = print_qflags,
> + },
> [M_MAX_OPTS] = { },
> },
> },
> @@ -584,6 +649,7 @@ static const struct mkfs_config_section config_sections[] = {
> int
> xfrog_write_mkfs_config(
> const struct xfs_fsop_geom *fsgeo,
> + unsigned int qflags,
> const struct fsxattr *fsx,
> int autofsck,
> FILE *fp)
> @@ -592,6 +658,7 @@ xfrog_write_mkfs_config(
> .fsgeo = fsgeo,
> .fsx = fsx,
> .autofsck = autofsck,
> + .qflags = qflags,
> };
> const struct mkfs_config_section *section = config_sections;
> const struct mkfs_config_opt *opt;
> diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
> index 5b6dabb76fabfe..5c45e0248d1de6 100644
> --- a/mkfs/xfs_mkfs.c
> +++ b/mkfs/xfs_mkfs.c
> @@ -6229,8 +6229,8 @@ main(
> }
>
> libxfs_fs_geometry(mp, &geo, XFS_FS_GEOM_MAX_STRUCT_VER);
> - error = xfrog_write_mkfs_config(&geo, &cli.fsx, cli.autofsck,
> - fp);
> + error = xfrog_write_mkfs_config(&geo, cli.sb_feat.qflags,
> + &cli.fsx, cli.autofsck, fp);
> if (error) {
> perror(cli.makecfg);
> exit(1);
> diff --git a/spaceman/info.c b/spaceman/info.c
> index 653b8ff98f5c3f..121556e6bd9909 100644
> --- a/spaceman/info.c
> +++ b/spaceman/info.c
> @@ -3,6 +3,7 @@
> * Copyright (C) 2018 Oracle. All Rights Reserved.
> * Author: Darrick J. Wong <darrick.wong@oracle.com>
> */
> +#include <sys/quota.h>
> #include "libxfs.h"
> #include "command.h"
> #include "init.h"
> @@ -11,6 +12,7 @@
> #include "libfrog/fsproperties.h"
> #include "libfrog/fsprops.h"
> #include "space.h"
> +#include "include/xqm.h"
>
> static void
> info_help(void)
> @@ -88,7 +90,7 @@ get_autofsck(
> ret = 0;
> goto out_fph;
> }
> - if (ret)
> + if (ret || !valuelen)
> goto out_fph;
>
> *autofsck = fsprop_autofsck_read(valuebuf);
> @@ -98,6 +100,43 @@ get_autofsck(
> return ret;
> }
>
> +struct qflags_xlate {
> + unsigned int qs_flag;
> + unsigned int mkcfg_qflag;
> +};
> +
> +static const struct qflags_xlate qsflags_xlate[] = {
> + { .qs_flag = XFS_QUOTA_UDQ_ACCT, .mkcfg_qflag = MAKECFG_UQUOTA_ACCT },
> + { .qs_flag = XFS_QUOTA_UDQ_ENFD, .mkcfg_qflag = MAKECFG_UQUOTA_ENFD },
> + { .qs_flag = XFS_QUOTA_GDQ_ACCT, .mkcfg_qflag = MAKECFG_GQUOTA_ACCT },
> + { .qs_flag = XFS_QUOTA_GDQ_ENFD, .mkcfg_qflag = MAKECFG_GQUOTA_ENFD },
> + { .qs_flag = XFS_QUOTA_PDQ_ACCT, .mkcfg_qflag = MAKECFG_PQUOTA_ACCT },
> + { .qs_flag = XFS_QUOTA_PDQ_ENFD, .mkcfg_qflag = MAKECFG_PQUOTA_ENFD },
> +};
> +
> +static int
> +get_qflags(
> + struct fileio *f,
> + unsigned int *qflags)
> +{
> + struct fs_quota_stat qstat;
> + int i;
> + int ret;
> +
> + *qflags = 0;
> +
> + ret = quotactl(QCMD(Q_XGETQSTAT, 0), f->fs_path.fs_name, 0,
> + (void *)&qstat);
> + if (ret)
> + return ret;
This needs to handle the kernel returning ENOSYS, which means either
that quota wasn't compiled into the kernel or it wasn't enabled. In
this case it's sufficient to set qflags to 0.
if (ret) {
if (errno == ENOSYS)
return 0;
return ret;
}
Will fix that for the next version.
--D
> + for (i = 0; i < ARRAY_SIZE(qsflags_xlate); i++)
> + if (qstat.qs_flags & qsflags_xlate[i].qs_flag)
> + *qflags |= qsflags_xlate[i].mkcfg_qflag;
> +
> + return 0;
> +}
> +
> static int makecfg_usage(void);
>
> static int
> @@ -109,6 +148,7 @@ makecfg_f(
> FILE *fp;
> bool close_fp = false;
> enum fsprop_autofsck autofsck;
> + unsigned int qflags;
> int c;
> int ret;
>
> @@ -145,6 +185,12 @@ makecfg_f(
> return 1;
> }
>
> + ret = get_qflags(file, &qflags);
> + if (ret) {
> + perror("quotactl");
> + return 1;
> + }
> +
> if (optind == argc) {
> fp = stdout;
> } else {
> @@ -156,7 +202,8 @@ makecfg_f(
> close_fp = true;
> }
>
> - ret = xfrog_write_mkfs_config(&file->xfd.fsgeom, &fsx, autofsck, fp);
> + ret = xfrog_write_mkfs_config(&file->xfd.fsgeom, qflags, &fsx,
> + autofsck, fp);
> if (ret) {
> if (close_fp)
> perror(argv[optind]);
>
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 6/8] makecfg: handle old V4 options that are almost always on by default
2026-09-04 1:29 [PATCHSET v2] xfsprogs: generate mkfs.xfs config files Darrick J. Wong
` (4 preceding siblings ...)
2026-09-04 1:31 ` [PATCH 5/8] makecfg: handle metadir quota options Darrick J. Wong
@ 2026-09-04 1:31 ` Darrick J. Wong
2026-09-04 1:31 ` [PATCH 7/8] xfs_admin: print configuration file for mounted filesystems Darrick J. Wong
2026-09-04 1:31 ` [PATCH 8/8] mkfs: allow specification of default options via configuration file Darrick J. Wong
7 siblings, 0 replies; 10+ messages in thread
From: Darrick J. Wong @ 2026-09-04 1:31 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
The same alternate LOLLM also observed that there are a couple of V4
options that are always enabled on a V5 filesystem. Since we support V4
for another 4 years, add these two options but only for the weird case
where someone wants to turn them off.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
libfrog/fsgeom.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/libfrog/fsgeom.c b/libfrog/fsgeom.c
index f1ca7e73df638a..24effad2678765 100644
--- a/libfrog/fsgeom.c
+++ b/libfrog/fsgeom.c
@@ -284,6 +284,7 @@ enum {
I_SPINODES = 0,
I_NREXT64,
I_EXCHANGE,
+ I_PROJID32BIT,
I_MAX_OPTS,
};
@@ -299,6 +300,11 @@ enum {
R_MAX_OPTS,
};
+enum {
+ L_LAZYSBCNTR,
+ L_MAX_OPTS,
+};
+
struct mkfs_config_opt;
struct mkfs_config_data {
@@ -342,6 +348,24 @@ print_fsgeom(
return 0;
}
+static int
+print_fsgeom_only_if_missing(
+ const struct mkfs_config_opt *opt,
+ const struct mkfs_config_data *data,
+ FILE *fp)
+{
+ const struct xfs_fsop_geom *fsgeo = data->fsgeo;
+ int ret;
+
+ if (fsgeo->flags & opt->fsgeom_flag)
+ return 0;
+
+ ret = fprintf(fp, "%s=0\n", opt->name);
+ if (ret <= 0)
+ return ret;
+ return 0;
+}
+
static int
print_xflag(
const struct mkfs_config_opt *opt,
@@ -606,9 +630,24 @@ static const struct mkfs_config_section config_sections[] = {
.name = "exchange",
.fsgeom_flag = XFS_FSOP_GEOM_FLAGS_EXCHANGE_RANGE,
},
+ [I_PROJID32BIT] = {
+ .name = "projid32bit",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_PROJID32,
+ .print_fn = print_fsgeom_only_if_missing,
+ },
[I_MAX_OPTS] = { },
},
},
+ {
+ .ini_section = "log",
+ .subopts = {
+ [L_LAZYSBCNTR] = {
+ .name = "lazy-count",
+ .fsgeom_flag = XFS_FSOP_GEOM_FLAGS_LAZYSB,
+ .print_fn = print_fsgeom_only_if_missing,
+ },
+ },
+ },
{
.ini_section = "naming",
.subopts = {
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 7/8] xfs_admin: print configuration file for mounted filesystems
2026-09-04 1:29 [PATCHSET v2] xfsprogs: generate mkfs.xfs config files Darrick J. Wong
` (5 preceding siblings ...)
2026-09-04 1:31 ` [PATCH 6/8] makecfg: handle old V4 options that are almost always on by default Darrick J. Wong
@ 2026-09-04 1:31 ` Darrick J. Wong
2026-09-04 1:31 ` [PATCH 8/8] mkfs: allow specification of default options via configuration file Darrick J. Wong
7 siblings, 0 replies; 10+ messages in thread
From: Darrick J. Wong @ 2026-09-04 1:31 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Teach xfs_admin to emit a mkfs.xfs config file for a mounted or
unmounted filesystem.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
-
Reviewed-by: Andrey Albershteyn <aalbersh@kernel.org>
---
db/xfs_admin.sh | 15 +++++++++++++--
man/man8/xfs_admin.8 | 7 +++++++
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/db/xfs_admin.sh b/db/xfs_admin.sh
index 52a658ba4a540f..13a876ad8d1377 100755
--- a/db/xfs_admin.sh
+++ b/db/xfs_admin.sh
@@ -11,16 +11,20 @@ DB_OPTS=""
DB_DEV_OPTS=""
REPAIR_OPTS=""
IO_OPTS=""
+SPACEMAN_OPTS=""
REPAIR_DEV_OPTS=""
LOG_OPTS=""
-USAGE="Usage: xfs_admin [-efjlpuV] [-c 0|1] [-L label] [-O v5_feature] [-r rtdev] [-U uuid] device [logdev]"
+USAGE="Usage: xfs_admin [-efjlpuV] [-C cfgfile] [-c 0|1] [-L label] [-O v5_feature] [-r rtdev] [-U uuid] device [logdev]"
-while getopts "c:efjlL:O:pr:uU:V" c
+while getopts "C:c:efjlL:O:pr:uU:V" c
do
case $c in
c) REPAIR_OPTS=$REPAIR_OPTS" -c lazycount="$OPTARG
require_offline=1
;;
+ C) DB_OPTS=$DB_OPTS" -c 'makecfg "$OPTARG"'"
+ SPACEMAN_OPTS=$SPACEMAN_OPTS" -c 'makecfg "$OPTARG"'"
+ ;;
e) DB_OPTS=$DB_OPTS" -c 'version extflg'"
require_offline=1
;;
@@ -72,6 +76,13 @@ case $# in
exit 2
fi
+ if [ -n "$SPACEMAN_OPTS" ]; then
+ eval xfs_spaceman -p xfs_admin $SPACEMAN_OPTS "$mntpt"
+ res=$?
+ test $res -ne 0 && exit $res
+ test -n "$IO_OPTS" || exit $res
+ fi
+
if [ -n "$IO_OPTS" ]; then
eval xfs_io -p xfs_admin $IO_OPTS "$mntpt"
exit $?
diff --git a/man/man8/xfs_admin.8 b/man/man8/xfs_admin.8
index 63f8ee90307b30..d2652c0598f551 100644
--- a/man/man8/xfs_admin.8
+++ b/man/man8/xfs_admin.8
@@ -8,6 +8,8 @@ .SH SYNOPSIS
] [
.BI \-O " featurelist"
] [
+.BR "\-C " cfgfile
+] [
.BR "\-c 0" | 1
] [
.B \-L
@@ -85,6 +87,11 @@ .SH OPTIONS
.B \-u
Print the current filesystem UUID (Universally Unique IDentifier).
.TP
+.BI "\-C " path
+Write a configuration file to the given path that can be re-read by mkfs.xfs
+for a future filesystem format attempt.
+This file will contain only user-visible features.
+.TP
.BR "\-c 0" | 1
Enable (1) or disable (0) lazy-counters in the filesystem.
.IP
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 8/8] mkfs: allow specification of default options via configuration file
2026-09-04 1:29 [PATCHSET v2] xfsprogs: generate mkfs.xfs config files Darrick J. Wong
` (6 preceding siblings ...)
2026-09-04 1:31 ` [PATCH 7/8] xfs_admin: print configuration file for mounted filesystems Darrick J. Wong
@ 2026-09-04 1:31 ` Darrick J. Wong
7 siblings, 0 replies; 10+ messages in thread
From: Darrick J. Wong @ 2026-09-04 1:31 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: hch, tytso, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Ted asked for the ability to set default mkfs options, but to retain the
ability respecify options via a separate configuration file or cli
options. This would be useful for running fstests with the default
featureset of (say) Linux 5.15 LTS, while still allowing individual
testcases to provide their own overrides. It's certainly less messy
than what Ted does today, which is a bash script that copies the desired
config file and changes things.
Cc: tytso@mit.edu
Cc: hch@infradead.org
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
man/man8/mkfs.xfs.8.in | 16 ++++-
mkfs/xfs_mkfs.c | 160 ++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 153 insertions(+), 23 deletions(-)
diff --git a/man/man8/mkfs.xfs.8.in b/man/man8/mkfs.xfs.8.in
index f28f668a1e7e68..521b73bed8c747 100644
--- a/man/man8/mkfs.xfs.8.in
+++ b/man/man8/mkfs.xfs.8.in
@@ -152,10 +152,22 @@ .SH OPTIONS
.BI \-c " configuration_file_option"
This option specifies the files that mkfs configuration will be obtained from.
The valid
-.I configuration_file_option
-is:
+.I configuration_file_options
+are:
.RS 1.2i
.TP
+.BI defaults= name
+Default configuration options will be sourced from the file specified by the
+.I name
+option string.
+This option can be use either an absolute or relative path to the configuration
+file to be read.
+Sample configuration files can be found in @mkfs_cfg_dir@.
+Options specified through the default configuration file can be overridden by
+a configuration file specified via
+.B options=
+or command line arguments, in that order.
+.TP
.BI makecfg= path
Write a configuration file to the file specified by the
.I path
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index 5c45e0248d1de6..7c81f5a975532e 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -62,6 +62,7 @@ enum {
enum {
C_OPTFILE = 0,
C_MAKECFG,
+ C_DEFOPTFILE,
C_MAX_OPTS,
};
@@ -314,6 +315,7 @@ static struct opt_params copts = {
.subopts = {
[C_OPTFILE] = "options",
[C_MAKECFG] = "makecfg",
+ [C_DEFOPTFILE] = "defaults",
[C_MAX_OPTS] = NULL,
},
.subopt_params = {
@@ -325,6 +327,10 @@ static struct opt_params copts = {
.conflicts = { { NULL, LAST_CONFLICT } },
.defaultval = 1,
},
+ { .index = C_DEFOPTFILE,
+ .conflicts = { { NULL, LAST_CONFLICT } },
+ .defaultval = SUBOPT_NEEDS_VAL,
+ },
},
};
@@ -1077,6 +1083,7 @@ struct cli_params {
int blocksize;
char *cfgfile;
+ char *defcfgfile;
char *protofile;
char *makecfg;
@@ -1214,7 +1221,7 @@ usage( void )
{
fprintf(stderr, _("Usage: %s\n\
/* blocksize */ [-b size=num]\n\
-/* config file */ [-c options=path,makecfg=path\n\
+/* config file */ [-c options=path,makecfg=path,defaults=path\n\
/* metadata */ [-m crc=0|1,finobt=0|1,uuid=xxx,rmapbt=0|1,reflink=0|1,\n\
inobtcount=0|1,bigtime=0|1,autofsck=xxx,\n\
metadir=0|1]\n\
@@ -1800,6 +1807,30 @@ cfgfile_opts_parser(
else
cli->makecfg = getstr(value, opts, subopt);
break;
+ case C_DEFOPTFILE:
+ /* already processed by defcfgfile_opts_parser; ignored */
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int
+defcfgfile_opts_parser(
+ struct opt_params *opts,
+ int subopt,
+ const char *value,
+ struct cli_params *cli)
+{
+ switch (subopt) {
+ case C_OPTFILE:
+ case C_MAKECFG:
+ /* will be processed by cfgfile_opts_parser; ignored */
+ break;
+ case C_DEFOPTFILE:
+ cli->defcfgfile = getstr(value, opts, subopt);
+ break;
default:
return -EINVAL;
}
@@ -2266,13 +2297,15 @@ sector_opts_parser(
return 0;
}
-static struct subopts {
+struct subopts {
struct opt_params *opts;
int (*parser)(struct opt_params *opts,
int subopt,
const char *value,
struct cli_params *cli);
-} subopt_tab[] = {
+};
+
+static const struct subopts subopt_tab[] = {
{ &bopts, block_opts_parser },
{ &copts, cfgfile_opts_parser },
{ &dopts, data_opts_parser },
@@ -2286,15 +2319,21 @@ static struct subopts {
{ NULL, NULL },
};
+static const struct subopts defcfg_subopt_tab[] = {
+ { &copts, defcfgfile_opts_parser },
+ { NULL, NULL },
+};
+
static void
parse_subopts(
- char opt,
- char *arg,
- struct cli_params *cli)
+ char opt,
+ char *arg,
+ const struct subopts *stab,
+ struct cli_params *cli)
{
- struct subopts *sop = &subopt_tab[0];
- char *p;
- int ret = 0;
+ const struct subopts *sop = stab;
+ char *p, *duparg;
+ int ret = 0;
while (sop->opts) {
if (sop->opts->name == opt)
@@ -2306,7 +2345,8 @@ parse_subopts(
if (!sop->opts)
return;
- p = arg;
+ /* getsubopt modifies duparg */
+ p = duparg = strdup(arg);
while (*p != '\0') {
char **subopts = (char **)sop->opts->subopts;
char *value;
@@ -2318,19 +2358,20 @@ parse_subopts(
if (ret)
unknown(opt, value);
}
+ free(duparg);
}
static bool
parse_cfgopt(
- const char *section,
- const char *name,
- const char *value,
- struct cli_params *cli)
+ const char *section,
+ const char *name,
+ const char *value,
+ struct cli_params *cli)
{
- struct subopts *sop = &subopt_tab[0];
- char **subopts;
- int ret = 0;
- int i;
+ const struct subopts *sop = &subopt_tab[0];
+ char **subopts;
+ int ret = 0;
+ int i;
while (sop->opts) {
if (sop->opts->ini_section[0] != '\0' &&
@@ -5789,6 +5830,64 @@ cfgfile_parse(
cli->cfgfile);
}
+static void
+reset_seen(
+ struct opt_params *opts)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAX_SUBOPTS; i++) {
+ opts->subopt_params[i].seen = false;
+ opts->subopt_params[i].str_seen = false;
+ }
+}
+
+static void
+defcfgfile_parse(
+ struct cli_params *cli)
+{
+ int error;
+
+ if (!cli->defcfgfile)
+ return;
+
+ error = ini_parse(cli->defcfgfile, cfgfile_parse_ini, cli);
+ if (error) {
+ if (error > 0) {
+ fprintf(stderr,
+ _("%s: Unrecognised input on line %d. Aborting.\n"),
+ cli->defcfgfile, error);
+ } else if (error == -1) {
+ fprintf(stderr,
+ _("Unable to open defaults config file %s. Aborting.\n"),
+ cli->defcfgfile);
+ } else if (error == -2) {
+ fprintf(stderr,
+ _("Memory allocation failure parsing %s. Aborting.\n"),
+ cli->defcfgfile);
+ } else {
+ fprintf(stderr,
+ _("Unknown error %d opening defaults config file %s. Aborting.\n"),
+ error, cli->defcfgfile);
+ }
+ exit(1);
+ }
+ printf(_("Parameters parsed from defaults config file %s successfully\n"),
+ cli->defcfgfile);
+
+ /* Now make it look like we haven't seen any cli options. */
+ reset_seen(&bopts);
+ reset_seen(&copts);
+ reset_seen(&dopts);
+ reset_seen(&iopts);
+ reset_seen(&lopts);
+ reset_seen(&mopts);
+ reset_seen(&nopts);
+ reset_seen(&popts);
+ reset_seen(&ropts);
+ reset_seen(&sopts);
+}
+
static void
set_autofsck(
struct xfs_mount *mp,
@@ -5938,6 +6037,17 @@ check_rt_meta_prealloc(
mp->m_finobt_nores = false;
}
+static inline int
+getopt_mkfs(
+ int argc,
+ char *const argv[],
+ const struct option *longopts,
+ int *longindex)
+{
+ return getopt_long(argc, argv, "b:c:d:i:l:L:m:n:KNp:qr:s:CfV",
+ longopts, longindex);
+}
+
int
main(
int argc,
@@ -6044,8 +6154,16 @@ main(
memcpy(&cli.sb_feat, &dft.sb_feat, sizeof(cli.sb_feat));
memcpy(&cli.fsx, &dft.fsx, sizeof(cli.fsx));
- while ((c = getopt_long(argc, argv, "b:c:d:i:l:L:m:n:KNp:qr:s:CfV",
- long_options, &option_index)) != EOF) {
+ /* Load default configuration, if specified */
+ while ((c = getopt_mkfs(argc, argv, long_options, &option_index)) != EOF) {
+ if (c == 'c') {
+ parse_subopts(c, optarg, defcfg_subopt_tab, &cli);
+ }
+ }
+ defcfgfile_parse(&cli);
+ optind = 1;
+
+ while ((c = getopt_mkfs(argc, argv, long_options, &option_index)) != EOF) {
switch (c) {
case 0:
break;
@@ -6063,7 +6181,7 @@ main(
case 'p':
case 'r':
case 's':
- parse_subopts(c, optarg, &cli);
+ parse_subopts(c, optarg, subopt_tab, &cli);
break;
case 'L':
if (strlen(optarg) > sizeof(sbp->sb_fname))
^ permalink raw reply related [flat|nested] 10+ messages in thread