aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--fs/xfs/libxfs/xfs_dquot_buf.c190
-rw-r--r--fs/xfs/libxfs/xfs_fs.h6
-rw-r--r--fs/xfs/libxfs/xfs_quota_defs.h43
-rw-r--r--fs/xfs/libxfs/xfs_sb.c1
-rw-r--r--fs/xfs/scrub/metapath.c76
-rw-r--r--fs/xfs/xfs_mount.c15
-rw-r--r--fs/xfs/xfs_mount.h21
-rw-r--r--fs/xfs/xfs_qm.c256
-rw-r--r--fs/xfs/xfs_qm_bhv.c18
-rw-r--r--fs/xfs/xfs_quota.h2
-rw-r--r--fs/xfs/xfs_super.c25
11 files changed, 589 insertions, 64 deletions
diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c
index 15a362e2f5ea..dceef2abd4e2 100644
--- a/fs/xfs/libxfs/xfs_dquot_buf.c
+++ b/fs/xfs/libxfs/xfs_dquot_buf.c
@@ -16,6 +16,9 @@
#include "xfs_trans.h"
#include "xfs_qm.h"
#include "xfs_error.h"
+#include "xfs_health.h"
+#include "xfs_metadir.h"
+#include "xfs_metafile.h"
int
xfs_calc_dquots_per_chunk(
@@ -323,3 +326,190 @@ xfs_dquot_to_disk_ts(
return cpu_to_be32(t);
}
+
+inline unsigned int
+xfs_dqinode_sick_mask(xfs_dqtype_t type)
+{
+ switch (type) {
+ case XFS_DQTYPE_USER:
+ return XFS_SICK_FS_UQUOTA;
+ case XFS_DQTYPE_GROUP:
+ return XFS_SICK_FS_GQUOTA;
+ case XFS_DQTYPE_PROJ:
+ return XFS_SICK_FS_PQUOTA;
+ }
+
+ ASSERT(0);
+ return 0;
+}
+
+/*
+ * Load the inode for a given type of quota, assuming that the sb fields have
+ * been sorted out. This is not true when switching quota types on a V4
+ * filesystem, so do not use this function for that. If metadir is enabled,
+ * @dp must be the /quota metadir.
+ *
+ * Returns -ENOENT if the quota inode field is NULLFSINO; 0 and an inode on
+ * success; or a negative errno.
+ */
+int
+xfs_dqinode_load(
+ struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ xfs_dqtype_t type,
+ struct xfs_inode **ipp)
+{
+ struct xfs_mount *mp = tp->t_mountp;
+ struct xfs_inode *ip;
+ enum xfs_metafile_type metafile_type = xfs_dqinode_metafile_type(type);
+ int error;
+
+ if (!xfs_has_metadir(mp)) {
+ xfs_ino_t ino;
+
+ switch (type) {
+ case XFS_DQTYPE_USER:
+ ino = mp->m_sb.sb_uquotino;
+ break;
+ case XFS_DQTYPE_GROUP:
+ ino = mp->m_sb.sb_gquotino;
+ break;
+ case XFS_DQTYPE_PROJ:
+ ino = mp->m_sb.sb_pquotino;
+ break;
+ default:
+ ASSERT(0);
+ return -EFSCORRUPTED;
+ }
+
+ /* Should have set 0 to NULLFSINO when loading superblock */
+ if (ino == NULLFSINO)
+ return -ENOENT;
+
+ error = xfs_trans_metafile_iget(tp, ino, metafile_type, &ip);
+ } else {
+ error = xfs_metadir_load(tp, dp, xfs_dqinode_path(type),
+ metafile_type, &ip);
+ if (error == -ENOENT)
+ return error;
+ }
+ if (error) {
+ if (xfs_metadata_is_sick(error))
+ xfs_fs_mark_sick(mp, xfs_dqinode_sick_mask(type));
+ return error;
+ }
+
+ if (XFS_IS_CORRUPT(mp, ip->i_df.if_format != XFS_DINODE_FMT_EXTENTS &&
+ ip->i_df.if_format != XFS_DINODE_FMT_BTREE)) {
+ xfs_irele(ip);
+ xfs_fs_mark_sick(mp, xfs_dqinode_sick_mask(type));
+ return -EFSCORRUPTED;
+ }
+
+ if (XFS_IS_CORRUPT(mp, ip->i_projid != 0)) {
+ xfs_irele(ip);
+ xfs_fs_mark_sick(mp, xfs_dqinode_sick_mask(type));
+ return -EFSCORRUPTED;
+ }
+
+ *ipp = ip;
+ return 0;
+}
+
+/* Create a metadata directory quota inode. */
+int
+xfs_dqinode_metadir_create(
+ struct xfs_inode *dp,
+ xfs_dqtype_t type,
+ struct xfs_inode **ipp)
+{
+ struct xfs_metadir_update upd = {
+ .dp = dp,
+ .metafile_type = xfs_dqinode_metafile_type(type),
+ .path = xfs_dqinode_path(type),
+ };
+ int error;
+
+ error = xfs_metadir_start_create(&upd);
+ if (error)
+ return error;
+
+ error = xfs_metadir_create(&upd, S_IFREG);
+ if (error)
+ return error;
+
+ xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE);
+
+ error = xfs_metadir_commit(&upd);
+ if (error)
+ return error;
+
+ xfs_finish_inode_setup(upd.ip);
+ *ipp = upd.ip;
+ return 0;
+}
+
+#ifndef __KERNEL__
+/* Link a metadata directory quota inode. */
+int
+xfs_dqinode_metadir_link(
+ struct xfs_inode *dp,
+ xfs_dqtype_t type,
+ struct xfs_inode *ip)
+{
+ struct xfs_metadir_update upd = {
+ .dp = dp,
+ .metafile_type = xfs_dqinode_metafile_type(type),
+ .path = xfs_dqinode_path(type),
+ .ip = ip,
+ };
+ int error;
+
+ error = xfs_metadir_start_link(&upd);
+ if (error)
+ return error;
+
+ error = xfs_metadir_link(&upd);
+ if (error)
+ return error;
+
+ xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE);
+
+ return xfs_metadir_commit(&upd);
+}
+#endif /* __KERNEL__ */
+
+/* Create the parent directory for all quota inodes and load it. */
+int
+xfs_dqinode_mkdir_parent(
+ struct xfs_mount *mp,
+ struct xfs_inode **dpp)
+{
+ if (!mp->m_metadirip) {
+ xfs_fs_mark_sick(mp, XFS_SICK_FS_METADIR);
+ return -EFSCORRUPTED;
+ }
+
+ return xfs_metadir_mkdir(mp->m_metadirip, "quota", dpp);
+}
+
+/*
+ * Load the parent directory of all quota inodes. Pass the inode to the caller
+ * because quota functions (e.g. QUOTARM) can be called on the quota files even
+ * if quotas are not enabled.
+ */
+int
+xfs_dqinode_load_parent(
+ struct xfs_trans *tp,
+ struct xfs_inode **dpp)
+{
+ struct xfs_mount *mp = tp->t_mountp;
+
+ if (!mp->m_metadirip) {
+ xfs_fs_mark_sick(mp, XFS_SICK_FS_METADIR);
+ return -EFSCORRUPTED;
+ }
+
+ return xfs_metadir_load(tp, mp->m_metadirip, "quota", XFS_METAFILE_DIR,
+ dpp);
+}
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 96f7d3c95fb4..41ce4d3d650e 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -825,9 +825,13 @@ struct xfs_scrub_vec_head {
#define XFS_SCRUB_METAPATH_RTDIR (1) /* rtrgroups metadir */
#define XFS_SCRUB_METAPATH_RTBITMAP (2) /* per-rtg bitmap */
#define XFS_SCRUB_METAPATH_RTSUMMARY (3) /* per-rtg summary */
+#define XFS_SCRUB_METAPATH_QUOTADIR (4) /* quota metadir */
+#define XFS_SCRUB_METAPATH_USRQUOTA (5) /* user quota */
+#define XFS_SCRUB_METAPATH_GRPQUOTA (6) /* group quota */
+#define XFS_SCRUB_METAPATH_PRJQUOTA (7) /* project quota */
/* Number of metapath sm_ino values */
-#define XFS_SCRUB_METAPATH_NR (4)
+#define XFS_SCRUB_METAPATH_NR (8)
/*
* ioctl limits
diff --git a/fs/xfs/libxfs/xfs_quota_defs.h b/fs/xfs/libxfs/xfs_quota_defs.h
index fb05f44f6c75..763d941a8420 100644
--- a/fs/xfs/libxfs/xfs_quota_defs.h
+++ b/fs/xfs/libxfs/xfs_quota_defs.h
@@ -143,4 +143,47 @@ time64_t xfs_dquot_from_disk_ts(struct xfs_disk_dquot *ddq,
__be32 dtimer);
__be32 xfs_dquot_to_disk_ts(struct xfs_dquot *ddq, time64_t timer);
+static inline const char *
+xfs_dqinode_path(xfs_dqtype_t type)
+{
+ switch (type) {
+ case XFS_DQTYPE_USER:
+ return "user";
+ case XFS_DQTYPE_GROUP:
+ return "group";
+ case XFS_DQTYPE_PROJ:
+ return "project";
+ }
+
+ ASSERT(0);
+ return NULL;
+}
+
+static inline enum xfs_metafile_type
+xfs_dqinode_metafile_type(xfs_dqtype_t type)
+{
+ switch (type) {
+ case XFS_DQTYPE_USER:
+ return XFS_METAFILE_USRQUOTA;
+ case XFS_DQTYPE_GROUP:
+ return XFS_METAFILE_GRPQUOTA;
+ case XFS_DQTYPE_PROJ:
+ return XFS_METAFILE_PRJQUOTA;
+ }
+
+ ASSERT(0);
+ return XFS_METAFILE_UNKNOWN;
+}
+
+unsigned int xfs_dqinode_sick_mask(xfs_dqtype_t type);
+
+int xfs_dqinode_load(struct xfs_trans *tp, struct xfs_inode *dp,
+ xfs_dqtype_t type, struct xfs_inode **ipp);
+int xfs_dqinode_metadir_create(struct xfs_inode *dp, xfs_dqtype_t type,
+ struct xfs_inode **ipp);
+int xfs_dqinode_metadir_link(struct xfs_inode *dp, xfs_dqtype_t type,
+ struct xfs_inode *ip);
+int xfs_dqinode_mkdir_parent(struct xfs_mount *mp, struct xfs_inode **dpp);
+int xfs_dqinode_load_parent(struct xfs_trans *tp, struct xfs_inode **dpp);
+
#endif /* __XFS_QUOTA_H__ */
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index c1254f019e53..e81b240b7158 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -858,6 +858,7 @@ xfs_sb_quota_to_disk(
if (xfs_sb_is_v5(from) &&
(from->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_METADIR)) {
+ to->sb_qflags = cpu_to_be16(from->sb_qflags);
to->sb_uquotino = cpu_to_be64(0);
to->sb_gquotino = cpu_to_be64(0);
to->sb_pquotino = cpu_to_be64(0);
diff --git a/fs/xfs/scrub/metapath.c b/fs/xfs/scrub/metapath.c
index b8e427fd7fa7..b78db6513465 100644
--- a/fs/xfs/scrub/metapath.c
+++ b/fs/xfs/scrub/metapath.c
@@ -165,6 +165,74 @@ out_put_rtg:
# define xchk_setup_metapath_rtginode(...) (-ENOENT)
#endif /* CONFIG_XFS_RT */
+#ifdef CONFIG_XFS_QUOTA
+/* Scan the /quota directory itself. */
+static int
+xchk_setup_metapath_quotadir(
+ struct xfs_scrub *sc)
+{
+ struct xfs_trans *tp;
+ struct xfs_inode *dp = NULL;
+ int error;
+
+ error = xfs_trans_alloc_empty(sc->mp, &tp);
+ if (error)
+ return error;
+
+ error = xfs_dqinode_load_parent(tp, &dp);
+ xfs_trans_cancel(tp);
+ if (error)
+ return error;
+
+ error = xchk_setup_metapath_scan(sc, sc->mp->m_metadirip,
+ kasprintf(GFP_KERNEL, "quota"), dp);
+ xfs_irele(dp);
+ return error;
+}
+
+/* Scan a quota inode under the /quota directory. */
+static int
+xchk_setup_metapath_dqinode(
+ struct xfs_scrub *sc,
+ xfs_dqtype_t type)
+{
+ struct xfs_trans *tp = NULL;
+ struct xfs_inode *dp = NULL;
+ struct xfs_inode *ip = NULL;
+ const char *path;
+ int error;
+
+ error = xfs_trans_alloc_empty(sc->mp, &tp);
+ if (error)
+ return error;
+
+ error = xfs_dqinode_load_parent(tp, &dp);
+ if (error)
+ goto out_cancel;
+
+ error = xfs_dqinode_load(tp, dp, type, &ip);
+ if (error)
+ goto out_dp;
+
+ xfs_trans_cancel(tp);
+ tp = NULL;
+
+ path = kasprintf(GFP_KERNEL, "%s", xfs_dqinode_path(type));
+ error = xchk_setup_metapath_scan(sc, dp, path, ip);
+
+ xfs_irele(ip);
+out_dp:
+ xfs_irele(dp);
+out_cancel:
+ if (tp)
+ xfs_trans_cancel(tp);
+ return error;
+}
+#else
+# define xchk_setup_metapath_quotadir(...) (-ENOENT)
+# define xchk_setup_metapath_dqinode(...) (-ENOENT)
+#endif /* CONFIG_XFS_QUOTA */
+
int
xchk_setup_metapath(
struct xfs_scrub *sc)
@@ -186,6 +254,14 @@ xchk_setup_metapath(
return xchk_setup_metapath_rtginode(sc, XFS_RTGI_BITMAP);
case XFS_SCRUB_METAPATH_RTSUMMARY:
return xchk_setup_metapath_rtginode(sc, XFS_RTGI_SUMMARY);
+ case XFS_SCRUB_METAPATH_QUOTADIR:
+ return xchk_setup_metapath_quotadir(sc);
+ case XFS_SCRUB_METAPATH_USRQUOTA:
+ return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_USER);
+ case XFS_SCRUB_METAPATH_GRPQUOTA:
+ return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_GROUP);
+ case XFS_SCRUB_METAPATH_PRJQUOTA:
+ return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_PROJ);
default:
return -ENOENT;
}
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index dba1f6fc6881..5918f433dba7 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -853,6 +853,13 @@ xfs_mountfs(
goto out_fail_wait;
/*
+ * If we're resuming quota status, pick up the preliminary qflags from
+ * the ondisk superblock so that we know if we should recover dquots.
+ */
+ if (xfs_is_resuming_quotaon(mp))
+ xfs_qm_resume_quotaon(mp);
+
+ /*
* Log's mount-time initialization. The first part of recovery can place
* some items on the AIL, to be handled when recovery is finished or
* cancelled.
@@ -866,6 +873,14 @@ xfs_mountfs(
}
/*
+ * If we're resuming quota status and recovered the log, re-sample the
+ * qflags from the ondisk superblock now that we've recovered it, just
+ * in case someone shut down enforcement just before a crash.
+ */
+ if (xfs_clear_resuming_quotaon(mp) && xlog_recovery_needed(mp->m_log))
+ xfs_qm_resume_quotaon(mp);
+
+ /*
* If logged xattrs are still enabled after log recovery finishes, then
* they'll be available until unmount. Otherwise, turn them off.
*/
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index ee1c3eb53d9f..db9dade7d22a 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -499,6 +499,8 @@ __XFS_HAS_FEAT(nouuid, NOUUID)
#define XFS_OPSTATE_WARNED_PPTR 16
/* Kernel has logged a warning about metadata dirs being used on this fs. */
#define XFS_OPSTATE_WARNED_METADIR 17
+/* Filesystem should use qflags to determine quotaon status */
+#define XFS_OPSTATE_RESUMING_QUOTAON 18
#define __XFS_IS_OPSTATE(name, NAME) \
static inline bool xfs_is_ ## name (struct xfs_mount *mp) \
@@ -523,9 +525,24 @@ __XFS_IS_OPSTATE(inodegc_enabled, INODEGC_ENABLED)
__XFS_IS_OPSTATE(blockgc_enabled, BLOCKGC_ENABLED)
#ifdef CONFIG_XFS_QUOTA
__XFS_IS_OPSTATE(quotacheck_running, QUOTACHECK_RUNNING)
+__XFS_IS_OPSTATE(resuming_quotaon, RESUMING_QUOTAON)
#else
-# define xfs_is_quotacheck_running(mp) (false)
-#endif
+static inline bool xfs_is_quotacheck_running(struct xfs_mount *mp)
+{
+ return false;
+}
+static inline bool xfs_is_resuming_quotaon(struct xfs_mount *mp)
+{
+ return false;
+}
+static inline void xfs_set_resuming_quotaon(struct xfs_mount *m)
+{
+}
+static inline bool xfs_clear_resuming_quotaon(struct xfs_mount *mp)
+{
+ return false;
+}
+#endif /* CONFIG_XFS_QUOTA */
__XFS_IS_OPSTATE(done_with_log_incompat, UNSET_LOG_INCOMPAT)
__XFS_IS_OPSTATE(using_logged_xattrs, USE_LARP)
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 28b1420bac1d..d9d09195eabb 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -40,7 +40,6 @@
STATIC int xfs_qm_init_quotainos(struct xfs_mount *mp);
STATIC int xfs_qm_init_quotainfo(struct xfs_mount *mp);
-STATIC void xfs_qm_destroy_quotainos(struct xfs_quotainfo *qi);
STATIC void xfs_qm_dqfree_one(struct xfs_dquot *dqp);
/*
* We use the batch lookup interface to iterate over the dquots as it
@@ -226,6 +225,24 @@ xfs_qm_unmount_rt(
xfs_rtgroup_rele(rtg);
}
+STATIC void
+xfs_qm_destroy_quotainos(
+ struct xfs_quotainfo *qi)
+{
+ if (qi->qi_uquotaip) {
+ xfs_irele(qi->qi_uquotaip);
+ qi->qi_uquotaip = NULL; /* paranoia */
+ }
+ if (qi->qi_gquotaip) {
+ xfs_irele(qi->qi_gquotaip);
+ qi->qi_gquotaip = NULL;
+ }
+ if (qi->qi_pquotaip) {
+ xfs_irele(qi->qi_pquotaip);
+ qi->qi_pquotaip = NULL;
+ }
+}
+
/*
* Called from the vfsops layer.
*/
@@ -250,20 +267,8 @@ xfs_qm_unmount_quotas(
/*
* Release the quota inodes.
*/
- if (mp->m_quotainfo) {
- if (mp->m_quotainfo->qi_uquotaip) {
- xfs_irele(mp->m_quotainfo->qi_uquotaip);
- mp->m_quotainfo->qi_uquotaip = NULL;
- }
- if (mp->m_quotainfo->qi_gquotaip) {
- xfs_irele(mp->m_quotainfo->qi_gquotaip);
- mp->m_quotainfo->qi_gquotaip = NULL;
- }
- if (mp->m_quotainfo->qi_pquotaip) {
- xfs_irele(mp->m_quotainfo->qi_pquotaip);
- mp->m_quotainfo->qi_pquotaip = NULL;
- }
- }
+ if (mp->m_quotainfo)
+ xfs_qm_destroy_quotainos(mp->m_quotainfo);
}
STATIC int
@@ -640,6 +645,157 @@ xfs_qm_init_timelimits(
xfs_qm_dqdestroy(dqp);
}
+static int
+xfs_qm_load_metadir_qinos(
+ struct xfs_mount *mp,
+ struct xfs_quotainfo *qi,
+ struct xfs_inode **dpp)
+{
+ struct xfs_trans *tp;
+ int error;
+
+ error = xfs_trans_alloc_empty(mp, &tp);
+ if (error)
+ return error;
+
+ error = xfs_dqinode_load_parent(tp, dpp);
+ if (error == -ENOENT) {
+ /* no quota dir directory, but we'll create one later */
+ error = 0;
+ goto out_trans;
+ }
+ if (error)
+ goto out_trans;
+
+ if (XFS_IS_UQUOTA_ON(mp)) {
+ error = xfs_dqinode_load(tp, *dpp, XFS_DQTYPE_USER,
+ &qi->qi_uquotaip);
+ if (error && error != -ENOENT)
+ goto out_trans;
+ }
+
+ if (XFS_IS_GQUOTA_ON(mp)) {
+ error = xfs_dqinode_load(tp, *dpp, XFS_DQTYPE_GROUP,
+ &qi->qi_gquotaip);
+ if (error && error != -ENOENT)
+ goto out_trans;
+ }
+
+ if (XFS_IS_PQUOTA_ON(mp)) {
+ error = xfs_dqinode_load(tp, *dpp, XFS_DQTYPE_PROJ,
+ &qi->qi_pquotaip);
+ if (error && error != -ENOENT)
+ goto out_trans;
+ }
+
+ error = 0;
+out_trans:
+ xfs_trans_cancel(tp);
+ return error;
+}
+
+/* Create quota inodes in the metadata directory tree. */
+STATIC int
+xfs_qm_create_metadir_qinos(
+ struct xfs_mount *mp,
+ struct xfs_quotainfo *qi,
+ struct xfs_inode **dpp)
+{
+ int error;
+
+ if (!*dpp) {
+ error = xfs_dqinode_mkdir_parent(mp, dpp);
+ if (error && error != -EEXIST)
+ return error;
+ }
+
+ if (XFS_IS_UQUOTA_ON(mp) && !qi->qi_uquotaip) {
+ error = xfs_dqinode_metadir_create(*dpp, XFS_DQTYPE_USER,
+ &qi->qi_uquotaip);
+ if (error)
+ return error;
+ }
+
+ if (XFS_IS_GQUOTA_ON(mp) && !qi->qi_gquotaip) {
+ error = xfs_dqinode_metadir_create(*dpp, XFS_DQTYPE_GROUP,
+ &qi->qi_gquotaip);
+ if (error)
+ return error;
+ }
+
+ if (XFS_IS_PQUOTA_ON(mp) && !qi->qi_pquotaip) {
+ error = xfs_dqinode_metadir_create(*dpp, XFS_DQTYPE_PROJ,
+ &qi->qi_pquotaip);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+/*
+ * Add QUOTABIT to sb_versionnum and initialize qflags in preparation for
+ * creating quota files on a metadir filesystem.
+ */
+STATIC int
+xfs_qm_prep_metadir_sb(
+ struct xfs_mount *mp)
+{
+ struct xfs_trans *tp;
+ int error;
+
+ error = xfs_trans_alloc(mp, &M_RES(mp)->tr_sb, 0, 0, 0, &tp);
+ if (error)
+ return error;
+
+ spin_lock(&mp->m_sb_lock);
+
+ xfs_add_quota(mp);
+
+ /* qflags will get updated fully _after_ quotacheck */
+ mp->m_sb.sb_qflags = mp->m_qflags & XFS_ALL_QUOTA_ACCT;
+
+ spin_unlock(&mp->m_sb_lock);
+ xfs_log_sb(tp);
+
+ return xfs_trans_commit(tp);
+}
+
+/*
+ * Load existing quota inodes or create them. Since this is a V5 filesystem,
+ * we don't have to deal with the grp/prjquota switcheroo thing from V4.
+ */
+STATIC int
+xfs_qm_init_metadir_qinos(
+ struct xfs_mount *mp)
+{
+ struct xfs_quotainfo *qi = mp->m_quotainfo;
+ struct xfs_inode *dp = NULL;
+ int error;
+
+ if (!xfs_has_quota(mp)) {
+ error = xfs_qm_prep_metadir_sb(mp);
+ if (error)
+ return error;
+ }
+
+ error = xfs_qm_load_metadir_qinos(mp, qi, &dp);
+ if (error)
+ goto out_err;
+
+ error = xfs_qm_create_metadir_qinos(mp, qi, &dp);
+ if (error)
+ goto out_err;
+
+ xfs_irele(dp);
+ return 0;
+out_err:
+ xfs_qm_destroy_quotainos(mp->m_quotainfo);
+ if (dp)
+ xfs_irele(dp);
+ return error;
+}
+
/*
* This initializes all the quota information that's kept in the
* mount structure
@@ -664,7 +820,10 @@ xfs_qm_init_quotainfo(
* See if quotainodes are setup, and if not, allocate them,
* and change the superblock accordingly.
*/
- error = xfs_qm_init_quotainos(mp);
+ if (xfs_has_metadir(mp))
+ error = xfs_qm_init_metadir_qinos(mp);
+ else
+ error = xfs_qm_init_quotainos(mp);
if (error)
goto out_free_lru;
@@ -1576,7 +1735,7 @@ xfs_qm_mount_quotas(
}
if (error) {
- xfs_warn(mp, "Failed to initialize disk quotas.");
+ xfs_warn(mp, "Failed to initialize disk quotas, err %d.", error);
return;
}
}
@@ -1595,31 +1754,26 @@ xfs_qm_qino_load(
xfs_dqtype_t type,
struct xfs_inode **ipp)
{
- xfs_ino_t ino = NULLFSINO;
- enum xfs_metafile_type metafile_type = XFS_METAFILE_UNKNOWN;
-
- switch (type) {
- case XFS_DQTYPE_USER:
- ino = mp->m_sb.sb_uquotino;
- metafile_type = XFS_METAFILE_USRQUOTA;
- break;
- case XFS_DQTYPE_GROUP:
- ino = mp->m_sb.sb_gquotino;
- metafile_type = XFS_METAFILE_GRPQUOTA;
- break;
- case XFS_DQTYPE_PROJ:
- ino = mp->m_sb.sb_pquotino;
- metafile_type = XFS_METAFILE_PRJQUOTA;
- break;
- default:
- ASSERT(0);
- return -EFSCORRUPTED;
- }
-
- if (ino == NULLFSINO)
- return -ENOENT;
-
- return xfs_metafile_iget(mp, ino, metafile_type, ipp);
+ struct xfs_trans *tp;
+ struct xfs_inode *dp = NULL;
+ int error;
+
+ error = xfs_trans_alloc_empty(mp, &tp);
+ if (error)
+ return error;
+
+ if (xfs_has_metadir(mp)) {
+ error = xfs_dqinode_load_parent(tp, &dp);
+ if (error)
+ goto out_cancel;
+ }
+
+ error = xfs_dqinode_load(tp, dp, type, ipp);
+ if (dp)
+ xfs_irele(dp);
+out_cancel:
+ xfs_trans_cancel(tp);
+ return error;
}
/*
@@ -1713,24 +1867,6 @@ error_rele:
}
STATIC void
-xfs_qm_destroy_quotainos(
- struct xfs_quotainfo *qi)
-{
- if (qi->qi_uquotaip) {
- xfs_irele(qi->qi_uquotaip);
- qi->qi_uquotaip = NULL; /* paranoia */
- }
- if (qi->qi_gquotaip) {
- xfs_irele(qi->qi_gquotaip);
- qi->qi_gquotaip = NULL;
- }
- if (qi->qi_pquotaip) {
- xfs_irele(qi->qi_pquotaip);
- qi->qi_pquotaip = NULL;
- }
-}
-
-STATIC void
xfs_qm_dqfree_one(
struct xfs_dquot *dqp)
{
diff --git a/fs/xfs/xfs_qm_bhv.c b/fs/xfs/xfs_qm_bhv.c
index a11436579877..79a96558f739 100644
--- a/fs/xfs/xfs_qm_bhv.c
+++ b/fs/xfs/xfs_qm_bhv.c
@@ -135,3 +135,21 @@ xfs_qm_newmount(
return 0;
}
+
+/*
+ * If the sysadmin didn't provide any quota mount options, restore the quota
+ * accounting and enforcement state from the ondisk superblock. Only do this
+ * for metadir filesystems because this is a behavior change.
+ */
+void
+xfs_qm_resume_quotaon(
+ struct xfs_mount *mp)
+{
+ if (!xfs_has_metadir(mp))
+ return;
+ if (xfs_has_norecovery(mp))
+ return;
+
+ mp->m_qflags = mp->m_sb.sb_qflags & (XFS_ALL_QUOTA_ACCT |
+ XFS_ALL_QUOTA_ENFD);
+}
diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h
index 645761997bf2..2d36d967380e 100644
--- a/fs/xfs/xfs_quota.h
+++ b/fs/xfs/xfs_quota.h
@@ -125,6 +125,7 @@ extern void xfs_qm_dqdetach(struct xfs_inode *);
extern void xfs_qm_dqrele(struct xfs_dquot *);
extern void xfs_qm_statvfs(struct xfs_inode *, struct kstatfs *);
extern int xfs_qm_newmount(struct xfs_mount *, uint *, uint *);
+void xfs_qm_resume_quotaon(struct xfs_mount *mp);
extern void xfs_qm_mount_quotas(struct xfs_mount *);
extern void xfs_qm_unmount(struct xfs_mount *);
extern void xfs_qm_unmount_quotas(struct xfs_mount *);
@@ -202,6 +203,7 @@ xfs_trans_reserve_quota_icreate(struct xfs_trans *tp, struct xfs_dquot *udqp,
#define xfs_qm_dqrele(d) do { (d) = (d); } while(0)
#define xfs_qm_statvfs(ip, s) do { } while(0)
#define xfs_qm_newmount(mp, a, b) (0)
+#define xfs_qm_resume_quotaon(mp) ((void)0)
#define xfs_qm_mount_quotas(mp)
#define xfs_qm_unmount(mp)
#define xfs_qm_unmount_quotas(mp)
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 3afeab684468..20fde2442768 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -67,6 +67,9 @@ enum xfs_dax_mode {
XFS_DAX_NEVER = 2,
};
+/* Were quota mount options provided? Must use the upper 16 bits of qflags. */
+#define XFS_QFLAGS_MNTOPTS (1U << 31)
+
static void
xfs_mount_set_dax_mode(
struct xfs_mount *mp,
@@ -1264,6 +1267,8 @@ xfs_fs_parse_param(
int size = 0;
int opt;
+ BUILD_BUG_ON(XFS_QFLAGS_MNTOPTS & XFS_MOUNT_QUOTA_ALL);
+
opt = fs_parse(fc, xfs_fs_parameters, param, &result);
if (opt < 0)
return opt;
@@ -1341,32 +1346,39 @@ xfs_fs_parse_param(
case Opt_noquota:
parsing_mp->m_qflags &= ~XFS_ALL_QUOTA_ACCT;
parsing_mp->m_qflags &= ~XFS_ALL_QUOTA_ENFD;
+ parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
return 0;
case Opt_quota:
case Opt_uquota:
case Opt_usrquota:
parsing_mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ENFD);
+ parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
return 0;
case Opt_qnoenforce:
case Opt_uqnoenforce:
parsing_mp->m_qflags |= XFS_UQUOTA_ACCT;
parsing_mp->m_qflags &= ~XFS_UQUOTA_ENFD;
+ parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
return 0;
case Opt_pquota:
case Opt_prjquota:
parsing_mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ENFD);
+ parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
return 0;
case Opt_pqnoenforce:
parsing_mp->m_qflags |= XFS_PQUOTA_ACCT;
parsing_mp->m_qflags &= ~XFS_PQUOTA_ENFD;
+ parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
return 0;
case Opt_gquota:
case Opt_grpquota:
parsing_mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ENFD);
+ parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
return 0;
case Opt_gqnoenforce:
parsing_mp->m_qflags |= XFS_GQUOTA_ACCT;
parsing_mp->m_qflags &= ~XFS_GQUOTA_ENFD;
+ parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
return 0;
case Opt_discard:
parsing_mp->m_features |= XFS_FEAT_DISCARD;
@@ -1433,7 +1445,8 @@ xfs_fs_validate_params(
return -EINVAL;
}
- if (!IS_ENABLED(CONFIG_XFS_QUOTA) && mp->m_qflags != 0) {
+ if (!IS_ENABLED(CONFIG_XFS_QUOTA) &&
+ (mp->m_qflags & ~XFS_QFLAGS_MNTOPTS)) {
xfs_warn(mp, "quota support not available in this kernel.");
return -EINVAL;
}
@@ -1768,6 +1781,14 @@ xfs_fs_fill_super(
if (xfs_has_parent(mp))
xfs_warn_experimental(mp, XFS_EXPERIMENTAL_PPTR);
+ /*
+ * If no quota mount options were provided, maybe we'll try to pick
+ * up the quota accounting and enforcement flags from the ondisk sb.
+ */
+ if (!(mp->m_qflags & XFS_QFLAGS_MNTOPTS))
+ xfs_set_resuming_quotaon(mp);
+ mp->m_qflags &= ~XFS_QFLAGS_MNTOPTS;
+
error = xfs_mountfs(mp);
if (error)
goto out_filestream_unmount;
@@ -1954,6 +1975,8 @@ xfs_fs_reconfigure(
int flags = fc->sb_flags;
int error;
+ new_mp->m_qflags &= ~XFS_QFLAGS_MNTOPTS;
+
/* version 5 superblocks always support version counters. */
if (xfs_has_crc(mp))
fc->sb_flags |= SB_I_VERSION;