162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2000-2006 Silicon Graphics, Inc. 462306a36Sopenharmony_ci * All Rights Reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_fs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_log_format.h" 1162306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1262306a36Sopenharmony_ci#include "xfs_mount.h" 1362306a36Sopenharmony_ci#include "xfs_inode.h" 1462306a36Sopenharmony_ci#include "xfs_quota.h" 1562306a36Sopenharmony_ci#include "xfs_trans.h" 1662306a36Sopenharmony_ci#include "xfs_buf_item.h" 1762306a36Sopenharmony_ci#include "xfs_trans_priv.h" 1862306a36Sopenharmony_ci#include "xfs_qm.h" 1962306a36Sopenharmony_ci#include "xfs_log.h" 2062306a36Sopenharmony_ci#include "xfs_log_priv.h" 2162306a36Sopenharmony_ci#include "xfs_log_recover.h" 2262306a36Sopenharmony_ci#include "xfs_error.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciSTATIC void 2562306a36Sopenharmony_cixlog_recover_dquot_ra_pass2( 2662306a36Sopenharmony_ci struct xlog *log, 2762306a36Sopenharmony_ci struct xlog_recover_item *item) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct xfs_mount *mp = log->l_mp; 3062306a36Sopenharmony_ci struct xfs_disk_dquot *recddq; 3162306a36Sopenharmony_ci struct xfs_dq_logformat *dq_f; 3262306a36Sopenharmony_ci uint type; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (mp->m_qflags == 0) 3562306a36Sopenharmony_ci return; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci recddq = item->ri_buf[1].i_addr; 3862306a36Sopenharmony_ci if (recddq == NULL) 3962306a36Sopenharmony_ci return; 4062306a36Sopenharmony_ci if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot)) 4162306a36Sopenharmony_ci return; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci type = recddq->d_type & XFS_DQTYPE_REC_MASK; 4462306a36Sopenharmony_ci ASSERT(type); 4562306a36Sopenharmony_ci if (log->l_quotaoffs_flag & type) 4662306a36Sopenharmony_ci return; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci dq_f = item->ri_buf[0].i_addr; 4962306a36Sopenharmony_ci ASSERT(dq_f); 5062306a36Sopenharmony_ci ASSERT(dq_f->qlf_len == 1); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci xlog_buf_readahead(log, dq_f->qlf_blkno, 5362306a36Sopenharmony_ci XFS_FSB_TO_BB(mp, dq_f->qlf_len), 5462306a36Sopenharmony_ci &xfs_dquot_buf_ra_ops); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* 5862306a36Sopenharmony_ci * Recover a dquot record 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ciSTATIC int 6162306a36Sopenharmony_cixlog_recover_dquot_commit_pass2( 6262306a36Sopenharmony_ci struct xlog *log, 6362306a36Sopenharmony_ci struct list_head *buffer_list, 6462306a36Sopenharmony_ci struct xlog_recover_item *item, 6562306a36Sopenharmony_ci xfs_lsn_t current_lsn) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct xfs_mount *mp = log->l_mp; 6862306a36Sopenharmony_ci struct xfs_buf *bp; 6962306a36Sopenharmony_ci struct xfs_dqblk *dqb; 7062306a36Sopenharmony_ci struct xfs_disk_dquot *ddq, *recddq; 7162306a36Sopenharmony_ci struct xfs_dq_logformat *dq_f; 7262306a36Sopenharmony_ci xfs_failaddr_t fa; 7362306a36Sopenharmony_ci int error; 7462306a36Sopenharmony_ci uint type; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * Filesystems are required to send in quota flags at mount time. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci if (mp->m_qflags == 0) 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci recddq = item->ri_buf[1].i_addr; 8362306a36Sopenharmony_ci if (recddq == NULL) { 8462306a36Sopenharmony_ci xfs_alert(log->l_mp, "NULL dquot in %s.", __func__); 8562306a36Sopenharmony_ci return -EFSCORRUPTED; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot)) { 8862306a36Sopenharmony_ci xfs_alert(log->l_mp, "dquot too small (%d) in %s.", 8962306a36Sopenharmony_ci item->ri_buf[1].i_len, __func__); 9062306a36Sopenharmony_ci return -EFSCORRUPTED; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * This type of quotas was turned off, so ignore this record. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci type = recddq->d_type & XFS_DQTYPE_REC_MASK; 9762306a36Sopenharmony_ci ASSERT(type); 9862306a36Sopenharmony_ci if (log->l_quotaoffs_flag & type) 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * At this point we know that quota was _not_ turned off. 10362306a36Sopenharmony_ci * Since the mount flags are not indicating to us otherwise, this 10462306a36Sopenharmony_ci * must mean that quota is on, and the dquot needs to be replayed. 10562306a36Sopenharmony_ci * Remember that we may not have fully recovered the superblock yet, 10662306a36Sopenharmony_ci * so we can't do the usual trick of looking at the SB quota bits. 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * The other possibility, of course, is that the quota subsystem was 10962306a36Sopenharmony_ci * removed since the last mount - ENOSYS. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci dq_f = item->ri_buf[0].i_addr; 11262306a36Sopenharmony_ci ASSERT(dq_f); 11362306a36Sopenharmony_ci fa = xfs_dquot_verify(mp, recddq, dq_f->qlf_id); 11462306a36Sopenharmony_ci if (fa) { 11562306a36Sopenharmony_ci xfs_alert(mp, "corrupt dquot ID 0x%x in log at %pS", 11662306a36Sopenharmony_ci dq_f->qlf_id, fa); 11762306a36Sopenharmony_ci return -EFSCORRUPTED; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci ASSERT(dq_f->qlf_len == 1); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * At this point we are assuming that the dquots have been allocated 12362306a36Sopenharmony_ci * and hence the buffer has valid dquots stamped in it. It should, 12462306a36Sopenharmony_ci * therefore, pass verifier validation. If the dquot is bad, then the 12562306a36Sopenharmony_ci * we'll return an error here, so we don't need to specifically check 12662306a36Sopenharmony_ci * the dquot in the buffer after the verifier has run. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, dq_f->qlf_blkno, 12962306a36Sopenharmony_ci XFS_FSB_TO_BB(mp, dq_f->qlf_len), 0, &bp, 13062306a36Sopenharmony_ci &xfs_dquot_buf_ops); 13162306a36Sopenharmony_ci if (error) 13262306a36Sopenharmony_ci return error; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci ASSERT(bp); 13562306a36Sopenharmony_ci dqb = xfs_buf_offset(bp, dq_f->qlf_boffset); 13662306a36Sopenharmony_ci ddq = &dqb->dd_diskdq; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* 13962306a36Sopenharmony_ci * If the dquot has an LSN in it, recover the dquot only if it's less 14062306a36Sopenharmony_ci * than the lsn of the transaction we are replaying. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci if (xfs_has_crc(mp)) { 14362306a36Sopenharmony_ci xfs_lsn_t lsn = be64_to_cpu(dqb->dd_lsn); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) { 14662306a36Sopenharmony_ci goto out_release; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci memcpy(ddq, recddq, item->ri_buf[1].i_len); 15162306a36Sopenharmony_ci if (xfs_has_crc(mp)) { 15262306a36Sopenharmony_ci xfs_update_cksum((char *)dqb, sizeof(struct xfs_dqblk), 15362306a36Sopenharmony_ci XFS_DQUOT_CRC_OFF); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Validate the recovered dquot. */ 15762306a36Sopenharmony_ci fa = xfs_dqblk_verify(log->l_mp, dqb, dq_f->qlf_id); 15862306a36Sopenharmony_ci if (fa) { 15962306a36Sopenharmony_ci XFS_CORRUPTION_ERROR("Bad dquot after recovery", 16062306a36Sopenharmony_ci XFS_ERRLEVEL_LOW, mp, dqb, 16162306a36Sopenharmony_ci sizeof(struct xfs_dqblk)); 16262306a36Sopenharmony_ci xfs_alert(mp, 16362306a36Sopenharmony_ci "Metadata corruption detected at %pS, dquot 0x%x", 16462306a36Sopenharmony_ci fa, dq_f->qlf_id); 16562306a36Sopenharmony_ci error = -EFSCORRUPTED; 16662306a36Sopenharmony_ci goto out_release; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci ASSERT(dq_f->qlf_size == 2); 17062306a36Sopenharmony_ci ASSERT(bp->b_mount == mp); 17162306a36Sopenharmony_ci bp->b_flags |= _XBF_LOGRECOVERY; 17262306a36Sopenharmony_ci xfs_buf_delwri_queue(bp, buffer_list); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciout_release: 17562306a36Sopenharmony_ci xfs_buf_relse(bp); 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciconst struct xlog_recover_item_ops xlog_dquot_item_ops = { 18062306a36Sopenharmony_ci .item_type = XFS_LI_DQUOT, 18162306a36Sopenharmony_ci .ra_pass2 = xlog_recover_dquot_ra_pass2, 18262306a36Sopenharmony_ci .commit_pass2 = xlog_recover_dquot_commit_pass2, 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * Recover QUOTAOFF records. We simply make a note of it in the xlog 18762306a36Sopenharmony_ci * structure, so that we know not to do any dquot item or dquot buffer recovery, 18862306a36Sopenharmony_ci * of that type. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ciSTATIC int 19162306a36Sopenharmony_cixlog_recover_quotaoff_commit_pass1( 19262306a36Sopenharmony_ci struct xlog *log, 19362306a36Sopenharmony_ci struct xlog_recover_item *item) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct xfs_qoff_logformat *qoff_f = item->ri_buf[0].i_addr; 19662306a36Sopenharmony_ci ASSERT(qoff_f); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* 19962306a36Sopenharmony_ci * The logitem format's flag tells us if this was user quotaoff, 20062306a36Sopenharmony_ci * group/project quotaoff or both. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ci if (qoff_f->qf_flags & XFS_UQUOTA_ACCT) 20362306a36Sopenharmony_ci log->l_quotaoffs_flag |= XFS_DQTYPE_USER; 20462306a36Sopenharmony_ci if (qoff_f->qf_flags & XFS_PQUOTA_ACCT) 20562306a36Sopenharmony_ci log->l_quotaoffs_flag |= XFS_DQTYPE_PROJ; 20662306a36Sopenharmony_ci if (qoff_f->qf_flags & XFS_GQUOTA_ACCT) 20762306a36Sopenharmony_ci log->l_quotaoffs_flag |= XFS_DQTYPE_GROUP; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciconst struct xlog_recover_item_ops xlog_quotaoff_item_ops = { 21362306a36Sopenharmony_ci .item_type = XFS_LI_QUOTAOFF, 21462306a36Sopenharmony_ci .commit_pass1 = xlog_recover_quotaoff_commit_pass1, 21562306a36Sopenharmony_ci /* nothing to commit in pass2 */ 21662306a36Sopenharmony_ci}; 217