#include "xfs_platform.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_bit.h"
#include "xfs_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_log_format.h"
#include "xfs_trans.h"
#include "xfs_inode.h"
#include "xfs_quota.h"
#include "xfs_qm.h"
#include "xfs_bmap.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/quota.h"
#include "scrub/trace.h"
void
xchk_dqiter_init(
struct xchk_dqiter *cursor,
struct xfs_scrub *sc,
xfs_dqtype_t dqtype)
{
cursor->sc = sc;
cursor->bmap.br_startoff = NULLFILEOFF;
cursor->dqtype = dqtype & XFS_DQTYPE_REC_MASK;
cursor->quota_ip = xfs_quota_inode(sc->mp, cursor->dqtype);
cursor->id = 0;
}
STATIC int
xchk_dquot_iter_revalidate_bmap(
struct xchk_dqiter *cursor)
{
struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo;
struct xfs_ifork *ifp = xfs_ifork_ptr(cursor->quota_ip,
XFS_DATA_FORK);
xfs_fileoff_t fileoff;
xfs_dqid_t this_id = cursor->id;
int nmaps = 1;
int error;
fileoff = this_id / qi->qi_dqperchunk;
if (cursor->bmap.br_startoff != NULLFILEOFF &&
cursor->if_seq == ifp->if_seq &&
cursor->bmap.br_startoff + cursor->bmap.br_blockcount > fileoff)
return 0;
error = xfs_bmapi_read(cursor->quota_ip, fileoff,
XFS_MAX_FILEOFF - fileoff, &cursor->bmap, &nmaps, 0);
if (error)
return error;
if (!nmaps) {
ASSERT(nmaps > 0);
return -EFSCORRUPTED;
}
if (cursor->bmap.br_startoff > fileoff) {
ASSERT(cursor->bmap.br_startoff == fileoff);
return -EFSCORRUPTED;
}
cursor->if_seq = ifp->if_seq;
trace_xchk_dquot_iter_revalidate_bmap(cursor, cursor->id);
return 0;
}
STATIC int
xchk_dquot_iter_advance_bmap(
struct xchk_dqiter *cursor,
uint64_t *next_ondisk_id)
{
struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo;
struct xfs_ifork *ifp = xfs_ifork_ptr(cursor->quota_ip,
XFS_DATA_FORK);
xfs_fileoff_t fileoff;
uint64_t next_id;
int nmaps = 1;
int error;
do {
fileoff = cursor->bmap.br_startoff + cursor->bmap.br_blockcount;
if (fileoff > XFS_DQ_ID_MAX / qi->qi_dqperchunk) {
*next_ondisk_id = -1ULL;
return 0;
}
error = xfs_bmapi_read(cursor->quota_ip, fileoff,
XFS_MAX_FILEOFF - fileoff, &cursor->bmap,
&nmaps, 0);
if (error)
return error;
if (!nmaps) {
*next_ondisk_id = -1ULL;
return 0;
}
if (cursor->bmap.br_startoff > fileoff) {
ASSERT(cursor->bmap.br_startoff == fileoff);
return -EFSCORRUPTED;
}
} while (!xfs_bmap_is_real_extent(&cursor->bmap));
next_id = cursor->bmap.br_startoff * qi->qi_dqperchunk;
if (next_id > XFS_DQ_ID_MAX) {
*next_ondisk_id = -1ULL;
return 0;
}
*next_ondisk_id = next_id;
cursor->if_seq = ifp->if_seq;
trace_xchk_dquot_iter_advance_bmap(cursor, *next_ondisk_id);
return 0;
}
STATIC void
xchk_dquot_iter_advance_incore(
struct xchk_dqiter *cursor,
uint64_t *next_incore_id)
{
struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo;
struct radix_tree_root *tree = xfs_dquot_tree(qi, cursor->dqtype);
struct xfs_dquot *dq;
unsigned int nr_found;
*next_incore_id = -1ULL;
mutex_lock(&qi->qi_tree_lock);
nr_found = radix_tree_gang_lookup(tree, (void **)&dq, cursor->id, 1);
if (nr_found)
*next_incore_id = dq->q_id;
mutex_unlock(&qi->qi_tree_lock);
trace_xchk_dquot_iter_advance_incore(cursor, *next_incore_id);
}
int
xchk_dquot_iter(
struct xchk_dqiter *cursor,
struct xfs_dquot **dqpp)
{
struct xfs_mount *mp = cursor->sc->mp;
struct xfs_dquot *dq = NULL;
uint64_t next_ondisk, next_incore = -1ULL;
unsigned int lock_mode;
int error = 0;
if (cursor->id > XFS_DQ_ID_MAX)
return 0;
next_ondisk = cursor->id;
lock_mode = xfs_ilock_data_map_shared(cursor->quota_ip);
error = xchk_dquot_iter_revalidate_bmap(cursor);
if (!error && !xfs_bmap_is_real_extent(&cursor->bmap))
error = xchk_dquot_iter_advance_bmap(cursor, &next_ondisk);
xfs_iunlock(cursor->quota_ip, lock_mode);
if (error)
return error;
if (next_ondisk > cursor->id)
xchk_dquot_iter_advance_incore(cursor, &next_incore);
cursor->id = min(next_ondisk, next_incore);
if (cursor->id > XFS_DQ_ID_MAX)
return 0;
trace_xchk_dquot_iter(cursor, cursor->id);
error = xfs_qm_dqget(mp, cursor->id, cursor->dqtype, false, &dq);
if (error)
return error;
cursor->id = dq->q_id + 1;
*dqpp = dq;
return 1;
}