18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Quota code necessary even when VFS quota support is not compiled
48c2ecf20Sopenharmony_ci * into the kernel.  The interesting stuff is over in dquot.c, here
58c2ecf20Sopenharmony_ci * we have symbols for initial quotactl(2) handling, the sysctl(2)
68c2ecf20Sopenharmony_ci * variables, etc - things needed even when quota support disabled.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/fs.h>
108c2ecf20Sopenharmony_ci#include <linux/namei.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <asm/current.h>
138c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/security.h>
168c2ecf20Sopenharmony_ci#include <linux/syscalls.h>
178c2ecf20Sopenharmony_ci#include <linux/capability.h>
188c2ecf20Sopenharmony_ci#include <linux/quotaops.h>
198c2ecf20Sopenharmony_ci#include <linux/types.h>
208c2ecf20Sopenharmony_ci#include <linux/writeback.h>
218c2ecf20Sopenharmony_ci#include <linux/nospec.h>
228c2ecf20Sopenharmony_ci#include "compat.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int check_quotactl_permission(struct super_block *sb, int type, int cmd,
258c2ecf20Sopenharmony_ci				     qid_t id)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	switch (cmd) {
288c2ecf20Sopenharmony_ci	/* these commands do not require any special privilegues */
298c2ecf20Sopenharmony_ci	case Q_GETFMT:
308c2ecf20Sopenharmony_ci	case Q_SYNC:
318c2ecf20Sopenharmony_ci	case Q_GETINFO:
328c2ecf20Sopenharmony_ci	case Q_XGETQSTAT:
338c2ecf20Sopenharmony_ci	case Q_XGETQSTATV:
348c2ecf20Sopenharmony_ci	case Q_XQUOTASYNC:
358c2ecf20Sopenharmony_ci		break;
368c2ecf20Sopenharmony_ci	/* allow to query information for dquots we "own" */
378c2ecf20Sopenharmony_ci	case Q_GETQUOTA:
388c2ecf20Sopenharmony_ci	case Q_XGETQUOTA:
398c2ecf20Sopenharmony_ci		if ((type == USRQUOTA && uid_eq(current_euid(), make_kuid(current_user_ns(), id))) ||
408c2ecf20Sopenharmony_ci		    (type == GRPQUOTA && in_egroup_p(make_kgid(current_user_ns(), id))))
418c2ecf20Sopenharmony_ci			break;
428c2ecf20Sopenharmony_ci		fallthrough;
438c2ecf20Sopenharmony_ci	default:
448c2ecf20Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN))
458c2ecf20Sopenharmony_ci			return -EPERM;
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return security_quotactl(cmd, type, id, sb);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void quota_sync_one(struct super_block *sb, void *arg)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	int type = *(int *)arg;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	if (sb->s_qcop && sb->s_qcop->quota_sync &&
568c2ecf20Sopenharmony_ci	    (sb->s_quota_types & (1 << type)))
578c2ecf20Sopenharmony_ci		sb->s_qcop->quota_sync(sb, type);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int quota_sync_all(int type)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	int ret;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ret = security_quotactl(Q_SYNC, type, 0, NULL);
658c2ecf20Sopenharmony_ci	if (!ret)
668c2ecf20Sopenharmony_ci		iterate_supers(quota_sync_one, &type);
678c2ecf20Sopenharmony_ci	return ret;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ciunsigned int qtype_enforce_flag(int type)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	switch (type) {
738c2ecf20Sopenharmony_ci	case USRQUOTA:
748c2ecf20Sopenharmony_ci		return FS_QUOTA_UDQ_ENFD;
758c2ecf20Sopenharmony_ci	case GRPQUOTA:
768c2ecf20Sopenharmony_ci		return FS_QUOTA_GDQ_ENFD;
778c2ecf20Sopenharmony_ci	case PRJQUOTA:
788c2ecf20Sopenharmony_ci		return FS_QUOTA_PDQ_ENFD;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci	return 0;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int quota_quotaon(struct super_block *sb, int type, qid_t id,
848c2ecf20Sopenharmony_ci		         const struct path *path)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	if (!sb->s_qcop->quota_on && !sb->s_qcop->quota_enable)
878c2ecf20Sopenharmony_ci		return -ENOSYS;
888c2ecf20Sopenharmony_ci	if (sb->s_qcop->quota_enable)
898c2ecf20Sopenharmony_ci		return sb->s_qcop->quota_enable(sb, qtype_enforce_flag(type));
908c2ecf20Sopenharmony_ci	if (IS_ERR(path))
918c2ecf20Sopenharmony_ci		return PTR_ERR(path);
928c2ecf20Sopenharmony_ci	return sb->s_qcop->quota_on(sb, type, id, path);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int quota_quotaoff(struct super_block *sb, int type)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	if (!sb->s_qcop->quota_off && !sb->s_qcop->quota_disable)
988c2ecf20Sopenharmony_ci		return -ENOSYS;
998c2ecf20Sopenharmony_ci	if (sb->s_qcop->quota_disable)
1008c2ecf20Sopenharmony_ci		return sb->s_qcop->quota_disable(sb, qtype_enforce_flag(type));
1018c2ecf20Sopenharmony_ci	return sb->s_qcop->quota_off(sb, type);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int quota_getfmt(struct super_block *sb, int type, void __user *addr)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	__u32 fmt;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (!sb_has_quota_active(sb, type))
1098c2ecf20Sopenharmony_ci		return -ESRCH;
1108c2ecf20Sopenharmony_ci	fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
1118c2ecf20Sopenharmony_ci	if (copy_to_user(addr, &fmt, sizeof(fmt)))
1128c2ecf20Sopenharmony_ci		return -EFAULT;
1138c2ecf20Sopenharmony_ci	return 0;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic int quota_getinfo(struct super_block *sb, int type, void __user *addr)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct qc_state state;
1198c2ecf20Sopenharmony_ci	struct qc_type_state *tstate;
1208c2ecf20Sopenharmony_ci	struct if_dqinfo uinfo;
1218c2ecf20Sopenharmony_ci	int ret;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (!sb->s_qcop->get_state)
1248c2ecf20Sopenharmony_ci		return -ENOSYS;
1258c2ecf20Sopenharmony_ci	ret = sb->s_qcop->get_state(sb, &state);
1268c2ecf20Sopenharmony_ci	if (ret)
1278c2ecf20Sopenharmony_ci		return ret;
1288c2ecf20Sopenharmony_ci	tstate = state.s_state + type;
1298c2ecf20Sopenharmony_ci	if (!(tstate->flags & QCI_ACCT_ENABLED))
1308c2ecf20Sopenharmony_ci		return -ESRCH;
1318c2ecf20Sopenharmony_ci	memset(&uinfo, 0, sizeof(uinfo));
1328c2ecf20Sopenharmony_ci	uinfo.dqi_bgrace = tstate->spc_timelimit;
1338c2ecf20Sopenharmony_ci	uinfo.dqi_igrace = tstate->ino_timelimit;
1348c2ecf20Sopenharmony_ci	if (tstate->flags & QCI_SYSFILE)
1358c2ecf20Sopenharmony_ci		uinfo.dqi_flags |= DQF_SYS_FILE;
1368c2ecf20Sopenharmony_ci	if (tstate->flags & QCI_ROOT_SQUASH)
1378c2ecf20Sopenharmony_ci		uinfo.dqi_flags |= DQF_ROOT_SQUASH;
1388c2ecf20Sopenharmony_ci	uinfo.dqi_valid = IIF_ALL;
1398c2ecf20Sopenharmony_ci	if (copy_to_user(addr, &uinfo, sizeof(uinfo)))
1408c2ecf20Sopenharmony_ci		return -EFAULT;
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int quota_setinfo(struct super_block *sb, int type, void __user *addr)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct if_dqinfo info;
1478c2ecf20Sopenharmony_ci	struct qc_info qinfo;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (copy_from_user(&info, addr, sizeof(info)))
1508c2ecf20Sopenharmony_ci		return -EFAULT;
1518c2ecf20Sopenharmony_ci	if (!sb->s_qcop->set_info)
1528c2ecf20Sopenharmony_ci		return -ENOSYS;
1538c2ecf20Sopenharmony_ci	if (info.dqi_valid & ~(IIF_FLAGS | IIF_BGRACE | IIF_IGRACE))
1548c2ecf20Sopenharmony_ci		return -EINVAL;
1558c2ecf20Sopenharmony_ci	memset(&qinfo, 0, sizeof(qinfo));
1568c2ecf20Sopenharmony_ci	if (info.dqi_valid & IIF_FLAGS) {
1578c2ecf20Sopenharmony_ci		if (info.dqi_flags & ~DQF_SETINFO_MASK)
1588c2ecf20Sopenharmony_ci			return -EINVAL;
1598c2ecf20Sopenharmony_ci		if (info.dqi_flags & DQF_ROOT_SQUASH)
1608c2ecf20Sopenharmony_ci			qinfo.i_flags |= QCI_ROOT_SQUASH;
1618c2ecf20Sopenharmony_ci		qinfo.i_fieldmask |= QC_FLAGS;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci	if (info.dqi_valid & IIF_BGRACE) {
1648c2ecf20Sopenharmony_ci		qinfo.i_spc_timelimit = info.dqi_bgrace;
1658c2ecf20Sopenharmony_ci		qinfo.i_fieldmask |= QC_SPC_TIMER;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci	if (info.dqi_valid & IIF_IGRACE) {
1688c2ecf20Sopenharmony_ci		qinfo.i_ino_timelimit = info.dqi_igrace;
1698c2ecf20Sopenharmony_ci		qinfo.i_fieldmask |= QC_INO_TIMER;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci	return sb->s_qcop->set_info(sb, type, &qinfo);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic inline qsize_t qbtos(qsize_t blocks)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	return blocks << QIF_DQBLKSIZE_BITS;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic inline qsize_t stoqb(qsize_t space)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	return (space + QIF_DQBLKSIZE - 1) >> QIF_DQBLKSIZE_BITS;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void copy_to_if_dqblk(struct if_dqblk *dst, struct qc_dqblk *src)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	memset(dst, 0, sizeof(*dst));
1878c2ecf20Sopenharmony_ci	dst->dqb_bhardlimit = stoqb(src->d_spc_hardlimit);
1888c2ecf20Sopenharmony_ci	dst->dqb_bsoftlimit = stoqb(src->d_spc_softlimit);
1898c2ecf20Sopenharmony_ci	dst->dqb_curspace = src->d_space;
1908c2ecf20Sopenharmony_ci	dst->dqb_ihardlimit = src->d_ino_hardlimit;
1918c2ecf20Sopenharmony_ci	dst->dqb_isoftlimit = src->d_ino_softlimit;
1928c2ecf20Sopenharmony_ci	dst->dqb_curinodes = src->d_ino_count;
1938c2ecf20Sopenharmony_ci	dst->dqb_btime = src->d_spc_timer;
1948c2ecf20Sopenharmony_ci	dst->dqb_itime = src->d_ino_timer;
1958c2ecf20Sopenharmony_ci	dst->dqb_valid = QIF_ALL;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int quota_getquota(struct super_block *sb, int type, qid_t id,
1998c2ecf20Sopenharmony_ci			  void __user *addr)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct kqid qid;
2028c2ecf20Sopenharmony_ci	struct qc_dqblk fdq;
2038c2ecf20Sopenharmony_ci	struct if_dqblk idq;
2048c2ecf20Sopenharmony_ci	int ret;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (!sb->s_qcop->get_dqblk)
2078c2ecf20Sopenharmony_ci		return -ENOSYS;
2088c2ecf20Sopenharmony_ci	qid = make_kqid(current_user_ns(), type, id);
2098c2ecf20Sopenharmony_ci	if (!qid_has_mapping(sb->s_user_ns, qid))
2108c2ecf20Sopenharmony_ci		return -EINVAL;
2118c2ecf20Sopenharmony_ci	ret = sb->s_qcop->get_dqblk(sb, qid, &fdq);
2128c2ecf20Sopenharmony_ci	if (ret)
2138c2ecf20Sopenharmony_ci		return ret;
2148c2ecf20Sopenharmony_ci	copy_to_if_dqblk(&idq, &fdq);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (compat_need_64bit_alignment_fixup()) {
2178c2ecf20Sopenharmony_ci		struct compat_if_dqblk __user *compat_dqblk = addr;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		if (copy_to_user(compat_dqblk, &idq, sizeof(*compat_dqblk)))
2208c2ecf20Sopenharmony_ci			return -EFAULT;
2218c2ecf20Sopenharmony_ci		if (put_user(idq.dqb_valid, &compat_dqblk->dqb_valid))
2228c2ecf20Sopenharmony_ci			return -EFAULT;
2238c2ecf20Sopenharmony_ci	} else {
2248c2ecf20Sopenharmony_ci		if (copy_to_user(addr, &idq, sizeof(idq)))
2258c2ecf20Sopenharmony_ci			return -EFAULT;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci	return 0;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/*
2318c2ecf20Sopenharmony_ci * Return quota for next active quota >= this id, if any exists,
2328c2ecf20Sopenharmony_ci * otherwise return -ENOENT via ->get_nextdqblk
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_cistatic int quota_getnextquota(struct super_block *sb, int type, qid_t id,
2358c2ecf20Sopenharmony_ci			  void __user *addr)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct kqid qid;
2388c2ecf20Sopenharmony_ci	struct qc_dqblk fdq;
2398c2ecf20Sopenharmony_ci	struct if_nextdqblk idq;
2408c2ecf20Sopenharmony_ci	int ret;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (!sb->s_qcop->get_nextdqblk)
2438c2ecf20Sopenharmony_ci		return -ENOSYS;
2448c2ecf20Sopenharmony_ci	qid = make_kqid(current_user_ns(), type, id);
2458c2ecf20Sopenharmony_ci	if (!qid_has_mapping(sb->s_user_ns, qid))
2468c2ecf20Sopenharmony_ci		return -EINVAL;
2478c2ecf20Sopenharmony_ci	ret = sb->s_qcop->get_nextdqblk(sb, &qid, &fdq);
2488c2ecf20Sopenharmony_ci	if (ret)
2498c2ecf20Sopenharmony_ci		return ret;
2508c2ecf20Sopenharmony_ci	/* struct if_nextdqblk is a superset of struct if_dqblk */
2518c2ecf20Sopenharmony_ci	copy_to_if_dqblk((struct if_dqblk *)&idq, &fdq);
2528c2ecf20Sopenharmony_ci	idq.dqb_id = from_kqid(current_user_ns(), qid);
2538c2ecf20Sopenharmony_ci	if (copy_to_user(addr, &idq, sizeof(idq)))
2548c2ecf20Sopenharmony_ci		return -EFAULT;
2558c2ecf20Sopenharmony_ci	return 0;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void copy_from_if_dqblk(struct qc_dqblk *dst, struct if_dqblk *src)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	dst->d_spc_hardlimit = qbtos(src->dqb_bhardlimit);
2618c2ecf20Sopenharmony_ci	dst->d_spc_softlimit = qbtos(src->dqb_bsoftlimit);
2628c2ecf20Sopenharmony_ci	dst->d_space = src->dqb_curspace;
2638c2ecf20Sopenharmony_ci	dst->d_ino_hardlimit = src->dqb_ihardlimit;
2648c2ecf20Sopenharmony_ci	dst->d_ino_softlimit = src->dqb_isoftlimit;
2658c2ecf20Sopenharmony_ci	dst->d_ino_count = src->dqb_curinodes;
2668c2ecf20Sopenharmony_ci	dst->d_spc_timer = src->dqb_btime;
2678c2ecf20Sopenharmony_ci	dst->d_ino_timer = src->dqb_itime;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	dst->d_fieldmask = 0;
2708c2ecf20Sopenharmony_ci	if (src->dqb_valid & QIF_BLIMITS)
2718c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_SPC_SOFT | QC_SPC_HARD;
2728c2ecf20Sopenharmony_ci	if (src->dqb_valid & QIF_SPACE)
2738c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_SPACE;
2748c2ecf20Sopenharmony_ci	if (src->dqb_valid & QIF_ILIMITS)
2758c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_INO_SOFT | QC_INO_HARD;
2768c2ecf20Sopenharmony_ci	if (src->dqb_valid & QIF_INODES)
2778c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_INO_COUNT;
2788c2ecf20Sopenharmony_ci	if (src->dqb_valid & QIF_BTIME)
2798c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_SPC_TIMER;
2808c2ecf20Sopenharmony_ci	if (src->dqb_valid & QIF_ITIME)
2818c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_INO_TIMER;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic int quota_setquota(struct super_block *sb, int type, qid_t id,
2858c2ecf20Sopenharmony_ci			  void __user *addr)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct qc_dqblk fdq;
2888c2ecf20Sopenharmony_ci	struct if_dqblk idq;
2898c2ecf20Sopenharmony_ci	struct kqid qid;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (compat_need_64bit_alignment_fixup()) {
2928c2ecf20Sopenharmony_ci		struct compat_if_dqblk __user *compat_dqblk = addr;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		if (copy_from_user(&idq, compat_dqblk, sizeof(*compat_dqblk)) ||
2958c2ecf20Sopenharmony_ci		    get_user(idq.dqb_valid, &compat_dqblk->dqb_valid))
2968c2ecf20Sopenharmony_ci			return -EFAULT;
2978c2ecf20Sopenharmony_ci	} else {
2988c2ecf20Sopenharmony_ci		if (copy_from_user(&idq, addr, sizeof(idq)))
2998c2ecf20Sopenharmony_ci			return -EFAULT;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci	if (!sb->s_qcop->set_dqblk)
3028c2ecf20Sopenharmony_ci		return -ENOSYS;
3038c2ecf20Sopenharmony_ci	qid = make_kqid(current_user_ns(), type, id);
3048c2ecf20Sopenharmony_ci	if (!qid_has_mapping(sb->s_user_ns, qid))
3058c2ecf20Sopenharmony_ci		return -EINVAL;
3068c2ecf20Sopenharmony_ci	copy_from_if_dqblk(&fdq, &idq);
3078c2ecf20Sopenharmony_ci	return sb->s_qcop->set_dqblk(sb, qid, &fdq);
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic int quota_enable(struct super_block *sb, void __user *addr)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	__u32 flags;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (copy_from_user(&flags, addr, sizeof(flags)))
3158c2ecf20Sopenharmony_ci		return -EFAULT;
3168c2ecf20Sopenharmony_ci	if (!sb->s_qcop->quota_enable)
3178c2ecf20Sopenharmony_ci		return -ENOSYS;
3188c2ecf20Sopenharmony_ci	return sb->s_qcop->quota_enable(sb, flags);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int quota_disable(struct super_block *sb, void __user *addr)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	__u32 flags;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (copy_from_user(&flags, addr, sizeof(flags)))
3268c2ecf20Sopenharmony_ci		return -EFAULT;
3278c2ecf20Sopenharmony_ci	if (!sb->s_qcop->quota_disable)
3288c2ecf20Sopenharmony_ci		return -ENOSYS;
3298c2ecf20Sopenharmony_ci	return sb->s_qcop->quota_disable(sb, flags);
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int quota_state_to_flags(struct qc_state *state)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	int flags = 0;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (state->s_state[USRQUOTA].flags & QCI_ACCT_ENABLED)
3378c2ecf20Sopenharmony_ci		flags |= FS_QUOTA_UDQ_ACCT;
3388c2ecf20Sopenharmony_ci	if (state->s_state[USRQUOTA].flags & QCI_LIMITS_ENFORCED)
3398c2ecf20Sopenharmony_ci		flags |= FS_QUOTA_UDQ_ENFD;
3408c2ecf20Sopenharmony_ci	if (state->s_state[GRPQUOTA].flags & QCI_ACCT_ENABLED)
3418c2ecf20Sopenharmony_ci		flags |= FS_QUOTA_GDQ_ACCT;
3428c2ecf20Sopenharmony_ci	if (state->s_state[GRPQUOTA].flags & QCI_LIMITS_ENFORCED)
3438c2ecf20Sopenharmony_ci		flags |= FS_QUOTA_GDQ_ENFD;
3448c2ecf20Sopenharmony_ci	if (state->s_state[PRJQUOTA].flags & QCI_ACCT_ENABLED)
3458c2ecf20Sopenharmony_ci		flags |= FS_QUOTA_PDQ_ACCT;
3468c2ecf20Sopenharmony_ci	if (state->s_state[PRJQUOTA].flags & QCI_LIMITS_ENFORCED)
3478c2ecf20Sopenharmony_ci		flags |= FS_QUOTA_PDQ_ENFD;
3488c2ecf20Sopenharmony_ci	return flags;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic int quota_getstate(struct super_block *sb, int type,
3528c2ecf20Sopenharmony_ci			  struct fs_quota_stat *fqs)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct qc_state state;
3558c2ecf20Sopenharmony_ci	int ret;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	memset(&state, 0, sizeof (struct qc_state));
3588c2ecf20Sopenharmony_ci	ret = sb->s_qcop->get_state(sb, &state);
3598c2ecf20Sopenharmony_ci	if (ret < 0)
3608c2ecf20Sopenharmony_ci		return ret;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	memset(fqs, 0, sizeof(*fqs));
3638c2ecf20Sopenharmony_ci	fqs->qs_version = FS_QSTAT_VERSION;
3648c2ecf20Sopenharmony_ci	fqs->qs_flags = quota_state_to_flags(&state);
3658c2ecf20Sopenharmony_ci	/* No quota enabled? */
3668c2ecf20Sopenharmony_ci	if (!fqs->qs_flags)
3678c2ecf20Sopenharmony_ci		return -ENOSYS;
3688c2ecf20Sopenharmony_ci	fqs->qs_incoredqs = state.s_incoredqs;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	fqs->qs_btimelimit = state.s_state[type].spc_timelimit;
3718c2ecf20Sopenharmony_ci	fqs->qs_itimelimit = state.s_state[type].ino_timelimit;
3728c2ecf20Sopenharmony_ci	fqs->qs_rtbtimelimit = state.s_state[type].rt_spc_timelimit;
3738c2ecf20Sopenharmony_ci	fqs->qs_bwarnlimit = state.s_state[type].spc_warnlimit;
3748c2ecf20Sopenharmony_ci	fqs->qs_iwarnlimit = state.s_state[type].ino_warnlimit;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Inodes may be allocated even if inactive; copy out if present */
3778c2ecf20Sopenharmony_ci	if (state.s_state[USRQUOTA].ino) {
3788c2ecf20Sopenharmony_ci		fqs->qs_uquota.qfs_ino = state.s_state[USRQUOTA].ino;
3798c2ecf20Sopenharmony_ci		fqs->qs_uquota.qfs_nblks = state.s_state[USRQUOTA].blocks;
3808c2ecf20Sopenharmony_ci		fqs->qs_uquota.qfs_nextents = state.s_state[USRQUOTA].nextents;
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci	if (state.s_state[GRPQUOTA].ino) {
3838c2ecf20Sopenharmony_ci		fqs->qs_gquota.qfs_ino = state.s_state[GRPQUOTA].ino;
3848c2ecf20Sopenharmony_ci		fqs->qs_gquota.qfs_nblks = state.s_state[GRPQUOTA].blocks;
3858c2ecf20Sopenharmony_ci		fqs->qs_gquota.qfs_nextents = state.s_state[GRPQUOTA].nextents;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci	if (state.s_state[PRJQUOTA].ino) {
3888c2ecf20Sopenharmony_ci		/*
3898c2ecf20Sopenharmony_ci		 * Q_XGETQSTAT doesn't have room for both group and project
3908c2ecf20Sopenharmony_ci		 * quotas.  So, allow the project quota values to be copied out
3918c2ecf20Sopenharmony_ci		 * only if there is no group quota information available.
3928c2ecf20Sopenharmony_ci		 */
3938c2ecf20Sopenharmony_ci		if (!(state.s_state[GRPQUOTA].flags & QCI_ACCT_ENABLED)) {
3948c2ecf20Sopenharmony_ci			fqs->qs_gquota.qfs_ino = state.s_state[PRJQUOTA].ino;
3958c2ecf20Sopenharmony_ci			fqs->qs_gquota.qfs_nblks =
3968c2ecf20Sopenharmony_ci					state.s_state[PRJQUOTA].blocks;
3978c2ecf20Sopenharmony_ci			fqs->qs_gquota.qfs_nextents =
3988c2ecf20Sopenharmony_ci					state.s_state[PRJQUOTA].nextents;
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int compat_copy_fs_qfilestat(struct compat_fs_qfilestat __user *to,
4058c2ecf20Sopenharmony_ci		struct fs_qfilestat *from)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	if (copy_to_user(to, from, sizeof(*to)) ||
4088c2ecf20Sopenharmony_ci	    put_user(from->qfs_nextents, &to->qfs_nextents))
4098c2ecf20Sopenharmony_ci		return -EFAULT;
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int compat_copy_fs_quota_stat(struct compat_fs_quota_stat __user *to,
4148c2ecf20Sopenharmony_ci		struct fs_quota_stat *from)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	if (put_user(from->qs_version, &to->qs_version) ||
4178c2ecf20Sopenharmony_ci	    put_user(from->qs_flags, &to->qs_flags) ||
4188c2ecf20Sopenharmony_ci	    put_user(from->qs_pad, &to->qs_pad) ||
4198c2ecf20Sopenharmony_ci	    compat_copy_fs_qfilestat(&to->qs_uquota, &from->qs_uquota) ||
4208c2ecf20Sopenharmony_ci	    compat_copy_fs_qfilestat(&to->qs_gquota, &from->qs_gquota) ||
4218c2ecf20Sopenharmony_ci	    put_user(from->qs_incoredqs, &to->qs_incoredqs) ||
4228c2ecf20Sopenharmony_ci	    put_user(from->qs_btimelimit, &to->qs_btimelimit) ||
4238c2ecf20Sopenharmony_ci	    put_user(from->qs_itimelimit, &to->qs_itimelimit) ||
4248c2ecf20Sopenharmony_ci	    put_user(from->qs_rtbtimelimit, &to->qs_rtbtimelimit) ||
4258c2ecf20Sopenharmony_ci	    put_user(from->qs_bwarnlimit, &to->qs_bwarnlimit) ||
4268c2ecf20Sopenharmony_ci	    put_user(from->qs_iwarnlimit, &to->qs_iwarnlimit))
4278c2ecf20Sopenharmony_ci		return -EFAULT;
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic int quota_getxstate(struct super_block *sb, int type, void __user *addr)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct fs_quota_stat fqs;
4348c2ecf20Sopenharmony_ci	int ret;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	if (!sb->s_qcop->get_state)
4378c2ecf20Sopenharmony_ci		return -ENOSYS;
4388c2ecf20Sopenharmony_ci	ret = quota_getstate(sb, type, &fqs);
4398c2ecf20Sopenharmony_ci	if (ret)
4408c2ecf20Sopenharmony_ci		return ret;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	if (compat_need_64bit_alignment_fixup())
4438c2ecf20Sopenharmony_ci		return compat_copy_fs_quota_stat(addr, &fqs);
4448c2ecf20Sopenharmony_ci	if (copy_to_user(addr, &fqs, sizeof(fqs)))
4458c2ecf20Sopenharmony_ci		return -EFAULT;
4468c2ecf20Sopenharmony_ci	return 0;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic int quota_getstatev(struct super_block *sb, int type,
4508c2ecf20Sopenharmony_ci			   struct fs_quota_statv *fqs)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct qc_state state;
4538c2ecf20Sopenharmony_ci	int ret;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	memset(&state, 0, sizeof (struct qc_state));
4568c2ecf20Sopenharmony_ci	ret = sb->s_qcop->get_state(sb, &state);
4578c2ecf20Sopenharmony_ci	if (ret < 0)
4588c2ecf20Sopenharmony_ci		return ret;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	memset(fqs, 0, sizeof(*fqs));
4618c2ecf20Sopenharmony_ci	fqs->qs_version = FS_QSTAT_VERSION;
4628c2ecf20Sopenharmony_ci	fqs->qs_flags = quota_state_to_flags(&state);
4638c2ecf20Sopenharmony_ci	/* No quota enabled? */
4648c2ecf20Sopenharmony_ci	if (!fqs->qs_flags)
4658c2ecf20Sopenharmony_ci		return -ENOSYS;
4668c2ecf20Sopenharmony_ci	fqs->qs_incoredqs = state.s_incoredqs;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	fqs->qs_btimelimit = state.s_state[type].spc_timelimit;
4698c2ecf20Sopenharmony_ci	fqs->qs_itimelimit = state.s_state[type].ino_timelimit;
4708c2ecf20Sopenharmony_ci	fqs->qs_rtbtimelimit = state.s_state[type].rt_spc_timelimit;
4718c2ecf20Sopenharmony_ci	fqs->qs_bwarnlimit = state.s_state[type].spc_warnlimit;
4728c2ecf20Sopenharmony_ci	fqs->qs_iwarnlimit = state.s_state[type].ino_warnlimit;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* Inodes may be allocated even if inactive; copy out if present */
4758c2ecf20Sopenharmony_ci	if (state.s_state[USRQUOTA].ino) {
4768c2ecf20Sopenharmony_ci		fqs->qs_uquota.qfs_ino = state.s_state[USRQUOTA].ino;
4778c2ecf20Sopenharmony_ci		fqs->qs_uquota.qfs_nblks = state.s_state[USRQUOTA].blocks;
4788c2ecf20Sopenharmony_ci		fqs->qs_uquota.qfs_nextents = state.s_state[USRQUOTA].nextents;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci	if (state.s_state[GRPQUOTA].ino) {
4818c2ecf20Sopenharmony_ci		fqs->qs_gquota.qfs_ino = state.s_state[GRPQUOTA].ino;
4828c2ecf20Sopenharmony_ci		fqs->qs_gquota.qfs_nblks = state.s_state[GRPQUOTA].blocks;
4838c2ecf20Sopenharmony_ci		fqs->qs_gquota.qfs_nextents = state.s_state[GRPQUOTA].nextents;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci	if (state.s_state[PRJQUOTA].ino) {
4868c2ecf20Sopenharmony_ci		fqs->qs_pquota.qfs_ino = state.s_state[PRJQUOTA].ino;
4878c2ecf20Sopenharmony_ci		fqs->qs_pquota.qfs_nblks = state.s_state[PRJQUOTA].blocks;
4888c2ecf20Sopenharmony_ci		fqs->qs_pquota.qfs_nextents = state.s_state[PRJQUOTA].nextents;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci	return 0;
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic int quota_getxstatev(struct super_block *sb, int type, void __user *addr)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	struct fs_quota_statv fqs;
4968c2ecf20Sopenharmony_ci	int ret;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	if (!sb->s_qcop->get_state)
4998c2ecf20Sopenharmony_ci		return -ENOSYS;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	memset(&fqs, 0, sizeof(fqs));
5028c2ecf20Sopenharmony_ci	if (copy_from_user(&fqs, addr, 1)) /* Just read qs_version */
5038c2ecf20Sopenharmony_ci		return -EFAULT;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	/* If this kernel doesn't support user specified version, fail */
5068c2ecf20Sopenharmony_ci	switch (fqs.qs_version) {
5078c2ecf20Sopenharmony_ci	case FS_QSTATV_VERSION1:
5088c2ecf20Sopenharmony_ci		break;
5098c2ecf20Sopenharmony_ci	default:
5108c2ecf20Sopenharmony_ci		return -EINVAL;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci	ret = quota_getstatev(sb, type, &fqs);
5138c2ecf20Sopenharmony_ci	if (!ret && copy_to_user(addr, &fqs, sizeof(fqs)))
5148c2ecf20Sopenharmony_ci		return -EFAULT;
5158c2ecf20Sopenharmony_ci	return ret;
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci/*
5198c2ecf20Sopenharmony_ci * XFS defines BBTOB and BTOBB macros inside fs/xfs/ and we cannot move them
5208c2ecf20Sopenharmony_ci * out of there as xfsprogs rely on definitions being in that header file. So
5218c2ecf20Sopenharmony_ci * just define same functions here for quota purposes.
5228c2ecf20Sopenharmony_ci */
5238c2ecf20Sopenharmony_ci#define XFS_BB_SHIFT 9
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic inline u64 quota_bbtob(u64 blocks)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	return blocks << XFS_BB_SHIFT;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic inline u64 quota_btobb(u64 bytes)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	return (bytes + (1 << XFS_BB_SHIFT) - 1) >> XFS_BB_SHIFT;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic inline s64 copy_from_xfs_dqblk_ts(const struct fs_disk_quota *d,
5368c2ecf20Sopenharmony_ci		__s32 timer, __s8 timer_hi)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	if (d->d_fieldmask & FS_DQ_BIGTIME)
5398c2ecf20Sopenharmony_ci		return (u32)timer | (s64)timer_hi << 32;
5408c2ecf20Sopenharmony_ci	return timer;
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic void copy_from_xfs_dqblk(struct qc_dqblk *dst, struct fs_disk_quota *src)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	dst->d_spc_hardlimit = quota_bbtob(src->d_blk_hardlimit);
5468c2ecf20Sopenharmony_ci	dst->d_spc_softlimit = quota_bbtob(src->d_blk_softlimit);
5478c2ecf20Sopenharmony_ci	dst->d_ino_hardlimit = src->d_ino_hardlimit;
5488c2ecf20Sopenharmony_ci	dst->d_ino_softlimit = src->d_ino_softlimit;
5498c2ecf20Sopenharmony_ci	dst->d_space = quota_bbtob(src->d_bcount);
5508c2ecf20Sopenharmony_ci	dst->d_ino_count = src->d_icount;
5518c2ecf20Sopenharmony_ci	dst->d_ino_timer = copy_from_xfs_dqblk_ts(src, src->d_itimer,
5528c2ecf20Sopenharmony_ci						  src->d_itimer_hi);
5538c2ecf20Sopenharmony_ci	dst->d_spc_timer = copy_from_xfs_dqblk_ts(src, src->d_btimer,
5548c2ecf20Sopenharmony_ci						  src->d_btimer_hi);
5558c2ecf20Sopenharmony_ci	dst->d_ino_warns = src->d_iwarns;
5568c2ecf20Sopenharmony_ci	dst->d_spc_warns = src->d_bwarns;
5578c2ecf20Sopenharmony_ci	dst->d_rt_spc_hardlimit = quota_bbtob(src->d_rtb_hardlimit);
5588c2ecf20Sopenharmony_ci	dst->d_rt_spc_softlimit = quota_bbtob(src->d_rtb_softlimit);
5598c2ecf20Sopenharmony_ci	dst->d_rt_space = quota_bbtob(src->d_rtbcount);
5608c2ecf20Sopenharmony_ci	dst->d_rt_spc_timer = copy_from_xfs_dqblk_ts(src, src->d_rtbtimer,
5618c2ecf20Sopenharmony_ci						     src->d_rtbtimer_hi);
5628c2ecf20Sopenharmony_ci	dst->d_rt_spc_warns = src->d_rtbwarns;
5638c2ecf20Sopenharmony_ci	dst->d_fieldmask = 0;
5648c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_ISOFT)
5658c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_INO_SOFT;
5668c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_IHARD)
5678c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_INO_HARD;
5688c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_BSOFT)
5698c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_SPC_SOFT;
5708c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_BHARD)
5718c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_SPC_HARD;
5728c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_RTBSOFT)
5738c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_RT_SPC_SOFT;
5748c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_RTBHARD)
5758c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_RT_SPC_HARD;
5768c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_BTIMER)
5778c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_SPC_TIMER;
5788c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_ITIMER)
5798c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_INO_TIMER;
5808c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_RTBTIMER)
5818c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_RT_SPC_TIMER;
5828c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_BWARNS)
5838c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_SPC_WARNS;
5848c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_IWARNS)
5858c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_INO_WARNS;
5868c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_RTBWARNS)
5878c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_RT_SPC_WARNS;
5888c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_BCOUNT)
5898c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_SPACE;
5908c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_ICOUNT)
5918c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_INO_COUNT;
5928c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_RTBCOUNT)
5938c2ecf20Sopenharmony_ci		dst->d_fieldmask |= QC_RT_SPACE;
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic void copy_qcinfo_from_xfs_dqblk(struct qc_info *dst,
5978c2ecf20Sopenharmony_ci				       struct fs_disk_quota *src)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	memset(dst, 0, sizeof(*dst));
6008c2ecf20Sopenharmony_ci	dst->i_spc_timelimit = src->d_btimer;
6018c2ecf20Sopenharmony_ci	dst->i_ino_timelimit = src->d_itimer;
6028c2ecf20Sopenharmony_ci	dst->i_rt_spc_timelimit = src->d_rtbtimer;
6038c2ecf20Sopenharmony_ci	dst->i_ino_warnlimit = src->d_iwarns;
6048c2ecf20Sopenharmony_ci	dst->i_spc_warnlimit = src->d_bwarns;
6058c2ecf20Sopenharmony_ci	dst->i_rt_spc_warnlimit = src->d_rtbwarns;
6068c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_BWARNS)
6078c2ecf20Sopenharmony_ci		dst->i_fieldmask |= QC_SPC_WARNS;
6088c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_IWARNS)
6098c2ecf20Sopenharmony_ci		dst->i_fieldmask |= QC_INO_WARNS;
6108c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_RTBWARNS)
6118c2ecf20Sopenharmony_ci		dst->i_fieldmask |= QC_RT_SPC_WARNS;
6128c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_BTIMER)
6138c2ecf20Sopenharmony_ci		dst->i_fieldmask |= QC_SPC_TIMER;
6148c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_ITIMER)
6158c2ecf20Sopenharmony_ci		dst->i_fieldmask |= QC_INO_TIMER;
6168c2ecf20Sopenharmony_ci	if (src->d_fieldmask & FS_DQ_RTBTIMER)
6178c2ecf20Sopenharmony_ci		dst->i_fieldmask |= QC_RT_SPC_TIMER;
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic int quota_setxquota(struct super_block *sb, int type, qid_t id,
6218c2ecf20Sopenharmony_ci			   void __user *addr)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	struct fs_disk_quota fdq;
6248c2ecf20Sopenharmony_ci	struct qc_dqblk qdq;
6258c2ecf20Sopenharmony_ci	struct kqid qid;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (copy_from_user(&fdq, addr, sizeof(fdq)))
6288c2ecf20Sopenharmony_ci		return -EFAULT;
6298c2ecf20Sopenharmony_ci	if (!sb->s_qcop->set_dqblk)
6308c2ecf20Sopenharmony_ci		return -ENOSYS;
6318c2ecf20Sopenharmony_ci	qid = make_kqid(current_user_ns(), type, id);
6328c2ecf20Sopenharmony_ci	if (!qid_has_mapping(sb->s_user_ns, qid))
6338c2ecf20Sopenharmony_ci		return -EINVAL;
6348c2ecf20Sopenharmony_ci	/* Are we actually setting timer / warning limits for all users? */
6358c2ecf20Sopenharmony_ci	if (from_kqid(sb->s_user_ns, qid) == 0 &&
6368c2ecf20Sopenharmony_ci	    fdq.d_fieldmask & (FS_DQ_WARNS_MASK | FS_DQ_TIMER_MASK)) {
6378c2ecf20Sopenharmony_ci		struct qc_info qinfo;
6388c2ecf20Sopenharmony_ci		int ret;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci		if (!sb->s_qcop->set_info)
6418c2ecf20Sopenharmony_ci			return -EINVAL;
6428c2ecf20Sopenharmony_ci		copy_qcinfo_from_xfs_dqblk(&qinfo, &fdq);
6438c2ecf20Sopenharmony_ci		ret = sb->s_qcop->set_info(sb, type, &qinfo);
6448c2ecf20Sopenharmony_ci		if (ret)
6458c2ecf20Sopenharmony_ci			return ret;
6468c2ecf20Sopenharmony_ci		/* These are already done */
6478c2ecf20Sopenharmony_ci		fdq.d_fieldmask &= ~(FS_DQ_WARNS_MASK | FS_DQ_TIMER_MASK);
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci	copy_from_xfs_dqblk(&qdq, &fdq);
6508c2ecf20Sopenharmony_ci	return sb->s_qcop->set_dqblk(sb, qid, &qdq);
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_cistatic inline void copy_to_xfs_dqblk_ts(const struct fs_disk_quota *d,
6548c2ecf20Sopenharmony_ci		__s32 *timer_lo, __s8 *timer_hi, s64 timer)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	*timer_lo = timer;
6578c2ecf20Sopenharmony_ci	if (d->d_fieldmask & FS_DQ_BIGTIME)
6588c2ecf20Sopenharmony_ci		*timer_hi = timer >> 32;
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_cistatic inline bool want_bigtime(s64 timer)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	return timer > S32_MAX || timer < S32_MIN;
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_cistatic void copy_to_xfs_dqblk(struct fs_disk_quota *dst, struct qc_dqblk *src,
6678c2ecf20Sopenharmony_ci			      int type, qid_t id)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	memset(dst, 0, sizeof(*dst));
6708c2ecf20Sopenharmony_ci	if (want_bigtime(src->d_ino_timer) || want_bigtime(src->d_spc_timer) ||
6718c2ecf20Sopenharmony_ci	    want_bigtime(src->d_rt_spc_timer))
6728c2ecf20Sopenharmony_ci		dst->d_fieldmask |= FS_DQ_BIGTIME;
6738c2ecf20Sopenharmony_ci	dst->d_version = FS_DQUOT_VERSION;
6748c2ecf20Sopenharmony_ci	dst->d_id = id;
6758c2ecf20Sopenharmony_ci	if (type == USRQUOTA)
6768c2ecf20Sopenharmony_ci		dst->d_flags = FS_USER_QUOTA;
6778c2ecf20Sopenharmony_ci	else if (type == PRJQUOTA)
6788c2ecf20Sopenharmony_ci		dst->d_flags = FS_PROJ_QUOTA;
6798c2ecf20Sopenharmony_ci	else
6808c2ecf20Sopenharmony_ci		dst->d_flags = FS_GROUP_QUOTA;
6818c2ecf20Sopenharmony_ci	dst->d_blk_hardlimit = quota_btobb(src->d_spc_hardlimit);
6828c2ecf20Sopenharmony_ci	dst->d_blk_softlimit = quota_btobb(src->d_spc_softlimit);
6838c2ecf20Sopenharmony_ci	dst->d_ino_hardlimit = src->d_ino_hardlimit;
6848c2ecf20Sopenharmony_ci	dst->d_ino_softlimit = src->d_ino_softlimit;
6858c2ecf20Sopenharmony_ci	dst->d_bcount = quota_btobb(src->d_space);
6868c2ecf20Sopenharmony_ci	dst->d_icount = src->d_ino_count;
6878c2ecf20Sopenharmony_ci	copy_to_xfs_dqblk_ts(dst, &dst->d_itimer, &dst->d_itimer_hi,
6888c2ecf20Sopenharmony_ci			     src->d_ino_timer);
6898c2ecf20Sopenharmony_ci	copy_to_xfs_dqblk_ts(dst, &dst->d_btimer, &dst->d_btimer_hi,
6908c2ecf20Sopenharmony_ci			     src->d_spc_timer);
6918c2ecf20Sopenharmony_ci	dst->d_iwarns = src->d_ino_warns;
6928c2ecf20Sopenharmony_ci	dst->d_bwarns = src->d_spc_warns;
6938c2ecf20Sopenharmony_ci	dst->d_rtb_hardlimit = quota_btobb(src->d_rt_spc_hardlimit);
6948c2ecf20Sopenharmony_ci	dst->d_rtb_softlimit = quota_btobb(src->d_rt_spc_softlimit);
6958c2ecf20Sopenharmony_ci	dst->d_rtbcount = quota_btobb(src->d_rt_space);
6968c2ecf20Sopenharmony_ci	copy_to_xfs_dqblk_ts(dst, &dst->d_rtbtimer, &dst->d_rtbtimer_hi,
6978c2ecf20Sopenharmony_ci			     src->d_rt_spc_timer);
6988c2ecf20Sopenharmony_ci	dst->d_rtbwarns = src->d_rt_spc_warns;
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic int quota_getxquota(struct super_block *sb, int type, qid_t id,
7028c2ecf20Sopenharmony_ci			   void __user *addr)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	struct fs_disk_quota fdq;
7058c2ecf20Sopenharmony_ci	struct qc_dqblk qdq;
7068c2ecf20Sopenharmony_ci	struct kqid qid;
7078c2ecf20Sopenharmony_ci	int ret;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	if (!sb->s_qcop->get_dqblk)
7108c2ecf20Sopenharmony_ci		return -ENOSYS;
7118c2ecf20Sopenharmony_ci	qid = make_kqid(current_user_ns(), type, id);
7128c2ecf20Sopenharmony_ci	if (!qid_has_mapping(sb->s_user_ns, qid))
7138c2ecf20Sopenharmony_ci		return -EINVAL;
7148c2ecf20Sopenharmony_ci	ret = sb->s_qcop->get_dqblk(sb, qid, &qdq);
7158c2ecf20Sopenharmony_ci	if (ret)
7168c2ecf20Sopenharmony_ci		return ret;
7178c2ecf20Sopenharmony_ci	copy_to_xfs_dqblk(&fdq, &qdq, type, id);
7188c2ecf20Sopenharmony_ci	if (copy_to_user(addr, &fdq, sizeof(fdq)))
7198c2ecf20Sopenharmony_ci		return -EFAULT;
7208c2ecf20Sopenharmony_ci	return ret;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci/*
7248c2ecf20Sopenharmony_ci * Return quota for next active quota >= this id, if any exists,
7258c2ecf20Sopenharmony_ci * otherwise return -ENOENT via ->get_nextdqblk.
7268c2ecf20Sopenharmony_ci */
7278c2ecf20Sopenharmony_cistatic int quota_getnextxquota(struct super_block *sb, int type, qid_t id,
7288c2ecf20Sopenharmony_ci			    void __user *addr)
7298c2ecf20Sopenharmony_ci{
7308c2ecf20Sopenharmony_ci	struct fs_disk_quota fdq;
7318c2ecf20Sopenharmony_ci	struct qc_dqblk qdq;
7328c2ecf20Sopenharmony_ci	struct kqid qid;
7338c2ecf20Sopenharmony_ci	qid_t id_out;
7348c2ecf20Sopenharmony_ci	int ret;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if (!sb->s_qcop->get_nextdqblk)
7378c2ecf20Sopenharmony_ci		return -ENOSYS;
7388c2ecf20Sopenharmony_ci	qid = make_kqid(current_user_ns(), type, id);
7398c2ecf20Sopenharmony_ci	if (!qid_has_mapping(sb->s_user_ns, qid))
7408c2ecf20Sopenharmony_ci		return -EINVAL;
7418c2ecf20Sopenharmony_ci	ret = sb->s_qcop->get_nextdqblk(sb, &qid, &qdq);
7428c2ecf20Sopenharmony_ci	if (ret)
7438c2ecf20Sopenharmony_ci		return ret;
7448c2ecf20Sopenharmony_ci	id_out = from_kqid(current_user_ns(), qid);
7458c2ecf20Sopenharmony_ci	copy_to_xfs_dqblk(&fdq, &qdq, type, id_out);
7468c2ecf20Sopenharmony_ci	if (copy_to_user(addr, &fdq, sizeof(fdq)))
7478c2ecf20Sopenharmony_ci		return -EFAULT;
7488c2ecf20Sopenharmony_ci	return ret;
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cistatic int quota_rmxquota(struct super_block *sb, void __user *addr)
7528c2ecf20Sopenharmony_ci{
7538c2ecf20Sopenharmony_ci	__u32 flags;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (copy_from_user(&flags, addr, sizeof(flags)))
7568c2ecf20Sopenharmony_ci		return -EFAULT;
7578c2ecf20Sopenharmony_ci	if (!sb->s_qcop->rm_xquota)
7588c2ecf20Sopenharmony_ci		return -ENOSYS;
7598c2ecf20Sopenharmony_ci	return sb->s_qcop->rm_xquota(sb, flags);
7608c2ecf20Sopenharmony_ci}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci/* Copy parameters and call proper function */
7638c2ecf20Sopenharmony_cistatic int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
7648c2ecf20Sopenharmony_ci		       void __user *addr, const struct path *path)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	int ret;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	type = array_index_nospec(type, MAXQUOTAS);
7698c2ecf20Sopenharmony_ci	/*
7708c2ecf20Sopenharmony_ci	 * Quota not supported on this fs? Check this before s_quota_types
7718c2ecf20Sopenharmony_ci	 * since they needn't be set if quota is not supported at all.
7728c2ecf20Sopenharmony_ci	 */
7738c2ecf20Sopenharmony_ci	if (!sb->s_qcop)
7748c2ecf20Sopenharmony_ci		return -ENOSYS;
7758c2ecf20Sopenharmony_ci	if (!(sb->s_quota_types & (1 << type)))
7768c2ecf20Sopenharmony_ci		return -EINVAL;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	ret = check_quotactl_permission(sb, type, cmd, id);
7798c2ecf20Sopenharmony_ci	if (ret < 0)
7808c2ecf20Sopenharmony_ci		return ret;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	switch (cmd) {
7838c2ecf20Sopenharmony_ci	case Q_QUOTAON:
7848c2ecf20Sopenharmony_ci		return quota_quotaon(sb, type, id, path);
7858c2ecf20Sopenharmony_ci	case Q_QUOTAOFF:
7868c2ecf20Sopenharmony_ci		return quota_quotaoff(sb, type);
7878c2ecf20Sopenharmony_ci	case Q_GETFMT:
7888c2ecf20Sopenharmony_ci		return quota_getfmt(sb, type, addr);
7898c2ecf20Sopenharmony_ci	case Q_GETINFO:
7908c2ecf20Sopenharmony_ci		return quota_getinfo(sb, type, addr);
7918c2ecf20Sopenharmony_ci	case Q_SETINFO:
7928c2ecf20Sopenharmony_ci		return quota_setinfo(sb, type, addr);
7938c2ecf20Sopenharmony_ci	case Q_GETQUOTA:
7948c2ecf20Sopenharmony_ci		return quota_getquota(sb, type, id, addr);
7958c2ecf20Sopenharmony_ci	case Q_GETNEXTQUOTA:
7968c2ecf20Sopenharmony_ci		return quota_getnextquota(sb, type, id, addr);
7978c2ecf20Sopenharmony_ci	case Q_SETQUOTA:
7988c2ecf20Sopenharmony_ci		return quota_setquota(sb, type, id, addr);
7998c2ecf20Sopenharmony_ci	case Q_SYNC:
8008c2ecf20Sopenharmony_ci		if (!sb->s_qcop->quota_sync)
8018c2ecf20Sopenharmony_ci			return -ENOSYS;
8028c2ecf20Sopenharmony_ci		return sb->s_qcop->quota_sync(sb, type);
8038c2ecf20Sopenharmony_ci	case Q_XQUOTAON:
8048c2ecf20Sopenharmony_ci		return quota_enable(sb, addr);
8058c2ecf20Sopenharmony_ci	case Q_XQUOTAOFF:
8068c2ecf20Sopenharmony_ci		return quota_disable(sb, addr);
8078c2ecf20Sopenharmony_ci	case Q_XQUOTARM:
8088c2ecf20Sopenharmony_ci		return quota_rmxquota(sb, addr);
8098c2ecf20Sopenharmony_ci	case Q_XGETQSTAT:
8108c2ecf20Sopenharmony_ci		return quota_getxstate(sb, type, addr);
8118c2ecf20Sopenharmony_ci	case Q_XGETQSTATV:
8128c2ecf20Sopenharmony_ci		return quota_getxstatev(sb, type, addr);
8138c2ecf20Sopenharmony_ci	case Q_XSETQLIM:
8148c2ecf20Sopenharmony_ci		return quota_setxquota(sb, type, id, addr);
8158c2ecf20Sopenharmony_ci	case Q_XGETQUOTA:
8168c2ecf20Sopenharmony_ci		return quota_getxquota(sb, type, id, addr);
8178c2ecf20Sopenharmony_ci	case Q_XGETNEXTQUOTA:
8188c2ecf20Sopenharmony_ci		return quota_getnextxquota(sb, type, id, addr);
8198c2ecf20Sopenharmony_ci	case Q_XQUOTASYNC:
8208c2ecf20Sopenharmony_ci		if (sb_rdonly(sb))
8218c2ecf20Sopenharmony_ci			return -EROFS;
8228c2ecf20Sopenharmony_ci		/* XFS quotas are fully coherent now, making this call a noop */
8238c2ecf20Sopenharmony_ci		return 0;
8248c2ecf20Sopenharmony_ci	default:
8258c2ecf20Sopenharmony_ci		return -EINVAL;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci#ifdef CONFIG_BLOCK
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci/* Return 1 if 'cmd' will block on frozen filesystem */
8328c2ecf20Sopenharmony_cistatic int quotactl_cmd_write(int cmd)
8338c2ecf20Sopenharmony_ci{
8348c2ecf20Sopenharmony_ci	/*
8358c2ecf20Sopenharmony_ci	 * We cannot allow Q_GETQUOTA and Q_GETNEXTQUOTA without write access
8368c2ecf20Sopenharmony_ci	 * as dquot_acquire() may allocate space for new structure and OCFS2
8378c2ecf20Sopenharmony_ci	 * needs to increment on-disk use count.
8388c2ecf20Sopenharmony_ci	 */
8398c2ecf20Sopenharmony_ci	switch (cmd) {
8408c2ecf20Sopenharmony_ci	case Q_GETFMT:
8418c2ecf20Sopenharmony_ci	case Q_GETINFO:
8428c2ecf20Sopenharmony_ci	case Q_SYNC:
8438c2ecf20Sopenharmony_ci	case Q_XGETQSTAT:
8448c2ecf20Sopenharmony_ci	case Q_XGETQSTATV:
8458c2ecf20Sopenharmony_ci	case Q_XGETQUOTA:
8468c2ecf20Sopenharmony_ci	case Q_XGETNEXTQUOTA:
8478c2ecf20Sopenharmony_ci	case Q_XQUOTASYNC:
8488c2ecf20Sopenharmony_ci		return 0;
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci	return 1;
8518c2ecf20Sopenharmony_ci}
8528c2ecf20Sopenharmony_ci#endif /* CONFIG_BLOCK */
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci/* Return true if quotactl command is manipulating quota on/off state */
8558c2ecf20Sopenharmony_cistatic bool quotactl_cmd_onoff(int cmd)
8568c2ecf20Sopenharmony_ci{
8578c2ecf20Sopenharmony_ci	return (cmd == Q_QUOTAON) || (cmd == Q_QUOTAOFF) ||
8588c2ecf20Sopenharmony_ci		 (cmd == Q_XQUOTAON) || (cmd == Q_XQUOTAOFF);
8598c2ecf20Sopenharmony_ci}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci/*
8628c2ecf20Sopenharmony_ci * look up a superblock on which quota ops will be performed
8638c2ecf20Sopenharmony_ci * - use the name of a block device to find the superblock thereon
8648c2ecf20Sopenharmony_ci */
8658c2ecf20Sopenharmony_cistatic struct super_block *quotactl_block(const char __user *special, int cmd)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci#ifdef CONFIG_BLOCK
8688c2ecf20Sopenharmony_ci	struct block_device *bdev;
8698c2ecf20Sopenharmony_ci	struct super_block *sb;
8708c2ecf20Sopenharmony_ci	struct filename *tmp = getname(special);
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	if (IS_ERR(tmp))
8738c2ecf20Sopenharmony_ci		return ERR_CAST(tmp);
8748c2ecf20Sopenharmony_ci	bdev = lookup_bdev(tmp->name);
8758c2ecf20Sopenharmony_ci	putname(tmp);
8768c2ecf20Sopenharmony_ci	if (IS_ERR(bdev))
8778c2ecf20Sopenharmony_ci		return ERR_CAST(bdev);
8788c2ecf20Sopenharmony_ci	if (quotactl_cmd_onoff(cmd))
8798c2ecf20Sopenharmony_ci		sb = get_super_exclusive_thawed(bdev);
8808c2ecf20Sopenharmony_ci	else if (quotactl_cmd_write(cmd))
8818c2ecf20Sopenharmony_ci		sb = get_super_thawed(bdev);
8828c2ecf20Sopenharmony_ci	else
8838c2ecf20Sopenharmony_ci		sb = get_super(bdev);
8848c2ecf20Sopenharmony_ci	bdput(bdev);
8858c2ecf20Sopenharmony_ci	if (!sb)
8868c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	return sb;
8898c2ecf20Sopenharmony_ci#else
8908c2ecf20Sopenharmony_ci	return ERR_PTR(-ENODEV);
8918c2ecf20Sopenharmony_ci#endif
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci/*
8958c2ecf20Sopenharmony_ci * This is the system call interface. This communicates with
8968c2ecf20Sopenharmony_ci * the user-level programs. Currently this only supports diskquota
8978c2ecf20Sopenharmony_ci * calls. Maybe we need to add the process quotas etc. in the future,
8988c2ecf20Sopenharmony_ci * but we probably should use rlimits for that.
8998c2ecf20Sopenharmony_ci */
9008c2ecf20Sopenharmony_ciSYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
9018c2ecf20Sopenharmony_ci		qid_t, id, void __user *, addr)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	uint cmds, type;
9048c2ecf20Sopenharmony_ci	struct super_block *sb = NULL;
9058c2ecf20Sopenharmony_ci	struct path path, *pathp = NULL;
9068c2ecf20Sopenharmony_ci	int ret;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	cmds = cmd >> SUBCMDSHIFT;
9098c2ecf20Sopenharmony_ci	type = cmd & SUBCMDMASK;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	if (type >= MAXQUOTAS)
9128c2ecf20Sopenharmony_ci		return -EINVAL;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	/*
9158c2ecf20Sopenharmony_ci	 * As a special case Q_SYNC can be called without a specific device.
9168c2ecf20Sopenharmony_ci	 * It will iterate all superblocks that have quota enabled and call
9178c2ecf20Sopenharmony_ci	 * the sync action on each of them.
9188c2ecf20Sopenharmony_ci	 */
9198c2ecf20Sopenharmony_ci	if (!special) {
9208c2ecf20Sopenharmony_ci		if (cmds == Q_SYNC)
9218c2ecf20Sopenharmony_ci			return quota_sync_all(type);
9228c2ecf20Sopenharmony_ci		return -ENODEV;
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	/*
9268c2ecf20Sopenharmony_ci	 * Path for quotaon has to be resolved before grabbing superblock
9278c2ecf20Sopenharmony_ci	 * because that gets s_umount sem which is also possibly needed by path
9288c2ecf20Sopenharmony_ci	 * resolution (think about autofs) and thus deadlocks could arise.
9298c2ecf20Sopenharmony_ci	 */
9308c2ecf20Sopenharmony_ci	if (cmds == Q_QUOTAON) {
9318c2ecf20Sopenharmony_ci		ret = user_path_at(AT_FDCWD, addr, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path);
9328c2ecf20Sopenharmony_ci		if (ret)
9338c2ecf20Sopenharmony_ci			pathp = ERR_PTR(ret);
9348c2ecf20Sopenharmony_ci		else
9358c2ecf20Sopenharmony_ci			pathp = &path;
9368c2ecf20Sopenharmony_ci	}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	sb = quotactl_block(special, cmds);
9398c2ecf20Sopenharmony_ci	if (IS_ERR(sb)) {
9408c2ecf20Sopenharmony_ci		ret = PTR_ERR(sb);
9418c2ecf20Sopenharmony_ci		goto out;
9428c2ecf20Sopenharmony_ci	}
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	ret = do_quotactl(sb, type, cmds, id, addr, pathp);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	if (!quotactl_cmd_onoff(cmds))
9478c2ecf20Sopenharmony_ci		drop_super(sb);
9488c2ecf20Sopenharmony_ci	else
9498c2ecf20Sopenharmony_ci		drop_super_exclusive(sb);
9508c2ecf20Sopenharmony_ciout:
9518c2ecf20Sopenharmony_ci	if (pathp && !IS_ERR(pathp))
9528c2ecf20Sopenharmony_ci		path_put(pathp);
9538c2ecf20Sopenharmony_ci	return ret;
9548c2ecf20Sopenharmony_ci}
955