X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/d7e50217d7adf6e52786a38bcaa4cd698cb9a79e..378393581903b274cb7a4d18e0d978071a6b592d:/bsd/hfs/hfs_quota.c?ds=sidebyside diff --git a/bsd/hfs/hfs_quota.c b/bsd/hfs/hfs_quota.c index c486d3a1c..80b01d62c 100644 --- a/bsd/hfs/hfs_quota.c +++ b/bsd/hfs/hfs_quota.c @@ -1,24 +1,21 @@ /* - * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. * * @APPLE_LICENSE_HEADER_END@ */ @@ -65,12 +62,14 @@ #include #include #include -#include #include #include #include +#include #include #include +#include +#include #include #include @@ -80,7 +79,9 @@ /* * Quota name to error message mapping. */ +#if 0 static char *quotatypes[] = INITQFNAMES; +#endif /* * Set up the quotas for a cnode. @@ -99,14 +100,14 @@ hfs_getinoquota(cp) int error; vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp; - hfsmp = VFSTOHFS(vp->v_mount); + hfsmp = VTOHFS(vp); /* * Set up the user quota based on file uid. * EINVAL means that quotas are not enabled. */ if (cp->c_dquot[USRQUOTA] == NODQUOT && (error = - dqget(vp, cp->c_uid, &hfsmp->hfs_qfiles[USRQUOTA], USRQUOTA, &cp->c_dquot[USRQUOTA])) && + dqget(cp->c_uid, &hfsmp->hfs_qfiles[USRQUOTA], USRQUOTA, &cp->c_dquot[USRQUOTA])) && error != EINVAL) return (error); /* @@ -115,7 +116,7 @@ hfs_getinoquota(cp) */ if (cp->c_dquot[GRPQUOTA] == NODQUOT && (error = - dqget(vp, cp->c_gid, &hfsmp->hfs_qfiles[GRPQUOTA], GRPQUOTA, &cp->c_dquot[GRPQUOTA])) && + dqget(cp->c_gid, &hfsmp->hfs_qfiles[GRPQUOTA], GRPQUOTA, &cp->c_dquot[GRPQUOTA])) && error != EINVAL) return (error); return (0); @@ -128,7 +129,7 @@ int hfs_chkdq(cp, change, cred, flags) register struct cnode *cp; int64_t change; - struct ucred *cred; + kauth_cred_t cred; int flags; { register struct dquot *dq; @@ -147,10 +148,8 @@ hfs_chkdq(cp, change, cred, flags) for (i = 0; i < MAXQUOTAS; i++) { if ((dq = cp->c_dquot[i]) == NODQUOT) continue; - while (dq->dq_flags & DQ_LOCK) { - dq->dq_flags |= DQ_WANT; - sleep((caddr_t)dq, PINOD+1); - } + dqlock(dq); + ncurbytes = dq->dq_curbytes + change; if (ncurbytes >= 0) dq->dq_curbytes = ncurbytes; @@ -158,13 +157,15 @@ hfs_chkdq(cp, change, cred, flags) dq->dq_curbytes = 0; dq->dq_flags &= ~DQ_BLKS; dq->dq_flags |= DQ_MOD; + + dqunlock(dq); } return (0); } p = current_proc(); if (cred == NOCRED) - cred = kernproc->p_ucred; - if ((cred->cr_uid != 0) || (p->p_flag & P_FORCEQUOTA)) { + cred = proc_ucred(kernproc); + if (suser(cred, NULL) || proc_forcequota(p)) { for (i = 0; i < MAXQUOTAS; i++) { if ((dq = cp->c_dquot[i]) == NODQUOT) continue; @@ -178,12 +179,12 @@ hfs_chkdq(cp, change, cred, flags) for (i = 0; i < MAXQUOTAS; i++) { if ((dq = cp->c_dquot[i]) == NODQUOT) continue; - while (dq->dq_flags & DQ_LOCK) { - dq->dq_flags |= DQ_WANT; - sleep((caddr_t)dq, PINOD+1); - } + dqlock(dq); + dq->dq_curbytes += change; dq->dq_flags |= DQ_MOD; + + dqunlock(dq); } } return (error); @@ -197,26 +198,30 @@ int hfs_chkdqchg(cp, change, cred, type) struct cnode *cp; int64_t change; - struct ucred *cred; + kauth_cred_t cred; int type; { register struct dquot *dq = cp->c_dquot[type]; - u_int64_t ncurbytes = dq->dq_curbytes + change; + u_int64_t ncurbytes; struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp; - + + dqlock(dq); + + ncurbytes = dq->dq_curbytes + change; /* * If user would exceed their hard limit, disallow space allocation. */ if (ncurbytes >= dq->dq_bhardlimit && dq->dq_bhardlimit) { if ((dq->dq_flags & DQ_BLKS) == 0 && - cp->c_uid == cred->cr_uid) { + cp->c_uid == kauth_cred_getuid(cred)) { #if 0 - printf("\n%s: write failed, %s disk limit reached\n", - vp->v_mount->mnt_stat.f_mntonname, + printf("\nwrite failed, %s disk limit reached\n", quotatypes[type]); #endif dq->dq_flags |= DQ_BLKS; } + dqunlock(dq); + return (EDQUOT); } /* @@ -224,31 +229,38 @@ hfs_chkdqchg(cp, change, cred, type) * allocation. Reset time limit as they cross their soft limit. */ if (ncurbytes >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) { + struct timeval tv; + + microuptime(&tv); if (dq->dq_curbytes < dq->dq_bsoftlimit) { - dq->dq_btime = time.tv_sec + - VFSTOHFS(vp->v_mount)->hfs_qfiles[type].qf_btime; + dq->dq_btime = tv.tv_sec + + VTOHFS(vp)->hfs_qfiles[type].qf_btime; #if 0 - if (cp->c_uid == cred->cr_uid) - printf("\n%s: warning, %s %s\n", - vp->v_mount->mnt_stat.f_mntonname, + if (cp->c_uid == kauth_cred_getuid(cred)) + printf("\nwarning, %s %s\n", quotatypes[type], "disk quota exceeded"); #endif + dqunlock(dq); + return (0); } - if (time.tv_sec > dq->dq_btime) { + if (tv.tv_sec > dq->dq_btime) { if ((dq->dq_flags & DQ_BLKS) == 0 && - cp->c_uid == cred->cr_uid) { + cp->c_uid == kauth_cred_getuid(cred)) { #if 0 - printf("\n%s: write failed, %s %s\n", - vp->v_mount->mnt_stat.f_mntonname, + printf("\nwrite failed, %s %s\n", quotatypes[type], "disk quota exceeded for too long"); #endif dq->dq_flags |= DQ_BLKS; } + dqunlock(dq); + return (EDQUOT); } } + dqunlock(dq); + return (0); } @@ -259,7 +271,7 @@ int hfs_chkiq(cp, change, cred, flags) register struct cnode *cp; long change; - struct ucred *cred; + kauth_cred_t cred; int flags; { register struct dquot *dq; @@ -277,10 +289,8 @@ hfs_chkiq(cp, change, cred, flags) for (i = 0; i < MAXQUOTAS; i++) { if ((dq = cp->c_dquot[i]) == NODQUOT) continue; - while (dq->dq_flags & DQ_LOCK) { - dq->dq_flags |= DQ_WANT; - sleep((caddr_t)dq, PINOD+1); - } + dqlock(dq); + ncurinodes = dq->dq_curinodes + change; if (ncurinodes >= 0) dq->dq_curinodes = ncurinodes; @@ -288,13 +298,15 @@ hfs_chkiq(cp, change, cred, flags) dq->dq_curinodes = 0; dq->dq_flags &= ~DQ_INODS; dq->dq_flags |= DQ_MOD; + + dqunlock(dq); } return (0); } p = current_proc(); if (cred == NOCRED) - cred = kernproc->p_ucred; - if ((cred->cr_uid != 0) || (p->p_flag & P_FORCEQUOTA)) { + cred = proc_ucred(kernproc); + if (suser(cred, NULL) || proc_forcequota(p)) { for (i = 0; i < MAXQUOTAS; i++) { if ((dq = cp->c_dquot[i]) == NODQUOT) continue; @@ -308,12 +320,12 @@ hfs_chkiq(cp, change, cred, flags) for (i = 0; i < MAXQUOTAS; i++) { if ((dq = cp->c_dquot[i]) == NODQUOT) continue; - while (dq->dq_flags & DQ_LOCK) { - dq->dq_flags |= DQ_WANT; - sleep((caddr_t)dq, PINOD+1); - } + dqlock(dq); + dq->dq_curinodes += change; dq->dq_flags |= DQ_MOD; + + dqunlock(dq); } } return (error); @@ -327,26 +339,30 @@ int hfs_chkiqchg(cp, change, cred, type) struct cnode *cp; long change; - struct ucred *cred; + kauth_cred_t cred; int type; { register struct dquot *dq = cp->c_dquot[type]; - long ncurinodes = dq->dq_curinodes + change; + long ncurinodes; struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp; + dqlock(dq); + + ncurinodes = dq->dq_curinodes + change; /* * If user would exceed their hard limit, disallow cnode allocation. */ if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) { if ((dq->dq_flags & DQ_INODS) == 0 && - cp->c_uid == cred->cr_uid) { + cp->c_uid == kauth_cred_getuid(cred)) { #if 0 - printf("\n%s: write failed, %s cnode limit reached\n", - vp->v_mount->mnt_stat.f_mntonname, + printf("\nwrite failed, %s cnode limit reached\n", quotatypes[type]); #endif dq->dq_flags |= DQ_INODS; } + dqunlock(dq); + return (EDQUOT); } /* @@ -354,31 +370,38 @@ hfs_chkiqchg(cp, change, cred, type) * allocation. Reset time limit as they cross their soft limit. */ if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) { + struct timeval tv; + + microuptime(&tv); if (dq->dq_curinodes < dq->dq_isoftlimit) { - dq->dq_itime = time.tv_sec + - VFSTOHFS(vp->v_mount)->hfs_qfiles[type].qf_itime; + dq->dq_itime = tv.tv_sec + + VTOHFS(vp)->hfs_qfiles[type].qf_itime; #if 0 - if (cp->c_uid == cred->cr_uid) - printf("\n%s: warning, %s %s\n", - vp->v_mount->mnt_stat.f_mntonname, + if (cp->c_uid == kauth_cred_getuid(cred)) + printf("\nwarning, %s %s\n", quotatypes[type], "cnode quota exceeded"); #endif + dqunlock(dq); + return (0); } - if (time.tv_sec > dq->dq_itime) { + if (tv.tv_sec > dq->dq_itime) { if ((dq->dq_flags & DQ_INODS) == 0 && - cp->c_uid == cred->cr_uid) { + cp->c_uid == kauth_cred_getuid(cred)) { #if 0 - printf("\n%s: write failed, %s %s\n", - vp->v_mount->mnt_stat.f_mntonname, + printf("\nwrite failed, %s %s\n", quotatypes[type], "cnode quota exceeded for too long"); #endif dq->dq_flags |= DQ_INODS; } + dqunlock(dq); + return (EDQUOT); } } + dqunlock(dq); + return (0); } @@ -392,12 +415,11 @@ hfs_chkdquot(cp) register struct cnode *cp; { struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp; - struct hfsmount *hfsmp = VFSTOHFS(vp->v_mount); + struct hfsmount *hfsmp = VTOHFS(vp); register int i; for (i = 0; i < MAXQUOTAS; i++) { - if (hfsmp->hfs_qfiles[i].qf_vp == NULLVP || - (hfsmp->hfs_qfiles[i].qf_qflags & (QTF_OPENING|QTF_CLOSING))) + if (hfsmp->hfs_qfiles[i].qf_vp == NULLVP) continue; if (cp->c_dquot[i] == NODQUOT) { vprint("chkdquot: missing dquot", vp); @@ -414,132 +436,181 @@ hfs_chkdquot(cp) /* * Q_QUOTAON - set up a quota file for a particular file system. */ +struct hfs_quotaon_cargs { + int error; +}; + +static int +hfs_quotaon_callback(struct vnode *vp, void *cargs) +{ + struct hfs_quotaon_cargs *args; + + args = (struct hfs_quotaon_cargs *)cargs; + + args->error = hfs_getinoquota(VTOC(vp)); + if (args->error) + return (VNODE_RETURNED_DONE); + + return (VNODE_RETURNED); +} + int -hfs_quotaon(p, mp, type, fname, segflg) +hfs_quotaon(p, mp, type, fnamep) struct proc *p; struct mount *mp; register int type; - caddr_t fname; - enum uio_seg segflg; + caddr_t fnamep; { struct hfsmount *hfsmp = VFSTOHFS(mp); - struct vnode *vp, **vpp; - struct vnode *nextvp; - struct dquot *dq; - int error; - struct nameidata nd; + struct quotafile *qfp; + struct vnode *vp; + int error = 0; + struct hfs_quotaon_cargs args; - vpp = &hfsmp->hfs_qfiles[type].qf_vp; - NDINIT(&nd, LOOKUP, FOLLOW, segflg, fname, p); - if (error = vn_open(&nd, FREAD|FWRITE, 0)) - return (error); - vp = nd.ni_vp; - VOP_UNLOCK(vp, 0, p); - if (vp->v_type != VREG) { - (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p); - return (EACCES); + qfp = &hfsmp->hfs_qfiles[type]; + + if ( (qf_get(qfp, QTF_OPENING)) ) + return (0); + + error = vnode_open(fnamep, FREAD|FWRITE, 0, 0, &vp, NULL); + if (error) { + goto out; } - if (*vpp != vp) - hfs_quotaoff(p, mp, type); - hfsmp->hfs_qfiles[type].qf_qflags |= QTF_OPENING; - mp->mnt_flag |= MNT_QUOTA; - vp->v_flag |= VNOFLUSH; - *vpp = vp; + if (!vnode_isreg(vp)) { + (void) vnode_close(vp, FREAD|FWRITE, NULL); + error = EACCES; + goto out; + } + vfs_setflags(mp, (uint64_t)((unsigned int)MNT_QUOTA)); + vnode_setnoflush(vp); /* * Save the credential of the process that turned on quotas. */ - crhold(p->p_ucred); - hfsmp->hfs_qfiles[type].qf_cred = p->p_ucred; - /* Finish initializing the quota file */ - if (error = dqfileopen(&hfsmp->hfs_qfiles[type], type)) - goto exit; + qfp->qf_cred = kauth_cred_proc_ref(p); + qfp->qf_vp = vp; + /* + * Finish initializing the quota file + */ + error = dqfileopen(qfp, type); + if (error) { + (void) vnode_close(vp, FREAD|FWRITE, NULL); + + kauth_cred_rele(qfp->qf_cred); + qfp->qf_cred = NOCRED; + qfp->qf_vp = NULLVP; + goto out; + } + qf_put(qfp, QTF_OPENING); + /* * Search vnodes associated with this mount point, * adding references to quota file being opened. * NB: only need to add dquot's for cnodes being modified. + * + * hfs_quota_callback will be called for each vnode open for + * 'write' (VNODE_WRITEABLE) hung off of this mount point + * the vnode will be in an 'unbusy' state (VNODE_WAIT) and + * properly referenced and unreferenced around the callback */ -again: - for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) { - nextvp = vp->v_mntvnodes.le_next; - if (vp->v_writecount == 0) - continue; - if (vget(vp, LK_EXCLUSIVE, p)) - goto again; - if (error = hfs_getinoquota(VTOC(vp))) { - vput(vp); - break; - } - vput(vp); - if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp) - goto again; - } -exit: - hfsmp->hfs_qfiles[type].qf_qflags &= ~QTF_OPENING; - if (error) + args.error = 0; + + vnode_iterate(mp, VNODE_WRITEABLE | VNODE_WAIT, hfs_quotaon_callback, (void *)&args); + + error = args.error; + + if (error) { hfs_quotaoff(p, mp, type); + } + return (error); + +out: + qf_put(qfp, QTF_OPENING); + return (error); } + /* * Q_QUOTAOFF - turn off disk quotas for a filesystem. */ +struct hfs_quotaoff_cargs { + int type; +}; + +static int +hfs_quotaoff_callback(struct vnode *vp, void *cargs) +{ + struct hfs_quotaoff_cargs *args; + struct cnode *cp; + struct dquot *dq; + + args = (struct hfs_quotaoff_cargs *)cargs; + + cp = VTOC(vp); + + dq = cp->c_dquot[args->type]; + cp->c_dquot[args->type] = NODQUOT; + + dqrele(dq); + + return (VNODE_RETURNED); +} + int -hfs_quotaoff(p, mp, type) - struct proc *p; - struct mount *mp; - register int type; +hfs_quotaoff(__unused struct proc *p, struct mount *mp, register int type) { - struct vnode *vp; - struct vnode *qvp, *nextvp; + struct vnode *qvp; struct hfsmount *hfsmp = VFSTOHFS(mp); - struct dquot *dq; - struct cnode *cp; + struct quotafile *qfp; int error; - struct ucred *cred; + kauth_cred_t cred; + struct hfs_quotaoff_cargs args; + + qfp = &hfsmp->hfs_qfiles[type]; - if ((qvp = hfsmp->hfs_qfiles[type].qf_vp) == NULLVP) - return (0); - hfsmp->hfs_qfiles[type].qf_qflags |= QTF_CLOSING; + if ( (qf_get(qfp, QTF_CLOSING)) ) + return (0); + qvp = qfp->qf_vp; /* * Sync out any orpaned dirty dquot entries. */ - dqsync_orphans(&hfsmp->hfs_qfiles[type]); + dqsync_orphans(qfp); /* * Search vnodes associated with this mount point, * deleting any references to quota file being closed. + * + * hfs_quotaoff_callback will be called for each vnode + * hung off of this mount point + * the vnode will be in an 'unbusy' state (VNODE_WAIT) and + * properly referenced and unreferenced around the callback */ -again: - for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) { - nextvp = vp->v_mntvnodes.le_next; - if (vget(vp, LK_EXCLUSIVE, p)) - goto again; - cp = VTOC(vp); - dq = cp->c_dquot[type]; - cp->c_dquot[type] = NODQUOT; - dqrele(vp, dq); - vput(vp); - if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp) - goto again; - } + args.type = type; + + vnode_iterate(mp, VNODE_WAIT, hfs_quotaoff_callback, (void *)&args); + dqflush(qvp); /* Finish tearing down the quota file */ - dqfileclose(&hfsmp->hfs_qfiles[type], type); - qvp->v_flag &= ~VNOFLUSH; - error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p); - hfsmp->hfs_qfiles[type].qf_vp = NULLVP; - cred = hfsmp->hfs_qfiles[type].qf_cred; + dqfileclose(qfp, type); + + vnode_clearnoflush(qvp); + error = vnode_close(qvp, FREAD|FWRITE, NULL); + + qfp->qf_vp = NULLVP; + cred = qfp->qf_cred; if (cred != NOCRED) { - hfsmp->hfs_qfiles[type].qf_cred = NOCRED; - crfree(cred); + qfp->qf_cred = NOCRED; + kauth_cred_rele(cred); } - hfsmp->hfs_qfiles[type].qf_qflags &= ~QTF_CLOSING; for (type = 0; type < MAXQUOTAS; type++) if (hfsmp->hfs_qfiles[type].qf_vp != NULLVP) break; if (type == MAXQUOTAS) - mp->mnt_flag &= ~MNT_QUOTA; + vfs_clearflags(mp, (uint64_t)((unsigned int)MNT_QUOTA)); + + qf_put(qfp, QTF_CLOSING); + return (error); } @@ -547,19 +618,25 @@ again: * Q_GETQUOTA - return current values in a dqblk structure. */ int -hfs_getquota(mp, id, type, addr) +hfs_getquota(mp, id, type, datap) struct mount *mp; u_long id; int type; - caddr_t addr; + caddr_t datap; { struct dquot *dq; int error; - if (error = dqget(NULLVP, id, &VFSTOHFS(mp)->hfs_qfiles[type], type, &dq)) + error = dqget(id, &VFSTOHFS(mp)->hfs_qfiles[type], type, &dq); + if (error) return (error); - error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk)); - dqrele(NULLVP, dq); + dqlock(dq); + + bcopy(&dq->dq_dqb, datap, sizeof(dq->dq_dqb)); + + dqunlock(dq); + dqrele(dq); + return (error); } @@ -567,47 +644,47 @@ hfs_getquota(mp, id, type, addr) * Q_SETQUOTA - assign an entire dqblk structure. */ int -hfs_setquota(mp, id, type, addr) +hfs_setquota(mp, id, type, datap) struct mount *mp; u_long id; int type; - caddr_t addr; + caddr_t datap; { - register struct dquot *dq; - struct dquot *ndq; + struct dquot *dq; struct hfsmount *hfsmp = VFSTOHFS(mp); - struct dqblk newlim; + struct dqblk * newlimp = (struct dqblk *) datap; + struct timeval tv; int error; - if (error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk))) - return (error); - if (error = dqget(NULLVP, id, &hfsmp->hfs_qfiles[type], type, &ndq)) + error = dqget(id, &hfsmp->hfs_qfiles[type], type, &dq); + if (error) return (error); - dq = ndq; - while (dq->dq_flags & DQ_LOCK) { - dq->dq_flags |= DQ_WANT; - sleep((caddr_t)dq, PINOD+1); - } + dqlock(dq); + /* * Copy all but the current values. * Reset time limit if previously had no soft limit or were * under it, but now have a soft limit and are over it. */ - newlim.dqb_curbytes = dq->dq_curbytes; - newlim.dqb_curinodes = dq->dq_curinodes; + newlimp->dqb_curbytes = dq->dq_curbytes; + newlimp->dqb_curinodes = dq->dq_curinodes; if (dq->dq_id != 0) { - newlim.dqb_btime = dq->dq_btime; - newlim.dqb_itime = dq->dq_itime; + newlimp->dqb_btime = dq->dq_btime; + newlimp->dqb_itime = dq->dq_itime; } - if (newlim.dqb_bsoftlimit && - dq->dq_curbytes >= newlim.dqb_bsoftlimit && - (dq->dq_bsoftlimit == 0 || dq->dq_curbytes < dq->dq_bsoftlimit)) - newlim.dqb_btime = time.tv_sec + hfsmp->hfs_qfiles[type].qf_btime; - if (newlim.dqb_isoftlimit && - dq->dq_curinodes >= newlim.dqb_isoftlimit && - (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) - newlim.dqb_itime = time.tv_sec + hfsmp->hfs_qfiles[type].qf_itime; - dq->dq_dqb = newlim; + if (newlimp->dqb_bsoftlimit && + dq->dq_curbytes >= newlimp->dqb_bsoftlimit && + (dq->dq_bsoftlimit == 0 || dq->dq_curbytes < dq->dq_bsoftlimit)) { + microuptime(&tv); + newlimp->dqb_btime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_btime; + } + if (newlimp->dqb_isoftlimit && + dq->dq_curinodes >= newlimp->dqb_isoftlimit && + (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) { + microuptime(&tv); + newlimp->dqb_itime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_itime; + } + bcopy(newlimp, &dq->dq_dqb, sizeof(dq->dq_dqb)); if (dq->dq_curbytes < dq->dq_bsoftlimit) dq->dq_flags &= ~DQ_BLKS; if (dq->dq_curinodes < dq->dq_isoftlimit) @@ -618,7 +695,10 @@ hfs_setquota(mp, id, type, addr) else dq->dq_flags &= ~DQ_FAKE; dq->dq_flags |= DQ_MOD; - dqrele(NULLVP, dq); + + dqunlock(dq); + dqrele(dq); + return (0); } @@ -626,60 +706,78 @@ hfs_setquota(mp, id, type, addr) * Q_SETUSE - set current cnode and byte usage. */ int -hfs_setuse(mp, id, type, addr) +hfs_setuse(mp, id, type, datap) struct mount *mp; u_long id; int type; - caddr_t addr; + caddr_t datap; { - register struct dquot *dq; struct hfsmount *hfsmp = VFSTOHFS(mp); - struct dquot *ndq; - struct dqblk usage; + struct dquot *dq; + struct timeval tv; int error; + struct dqblk *quotablkp = (struct dqblk *) datap; + + error = dqget(id, &hfsmp->hfs_qfiles[type], type, &dq); + if (error) + return (error); + dqlock(dq); - if (error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk))) - return (error); - if (error = dqget(NULLVP, id, &hfsmp->hfs_qfiles[type], type, &ndq)) - return (error); - dq = ndq; - while (dq->dq_flags & DQ_LOCK) { - dq->dq_flags |= DQ_WANT; - sleep((caddr_t)dq, PINOD+1); - } /* * Reset time limit if have a soft limit and were * previously under it, but are now over it. */ if (dq->dq_bsoftlimit && dq->dq_curbytes < dq->dq_bsoftlimit && - usage.dqb_curbytes >= dq->dq_bsoftlimit) - dq->dq_btime = time.tv_sec + hfsmp->hfs_qfiles[type].qf_btime; + quotablkp->dqb_curbytes >= dq->dq_bsoftlimit) { + microuptime(&tv); + dq->dq_btime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_btime; + } if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit && - usage.dqb_curinodes >= dq->dq_isoftlimit) - dq->dq_itime = time.tv_sec + hfsmp->hfs_qfiles[type].qf_itime; - dq->dq_curbytes = usage.dqb_curbytes; - dq->dq_curinodes = usage.dqb_curinodes; + quotablkp->dqb_curinodes >= dq->dq_isoftlimit) { + microuptime(&tv); + dq->dq_itime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_itime; + } + dq->dq_curbytes = quotablkp->dqb_curbytes; + dq->dq_curinodes = quotablkp->dqb_curinodes; if (dq->dq_curbytes < dq->dq_bsoftlimit) dq->dq_flags &= ~DQ_BLKS; if (dq->dq_curinodes < dq->dq_isoftlimit) dq->dq_flags &= ~DQ_INODS; dq->dq_flags |= DQ_MOD; - dqrele(NULLVP, dq); + + dqunlock(dq); + dqrele(dq); + return (0); } + /* * Q_SYNC - sync quota files to disk. */ +static int +hfs_qsync_callback(struct vnode *vp, __unused void *cargs) +{ + struct cnode *cp; + struct dquot *dq; + int i; + + cp = VTOC(vp); + + for (i = 0; i < MAXQUOTAS; i++) { + dq = cp->c_dquot[i]; + if (dq != NODQUOT && (dq->dq_flags & DQ_MOD)) + dqsync(dq); + } + return (VNODE_RETURNED); +} + int hfs_qsync(mp) struct mount *mp; { struct hfsmount *hfsmp = VFSTOHFS(mp); - struct proc *p = current_proc(); /* XXX */ - struct vnode *vp, *nextvp; - struct dquot *dq; - int i, error; + int i; /* * Check if the mount point has any quotas. @@ -701,48 +799,14 @@ hfs_qsync(mp) /* * Search vnodes associated with this mount point, * synchronizing any modified dquot structures. + * + * hfs_qsync_callback will be called for each vnode + * hung off of this mount point + * the vnode will be + * properly referenced and unreferenced around the callback */ - simple_lock(&mntvnode_slock); -again: - for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) { - if (vp->v_mount != mp) - goto again; - - nextvp = vp->v_mntvnodes.le_next; - simple_lock(&vp->v_interlock); - simple_unlock(&mntvnode_slock); - - error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p); - if (error) { - simple_lock(&mntvnode_slock); - if (error == ENOENT) - goto again; - continue; - } - - // Make sure that this is really an hfs vnode. - // - if ( vp->v_mount != mp - || vp->v_type == VNON - || vp->v_tag != VT_HFS - || VTOC(vp) == NULL) { - - vput(vp); - simple_lock(&mntvnode_slock); - goto again; - } + vnode_iterate(mp, 0, hfs_qsync_callback, (void *)NULL); - for (i = 0; i < MAXQUOTAS; i++) { - dq = VTOC(vp)->c_dquot[i]; - if (dq != NODQUOT && (dq->dq_flags & DQ_MOD)) - dqsync(vp, dq); - } - vput(vp); - simple_lock(&mntvnode_slock); - if (vp->v_mntvnodes.le_next != nextvp) - goto again; - } - simple_unlock(&mntvnode_slock); return (0); } @@ -750,21 +814,21 @@ again: * Q_QUOTASTAT - get quota on/off status */ int -hfs_quotastat(mp, type, addr) +hfs_quotastat(mp, type, datap) struct mount *mp; register int type; - caddr_t addr; + caddr_t datap; { struct hfsmount *hfsmp = VFSTOHFS(mp); int error = 0; int qstat; - if ((mp->mnt_flag & MNT_QUOTA) && (hfsmp->hfs_qfiles[type].qf_vp != NULLVP)) + if ((((unsigned int)vfs_flags(mp)) & MNT_QUOTA) && (hfsmp->hfs_qfiles[type].qf_vp != NULLVP)) qstat = 1; /* quotas are on for this type */ else qstat = 0; /* quotas are off for this type */ - error = copyout ((caddr_t)&qstat, addr, sizeof(qstat)); + *((int *)datap) = qstat; return (error); }