@@ -509,7 +509,8 @@ secondary_sb_whack(
rval |= XR_AG_SB_SEC;
}
- rval |= secondary_sb_quota(mp, sbuf, sb, i, do_bzero);
+ if (!xfs_has_metadir(mp))
+ rval |= secondary_sb_quota(mp, sbuf, sb, i, do_bzero);
/*
* if the secondaries agree on a stripe unit/width or inode
@@ -15,6 +15,7 @@
#include "progress.h"
#include "scan.h"
#include "rt.h"
+#include "quotacheck.h"
/* workaround craziness in the xlog routines */
int xlog_recover_do_trans(struct xlog *log, struct xlog_recover *t, int p)
@@ -625,6 +626,8 @@ phase2(
}
discover_rtgroup_inodes(mp);
+ if (xfs_has_metadir(mp) && xfs_has_quota(mp))
+ discover_quota_inodes(mp);
/*
* Upgrade the filesystem now that we've done a preliminary check of
@@ -20,6 +20,7 @@
#include "versions.h"
#include "repair/pptr.h"
#include "repair/rt.h"
+#include "repair/quotacheck.h"
static xfs_ino_t orphanage_ino;
@@ -3180,7 +3181,7 @@ mark_standalone_inodes(xfs_mount_t *mp)
mark_inode(mp, mp->m_sb.sb_rsumino);
}
- if (!fs_quotas)
+ if (!fs_quotas || xfs_has_metadir(mp))
return;
if (has_quota_inode(XFS_DQTYPE_USER))
@@ -3402,6 +3403,116 @@ _(" - resetting contents of realtime bitmap and summary inodes\n"));
}
}
+static bool
+ensure_quota_file(
+ struct xfs_inode *dp,
+ xfs_dqtype_t type)
+{
+ struct xfs_mount *mp = dp->i_mount;
+ struct xfs_inode *ip;
+ const char *name = libxfs_dqinode_path(type);
+ int error;
+
+ if (!has_quota_inode(type))
+ return false;
+
+ if (no_modify) {
+ if (lost_quota_inode(type))
+ do_warn(_("would reset %s quota inode\n"), name);
+ return false;
+ }
+
+ if (!lost_quota_inode(type)) {
+ /*
+ * The /quotas directory has been discarded, but we should
+ * be able to iget the quota files directly.
+ */
+ error = -libxfs_metafile_iget(mp, get_quota_inode(type),
+ xfs_dqinode_metafile_type(type), &ip);
+ if (error) {
+ do_warn(
+_("Could not open %s quota inode, error %d\n"),
+ name, error);
+ lose_quota_inode(type);
+ }
+ }
+
+ if (lost_quota_inode(type)) {
+ /*
+ * The inode was bad or missing, state that we'll make a new
+ * one even though we always create a new one.
+ */
+ do_warn(_("resetting %s quota inode\n"), name);
+ error = -libxfs_dqinode_metadir_create(dp, type, &ip);
+ if (error) {
+ do_warn(
+_("Couldn't create %s quota inode, error %d\n"),
+ name, error);
+ goto bad;
+ }
+ } else {
+ struct xfs_trans *tp;
+
+ /* Erase parent pointers before we create the new link */
+ try_erase_parent_ptrs(ip);
+
+ error = -libxfs_dqinode_metadir_link(dp, type, ip);
+ if (error) {
+ do_warn(
+_("Couldn't link %s quota inode, error %d\n"),
+ name, error);
+ goto bad;
+ }
+
+ /*
+ * Reset the link count to 1 because quota files are never
+ * hardlinked, but the link above probably bumped it.
+ */
+ error = -libxfs_trans_alloc_inode(ip, &M_RES(mp)->tr_ichange,
+ 0, 0, false, &tp);
+ if (!error) {
+ set_nlink(VFS_I(ip), 1);
+ libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ error = -libxfs_trans_commit(tp);
+ }
+ if (error)
+ do_error(
+_("Couldn't reset link count on %s quota inode, error %d\n"),
+ name, error);
+ }
+
+ /* Mark the inode in use. */
+ mark_ino_inuse(mp, ip->i_ino, S_IFREG, dp->i_ino);
+ mark_ino_metadata(mp, ip->i_ino);
+ libxfs_irele(ip);
+ return true;
+bad:
+ /* Zeroes qflags */
+ quotacheck_skip();
+ return false;
+}
+
+static void
+reset_quota_metadir_inodes(
+ struct xfs_mount *mp)
+{
+ struct xfs_inode *dp = NULL;
+ int error;
+
+ error = -libxfs_dqinode_mkdir_parent(mp, &dp);
+ if (error)
+ do_error(_("failed to create quota metadir (%d)\n"),
+ error);
+
+ mark_ino_inuse(mp, dp->i_ino, S_IFDIR, mp->m_metadirip->i_ino);
+ mark_ino_metadata(mp, dp->i_ino);
+
+ ensure_quota_file(dp, XFS_DQTYPE_USER);
+ ensure_quota_file(dp, XFS_DQTYPE_GROUP);
+ ensure_quota_file(dp, XFS_DQTYPE_PROJ);
+ libxfs_irele(dp);
+}
+
void
phase6(xfs_mount_t *mp)
{
@@ -3455,6 +3566,9 @@ phase6(xfs_mount_t *mp)
else
reset_rt_sb_inodes(mp);
+ if (xfs_has_metadir(mp) && xfs_has_quota(mp) && !no_modify)
+ reset_quota_metadir_inodes(mp);
+
mark_standalone_inodes(mp);
do_log(_(" - traversing filesystem ...\n"));
@@ -645,3 +645,74 @@ update_sb_quotinos(
if (dirty)
libxfs_sb_to_disk(sbp->b_addr, &mp->m_sb);
}
+
+static inline int
+mark_quota_inode(
+ struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ xfs_dqtype_t type)
+{
+ struct xfs_inode *ip;
+ int error;
+
+ error = -libxfs_dqinode_load(tp, dp, type, &ip);
+ if (error == ENOENT)
+ return 0;
+ if (error)
+ goto out_corrupt;
+
+ set_quota_inode(type, ip->i_ino);
+ libxfs_irele(ip);
+ return 0;
+
+out_corrupt:
+ lose_quota_inode(type);
+ return error;
+}
+
+/* Mark the reachable quota metadata inodes prior to the inode scan. */
+void
+discover_quota_inodes(
+ struct xfs_mount *mp)
+{
+ struct xfs_trans *tp;
+ struct xfs_inode *dp = NULL;
+ int error, err2;
+
+ error = -libxfs_trans_alloc_empty(mp, &tp);
+ if (error)
+ goto out;
+
+ error = -libxfs_dqinode_load_parent(tp, &dp);
+ if (error)
+ goto out_cancel;
+
+ error = mark_quota_inode(tp, dp, XFS_DQTYPE_USER);
+ err2 = mark_quota_inode(tp, dp, XFS_DQTYPE_GROUP);
+ if (err2 && !error)
+ error = err2;
+ error = mark_quota_inode(tp, dp, XFS_DQTYPE_PROJ);
+ if (err2 && !error)
+ error = err2;
+
+ libxfs_irele(dp);
+out_cancel:
+ libxfs_trans_cancel(tp);
+out:
+ if (error) {
+ switch (error) {
+ case EFSCORRUPTED:
+ do_warn(
+ _("corruption in metadata directory tree while discovering quota inodes\n"));
+ break;
+ case ENOENT:
+ /* Do nothing, we'll just clear qflags later. */
+ break;
+ default:
+ do_warn(
+ _("couldn't discover quota inodes, err %d\n"),
+ error);
+ break;
+ }
+ }
+}
@@ -14,5 +14,6 @@ int quotacheck_setup(struct xfs_mount *mp);
void quotacheck_teardown(void);
void update_sb_quotinos(struct xfs_mount *mp, struct xfs_buf *sbp);
+void discover_quota_inodes(struct xfs_mount *mp);
#endif /* __XFS_REPAIR_QUOTACHECK_H__ */
@@ -1487,7 +1487,8 @@ _("Warning: project quota information would be cleared.\n"
if (!sbp)
do_error(_("couldn't get superblock\n"));
- update_sb_quotinos(mp, sbp);
+ if (!xfs_has_metadir(mp))
+ update_sb_quotinos(mp, sbp);
if ((mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD) != quotacheck_results()) {
do_warn(_("Note - quota info will be regenerated on next "