1*0a6a1f1dSLionel Sambuc /* $NetBSD: ulfs_quota1.c,v 1.9 2015/07/26 08:33:53 hannken Exp $ */
284d9c625SLionel Sambuc /* from NetBSD: ufs_quota1.c,v 1.18 2012/02/02 03:00:48 matt Exp */
384d9c625SLionel Sambuc
484d9c625SLionel Sambuc /*
584d9c625SLionel Sambuc * Copyright (c) 1982, 1986, 1990, 1993, 1995
684d9c625SLionel Sambuc * The Regents of the University of California. All rights reserved.
784d9c625SLionel Sambuc *
884d9c625SLionel Sambuc * This code is derived from software contributed to Berkeley by
984d9c625SLionel Sambuc * Robert Elz at The University of Melbourne.
1084d9c625SLionel Sambuc *
1184d9c625SLionel Sambuc * Redistribution and use in source and binary forms, with or without
1284d9c625SLionel Sambuc * modification, are permitted provided that the following conditions
1384d9c625SLionel Sambuc * are met:
1484d9c625SLionel Sambuc * 1. Redistributions of source code must retain the above copyright
1584d9c625SLionel Sambuc * notice, this list of conditions and the following disclaimer.
1684d9c625SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright
1784d9c625SLionel Sambuc * notice, this list of conditions and the following disclaimer in the
1884d9c625SLionel Sambuc * documentation and/or other materials provided with the distribution.
1984d9c625SLionel Sambuc * 3. Neither the name of the University nor the names of its contributors
2084d9c625SLionel Sambuc * may be used to endorse or promote products derived from this software
2184d9c625SLionel Sambuc * without specific prior written permission.
2284d9c625SLionel Sambuc *
2384d9c625SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2484d9c625SLionel Sambuc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2584d9c625SLionel Sambuc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2684d9c625SLionel Sambuc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2784d9c625SLionel Sambuc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2884d9c625SLionel Sambuc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2984d9c625SLionel Sambuc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3084d9c625SLionel Sambuc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3184d9c625SLionel Sambuc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3284d9c625SLionel Sambuc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3384d9c625SLionel Sambuc * SUCH DAMAGE.
3484d9c625SLionel Sambuc *
3584d9c625SLionel Sambuc * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
3684d9c625SLionel Sambuc */
3784d9c625SLionel Sambuc
3884d9c625SLionel Sambuc #include <sys/cdefs.h>
39*0a6a1f1dSLionel Sambuc __KERNEL_RCSID(0, "$NetBSD: ulfs_quota1.c,v 1.9 2015/07/26 08:33:53 hannken Exp $");
4084d9c625SLionel Sambuc
4184d9c625SLionel Sambuc #include <sys/param.h>
4284d9c625SLionel Sambuc #include <sys/kernel.h>
4384d9c625SLionel Sambuc #include <sys/systm.h>
4484d9c625SLionel Sambuc #include <sys/namei.h>
4584d9c625SLionel Sambuc #include <sys/file.h>
4684d9c625SLionel Sambuc #include <sys/proc.h>
4784d9c625SLionel Sambuc #include <sys/vnode.h>
4884d9c625SLionel Sambuc #include <sys/mount.h>
4984d9c625SLionel Sambuc #include <sys/kauth.h>
5084d9c625SLionel Sambuc
5184d9c625SLionel Sambuc #include <ufs/lfs/ulfs_quota1.h>
5284d9c625SLionel Sambuc #include <ufs/lfs/ulfs_inode.h>
5384d9c625SLionel Sambuc #include <ufs/lfs/ulfsmount.h>
5484d9c625SLionel Sambuc #include <ufs/lfs/ulfs_extern.h>
5584d9c625SLionel Sambuc #include <ufs/lfs/ulfs_quota.h>
5684d9c625SLionel Sambuc
5784d9c625SLionel Sambuc static int chkdqchg(struct inode *, int64_t, kauth_cred_t, int);
5884d9c625SLionel Sambuc static int chkiqchg(struct inode *, int32_t, kauth_cred_t, int);
5984d9c625SLionel Sambuc
6084d9c625SLionel Sambuc /*
6184d9c625SLionel Sambuc * Update disk usage, and take corrective action.
6284d9c625SLionel Sambuc */
6384d9c625SLionel Sambuc int
lfs_chkdq1(struct inode * ip,int64_t change,kauth_cred_t cred,int flags)6484d9c625SLionel Sambuc lfs_chkdq1(struct inode *ip, int64_t change, kauth_cred_t cred, int flags)
6584d9c625SLionel Sambuc {
6684d9c625SLionel Sambuc struct dquot *dq;
6784d9c625SLionel Sambuc int i;
6884d9c625SLionel Sambuc int ncurblocks, error;
6984d9c625SLionel Sambuc
7084d9c625SLionel Sambuc if ((error = lfs_getinoquota(ip)) != 0)
7184d9c625SLionel Sambuc return error;
7284d9c625SLionel Sambuc if (change == 0)
7384d9c625SLionel Sambuc return (0);
7484d9c625SLionel Sambuc if (change < 0) {
7584d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
7684d9c625SLionel Sambuc if ((dq = ip->i_dquot[i]) == NODQUOT)
7784d9c625SLionel Sambuc continue;
7884d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
7984d9c625SLionel Sambuc ncurblocks = dq->dq_curblocks + change;
8084d9c625SLionel Sambuc if (ncurblocks >= 0)
8184d9c625SLionel Sambuc dq->dq_curblocks = ncurblocks;
8284d9c625SLionel Sambuc else
8384d9c625SLionel Sambuc dq->dq_curblocks = 0;
8484d9c625SLionel Sambuc dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
8584d9c625SLionel Sambuc dq->dq_flags |= DQ_MOD;
8684d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
8784d9c625SLionel Sambuc }
8884d9c625SLionel Sambuc return (0);
8984d9c625SLionel Sambuc }
9084d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
9184d9c625SLionel Sambuc if ((dq = ip->i_dquot[i]) == NODQUOT)
9284d9c625SLionel Sambuc continue;
9384d9c625SLionel Sambuc if ((flags & FORCE) == 0 &&
9484d9c625SLionel Sambuc kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
9584d9c625SLionel Sambuc KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT, KAUTH_ARG(i),
9684d9c625SLionel Sambuc KAUTH_ARG(QL_BLOCK), NULL) != 0) {
9784d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
9884d9c625SLionel Sambuc error = chkdqchg(ip, change, cred, i);
9984d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
10084d9c625SLionel Sambuc if (error != 0)
10184d9c625SLionel Sambuc return (error);
10284d9c625SLionel Sambuc }
10384d9c625SLionel Sambuc }
10484d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
10584d9c625SLionel Sambuc if ((dq = ip->i_dquot[i]) == NODQUOT)
10684d9c625SLionel Sambuc continue;
10784d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
10884d9c625SLionel Sambuc dq->dq_curblocks += change;
10984d9c625SLionel Sambuc dq->dq_flags |= DQ_MOD;
11084d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
11184d9c625SLionel Sambuc }
11284d9c625SLionel Sambuc return (0);
11384d9c625SLionel Sambuc }
11484d9c625SLionel Sambuc
11584d9c625SLionel Sambuc /*
11684d9c625SLionel Sambuc * Check for a valid change to a users allocation.
11784d9c625SLionel Sambuc * Issue an error message if appropriate.
11884d9c625SLionel Sambuc */
11984d9c625SLionel Sambuc static int
chkdqchg(struct inode * ip,int64_t change,kauth_cred_t cred,int type)12084d9c625SLionel Sambuc chkdqchg(struct inode *ip, int64_t change, kauth_cred_t cred, int type)
12184d9c625SLionel Sambuc {
12284d9c625SLionel Sambuc struct dquot *dq = ip->i_dquot[type];
12384d9c625SLionel Sambuc long ncurblocks = dq->dq_curblocks + change;
12484d9c625SLionel Sambuc
12584d9c625SLionel Sambuc KASSERT(mutex_owned(&dq->dq_interlock));
12684d9c625SLionel Sambuc /*
12784d9c625SLionel Sambuc * If user would exceed their hard limit, disallow space allocation.
12884d9c625SLionel Sambuc */
12984d9c625SLionel Sambuc if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
13084d9c625SLionel Sambuc if ((dq->dq_flags & DQ_WARN(QL_BLOCK)) == 0 &&
13184d9c625SLionel Sambuc ip->i_uid == kauth_cred_geteuid(cred)) {
13284d9c625SLionel Sambuc uprintf("\n%s: write failed, %s disk limit reached\n",
13384d9c625SLionel Sambuc ITOV(ip)->v_mount->mnt_stat.f_mntonname,
13484d9c625SLionel Sambuc lfs_quotatypes[type]);
13584d9c625SLionel Sambuc dq->dq_flags |= DQ_WARN(QL_BLOCK);
13684d9c625SLionel Sambuc }
13784d9c625SLionel Sambuc return (EDQUOT);
13884d9c625SLionel Sambuc }
13984d9c625SLionel Sambuc /*
14084d9c625SLionel Sambuc * If user is over their soft limit for too long, disallow space
14184d9c625SLionel Sambuc * allocation. Reset time limit as they cross their soft limit.
14284d9c625SLionel Sambuc */
14384d9c625SLionel Sambuc if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
14484d9c625SLionel Sambuc if (dq->dq_curblocks < dq->dq_bsoftlimit) {
14584d9c625SLionel Sambuc dq->dq_btime =
14684d9c625SLionel Sambuc time_second + ip->i_ump->umq1_btime[type];
14784d9c625SLionel Sambuc if (ip->i_uid == kauth_cred_geteuid(cred))
14884d9c625SLionel Sambuc uprintf("\n%s: warning, %s %s\n",
14984d9c625SLionel Sambuc ITOV(ip)->v_mount->mnt_stat.f_mntonname,
15084d9c625SLionel Sambuc lfs_quotatypes[type], "disk quota exceeded");
15184d9c625SLionel Sambuc return (0);
15284d9c625SLionel Sambuc }
15384d9c625SLionel Sambuc if (time_second > dq->dq_btime) {
15484d9c625SLionel Sambuc if ((dq->dq_flags & DQ_WARN(QL_BLOCK)) == 0 &&
15584d9c625SLionel Sambuc ip->i_uid == kauth_cred_geteuid(cred)) {
15684d9c625SLionel Sambuc uprintf("\n%s: write failed, %s %s\n",
15784d9c625SLionel Sambuc ITOV(ip)->v_mount->mnt_stat.f_mntonname,
15884d9c625SLionel Sambuc lfs_quotatypes[type],
15984d9c625SLionel Sambuc "disk quota exceeded for too long");
16084d9c625SLionel Sambuc dq->dq_flags |= DQ_WARN(QL_BLOCK);
16184d9c625SLionel Sambuc }
16284d9c625SLionel Sambuc return (EDQUOT);
16384d9c625SLionel Sambuc }
16484d9c625SLionel Sambuc }
16584d9c625SLionel Sambuc return (0);
16684d9c625SLionel Sambuc }
16784d9c625SLionel Sambuc
16884d9c625SLionel Sambuc /*
16984d9c625SLionel Sambuc * Check the inode limit, applying corrective action.
17084d9c625SLionel Sambuc */
17184d9c625SLionel Sambuc int
lfs_chkiq1(struct inode * ip,int32_t change,kauth_cred_t cred,int flags)17284d9c625SLionel Sambuc lfs_chkiq1(struct inode *ip, int32_t change, kauth_cred_t cred, int flags)
17384d9c625SLionel Sambuc {
17484d9c625SLionel Sambuc struct dquot *dq;
17584d9c625SLionel Sambuc int i;
17684d9c625SLionel Sambuc int ncurinodes, error;
17784d9c625SLionel Sambuc
17884d9c625SLionel Sambuc if ((error = lfs_getinoquota(ip)) != 0)
17984d9c625SLionel Sambuc return error;
18084d9c625SLionel Sambuc if (change == 0)
18184d9c625SLionel Sambuc return (0);
18284d9c625SLionel Sambuc if (change < 0) {
18384d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
18484d9c625SLionel Sambuc if ((dq = ip->i_dquot[i]) == NODQUOT)
18584d9c625SLionel Sambuc continue;
18684d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
18784d9c625SLionel Sambuc ncurinodes = dq->dq_curinodes + change;
18884d9c625SLionel Sambuc if (ncurinodes >= 0)
18984d9c625SLionel Sambuc dq->dq_curinodes = ncurinodes;
19084d9c625SLionel Sambuc else
19184d9c625SLionel Sambuc dq->dq_curinodes = 0;
19284d9c625SLionel Sambuc dq->dq_flags &= ~DQ_WARN(QL_FILE);
19384d9c625SLionel Sambuc dq->dq_flags |= DQ_MOD;
19484d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
19584d9c625SLionel Sambuc }
19684d9c625SLionel Sambuc return (0);
19784d9c625SLionel Sambuc }
19884d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
19984d9c625SLionel Sambuc if ((dq = ip->i_dquot[i]) == NODQUOT)
20084d9c625SLionel Sambuc continue;
20184d9c625SLionel Sambuc if ((flags & FORCE) == 0 && kauth_authorize_system(cred,
20284d9c625SLionel Sambuc KAUTH_SYSTEM_FS_QUOTA, KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT,
20384d9c625SLionel Sambuc KAUTH_ARG(i), KAUTH_ARG(QL_FILE), NULL) != 0) {
20484d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
20584d9c625SLionel Sambuc error = chkiqchg(ip, change, cred, i);
20684d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
20784d9c625SLionel Sambuc if (error != 0)
20884d9c625SLionel Sambuc return (error);
20984d9c625SLionel Sambuc }
21084d9c625SLionel Sambuc }
21184d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
21284d9c625SLionel Sambuc if ((dq = ip->i_dquot[i]) == NODQUOT)
21384d9c625SLionel Sambuc continue;
21484d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
21584d9c625SLionel Sambuc dq->dq_curinodes += change;
21684d9c625SLionel Sambuc dq->dq_flags |= DQ_MOD;
21784d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
21884d9c625SLionel Sambuc }
21984d9c625SLionel Sambuc return (0);
22084d9c625SLionel Sambuc }
22184d9c625SLionel Sambuc
22284d9c625SLionel Sambuc /*
22384d9c625SLionel Sambuc * Check for a valid change to a users allocation.
22484d9c625SLionel Sambuc * Issue an error message if appropriate.
22584d9c625SLionel Sambuc */
22684d9c625SLionel Sambuc static int
chkiqchg(struct inode * ip,int32_t change,kauth_cred_t cred,int type)22784d9c625SLionel Sambuc chkiqchg(struct inode *ip, int32_t change, kauth_cred_t cred, int type)
22884d9c625SLionel Sambuc {
22984d9c625SLionel Sambuc struct dquot *dq = ip->i_dquot[type];
23084d9c625SLionel Sambuc long ncurinodes = dq->dq_curinodes + change;
23184d9c625SLionel Sambuc
23284d9c625SLionel Sambuc KASSERT(mutex_owned(&dq->dq_interlock));
23384d9c625SLionel Sambuc /*
23484d9c625SLionel Sambuc * If user would exceed their hard limit, disallow inode allocation.
23584d9c625SLionel Sambuc */
23684d9c625SLionel Sambuc if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
23784d9c625SLionel Sambuc if ((dq->dq_flags & DQ_WARN(QL_FILE)) == 0 &&
23884d9c625SLionel Sambuc ip->i_uid == kauth_cred_geteuid(cred)) {
23984d9c625SLionel Sambuc uprintf("\n%s: write failed, %s inode limit reached\n",
24084d9c625SLionel Sambuc ITOV(ip)->v_mount->mnt_stat.f_mntonname,
24184d9c625SLionel Sambuc lfs_quotatypes[type]);
24284d9c625SLionel Sambuc dq->dq_flags |= DQ_WARN(QL_FILE);
24384d9c625SLionel Sambuc }
24484d9c625SLionel Sambuc return (EDQUOT);
24584d9c625SLionel Sambuc }
24684d9c625SLionel Sambuc /*
24784d9c625SLionel Sambuc * If user is over their soft limit for too long, disallow inode
24884d9c625SLionel Sambuc * allocation. Reset time limit as they cross their soft limit.
24984d9c625SLionel Sambuc */
25084d9c625SLionel Sambuc if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
25184d9c625SLionel Sambuc if (dq->dq_curinodes < dq->dq_isoftlimit) {
25284d9c625SLionel Sambuc dq->dq_itime =
25384d9c625SLionel Sambuc time_second + ip->i_ump->umq1_itime[type];
25484d9c625SLionel Sambuc if (ip->i_uid == kauth_cred_geteuid(cred))
25584d9c625SLionel Sambuc uprintf("\n%s: warning, %s %s\n",
25684d9c625SLionel Sambuc ITOV(ip)->v_mount->mnt_stat.f_mntonname,
25784d9c625SLionel Sambuc lfs_quotatypes[type], "inode quota exceeded");
25884d9c625SLionel Sambuc return (0);
25984d9c625SLionel Sambuc }
26084d9c625SLionel Sambuc if (time_second > dq->dq_itime) {
26184d9c625SLionel Sambuc if ((dq->dq_flags & DQ_WARN(QL_FILE)) == 0 &&
26284d9c625SLionel Sambuc ip->i_uid == kauth_cred_geteuid(cred)) {
26384d9c625SLionel Sambuc uprintf("\n%s: write failed, %s %s\n",
26484d9c625SLionel Sambuc ITOV(ip)->v_mount->mnt_stat.f_mntonname,
26584d9c625SLionel Sambuc lfs_quotatypes[type],
26684d9c625SLionel Sambuc "inode quota exceeded for too long");
26784d9c625SLionel Sambuc dq->dq_flags |= DQ_WARN(QL_FILE);
26884d9c625SLionel Sambuc }
26984d9c625SLionel Sambuc return (EDQUOT);
27084d9c625SLionel Sambuc }
27184d9c625SLionel Sambuc }
27284d9c625SLionel Sambuc return (0);
27384d9c625SLionel Sambuc }
27484d9c625SLionel Sambuc
27584d9c625SLionel Sambuc int
lfsquota1_umount(struct mount * mp,int flags)27684d9c625SLionel Sambuc lfsquota1_umount(struct mount *mp, int flags)
27784d9c625SLionel Sambuc {
27884d9c625SLionel Sambuc int i, error;
27984d9c625SLionel Sambuc struct ulfsmount *ump = VFSTOULFS(mp);
28084d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
28184d9c625SLionel Sambuc struct lwp *l = curlwp;
28284d9c625SLionel Sambuc
28384d9c625SLionel Sambuc if ((fs->um_flags & ULFS_QUOTA) == 0)
28484d9c625SLionel Sambuc return 0;
28584d9c625SLionel Sambuc
28684d9c625SLionel Sambuc if ((error = vflush(mp, NULLVP, SKIPSYSTEM | flags)) != 0)
28784d9c625SLionel Sambuc return (error);
28884d9c625SLionel Sambuc
28984d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
29084d9c625SLionel Sambuc if (ump->um_quotas[i] != NULLVP) {
29184d9c625SLionel Sambuc lfsquota1_handle_cmd_quotaoff(l, ump, i);
29284d9c625SLionel Sambuc }
29384d9c625SLionel Sambuc }
29484d9c625SLionel Sambuc return 0;
29584d9c625SLionel Sambuc }
29684d9c625SLionel Sambuc
29784d9c625SLionel Sambuc /*
29884d9c625SLionel Sambuc * Code to process quotactl commands.
29984d9c625SLionel Sambuc */
30084d9c625SLionel Sambuc
30184d9c625SLionel Sambuc /*
30284d9c625SLionel Sambuc * set up a quota file for a particular file system.
30384d9c625SLionel Sambuc */
30484d9c625SLionel Sambuc int
lfsquota1_handle_cmd_quotaon(struct lwp * l,struct ulfsmount * ump,int type,const char * fname)30584d9c625SLionel Sambuc lfsquota1_handle_cmd_quotaon(struct lwp *l, struct ulfsmount *ump, int type,
30684d9c625SLionel Sambuc const char *fname)
30784d9c625SLionel Sambuc {
30884d9c625SLionel Sambuc struct mount *mp = ump->um_mountp;
30984d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
310*0a6a1f1dSLionel Sambuc struct vnode *vp, **vpp;
311*0a6a1f1dSLionel Sambuc struct vnode_iterator *marker;
31284d9c625SLionel Sambuc struct dquot *dq;
31384d9c625SLionel Sambuc int error;
31484d9c625SLionel Sambuc struct pathbuf *pb;
31584d9c625SLionel Sambuc struct nameidata nd;
31684d9c625SLionel Sambuc
31784d9c625SLionel Sambuc if (fs->um_flags & ULFS_QUOTA2) {
31884d9c625SLionel Sambuc uprintf("%s: quotas v2 already enabled\n",
31984d9c625SLionel Sambuc mp->mnt_stat.f_mntonname);
32084d9c625SLionel Sambuc return (EBUSY);
32184d9c625SLionel Sambuc }
32284d9c625SLionel Sambuc
32384d9c625SLionel Sambuc vpp = &ump->um_quotas[type];
32484d9c625SLionel Sambuc
32584d9c625SLionel Sambuc pb = pathbuf_create(fname);
32684d9c625SLionel Sambuc if (pb == NULL) {
32784d9c625SLionel Sambuc return ENOMEM;
32884d9c625SLionel Sambuc }
32984d9c625SLionel Sambuc NDINIT(&nd, LOOKUP, FOLLOW, pb);
33084d9c625SLionel Sambuc if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0) {
33184d9c625SLionel Sambuc pathbuf_destroy(pb);
33284d9c625SLionel Sambuc return error;
33384d9c625SLionel Sambuc }
33484d9c625SLionel Sambuc vp = nd.ni_vp;
33584d9c625SLionel Sambuc pathbuf_destroy(pb);
33684d9c625SLionel Sambuc
33784d9c625SLionel Sambuc VOP_UNLOCK(vp);
33884d9c625SLionel Sambuc if (vp->v_type != VREG) {
33984d9c625SLionel Sambuc (void) vn_close(vp, FREAD|FWRITE, l->l_cred);
34084d9c625SLionel Sambuc return (EACCES);
34184d9c625SLionel Sambuc }
34284d9c625SLionel Sambuc if (*vpp != vp)
34384d9c625SLionel Sambuc lfsquota1_handle_cmd_quotaoff(l, ump, type);
34484d9c625SLionel Sambuc mutex_enter(&lfs_dqlock);
34584d9c625SLionel Sambuc while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
34684d9c625SLionel Sambuc cv_wait(&lfs_dqcv, &lfs_dqlock);
34784d9c625SLionel Sambuc ump->umq1_qflags[type] |= QTF_OPENING;
34884d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
34984d9c625SLionel Sambuc mp->mnt_flag |= MNT_QUOTA;
35084d9c625SLionel Sambuc vp->v_vflag |= VV_SYSTEM; /* XXXSMP */
35184d9c625SLionel Sambuc *vpp = vp;
35284d9c625SLionel Sambuc /*
35384d9c625SLionel Sambuc * Save the credential of the process that turned on quotas.
35484d9c625SLionel Sambuc * Set up the time limits for this quota.
35584d9c625SLionel Sambuc */
35684d9c625SLionel Sambuc kauth_cred_hold(l->l_cred);
35784d9c625SLionel Sambuc ump->um_cred[type] = l->l_cred;
35884d9c625SLionel Sambuc ump->umq1_btime[type] = MAX_DQ_TIME;
35984d9c625SLionel Sambuc ump->umq1_itime[type] = MAX_IQ_TIME;
36084d9c625SLionel Sambuc if (lfs_dqget(NULLVP, 0, ump, type, &dq) == 0) {
36184d9c625SLionel Sambuc if (dq->dq_btime > 0)
36284d9c625SLionel Sambuc ump->umq1_btime[type] = dq->dq_btime;
36384d9c625SLionel Sambuc if (dq->dq_itime > 0)
36484d9c625SLionel Sambuc ump->umq1_itime[type] = dq->dq_itime;
36584d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
36684d9c625SLionel Sambuc }
36784d9c625SLionel Sambuc /*
36884d9c625SLionel Sambuc * Search vnodes associated with this mount point,
36984d9c625SLionel Sambuc * adding references to quota file being opened.
37084d9c625SLionel Sambuc * NB: only need to add dquot's for inodes being modified.
37184d9c625SLionel Sambuc */
372*0a6a1f1dSLionel Sambuc vfs_vnode_iterator_init(mp, &marker);
373*0a6a1f1dSLionel Sambuc while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
374*0a6a1f1dSLionel Sambuc error = vn_lock(vp, LK_EXCLUSIVE);
375*0a6a1f1dSLionel Sambuc if (error) {
376*0a6a1f1dSLionel Sambuc vrele(vp);
37784d9c625SLionel Sambuc continue;
37884d9c625SLionel Sambuc }
379*0a6a1f1dSLionel Sambuc mutex_enter(vp->v_interlock);
380*0a6a1f1dSLionel Sambuc if (VTOI(vp) == NULL || vp->v_type == VNON ||
381*0a6a1f1dSLionel Sambuc vp->v_writecount == 0) {
382*0a6a1f1dSLionel Sambuc mutex_exit(vp->v_interlock);
383*0a6a1f1dSLionel Sambuc vput(vp);
384*0a6a1f1dSLionel Sambuc continue;
38584d9c625SLionel Sambuc }
386*0a6a1f1dSLionel Sambuc mutex_exit(vp->v_interlock);
38784d9c625SLionel Sambuc if ((error = lfs_getinoquota(VTOI(vp))) != 0) {
38884d9c625SLionel Sambuc vput(vp);
38984d9c625SLionel Sambuc break;
39084d9c625SLionel Sambuc }
39184d9c625SLionel Sambuc vput(vp);
39284d9c625SLionel Sambuc }
393*0a6a1f1dSLionel Sambuc vfs_vnode_iterator_destroy(marker);
39484d9c625SLionel Sambuc
39584d9c625SLionel Sambuc mutex_enter(&lfs_dqlock);
39684d9c625SLionel Sambuc ump->umq1_qflags[type] &= ~QTF_OPENING;
39784d9c625SLionel Sambuc cv_broadcast(&lfs_dqcv);
39884d9c625SLionel Sambuc if (error == 0)
39984d9c625SLionel Sambuc fs->um_flags |= ULFS_QUOTA;
40084d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
40184d9c625SLionel Sambuc if (error)
40284d9c625SLionel Sambuc lfsquota1_handle_cmd_quotaoff(l, ump, type);
40384d9c625SLionel Sambuc return (error);
40484d9c625SLionel Sambuc }
40584d9c625SLionel Sambuc
40684d9c625SLionel Sambuc /*
40784d9c625SLionel Sambuc * turn off disk quotas for a filesystem.
40884d9c625SLionel Sambuc */
40984d9c625SLionel Sambuc int
lfsquota1_handle_cmd_quotaoff(struct lwp * l,struct ulfsmount * ump,int type)41084d9c625SLionel Sambuc lfsquota1_handle_cmd_quotaoff(struct lwp *l, struct ulfsmount *ump, int type)
41184d9c625SLionel Sambuc {
41284d9c625SLionel Sambuc struct mount *mp = ump->um_mountp;
41384d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
41484d9c625SLionel Sambuc struct vnode *vp;
415*0a6a1f1dSLionel Sambuc struct vnode *qvp;
416*0a6a1f1dSLionel Sambuc struct vnode_iterator *marker;
41784d9c625SLionel Sambuc struct dquot *dq;
41884d9c625SLionel Sambuc struct inode *ip;
41984d9c625SLionel Sambuc kauth_cred_t cred;
42084d9c625SLionel Sambuc int i, error;
42184d9c625SLionel Sambuc
42284d9c625SLionel Sambuc mutex_enter(&lfs_dqlock);
42384d9c625SLionel Sambuc while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
42484d9c625SLionel Sambuc cv_wait(&lfs_dqcv, &lfs_dqlock);
42584d9c625SLionel Sambuc if ((qvp = ump->um_quotas[type]) == NULLVP) {
42684d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
42784d9c625SLionel Sambuc return (0);
42884d9c625SLionel Sambuc }
42984d9c625SLionel Sambuc ump->umq1_qflags[type] |= QTF_CLOSING;
43084d9c625SLionel Sambuc fs->um_flags &= ~ULFS_QUOTA;
43184d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
43284d9c625SLionel Sambuc /*
43384d9c625SLionel Sambuc * Search vnodes associated with this mount point,
43484d9c625SLionel Sambuc * deleting any references to quota file being closed.
43584d9c625SLionel Sambuc */
436*0a6a1f1dSLionel Sambuc vfs_vnode_iterator_init(mp, &marker);
437*0a6a1f1dSLionel Sambuc while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
438*0a6a1f1dSLionel Sambuc error = vn_lock(vp, LK_EXCLUSIVE);
439*0a6a1f1dSLionel Sambuc if (error) {
440*0a6a1f1dSLionel Sambuc vrele(vp);
44184d9c625SLionel Sambuc continue;
44284d9c625SLionel Sambuc }
44384d9c625SLionel Sambuc ip = VTOI(vp);
444*0a6a1f1dSLionel Sambuc if (ip == NULL || vp->v_type == VNON) {
445*0a6a1f1dSLionel Sambuc vput(vp);
446*0a6a1f1dSLionel Sambuc continue;
447*0a6a1f1dSLionel Sambuc }
44884d9c625SLionel Sambuc dq = ip->i_dquot[type];
44984d9c625SLionel Sambuc ip->i_dquot[type] = NODQUOT;
45084d9c625SLionel Sambuc lfs_dqrele(vp, dq);
45184d9c625SLionel Sambuc vput(vp);
45284d9c625SLionel Sambuc }
453*0a6a1f1dSLionel Sambuc vfs_vnode_iterator_destroy(marker);
45484d9c625SLionel Sambuc #ifdef DIAGNOSTIC
45584d9c625SLionel Sambuc lfs_dqflush(qvp);
45684d9c625SLionel Sambuc #endif
45784d9c625SLionel Sambuc qvp->v_vflag &= ~VV_SYSTEM;
45884d9c625SLionel Sambuc error = vn_close(qvp, FREAD|FWRITE, l->l_cred);
45984d9c625SLionel Sambuc mutex_enter(&lfs_dqlock);
46084d9c625SLionel Sambuc ump->um_quotas[type] = NULLVP;
46184d9c625SLionel Sambuc cred = ump->um_cred[type];
46284d9c625SLionel Sambuc ump->um_cred[type] = NOCRED;
46384d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++)
46484d9c625SLionel Sambuc if (ump->um_quotas[i] != NULLVP)
46584d9c625SLionel Sambuc break;
46684d9c625SLionel Sambuc ump->umq1_qflags[type] &= ~QTF_CLOSING;
46784d9c625SLionel Sambuc cv_broadcast(&lfs_dqcv);
46884d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
46984d9c625SLionel Sambuc kauth_cred_free(cred);
47084d9c625SLionel Sambuc if (i == ULFS_MAXQUOTAS)
47184d9c625SLionel Sambuc mp->mnt_flag &= ~MNT_QUOTA;
47284d9c625SLionel Sambuc return (error);
47384d9c625SLionel Sambuc }
47484d9c625SLionel Sambuc
47584d9c625SLionel Sambuc int
lfsquota1_handle_cmd_get(struct ulfsmount * ump,const struct quotakey * qk,struct quotaval * qv)47684d9c625SLionel Sambuc lfsquota1_handle_cmd_get(struct ulfsmount *ump, const struct quotakey *qk,
47784d9c625SLionel Sambuc struct quotaval *qv)
47884d9c625SLionel Sambuc {
47984d9c625SLionel Sambuc struct dquot *dq;
48084d9c625SLionel Sambuc int error;
48184d9c625SLionel Sambuc struct quotaval blocks, files;
48284d9c625SLionel Sambuc int idtype;
48384d9c625SLionel Sambuc id_t id;
48484d9c625SLionel Sambuc
48584d9c625SLionel Sambuc idtype = qk->qk_idtype;
48684d9c625SLionel Sambuc id = qk->qk_id;
48784d9c625SLionel Sambuc
48884d9c625SLionel Sambuc if (ump->um_quotas[idtype] == NULLVP)
48984d9c625SLionel Sambuc return ENODEV;
49084d9c625SLionel Sambuc
49184d9c625SLionel Sambuc if (id == QUOTA_DEFAULTID) { /* we want the grace period of id 0 */
49284d9c625SLionel Sambuc if ((error = lfs_dqget(NULLVP, 0, ump, idtype, &dq)) != 0)
49384d9c625SLionel Sambuc return error;
49484d9c625SLionel Sambuc
49584d9c625SLionel Sambuc } else {
49684d9c625SLionel Sambuc if ((error = lfs_dqget(NULLVP, id, ump, idtype, &dq)) != 0)
49784d9c625SLionel Sambuc return error;
49884d9c625SLionel Sambuc }
49984d9c625SLionel Sambuc lfs_dqblk_to_quotavals(&dq->dq_un.dq1_dqb, &blocks, &files);
50084d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
50184d9c625SLionel Sambuc if (id == QUOTA_DEFAULTID) {
50284d9c625SLionel Sambuc if (blocks.qv_expiretime > 0)
50384d9c625SLionel Sambuc blocks.qv_grace = blocks.qv_expiretime;
50484d9c625SLionel Sambuc else
50584d9c625SLionel Sambuc blocks.qv_grace = MAX_DQ_TIME;
50684d9c625SLionel Sambuc if (files.qv_expiretime > 0)
50784d9c625SLionel Sambuc files.qv_grace = files.qv_expiretime;
50884d9c625SLionel Sambuc else
50984d9c625SLionel Sambuc files.qv_grace = MAX_DQ_TIME;
51084d9c625SLionel Sambuc }
51184d9c625SLionel Sambuc
51284d9c625SLionel Sambuc switch (qk->qk_objtype) {
51384d9c625SLionel Sambuc case QUOTA_OBJTYPE_BLOCKS:
51484d9c625SLionel Sambuc *qv = blocks;
51584d9c625SLionel Sambuc break;
51684d9c625SLionel Sambuc case QUOTA_OBJTYPE_FILES:
51784d9c625SLionel Sambuc *qv = files;
51884d9c625SLionel Sambuc break;
51984d9c625SLionel Sambuc default:
52084d9c625SLionel Sambuc return EINVAL;
52184d9c625SLionel Sambuc }
52284d9c625SLionel Sambuc
52384d9c625SLionel Sambuc return 0;
52484d9c625SLionel Sambuc }
52584d9c625SLionel Sambuc
52684d9c625SLionel Sambuc static uint32_t
quota1_encode_limit(uint64_t lim)52784d9c625SLionel Sambuc quota1_encode_limit(uint64_t lim)
52884d9c625SLionel Sambuc {
52984d9c625SLionel Sambuc if (lim == QUOTA_NOLIMIT || lim >= 0xffffffff) {
53084d9c625SLionel Sambuc return 0;
53184d9c625SLionel Sambuc }
53284d9c625SLionel Sambuc return lim;
53384d9c625SLionel Sambuc }
53484d9c625SLionel Sambuc
53584d9c625SLionel Sambuc int
lfsquota1_handle_cmd_put(struct ulfsmount * ump,const struct quotakey * key,const struct quotaval * val)53684d9c625SLionel Sambuc lfsquota1_handle_cmd_put(struct ulfsmount *ump, const struct quotakey *key,
53784d9c625SLionel Sambuc const struct quotaval *val)
53884d9c625SLionel Sambuc {
53984d9c625SLionel Sambuc struct dquot *dq;
54084d9c625SLionel Sambuc struct dqblk dqb;
54184d9c625SLionel Sambuc int error;
54284d9c625SLionel Sambuc
54384d9c625SLionel Sambuc switch (key->qk_idtype) {
54484d9c625SLionel Sambuc case QUOTA_IDTYPE_USER:
54584d9c625SLionel Sambuc case QUOTA_IDTYPE_GROUP:
54684d9c625SLionel Sambuc break;
54784d9c625SLionel Sambuc default:
54884d9c625SLionel Sambuc return EINVAL;
54984d9c625SLionel Sambuc }
55084d9c625SLionel Sambuc
55184d9c625SLionel Sambuc switch (key->qk_objtype) {
55284d9c625SLionel Sambuc case QUOTA_OBJTYPE_BLOCKS:
55384d9c625SLionel Sambuc case QUOTA_OBJTYPE_FILES:
55484d9c625SLionel Sambuc break;
55584d9c625SLionel Sambuc default:
55684d9c625SLionel Sambuc return EINVAL;
55784d9c625SLionel Sambuc }
55884d9c625SLionel Sambuc
55984d9c625SLionel Sambuc if (ump->um_quotas[key->qk_idtype] == NULLVP)
56084d9c625SLionel Sambuc return ENODEV;
56184d9c625SLionel Sambuc
56284d9c625SLionel Sambuc if (key->qk_id == QUOTA_DEFAULTID) {
56384d9c625SLionel Sambuc /* just update grace times */
56484d9c625SLionel Sambuc id_t id = 0;
56584d9c625SLionel Sambuc
56684d9c625SLionel Sambuc if ((error = lfs_dqget(NULLVP, id, ump, key->qk_idtype, &dq)) != 0)
56784d9c625SLionel Sambuc return error;
56884d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
56984d9c625SLionel Sambuc if (val->qv_grace != QUOTA_NOTIME) {
57084d9c625SLionel Sambuc if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS)
57184d9c625SLionel Sambuc ump->umq1_btime[key->qk_idtype] = dq->dq_btime =
57284d9c625SLionel Sambuc val->qv_grace;
57384d9c625SLionel Sambuc if (key->qk_objtype == QUOTA_OBJTYPE_FILES)
57484d9c625SLionel Sambuc ump->umq1_itime[key->qk_idtype] = dq->dq_itime =
57584d9c625SLionel Sambuc val->qv_grace;
57684d9c625SLionel Sambuc }
57784d9c625SLionel Sambuc dq->dq_flags |= DQ_MOD;
57884d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
57984d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
58084d9c625SLionel Sambuc return 0;
58184d9c625SLionel Sambuc }
58284d9c625SLionel Sambuc
58384d9c625SLionel Sambuc if ((error = lfs_dqget(NULLVP, key->qk_id, ump, key->qk_idtype, &dq)) != 0)
58484d9c625SLionel Sambuc return (error);
58584d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
58684d9c625SLionel Sambuc /*
58784d9c625SLionel Sambuc * Copy all but the current values.
58884d9c625SLionel Sambuc * Reset time limit if previously had no soft limit or were
58984d9c625SLionel Sambuc * under it, but now have a soft limit and are over it.
59084d9c625SLionel Sambuc */
59184d9c625SLionel Sambuc dqb.dqb_curblocks = dq->dq_curblocks;
59284d9c625SLionel Sambuc dqb.dqb_curinodes = dq->dq_curinodes;
59384d9c625SLionel Sambuc dqb.dqb_btime = dq->dq_btime;
59484d9c625SLionel Sambuc dqb.dqb_itime = dq->dq_itime;
59584d9c625SLionel Sambuc if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) {
59684d9c625SLionel Sambuc dqb.dqb_bsoftlimit = quota1_encode_limit(val->qv_softlimit);
59784d9c625SLionel Sambuc dqb.dqb_bhardlimit = quota1_encode_limit(val->qv_hardlimit);
59884d9c625SLionel Sambuc dqb.dqb_isoftlimit = dq->dq_isoftlimit;
59984d9c625SLionel Sambuc dqb.dqb_ihardlimit = dq->dq_ihardlimit;
60084d9c625SLionel Sambuc } else {
60184d9c625SLionel Sambuc KASSERT(key->qk_objtype == QUOTA_OBJTYPE_FILES);
60284d9c625SLionel Sambuc dqb.dqb_bsoftlimit = dq->dq_bsoftlimit;
60384d9c625SLionel Sambuc dqb.dqb_bhardlimit = dq->dq_bhardlimit;
60484d9c625SLionel Sambuc dqb.dqb_isoftlimit = quota1_encode_limit(val->qv_softlimit);
60584d9c625SLionel Sambuc dqb.dqb_ihardlimit = quota1_encode_limit(val->qv_hardlimit);
60684d9c625SLionel Sambuc }
60784d9c625SLionel Sambuc if (dq->dq_id == 0 && val->qv_grace != QUOTA_NOTIME) {
60884d9c625SLionel Sambuc /* also update grace time if available */
60984d9c625SLionel Sambuc if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) {
61084d9c625SLionel Sambuc ump->umq1_btime[key->qk_idtype] = dqb.dqb_btime =
61184d9c625SLionel Sambuc val->qv_grace;
61284d9c625SLionel Sambuc }
61384d9c625SLionel Sambuc if (key->qk_objtype == QUOTA_OBJTYPE_FILES) {
61484d9c625SLionel Sambuc ump->umq1_itime[key->qk_idtype] = dqb.dqb_itime =
61584d9c625SLionel Sambuc val->qv_grace;
61684d9c625SLionel Sambuc }
61784d9c625SLionel Sambuc }
61884d9c625SLionel Sambuc if (dqb.dqb_bsoftlimit &&
61984d9c625SLionel Sambuc dq->dq_curblocks >= dqb.dqb_bsoftlimit &&
62084d9c625SLionel Sambuc (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
62184d9c625SLionel Sambuc dqb.dqb_btime = time_second + ump->umq1_btime[key->qk_idtype];
62284d9c625SLionel Sambuc if (dqb.dqb_isoftlimit &&
62384d9c625SLionel Sambuc dq->dq_curinodes >= dqb.dqb_isoftlimit &&
62484d9c625SLionel Sambuc (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
62584d9c625SLionel Sambuc dqb.dqb_itime = time_second + ump->umq1_itime[key->qk_idtype];
62684d9c625SLionel Sambuc dq->dq_un.dq1_dqb = dqb;
62784d9c625SLionel Sambuc if (dq->dq_curblocks < dq->dq_bsoftlimit)
62884d9c625SLionel Sambuc dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
62984d9c625SLionel Sambuc if (dq->dq_curinodes < dq->dq_isoftlimit)
63084d9c625SLionel Sambuc dq->dq_flags &= ~DQ_WARN(QL_FILE);
63184d9c625SLionel Sambuc if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
63284d9c625SLionel Sambuc dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
63384d9c625SLionel Sambuc dq->dq_flags |= DQ_FAKE;
63484d9c625SLionel Sambuc else
63584d9c625SLionel Sambuc dq->dq_flags &= ~DQ_FAKE;
63684d9c625SLionel Sambuc dq->dq_flags |= DQ_MOD;
63784d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
63884d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
63984d9c625SLionel Sambuc return (0);
64084d9c625SLionel Sambuc }
64184d9c625SLionel Sambuc
64284d9c625SLionel Sambuc
64384d9c625SLionel Sambuc #if 0
64484d9c625SLionel Sambuc /*
64584d9c625SLionel Sambuc * Q_SETQUOTA - assign an entire dqblk structure.
64684d9c625SLionel Sambuc */
64784d9c625SLionel Sambuc int
64884d9c625SLionel Sambuc setquota1(struct mount *mp, u_long id, int type, struct dqblk *dqb)
64984d9c625SLionel Sambuc {
65084d9c625SLionel Sambuc struct dquot *dq;
65184d9c625SLionel Sambuc struct dquot *ndq;
65284d9c625SLionel Sambuc struct ulfsmount *ump = VFSTOULFS(mp);
65384d9c625SLionel Sambuc
65484d9c625SLionel Sambuc
65584d9c625SLionel Sambuc if ((error = lfs_dqget(NULLVP, id, ump, type, &ndq)) != 0)
65684d9c625SLionel Sambuc return (error);
65784d9c625SLionel Sambuc dq = ndq;
65884d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
65984d9c625SLionel Sambuc /*
66084d9c625SLionel Sambuc * Copy all but the current values.
66184d9c625SLionel Sambuc * Reset time limit if previously had no soft limit or were
66284d9c625SLionel Sambuc * under it, but now have a soft limit and are over it.
66384d9c625SLionel Sambuc */
66484d9c625SLionel Sambuc dqb->dqb_curblocks = dq->dq_curblocks;
66584d9c625SLionel Sambuc dqb->dqb_curinodes = dq->dq_curinodes;
66684d9c625SLionel Sambuc if (dq->dq_id != 0) {
66784d9c625SLionel Sambuc dqb->dqb_btime = dq->dq_btime;
66884d9c625SLionel Sambuc dqb->dqb_itime = dq->dq_itime;
66984d9c625SLionel Sambuc }
67084d9c625SLionel Sambuc if (dqb->dqb_bsoftlimit &&
67184d9c625SLionel Sambuc dq->dq_curblocks >= dqb->dqb_bsoftlimit &&
67284d9c625SLionel Sambuc (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
67384d9c625SLionel Sambuc dqb->dqb_btime = time_second + ump->umq1_btime[type];
67484d9c625SLionel Sambuc if (dqb->dqb_isoftlimit &&
67584d9c625SLionel Sambuc dq->dq_curinodes >= dqb->dqb_isoftlimit &&
67684d9c625SLionel Sambuc (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
67784d9c625SLionel Sambuc dqb->dqb_itime = time_second + ump->umq1_itime[type];
67884d9c625SLionel Sambuc dq->dq_un.dq1_dqb = *dqb;
67984d9c625SLionel Sambuc if (dq->dq_curblocks < dq->dq_bsoftlimit)
68084d9c625SLionel Sambuc dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
68184d9c625SLionel Sambuc if (dq->dq_curinodes < dq->dq_isoftlimit)
68284d9c625SLionel Sambuc dq->dq_flags &= ~DQ_WARN(QL_FILE);
68384d9c625SLionel Sambuc if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
68484d9c625SLionel Sambuc dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
68584d9c625SLionel Sambuc dq->dq_flags |= DQ_FAKE;
68684d9c625SLionel Sambuc else
68784d9c625SLionel Sambuc dq->dq_flags &= ~DQ_FAKE;
68884d9c625SLionel Sambuc dq->dq_flags |= DQ_MOD;
68984d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
69084d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
69184d9c625SLionel Sambuc return (0);
69284d9c625SLionel Sambuc }
69384d9c625SLionel Sambuc
69484d9c625SLionel Sambuc /*
69584d9c625SLionel Sambuc * Q_SETUSE - set current inode and block usage.
69684d9c625SLionel Sambuc */
69784d9c625SLionel Sambuc int
69884d9c625SLionel Sambuc setuse(struct mount *mp, u_long id, int type, void *addr)
69984d9c625SLionel Sambuc {
70084d9c625SLionel Sambuc struct dquot *dq;
70184d9c625SLionel Sambuc struct ulfsmount *ump = VFSTOULFS(mp);
70284d9c625SLionel Sambuc struct dquot *ndq;
70384d9c625SLionel Sambuc struct dqblk usage;
70484d9c625SLionel Sambuc int error;
70584d9c625SLionel Sambuc
70684d9c625SLionel Sambuc error = copyin(addr, (void *)&usage, sizeof (struct dqblk));
70784d9c625SLionel Sambuc if (error)
70884d9c625SLionel Sambuc return (error);
70984d9c625SLionel Sambuc if ((error = lfs_dqget(NULLVP, id, ump, type, &ndq)) != 0)
71084d9c625SLionel Sambuc return (error);
71184d9c625SLionel Sambuc dq = ndq;
71284d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
71384d9c625SLionel Sambuc /*
71484d9c625SLionel Sambuc * Reset time limit if have a soft limit and were
71584d9c625SLionel Sambuc * previously under it, but are now over it.
71684d9c625SLionel Sambuc */
71784d9c625SLionel Sambuc if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
71884d9c625SLionel Sambuc usage.dqb_curblocks >= dq->dq_bsoftlimit)
71984d9c625SLionel Sambuc dq->dq_btime = time_second + ump->umq1_btime[type];
72084d9c625SLionel Sambuc if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
72184d9c625SLionel Sambuc usage.dqb_curinodes >= dq->dq_isoftlimit)
72284d9c625SLionel Sambuc dq->dq_itime = time_second + ump->umq1_itime[type];
72384d9c625SLionel Sambuc dq->dq_curblocks = usage.dqb_curblocks;
72484d9c625SLionel Sambuc dq->dq_curinodes = usage.dqb_curinodes;
72584d9c625SLionel Sambuc if (dq->dq_curblocks < dq->dq_bsoftlimit)
72684d9c625SLionel Sambuc dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
72784d9c625SLionel Sambuc if (dq->dq_curinodes < dq->dq_isoftlimit)
72884d9c625SLionel Sambuc dq->dq_flags &= ~DQ_WARN(QL_FILE);
72984d9c625SLionel Sambuc dq->dq_flags |= DQ_MOD;
73084d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
73184d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
73284d9c625SLionel Sambuc return (0);
73384d9c625SLionel Sambuc }
73484d9c625SLionel Sambuc #endif
73584d9c625SLionel Sambuc
73684d9c625SLionel Sambuc /*
73784d9c625SLionel Sambuc * Q_SYNC - sync quota files to disk.
73884d9c625SLionel Sambuc */
73984d9c625SLionel Sambuc int
lfs_q1sync(struct mount * mp)74084d9c625SLionel Sambuc lfs_q1sync(struct mount *mp)
74184d9c625SLionel Sambuc {
74284d9c625SLionel Sambuc struct ulfsmount *ump = VFSTOULFS(mp);
743*0a6a1f1dSLionel Sambuc struct vnode *vp;
744*0a6a1f1dSLionel Sambuc struct vnode_iterator *marker;
74584d9c625SLionel Sambuc struct dquot *dq;
74684d9c625SLionel Sambuc int i, error;
74784d9c625SLionel Sambuc
74884d9c625SLionel Sambuc /*
74984d9c625SLionel Sambuc * Check if the mount point has any quotas.
75084d9c625SLionel Sambuc * If not, simply return.
75184d9c625SLionel Sambuc */
75284d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++)
75384d9c625SLionel Sambuc if (ump->um_quotas[i] != NULLVP)
75484d9c625SLionel Sambuc break;
75584d9c625SLionel Sambuc if (i == ULFS_MAXQUOTAS)
75684d9c625SLionel Sambuc return (0);
75784d9c625SLionel Sambuc
75884d9c625SLionel Sambuc /*
75984d9c625SLionel Sambuc * Search vnodes associated with this mount point,
76084d9c625SLionel Sambuc * synchronizing any modified dquot structures.
76184d9c625SLionel Sambuc */
762*0a6a1f1dSLionel Sambuc vfs_vnode_iterator_init(mp, &marker);
763*0a6a1f1dSLionel Sambuc while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
764*0a6a1f1dSLionel Sambuc error = vn_lock(vp, LK_EXCLUSIVE);
765*0a6a1f1dSLionel Sambuc if (error) {
766*0a6a1f1dSLionel Sambuc vrele(vp);
76784d9c625SLionel Sambuc continue;
76884d9c625SLionel Sambuc }
769*0a6a1f1dSLionel Sambuc if (VTOI(vp) == NULL || vp->v_type == VNON) {
770*0a6a1f1dSLionel Sambuc vput(vp);
77184d9c625SLionel Sambuc continue;
77284d9c625SLionel Sambuc }
77384d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
77484d9c625SLionel Sambuc dq = VTOI(vp)->i_dquot[i];
77584d9c625SLionel Sambuc if (dq == NODQUOT)
77684d9c625SLionel Sambuc continue;
77784d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
77884d9c625SLionel Sambuc if (dq->dq_flags & DQ_MOD)
77984d9c625SLionel Sambuc lfs_dq1sync(vp, dq);
78084d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
78184d9c625SLionel Sambuc }
78284d9c625SLionel Sambuc vput(vp);
78384d9c625SLionel Sambuc }
784*0a6a1f1dSLionel Sambuc vfs_vnode_iterator_destroy(marker);
78584d9c625SLionel Sambuc return (0);
78684d9c625SLionel Sambuc }
78784d9c625SLionel Sambuc
78884d9c625SLionel Sambuc /*
78984d9c625SLionel Sambuc * Obtain a dquot structure for the specified identifier and quota file
79084d9c625SLionel Sambuc * reading the information from the file if necessary.
79184d9c625SLionel Sambuc */
79284d9c625SLionel Sambuc int
lfs_dq1get(struct vnode * dqvp,u_long id,struct ulfsmount * ump,int type,struct dquot * dq)79384d9c625SLionel Sambuc lfs_dq1get(struct vnode *dqvp, u_long id, struct ulfsmount *ump, int type,
79484d9c625SLionel Sambuc struct dquot *dq)
79584d9c625SLionel Sambuc {
79684d9c625SLionel Sambuc struct iovec aiov;
79784d9c625SLionel Sambuc struct uio auio;
79884d9c625SLionel Sambuc int error;
79984d9c625SLionel Sambuc
80084d9c625SLionel Sambuc KASSERT(mutex_owned(&dq->dq_interlock));
80184d9c625SLionel Sambuc vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
80284d9c625SLionel Sambuc auio.uio_iov = &aiov;
80384d9c625SLionel Sambuc auio.uio_iovcnt = 1;
80484d9c625SLionel Sambuc aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
80584d9c625SLionel Sambuc aiov.iov_len = sizeof (struct dqblk);
80684d9c625SLionel Sambuc auio.uio_resid = sizeof (struct dqblk);
80784d9c625SLionel Sambuc auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
80884d9c625SLionel Sambuc auio.uio_rw = UIO_READ;
80984d9c625SLionel Sambuc UIO_SETUP_SYSSPACE(&auio);
81084d9c625SLionel Sambuc error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
81184d9c625SLionel Sambuc if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
81284d9c625SLionel Sambuc memset((void *)&dq->dq_un.dq1_dqb, 0, sizeof(struct dqblk));
81384d9c625SLionel Sambuc VOP_UNLOCK(dqvp);
81484d9c625SLionel Sambuc /*
81584d9c625SLionel Sambuc * I/O error in reading quota file, release
81684d9c625SLionel Sambuc * quota structure and reflect problem to caller.
81784d9c625SLionel Sambuc */
81884d9c625SLionel Sambuc if (error)
81984d9c625SLionel Sambuc return (error);
82084d9c625SLionel Sambuc /*
82184d9c625SLionel Sambuc * Check for no limit to enforce.
82284d9c625SLionel Sambuc * Initialize time values if necessary.
82384d9c625SLionel Sambuc */
82484d9c625SLionel Sambuc if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
82584d9c625SLionel Sambuc dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
82684d9c625SLionel Sambuc dq->dq_flags |= DQ_FAKE;
82784d9c625SLionel Sambuc if (dq->dq_id != 0) {
82884d9c625SLionel Sambuc if (dq->dq_btime == 0)
82984d9c625SLionel Sambuc dq->dq_btime = time_second + ump->umq1_btime[type];
83084d9c625SLionel Sambuc if (dq->dq_itime == 0)
83184d9c625SLionel Sambuc dq->dq_itime = time_second + ump->umq1_itime[type];
83284d9c625SLionel Sambuc }
83384d9c625SLionel Sambuc return (0);
83484d9c625SLionel Sambuc }
83584d9c625SLionel Sambuc
83684d9c625SLionel Sambuc /*
83784d9c625SLionel Sambuc * Update the disk quota in the quota file.
83884d9c625SLionel Sambuc */
83984d9c625SLionel Sambuc int
lfs_dq1sync(struct vnode * vp,struct dquot * dq)84084d9c625SLionel Sambuc lfs_dq1sync(struct vnode *vp, struct dquot *dq)
84184d9c625SLionel Sambuc {
84284d9c625SLionel Sambuc struct vnode *dqvp;
84384d9c625SLionel Sambuc struct iovec aiov;
84484d9c625SLionel Sambuc struct uio auio;
84584d9c625SLionel Sambuc int error;
84684d9c625SLionel Sambuc
84784d9c625SLionel Sambuc if (dq == NODQUOT)
84884d9c625SLionel Sambuc panic("dq1sync: dquot");
84984d9c625SLionel Sambuc KASSERT(mutex_owned(&dq->dq_interlock));
85084d9c625SLionel Sambuc if ((dq->dq_flags & DQ_MOD) == 0)
85184d9c625SLionel Sambuc return (0);
85284d9c625SLionel Sambuc if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
85384d9c625SLionel Sambuc panic("dq1sync: file");
85484d9c625SLionel Sambuc KASSERT(dqvp != vp);
85584d9c625SLionel Sambuc vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
85684d9c625SLionel Sambuc auio.uio_iov = &aiov;
85784d9c625SLionel Sambuc auio.uio_iovcnt = 1;
85884d9c625SLionel Sambuc aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
85984d9c625SLionel Sambuc aiov.iov_len = sizeof (struct dqblk);
86084d9c625SLionel Sambuc auio.uio_resid = sizeof (struct dqblk);
86184d9c625SLionel Sambuc auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
86284d9c625SLionel Sambuc auio.uio_rw = UIO_WRITE;
86384d9c625SLionel Sambuc UIO_SETUP_SYSSPACE(&auio);
86484d9c625SLionel Sambuc error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
86584d9c625SLionel Sambuc if (auio.uio_resid && error == 0)
86684d9c625SLionel Sambuc error = EIO;
86784d9c625SLionel Sambuc dq->dq_flags &= ~DQ_MOD;
86884d9c625SLionel Sambuc VOP_UNLOCK(dqvp);
86984d9c625SLionel Sambuc return (error);
87084d9c625SLionel Sambuc }
871