#include "versions.h"
#include "repair/pptr.h"
#include "repair/rt.h"
+#include "repair/quotacheck.h"
static xfs_ino_t orphanage_ino;
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))
}
}
+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)
{
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"));
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;
+ }
+ }
+}