162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2008, Christoph Hellwig
462306a36Sopenharmony_ci * All Rights Reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include "xfs.h"
762306a36Sopenharmony_ci#include "xfs_shared.h"
862306a36Sopenharmony_ci#include "xfs_format.h"
962306a36Sopenharmony_ci#include "xfs_log_format.h"
1062306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1162306a36Sopenharmony_ci#include "xfs_mount.h"
1262306a36Sopenharmony_ci#include "xfs_inode.h"
1362306a36Sopenharmony_ci#include "xfs_quota.h"
1462306a36Sopenharmony_ci#include "xfs_trans.h"
1562306a36Sopenharmony_ci#include "xfs_icache.h"
1662306a36Sopenharmony_ci#include "xfs_qm.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic void
2062306a36Sopenharmony_cixfs_qm_fill_state(
2162306a36Sopenharmony_ci	struct qc_type_state	*tstate,
2262306a36Sopenharmony_ci	struct xfs_mount	*mp,
2362306a36Sopenharmony_ci	struct xfs_inode	*ip,
2462306a36Sopenharmony_ci	xfs_ino_t		ino,
2562306a36Sopenharmony_ci	struct xfs_def_quota	*defq)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	bool			tempqip = false;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	tstate->ino = ino;
3062306a36Sopenharmony_ci	if (!ip && ino == NULLFSINO)
3162306a36Sopenharmony_ci		return;
3262306a36Sopenharmony_ci	if (!ip) {
3362306a36Sopenharmony_ci		if (xfs_iget(mp, NULL, ino, 0, 0, &ip))
3462306a36Sopenharmony_ci			return;
3562306a36Sopenharmony_ci		tempqip = true;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci	tstate->flags |= QCI_SYSFILE;
3862306a36Sopenharmony_ci	tstate->blocks = ip->i_nblocks;
3962306a36Sopenharmony_ci	tstate->nextents = ip->i_df.if_nextents;
4062306a36Sopenharmony_ci	tstate->spc_timelimit = (u32)defq->blk.time;
4162306a36Sopenharmony_ci	tstate->ino_timelimit = (u32)defq->ino.time;
4262306a36Sopenharmony_ci	tstate->rt_spc_timelimit = (u32)defq->rtb.time;
4362306a36Sopenharmony_ci	tstate->spc_warnlimit = 0;
4462306a36Sopenharmony_ci	tstate->ino_warnlimit = 0;
4562306a36Sopenharmony_ci	tstate->rt_spc_warnlimit = 0;
4662306a36Sopenharmony_ci	if (tempqip)
4762306a36Sopenharmony_ci		xfs_irele(ip);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * Return quota status information, such as enforcements, quota file inode
5262306a36Sopenharmony_ci * numbers etc.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic int
5562306a36Sopenharmony_cixfs_fs_get_quota_state(
5662306a36Sopenharmony_ci	struct super_block	*sb,
5762306a36Sopenharmony_ci	struct qc_state		*state)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct xfs_mount *mp = XFS_M(sb);
6062306a36Sopenharmony_ci	struct xfs_quotainfo *q = mp->m_quotainfo;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	memset(state, 0, sizeof(*state));
6362306a36Sopenharmony_ci	if (!XFS_IS_QUOTA_ON(mp))
6462306a36Sopenharmony_ci		return 0;
6562306a36Sopenharmony_ci	state->s_incoredqs = q->qi_dquots;
6662306a36Sopenharmony_ci	if (XFS_IS_UQUOTA_ON(mp))
6762306a36Sopenharmony_ci		state->s_state[USRQUOTA].flags |= QCI_ACCT_ENABLED;
6862306a36Sopenharmony_ci	if (XFS_IS_UQUOTA_ENFORCED(mp))
6962306a36Sopenharmony_ci		state->s_state[USRQUOTA].flags |= QCI_LIMITS_ENFORCED;
7062306a36Sopenharmony_ci	if (XFS_IS_GQUOTA_ON(mp))
7162306a36Sopenharmony_ci		state->s_state[GRPQUOTA].flags |= QCI_ACCT_ENABLED;
7262306a36Sopenharmony_ci	if (XFS_IS_GQUOTA_ENFORCED(mp))
7362306a36Sopenharmony_ci		state->s_state[GRPQUOTA].flags |= QCI_LIMITS_ENFORCED;
7462306a36Sopenharmony_ci	if (XFS_IS_PQUOTA_ON(mp))
7562306a36Sopenharmony_ci		state->s_state[PRJQUOTA].flags |= QCI_ACCT_ENABLED;
7662306a36Sopenharmony_ci	if (XFS_IS_PQUOTA_ENFORCED(mp))
7762306a36Sopenharmony_ci		state->s_state[PRJQUOTA].flags |= QCI_LIMITS_ENFORCED;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	xfs_qm_fill_state(&state->s_state[USRQUOTA], mp, q->qi_uquotaip,
8062306a36Sopenharmony_ci			  mp->m_sb.sb_uquotino, &q->qi_usr_default);
8162306a36Sopenharmony_ci	xfs_qm_fill_state(&state->s_state[GRPQUOTA], mp, q->qi_gquotaip,
8262306a36Sopenharmony_ci			  mp->m_sb.sb_gquotino, &q->qi_grp_default);
8362306a36Sopenharmony_ci	xfs_qm_fill_state(&state->s_state[PRJQUOTA], mp, q->qi_pquotaip,
8462306a36Sopenharmony_ci			  mp->m_sb.sb_pquotino, &q->qi_prj_default);
8562306a36Sopenharmony_ci	return 0;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ciSTATIC xfs_dqtype_t
8962306a36Sopenharmony_cixfs_quota_type(int type)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	switch (type) {
9262306a36Sopenharmony_ci	case USRQUOTA:
9362306a36Sopenharmony_ci		return XFS_DQTYPE_USER;
9462306a36Sopenharmony_ci	case GRPQUOTA:
9562306a36Sopenharmony_ci		return XFS_DQTYPE_GROUP;
9662306a36Sopenharmony_ci	default:
9762306a36Sopenharmony_ci		return XFS_DQTYPE_PROJ;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#define XFS_QC_SETINFO_MASK (QC_TIMER_MASK)
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/*
10462306a36Sopenharmony_ci * Adjust quota timers & warnings
10562306a36Sopenharmony_ci */
10662306a36Sopenharmony_cistatic int
10762306a36Sopenharmony_cixfs_fs_set_info(
10862306a36Sopenharmony_ci	struct super_block	*sb,
10962306a36Sopenharmony_ci	int			type,
11062306a36Sopenharmony_ci	struct qc_info		*info)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct xfs_mount	*mp = XFS_M(sb);
11362306a36Sopenharmony_ci	struct qc_dqblk		newlim;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (sb_rdonly(sb))
11662306a36Sopenharmony_ci		return -EROFS;
11762306a36Sopenharmony_ci	if (!XFS_IS_QUOTA_ON(mp))
11862306a36Sopenharmony_ci		return -ENOSYS;
11962306a36Sopenharmony_ci	if (info->i_fieldmask & ~XFS_QC_SETINFO_MASK)
12062306a36Sopenharmony_ci		return -EINVAL;
12162306a36Sopenharmony_ci	if ((info->i_fieldmask & XFS_QC_SETINFO_MASK) == 0)
12262306a36Sopenharmony_ci		return 0;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	newlim.d_fieldmask = info->i_fieldmask;
12562306a36Sopenharmony_ci	newlim.d_spc_timer = info->i_spc_timelimit;
12662306a36Sopenharmony_ci	newlim.d_ino_timer = info->i_ino_timelimit;
12762306a36Sopenharmony_ci	newlim.d_rt_spc_timer = info->i_rt_spc_timelimit;
12862306a36Sopenharmony_ci	newlim.d_ino_warns = info->i_ino_warnlimit;
12962306a36Sopenharmony_ci	newlim.d_spc_warns = info->i_spc_warnlimit;
13062306a36Sopenharmony_ci	newlim.d_rt_spc_warns = info->i_rt_spc_warnlimit;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return xfs_qm_scall_setqlim(mp, 0, xfs_quota_type(type), &newlim);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic unsigned int
13662306a36Sopenharmony_cixfs_quota_flags(unsigned int uflags)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	unsigned int flags = 0;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (uflags & FS_QUOTA_UDQ_ACCT)
14162306a36Sopenharmony_ci		flags |= XFS_UQUOTA_ACCT;
14262306a36Sopenharmony_ci	if (uflags & FS_QUOTA_PDQ_ACCT)
14362306a36Sopenharmony_ci		flags |= XFS_PQUOTA_ACCT;
14462306a36Sopenharmony_ci	if (uflags & FS_QUOTA_GDQ_ACCT)
14562306a36Sopenharmony_ci		flags |= XFS_GQUOTA_ACCT;
14662306a36Sopenharmony_ci	if (uflags & FS_QUOTA_UDQ_ENFD)
14762306a36Sopenharmony_ci		flags |= XFS_UQUOTA_ENFD;
14862306a36Sopenharmony_ci	if (uflags & FS_QUOTA_GDQ_ENFD)
14962306a36Sopenharmony_ci		flags |= XFS_GQUOTA_ENFD;
15062306a36Sopenharmony_ci	if (uflags & FS_QUOTA_PDQ_ENFD)
15162306a36Sopenharmony_ci		flags |= XFS_PQUOTA_ENFD;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return flags;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ciSTATIC int
15762306a36Sopenharmony_cixfs_quota_enable(
15862306a36Sopenharmony_ci	struct super_block	*sb,
15962306a36Sopenharmony_ci	unsigned int		uflags)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	struct xfs_mount	*mp = XFS_M(sb);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (sb_rdonly(sb))
16462306a36Sopenharmony_ci		return -EROFS;
16562306a36Sopenharmony_ci	if (!XFS_IS_QUOTA_ON(mp))
16662306a36Sopenharmony_ci		return -ENOSYS;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return xfs_qm_scall_quotaon(mp, xfs_quota_flags(uflags));
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ciSTATIC int
17262306a36Sopenharmony_cixfs_quota_disable(
17362306a36Sopenharmony_ci	struct super_block	*sb,
17462306a36Sopenharmony_ci	unsigned int		uflags)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct xfs_mount	*mp = XFS_M(sb);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (sb_rdonly(sb))
17962306a36Sopenharmony_ci		return -EROFS;
18062306a36Sopenharmony_ci	if (!XFS_IS_QUOTA_ON(mp))
18162306a36Sopenharmony_ci		return -ENOSYS;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return xfs_qm_scall_quotaoff(mp, xfs_quota_flags(uflags));
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ciSTATIC int
18762306a36Sopenharmony_cixfs_fs_rm_xquota(
18862306a36Sopenharmony_ci	struct super_block	*sb,
18962306a36Sopenharmony_ci	unsigned int		uflags)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct xfs_mount	*mp = XFS_M(sb);
19262306a36Sopenharmony_ci	unsigned int		flags = 0;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (sb_rdonly(sb))
19562306a36Sopenharmony_ci		return -EROFS;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (XFS_IS_QUOTA_ON(mp))
19862306a36Sopenharmony_ci		return -EINVAL;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (uflags & ~(FS_USER_QUOTA | FS_GROUP_QUOTA | FS_PROJ_QUOTA))
20162306a36Sopenharmony_ci		return -EINVAL;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (uflags & FS_USER_QUOTA)
20462306a36Sopenharmony_ci		flags |= XFS_QMOPT_UQUOTA;
20562306a36Sopenharmony_ci	if (uflags & FS_GROUP_QUOTA)
20662306a36Sopenharmony_ci		flags |= XFS_QMOPT_GQUOTA;
20762306a36Sopenharmony_ci	if (uflags & FS_PROJ_QUOTA)
20862306a36Sopenharmony_ci		flags |= XFS_QMOPT_PQUOTA;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return xfs_qm_scall_trunc_qfiles(mp, flags);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ciSTATIC int
21462306a36Sopenharmony_cixfs_fs_get_dqblk(
21562306a36Sopenharmony_ci	struct super_block	*sb,
21662306a36Sopenharmony_ci	struct kqid		qid,
21762306a36Sopenharmony_ci	struct qc_dqblk		*qdq)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct xfs_mount	*mp = XFS_M(sb);
22062306a36Sopenharmony_ci	xfs_dqid_t		id;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (!XFS_IS_QUOTA_ON(mp))
22362306a36Sopenharmony_ci		return -ENOSYS;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	id = from_kqid(&init_user_ns, qid);
22662306a36Sopenharmony_ci	return xfs_qm_scall_getquota(mp, id, xfs_quota_type(qid.type), qdq);
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci/* Return quota info for active quota >= this qid */
23062306a36Sopenharmony_ciSTATIC int
23162306a36Sopenharmony_cixfs_fs_get_nextdqblk(
23262306a36Sopenharmony_ci	struct super_block	*sb,
23362306a36Sopenharmony_ci	struct kqid		*qid,
23462306a36Sopenharmony_ci	struct qc_dqblk		*qdq)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	int			ret;
23762306a36Sopenharmony_ci	struct xfs_mount	*mp = XFS_M(sb);
23862306a36Sopenharmony_ci	xfs_dqid_t		id;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (!XFS_IS_QUOTA_ON(mp))
24162306a36Sopenharmony_ci		return -ENOSYS;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	id = from_kqid(&init_user_ns, *qid);
24462306a36Sopenharmony_ci	ret = xfs_qm_scall_getquota_next(mp, &id, xfs_quota_type(qid->type),
24562306a36Sopenharmony_ci			qdq);
24662306a36Sopenharmony_ci	if (ret)
24762306a36Sopenharmony_ci		return ret;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/* ID may be different, so convert back what we got */
25062306a36Sopenharmony_ci	*qid = make_kqid(current_user_ns(), qid->type, id);
25162306a36Sopenharmony_ci	return 0;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ciSTATIC int
25562306a36Sopenharmony_cixfs_fs_set_dqblk(
25662306a36Sopenharmony_ci	struct super_block	*sb,
25762306a36Sopenharmony_ci	struct kqid		qid,
25862306a36Sopenharmony_ci	struct qc_dqblk		*qdq)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct xfs_mount	*mp = XFS_M(sb);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (sb_rdonly(sb))
26362306a36Sopenharmony_ci		return -EROFS;
26462306a36Sopenharmony_ci	if (!XFS_IS_QUOTA_ON(mp))
26562306a36Sopenharmony_ci		return -ENOSYS;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return xfs_qm_scall_setqlim(mp, from_kqid(&init_user_ns, qid),
26862306a36Sopenharmony_ci				     xfs_quota_type(qid.type), qdq);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ciconst struct quotactl_ops xfs_quotactl_operations = {
27262306a36Sopenharmony_ci	.get_state		= xfs_fs_get_quota_state,
27362306a36Sopenharmony_ci	.set_info		= xfs_fs_set_info,
27462306a36Sopenharmony_ci	.quota_enable		= xfs_quota_enable,
27562306a36Sopenharmony_ci	.quota_disable		= xfs_quota_disable,
27662306a36Sopenharmony_ci	.rm_xquota		= xfs_fs_rm_xquota,
27762306a36Sopenharmony_ci	.get_dqblk		= xfs_fs_get_dqblk,
27862306a36Sopenharmony_ci	.get_nextdqblk		= xfs_fs_get_nextdqblk,
27962306a36Sopenharmony_ci	.set_dqblk		= xfs_fs_set_dqblk,
28062306a36Sopenharmony_ci};
281