@@ -21,6 +21,13 @@ Mount Options
When mounting an XFS filesystem, the following options are accepted.
+ add_log_feat/noadd_log_feat
+ Permit unprivileged userspace to use functionality that requires
+ the addition of log incompat feature bits to the superblock.
+ The feature bits will be cleared during a clean unmount.
+ Old kernels cannot recover dirty logs if they do not recognize
+ all log incompat feature bits.
+
allocsize=size
Sets the buffered I/O end-of-file preallocation size when
doing delayed allocation writeout (default size is 64KiB).
@@ -1279,6 +1279,27 @@ xfs_force_summary_recalc(
xfs_fs_mark_sick(mp, XFS_SICK_FS_COUNTERS);
}
+/*
+ * Allow the log feature upgrade only if the sysadmin permits it via mount
+ * option; or the caller is the administrator. If the @want_audit parameter
+ * is true, then a denial due to insufficient privileges will be logged.
+ */
+bool
+xfs_can_add_incompat_log_features(
+ struct xfs_mount *mp,
+ bool want_audit)
+{
+ /* Always allowed if the mount option is set */
+ if (mp->m_features & XFS_FEAT_ADD_LOG_FEAT)
+ return true;
+
+ /* Allowed for administrators */
+ if (want_audit)
+ return capable(CAP_SYS_ADMIN);
+
+ return has_capability_noaudit(current, CAP_SYS_ADMIN);
+}
+
/*
* Enable a log incompat feature flag in the primary superblock. The caller
* cannot have any other transactions in progress.
@@ -1320,6 +1341,11 @@ xfs_add_incompat_log_feature(
if (xfs_sb_has_incompat_log_feature(&mp->m_sb, feature))
goto rele;
+ if (!xfs_can_add_incompat_log_features(mp, true)) {
+ error = -EOPNOTSUPP;
+ goto rele;
+ }
+
/*
* Write the primary superblock to disk immediately, because we need
* the log_incompat bit to be set in the primary super now to protect
@@ -294,6 +294,7 @@ typedef struct xfs_mount {
#define XFS_FEAT_NREXT64 (1ULL << 26) /* large extent counters */
/* Mount features */
+#define XFS_FEAT_ADD_LOG_FEAT (1ULL << 47) /* can add log incompat features */
#define XFS_FEAT_NOATTR2 (1ULL << 48) /* disable attr2 creation */
#define XFS_FEAT_NOALIGN (1ULL << 49) /* ignore alignment */
#define XFS_FEAT_ALLOCSIZE (1ULL << 50) /* user specified allocation size */
@@ -356,6 +357,8 @@ __XFS_HAS_FEAT(bigtime, BIGTIME)
__XFS_HAS_FEAT(needsrepair, NEEDSREPAIR)
__XFS_HAS_FEAT(large_extent_counts, NREXT64)
+bool xfs_can_add_incompat_log_features(struct xfs_mount *mp, bool want_audit);
+
/*
* Mount features
*
@@ -103,7 +103,8 @@ enum {
Opt_filestreams, Opt_quota, Opt_noquota, Opt_usrquota, Opt_grpquota,
Opt_prjquota, Opt_uquota, Opt_gquota, Opt_pquota,
Opt_uqnoenforce, Opt_gqnoenforce, Opt_pqnoenforce, Opt_qnoenforce,
- Opt_discard, Opt_nodiscard, Opt_dax, Opt_dax_enum,
+ Opt_discard, Opt_nodiscard, Opt_dax, Opt_dax_enum, Opt_add_log_feat,
+ Opt_noadd_log_feat,
};
static const struct fs_parameter_spec xfs_fs_parameters[] = {
@@ -148,6 +149,8 @@ static const struct fs_parameter_spec xfs_fs_parameters[] = {
fsparam_flag("nodiscard", Opt_nodiscard),
fsparam_flag("dax", Opt_dax),
fsparam_enum("dax", Opt_dax_enum, dax_param_enums),
+ fsparam_flag("add_log_feat", Opt_add_log_feat),
+ fsparam_flag("noadd_log_feat", Opt_noadd_log_feat),
{}
};
@@ -176,6 +179,7 @@ xfs_fs_show_options(
{ XFS_FEAT_LARGE_IOSIZE, ",largeio" },
{ XFS_FEAT_DAX_ALWAYS, ",dax=always" },
{ XFS_FEAT_DAX_NEVER, ",dax=never" },
+ { XFS_FEAT_ADD_LOG_FEAT, ",add_log_feat" },
{ 0, NULL }
};
struct xfs_mount *mp = XFS_M(root->d_sb);
@@ -1371,6 +1375,12 @@ xfs_fs_parse_param(
xfs_mount_set_dax_mode(parsing_mp, result.uint_32);
return 0;
#endif
+ case Opt_add_log_feat:
+ parsing_mp->m_features |= XFS_FEAT_ADD_LOG_FEAT;
+ return 0;
+ case Opt_noadd_log_feat:
+ parsing_mp->m_features &= ~XFS_FEAT_ADD_LOG_FEAT;
+ return 0;
/* Following mount options will be removed in September 2025 */
case Opt_ikeep:
xfs_fs_warn_deprecated(fc, param, XFS_FEAT_IKEEP, true);