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