10b61f8a4SDave Chinner // SPDX-License-Identifier: GPL-2.0
2c59d87c4SChristoph Hellwig /*
3c59d87c4SChristoph Hellwig * Copyright (c) 2008, Christoph Hellwig
4c59d87c4SChristoph Hellwig * All Rights Reserved.
5c59d87c4SChristoph Hellwig */
6c59d87c4SChristoph Hellwig #include "xfs.h"
75467b34bSDarrick J. Wong #include "xfs_shared.h"
86ca1c906SDave Chinner #include "xfs_format.h"
9239880efSDave Chinner #include "xfs_log_format.h"
107fd36c44SDave Chinner #include "xfs_trans_resv.h"
11c59d87c4SChristoph Hellwig #include "xfs_mount.h"
12a4fbe6abSDave Chinner #include "xfs_inode.h"
13c59d87c4SChristoph Hellwig #include "xfs_quota.h"
14c59d87c4SChristoph Hellwig #include "xfs_trans.h"
155d3684c2SJan Kara #include "xfs_icache.h"
16c59d87c4SChristoph Hellwig #include "xfs_qm.h"
17c59d87c4SChristoph Hellwig
18c59d87c4SChristoph Hellwig
195d3684c2SJan Kara static void
xfs_qm_fill_state(struct qc_type_state * tstate,struct xfs_mount * mp,struct xfs_inode * ip,xfs_ino_t ino,struct xfs_def_quota * defq)205d3684c2SJan Kara xfs_qm_fill_state(
215d3684c2SJan Kara struct qc_type_state *tstate,
225d3684c2SJan Kara struct xfs_mount *mp,
235d3684c2SJan Kara struct xfs_inode *ip,
24e850301fSEric Sandeen xfs_ino_t ino,
25e850301fSEric Sandeen struct xfs_def_quota *defq)
265d3684c2SJan Kara {
275d3684c2SJan Kara bool tempqip = false;
285d3684c2SJan Kara
295d3684c2SJan Kara tstate->ino = ino;
305d3684c2SJan Kara if (!ip && ino == NULLFSINO)
315d3684c2SJan Kara return;
325d3684c2SJan Kara if (!ip) {
335d3684c2SJan Kara if (xfs_iget(mp, NULL, ino, 0, 0, &ip))
345d3684c2SJan Kara return;
355d3684c2SJan Kara tempqip = true;
365d3684c2SJan Kara }
375d3684c2SJan Kara tstate->flags |= QCI_SYSFILE;
386e73a545SChristoph Hellwig tstate->blocks = ip->i_nblocks;
39daf83964SChristoph Hellwig tstate->nextents = ip->i_df.if_nextents;
40438769e3SDarrick J. Wong tstate->spc_timelimit = (u32)defq->blk.time;
41438769e3SDarrick J. Wong tstate->ino_timelimit = (u32)defq->ino.time;
42438769e3SDarrick J. Wong tstate->rt_spc_timelimit = (u32)defq->rtb.time;
435cc21e52SCatherine Hoang tstate->spc_warnlimit = 0;
445cc21e52SCatherine Hoang tstate->ino_warnlimit = 0;
455cc21e52SCatherine Hoang tstate->rt_spc_warnlimit = 0;
465d3684c2SJan Kara if (tempqip)
4744a8736bSDarrick J. Wong xfs_irele(ip);
485d3684c2SJan Kara }
495d3684c2SJan Kara
505d3684c2SJan Kara /*
515d3684c2SJan Kara * Return quota status information, such as enforcements, quota file inode
525d3684c2SJan Kara * numbers etc.
535d3684c2SJan Kara */
545d3684c2SJan Kara static int
xfs_fs_get_quota_state(struct super_block * sb,struct qc_state * state)555d3684c2SJan Kara xfs_fs_get_quota_state(
565d3684c2SJan Kara struct super_block *sb,
575d3684c2SJan Kara struct qc_state *state)
585d3684c2SJan Kara {
595d3684c2SJan Kara struct xfs_mount *mp = XFS_M(sb);
605d3684c2SJan Kara struct xfs_quotainfo *q = mp->m_quotainfo;
615d3684c2SJan Kara
625d3684c2SJan Kara memset(state, 0, sizeof(*state));
63149e53afSChristoph Hellwig if (!XFS_IS_QUOTA_ON(mp))
645d3684c2SJan Kara return 0;
655d3684c2SJan Kara state->s_incoredqs = q->qi_dquots;
66149e53afSChristoph Hellwig if (XFS_IS_UQUOTA_ON(mp))
675d3684c2SJan Kara state->s_state[USRQUOTA].flags |= QCI_ACCT_ENABLED;
685d3684c2SJan Kara if (XFS_IS_UQUOTA_ENFORCED(mp))
695d3684c2SJan Kara state->s_state[USRQUOTA].flags |= QCI_LIMITS_ENFORCED;
70149e53afSChristoph Hellwig if (XFS_IS_GQUOTA_ON(mp))
715d3684c2SJan Kara state->s_state[GRPQUOTA].flags |= QCI_ACCT_ENABLED;
725d3684c2SJan Kara if (XFS_IS_GQUOTA_ENFORCED(mp))
735d3684c2SJan Kara state->s_state[GRPQUOTA].flags |= QCI_LIMITS_ENFORCED;
74149e53afSChristoph Hellwig if (XFS_IS_PQUOTA_ON(mp))
755d3684c2SJan Kara state->s_state[PRJQUOTA].flags |= QCI_ACCT_ENABLED;
765d3684c2SJan Kara if (XFS_IS_PQUOTA_ENFORCED(mp))
775d3684c2SJan Kara state->s_state[PRJQUOTA].flags |= QCI_LIMITS_ENFORCED;
785d3684c2SJan Kara
795d3684c2SJan Kara xfs_qm_fill_state(&state->s_state[USRQUOTA], mp, q->qi_uquotaip,
80e850301fSEric Sandeen mp->m_sb.sb_uquotino, &q->qi_usr_default);
815d3684c2SJan Kara xfs_qm_fill_state(&state->s_state[GRPQUOTA], mp, q->qi_gquotaip,
82e850301fSEric Sandeen mp->m_sb.sb_gquotino, &q->qi_grp_default);
835d3684c2SJan Kara xfs_qm_fill_state(&state->s_state[PRJQUOTA], mp, q->qi_pquotaip,
84e850301fSEric Sandeen mp->m_sb.sb_pquotino, &q->qi_prj_default);
855d3684c2SJan Kara return 0;
865d3684c2SJan Kara }
875d3684c2SJan Kara
881a7ed271SDarrick J. Wong STATIC xfs_dqtype_t
xfs_quota_type(int type)89c59d87c4SChristoph Hellwig xfs_quota_type(int type)
90c59d87c4SChristoph Hellwig {
91c59d87c4SChristoph Hellwig switch (type) {
92c59d87c4SChristoph Hellwig case USRQUOTA:
938cd4901dSDarrick J. Wong return XFS_DQTYPE_USER;
94c59d87c4SChristoph Hellwig case GRPQUOTA:
958cd4901dSDarrick J. Wong return XFS_DQTYPE_GROUP;
96c59d87c4SChristoph Hellwig default:
978cd4901dSDarrick J. Wong return XFS_DQTYPE_PROJ;
98c59d87c4SChristoph Hellwig }
99c59d87c4SChristoph Hellwig }
100c59d87c4SChristoph Hellwig
101*5349b2afSCatherine Hoang #define XFS_QC_SETINFO_MASK (QC_TIMER_MASK)
102c14cad9eSJan Kara
103c14cad9eSJan Kara /*
104c14cad9eSJan Kara * Adjust quota timers & warnings
105c14cad9eSJan Kara */
106c14cad9eSJan Kara static int
xfs_fs_set_info(struct super_block * sb,int type,struct qc_info * info)107c14cad9eSJan Kara xfs_fs_set_info(
108c14cad9eSJan Kara struct super_block *sb,
109c14cad9eSJan Kara int type,
110c14cad9eSJan Kara struct qc_info *info)
111c14cad9eSJan Kara {
112c14cad9eSJan Kara struct xfs_mount *mp = XFS_M(sb);
113c14cad9eSJan Kara struct qc_dqblk newlim;
114c14cad9eSJan Kara
115bc98a42cSDavid Howells if (sb_rdonly(sb))
116c14cad9eSJan Kara return -EROFS;
117c14cad9eSJan Kara if (!XFS_IS_QUOTA_ON(mp))
118149e53afSChristoph Hellwig return -ENOSYS;
119c14cad9eSJan Kara if (info->i_fieldmask & ~XFS_QC_SETINFO_MASK)
120c14cad9eSJan Kara return -EINVAL;
121c14cad9eSJan Kara if ((info->i_fieldmask & XFS_QC_SETINFO_MASK) == 0)
122c14cad9eSJan Kara return 0;
123c14cad9eSJan Kara
124c14cad9eSJan Kara newlim.d_fieldmask = info->i_fieldmask;
125c14cad9eSJan Kara newlim.d_spc_timer = info->i_spc_timelimit;
126c14cad9eSJan Kara newlim.d_ino_timer = info->i_ino_timelimit;
127c14cad9eSJan Kara newlim.d_rt_spc_timer = info->i_rt_spc_timelimit;
128c14cad9eSJan Kara newlim.d_ino_warns = info->i_ino_warnlimit;
129c14cad9eSJan Kara newlim.d_spc_warns = info->i_spc_warnlimit;
130c14cad9eSJan Kara newlim.d_rt_spc_warns = info->i_rt_spc_warnlimit;
131c14cad9eSJan Kara
132c14cad9eSJan Kara return xfs_qm_scall_setqlim(mp, 0, xfs_quota_type(type), &newlim);
133c14cad9eSJan Kara }
134c14cad9eSJan Kara
13538e478c4SJan Kara static unsigned int
xfs_quota_flags(unsigned int uflags)13638e478c4SJan Kara xfs_quota_flags(unsigned int uflags)
137c59d87c4SChristoph Hellwig {
138c59d87c4SChristoph Hellwig unsigned int flags = 0;
139c59d87c4SChristoph Hellwig
140c59d87c4SChristoph Hellwig if (uflags & FS_QUOTA_UDQ_ACCT)
141c59d87c4SChristoph Hellwig flags |= XFS_UQUOTA_ACCT;
142c59d87c4SChristoph Hellwig if (uflags & FS_QUOTA_PDQ_ACCT)
143c59d87c4SChristoph Hellwig flags |= XFS_PQUOTA_ACCT;
144c59d87c4SChristoph Hellwig if (uflags & FS_QUOTA_GDQ_ACCT)
145c59d87c4SChristoph Hellwig flags |= XFS_GQUOTA_ACCT;
146c59d87c4SChristoph Hellwig if (uflags & FS_QUOTA_UDQ_ENFD)
147c59d87c4SChristoph Hellwig flags |= XFS_UQUOTA_ENFD;
14883e782e1SChandra Seetharaman if (uflags & FS_QUOTA_GDQ_ENFD)
14983e782e1SChandra Seetharaman flags |= XFS_GQUOTA_ENFD;
15083e782e1SChandra Seetharaman if (uflags & FS_QUOTA_PDQ_ENFD)
15183e782e1SChandra Seetharaman flags |= XFS_PQUOTA_ENFD;
152c59d87c4SChristoph Hellwig
15338e478c4SJan Kara return flags;
154c59d87c4SChristoph Hellwig }
155c59d87c4SChristoph Hellwig
15638e478c4SJan Kara STATIC int
xfs_quota_enable(struct super_block * sb,unsigned int uflags)15738e478c4SJan Kara xfs_quota_enable(
15838e478c4SJan Kara struct super_block *sb,
15938e478c4SJan Kara unsigned int uflags)
16038e478c4SJan Kara {
16138e478c4SJan Kara struct xfs_mount *mp = XFS_M(sb);
16238e478c4SJan Kara
163bc98a42cSDavid Howells if (sb_rdonly(sb))
16438e478c4SJan Kara return -EROFS;
165149e53afSChristoph Hellwig if (!XFS_IS_QUOTA_ON(mp))
16638e478c4SJan Kara return -ENOSYS;
16738e478c4SJan Kara
16838e478c4SJan Kara return xfs_qm_scall_quotaon(mp, xfs_quota_flags(uflags));
16938e478c4SJan Kara }
17038e478c4SJan Kara
17138e478c4SJan Kara STATIC int
xfs_quota_disable(struct super_block * sb,unsigned int uflags)17238e478c4SJan Kara xfs_quota_disable(
17338e478c4SJan Kara struct super_block *sb,
17438e478c4SJan Kara unsigned int uflags)
17538e478c4SJan Kara {
17638e478c4SJan Kara struct xfs_mount *mp = XFS_M(sb);
17738e478c4SJan Kara
178bc98a42cSDavid Howells if (sb_rdonly(sb))
17938e478c4SJan Kara return -EROFS;
18038e478c4SJan Kara if (!XFS_IS_QUOTA_ON(mp))
181149e53afSChristoph Hellwig return -ENOSYS;
18238e478c4SJan Kara
18338e478c4SJan Kara return xfs_qm_scall_quotaoff(mp, xfs_quota_flags(uflags));
184c59d87c4SChristoph Hellwig }
185c59d87c4SChristoph Hellwig
186c59d87c4SChristoph Hellwig STATIC int
xfs_fs_rm_xquota(struct super_block * sb,unsigned int uflags)1879da93f9bSEric Sandeen xfs_fs_rm_xquota(
1889da93f9bSEric Sandeen struct super_block *sb,
1899da93f9bSEric Sandeen unsigned int uflags)
1909da93f9bSEric Sandeen {
1919da93f9bSEric Sandeen struct xfs_mount *mp = XFS_M(sb);
1929da93f9bSEric Sandeen unsigned int flags = 0;
1939da93f9bSEric Sandeen
194bc98a42cSDavid Howells if (sb_rdonly(sb))
1959da93f9bSEric Sandeen return -EROFS;
1969da93f9bSEric Sandeen
1979da93f9bSEric Sandeen if (XFS_IS_QUOTA_ON(mp))
1989da93f9bSEric Sandeen return -EINVAL;
1999da93f9bSEric Sandeen
2003dd4d40bSJan Kara if (uflags & ~(FS_USER_QUOTA | FS_GROUP_QUOTA | FS_PROJ_QUOTA))
2013dd4d40bSJan Kara return -EINVAL;
2023dd4d40bSJan Kara
2039da93f9bSEric Sandeen if (uflags & FS_USER_QUOTA)
20441ed4a5fSDarrick J. Wong flags |= XFS_QMOPT_UQUOTA;
2059da93f9bSEric Sandeen if (uflags & FS_GROUP_QUOTA)
20641ed4a5fSDarrick J. Wong flags |= XFS_QMOPT_GQUOTA;
20774dc93a9SJie Liu if (uflags & FS_PROJ_QUOTA)
20841ed4a5fSDarrick J. Wong flags |= XFS_QMOPT_PQUOTA;
2099da93f9bSEric Sandeen
2102451337dSDave Chinner return xfs_qm_scall_trunc_qfiles(mp, flags);
2119da93f9bSEric Sandeen }
2129da93f9bSEric Sandeen
2139da93f9bSEric Sandeen STATIC int
xfs_fs_get_dqblk(struct super_block * sb,struct kqid qid,struct qc_dqblk * qdq)214c59d87c4SChristoph Hellwig xfs_fs_get_dqblk(
215c59d87c4SChristoph Hellwig struct super_block *sb,
21674a8a103SEric W. Biederman struct kqid qid,
21714bf61ffSJan Kara struct qc_dqblk *qdq)
218c59d87c4SChristoph Hellwig {
219c59d87c4SChristoph Hellwig struct xfs_mount *mp = XFS_M(sb);
220296c24e2SEric Sandeen xfs_dqid_t id;
221c59d87c4SChristoph Hellwig
222c59d87c4SChristoph Hellwig if (!XFS_IS_QUOTA_ON(mp))
223149e53afSChristoph Hellwig return -ENOSYS;
224c59d87c4SChristoph Hellwig
225296c24e2SEric Sandeen id = from_kqid(&init_user_ns, qid);
2262e330e76SDarrick J. Wong return xfs_qm_scall_getquota(mp, id, xfs_quota_type(qid.type), qdq);
227296c24e2SEric Sandeen }
228296c24e2SEric Sandeen
229296c24e2SEric Sandeen /* Return quota info for active quota >= this qid */
230296c24e2SEric Sandeen STATIC int
xfs_fs_get_nextdqblk(struct super_block * sb,struct kqid * qid,struct qc_dqblk * qdq)231296c24e2SEric Sandeen xfs_fs_get_nextdqblk(
232296c24e2SEric Sandeen struct super_block *sb,
233296c24e2SEric Sandeen struct kqid *qid,
234296c24e2SEric Sandeen struct qc_dqblk *qdq)
235296c24e2SEric Sandeen {
236296c24e2SEric Sandeen int ret;
237296c24e2SEric Sandeen struct xfs_mount *mp = XFS_M(sb);
238296c24e2SEric Sandeen xfs_dqid_t id;
239296c24e2SEric Sandeen
240296c24e2SEric Sandeen if (!XFS_IS_QUOTA_ON(mp))
241149e53afSChristoph Hellwig return -ENOSYS;
242296c24e2SEric Sandeen
243296c24e2SEric Sandeen id = from_kqid(&init_user_ns, *qid);
2442e330e76SDarrick J. Wong ret = xfs_qm_scall_getquota_next(mp, &id, xfs_quota_type(qid->type),
2452e330e76SDarrick J. Wong qdq);
246296c24e2SEric Sandeen if (ret)
247296c24e2SEric Sandeen return ret;
248296c24e2SEric Sandeen
249296c24e2SEric Sandeen /* ID may be different, so convert back what we got */
250296c24e2SEric Sandeen *qid = make_kqid(current_user_ns(), qid->type, id);
251296c24e2SEric Sandeen return 0;
252c59d87c4SChristoph Hellwig }
253c59d87c4SChristoph Hellwig
254c59d87c4SChristoph Hellwig STATIC int
xfs_fs_set_dqblk(struct super_block * sb,struct kqid qid,struct qc_dqblk * qdq)255c59d87c4SChristoph Hellwig xfs_fs_set_dqblk(
256c59d87c4SChristoph Hellwig struct super_block *sb,
25774a8a103SEric W. Biederman struct kqid qid,
25814bf61ffSJan Kara struct qc_dqblk *qdq)
259c59d87c4SChristoph Hellwig {
260c59d87c4SChristoph Hellwig struct xfs_mount *mp = XFS_M(sb);
261c59d87c4SChristoph Hellwig
262bc98a42cSDavid Howells if (sb_rdonly(sb))
263c59d87c4SChristoph Hellwig return -EROFS;
264c59d87c4SChristoph Hellwig if (!XFS_IS_QUOTA_ON(mp))
265149e53afSChristoph Hellwig return -ENOSYS;
266c59d87c4SChristoph Hellwig
2672451337dSDave Chinner return xfs_qm_scall_setqlim(mp, from_kqid(&init_user_ns, qid),
26814bf61ffSJan Kara xfs_quota_type(qid.type), qdq);
269c59d87c4SChristoph Hellwig }
270c59d87c4SChristoph Hellwig
271c59d87c4SChristoph Hellwig const struct quotactl_ops xfs_quotactl_operations = {
2725d3684c2SJan Kara .get_state = xfs_fs_get_quota_state,
273c14cad9eSJan Kara .set_info = xfs_fs_set_info,
27438e478c4SJan Kara .quota_enable = xfs_quota_enable,
27538e478c4SJan Kara .quota_disable = xfs_quota_disable,
2769da93f9bSEric Sandeen .rm_xquota = xfs_fs_rm_xquota,
277c59d87c4SChristoph Hellwig .get_dqblk = xfs_fs_get_dqblk,
278296c24e2SEric Sandeen .get_nextdqblk = xfs_fs_get_nextdqblk,
279c59d87c4SChristoph Hellwig .set_dqblk = xfs_fs_set_dqblk,
280c59d87c4SChristoph Hellwig };
281