/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * Quota system calls. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int opendq(); static int setquota(); static int getquota(); static int quotasync(); /* * Quota sub-system init flag. */ int quotas_initialized = 0; /* * Sys call to allow users to find out * their current position wrt quota's * and to allow privileged users to alter it. */ /*ARGSUSED*/ int quotactl(struct vnode *vp, intptr_t arg, int flag, struct cred *cr) { struct quotctl quot; struct ufsvfs *ufsvfsp; int error = 0; if ((flag & DATAMODEL_MASK) == DATAMODEL_NATIVE) { if (copyin((caddr_t)arg, ", sizeof (struct quotctl))) return (EFAULT); } #ifdef _SYSCALL32_IMPL else { /* quotctl struct from ILP32 callers */ struct quotctl32 quot32; if (copyin((caddr_t)arg, "32, sizeof (struct quotctl32))) return (EFAULT); quot.op = quot32.op; quot.uid = quot32.uid; quot.addr = (caddr_t)(uintptr_t)quot32.addr; } #endif /* _SYSCALL32_IMPL */ if (quot.uid < 0) quot.uid = crgetruid(cr); if (quot.op == Q_SYNC && vp == NULL) { ufsvfsp = NULL; } else if (quot.op != Q_ALLSYNC) { ufsvfsp = (struct ufsvfs *)(vp->v_vfsp->vfs_data); } switch (quot.op) { case Q_QUOTAON: rw_enter(&dq_rwlock, RW_WRITER); if (quotas_initialized == 0) { qtinit2(); quotas_initialized = 1; } rw_exit(&dq_rwlock); error = opendq(ufsvfsp, vp, cr); break; case Q_QUOTAOFF: error = closedq(ufsvfsp, cr); if (!error) { invalidatedq(ufsvfsp); } break; case Q_SETQUOTA: case Q_SETQLIM: error = setquota(quot.op, (uid_t)quot.uid, ufsvfsp, quot.addr, cr); break; case Q_GETQUOTA: error = getquota((uid_t)quot.uid, ufsvfsp, (caddr_t)quot.addr, cr); break; case Q_SYNC: error = qsync(ufsvfsp); break; case Q_ALLSYNC: (void) qsync(NULL); break; default: error = EINVAL; break; } return (error); } static int opendq_scan_inode(struct inode *ip, void *arg) { struct ufsvfs *ufsvfsp = ip->i_ufsvfs; /* * wrong file system or this is the quota inode; keep looking */ if (ufsvfsp != (struct ufsvfs *)arg || ip == ip->i_ufsvfs->vfs_qinod) { return (0); } ASSERT(RW_WRITE_HELD(&ufsvfsp->vfs_dqrwlock)); rw_enter(&ip->i_contents, RW_WRITER); /* * This inode is in the cache (by definition), is still valid, * and is not a shadow inode or extended attribute directory inode, * but does not have a quota so get the quota information. */ if (ip->i_mode && (ip->i_mode & IFMT) != IFSHAD && (ip->i_mode & IFMT) != IFATTRDIR && ip->i_dquot == NULL) { ip->i_dquot = getinoquota(ip); } rw_exit(&ip->i_contents); return (0); } /* * Set the quota file up for a particular file system. * Called as the result of a quotaon (Q_QUOTAON) ioctl. */ static int opendq( struct ufsvfs *ufsvfsp, struct vnode *vp, /* quota file */ struct cred *cr) { struct inode *qip; struct dquot *dqp; int error; int quotaon = 0; if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) return (EPERM); VN_HOLD(vp); /* * Check to be sure its a regular file. */ if (vp->v_type != VREG) { VN_RELE(vp); return (EACCES); } rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER); /* * We have vfs_dqrwlock as writer, so if quotas are disabled, * then vfs_qinod should be NULL or we have a race somewhere. */ ASSERT((ufsvfsp->vfs_qflags & MQ_ENABLED) || (ufsvfsp->vfs_qinod == 0)); if ((ufsvfsp->vfs_qflags & MQ_ENABLED) != 0) { /* * Quotas are already enabled on this file system. * * If the "quotas" file was replaced (different inode) * while quotas were enabled we don't want to re-enable * them with a new "quotas" file. Simply print a warning * message to the console, release the new vnode, and * return. * XXX - The right way to fix this is to return EBUSY * for the ioctl() issued by 'quotaon'. */ if (VTOI(vp) != ufsvfsp->vfs_qinod) { cmn_err(CE_WARN, "Previous quota file still in use." " Disable quotas on %s before enabling.\n", VTOI(vp)->i_fs->fs_fsmnt); VN_RELE(vp); rw_exit(&ufsvfsp->vfs_dqrwlock); return (0); } (void) quotasync(ufsvfsp, /* do_lock */ 0); /* remove extra hold on quota file */ VN_RELE(vp); quotaon++; qip = ufsvfsp->vfs_qinod; } else { int qlen; ufsvfsp->vfs_qinod = VTOI(vp); qip = ufsvfsp->vfs_qinod; /* * Force the file to have no partially allocated blocks * to prevent a realloc from changing the location of * the data. We must do this even if not logging in * case we later remount to logging. */ qlen = qip->i_fs->fs_bsize * NDADDR; /* * Largefiles: i_size needs to be atomically accessed now. */ rw_enter(&qip->i_contents, RW_WRITER); if (qip->i_size < qlen) { if (ufs_itrunc(qip, (u_offset_t)qlen, (int)0, cr) != 0) cmn_err(CE_WARN, "opendq failed to remove frags" " from quota file\n"); rw_exit(&qip->i_contents); (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)qip->i_size, B_INVAL, kcred, NULL); } else { rw_exit(&qip->i_contents); } TRANS_MATA_IGET(ufsvfsp, qip); } /* * The file system time limits are in the dquot for uid 0. * The time limits set the relative time the other users * can be over quota for this file system. * If it is zero a default is used (see quota.h). */ error = getdiskquota((uid_t)0, ufsvfsp, 1, &dqp); if (error == 0) { mutex_enter(&dqp->dq_lock); ufsvfsp->vfs_btimelimit = (dqp->dq_btimelimit? dqp->dq_btimelimit: DQ_BTIMELIMIT); ufsvfsp->vfs_ftimelimit = (dqp->dq_ftimelimit? dqp->dq_ftimelimit: DQ_FTIMELIMIT); ufsvfsp->vfs_qflags = MQ_ENABLED; /* enable quotas */ vfs_setmntopt(ufsvfsp->vfs_vfs, MNTOPT_QUOTA, NULL, 0); dqput(dqp); mutex_exit(&dqp->dq_lock); } else if (!quotaon) { /* * Some sort of I/O error on the quota file, and quotas were * not already on when we got here so clean up. */ ufsvfsp->vfs_qflags = 0; ufsvfsp->vfs_qinod = NULL; VN_RELE(ITOV(qip)); } /* * If quotas are enabled update all valid inodes in the * cache with quota information. */ if (ufsvfsp->vfs_qflags & MQ_ENABLED) { (void) ufs_scan_inodes(0, opendq_scan_inode, ufsvfsp, ufsvfsp); } rw_exit(&ufsvfsp->vfs_dqrwlock); return (error); } static int closedq_scan_inode(struct inode *ip, void *arg) { struct dquot *dqp; struct ufsvfs *ufsvfsp = ip->i_ufsvfs; /* * wrong file system; keep looking */ if (ufsvfsp != (struct ufsvfs *)arg) return (0); ASSERT(RW_WRITE_HELD(&ufsvfsp->vfs_dqrwlock)); rw_enter(&ip->i_contents, RW_WRITER); /* * Shadow inodes and extended attribute directories * do not have quota info records. */ if ((dqp = ip->i_dquot) != NULL) { ASSERT((ip->i_mode & IFMT) != IFSHAD); ASSERT((ip->i_mode & IFMT) != IFATTRDIR); ip->i_dquot = NULL; mutex_enter(&dqp->dq_lock); dqput(dqp); /* * If we have a pending logging file system quota * transaction, then cancel it. Clear the flag to * prevent ufs_trans_push_quota() from trying to * deal with this transaction just in case it is * waiting for the mutex. We decrement the counter * since the transaction won't be needing the quota * info record anymore. */ if (dqp->dq_flags & DQ_TRANS) { dqp->dq_flags &= ~DQ_TRANS; dqput(dqp); } mutex_exit(&dqp->dq_lock); } rw_exit(&ip->i_contents); return (0); } /* * Close off disk quotas for a file system. */ int closedq(struct ufsvfs *ufsvfsp, struct cred *cr) { struct inode *qip; if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) return (EPERM); rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER); /* * Quotas are not enabled on this file system so there is * nothing more to do. */ if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) { rw_exit(&ufsvfsp->vfs_dqrwlock); return (0); } /* * At this point, the quota subsystem is quiescent on this file * system so we can do all the work necessary to dismantle the * quota stuff. */ qip = ufsvfsp->vfs_qinod; if (!qip) return (ufs_fault(ufsvfsp->vfs_root, "closedq: NULL qip")); ufsvfsp->vfs_qflags = 0; /* disable quotas */ vfs_setmntopt(ufsvfsp->vfs_vfs, MNTOPT_NOQUOTA, NULL, 0); /* * ufs_scan_inodes() depends on vfs_qinod, so we can't * clear it until afterwards. */ (void) ufs_scan_inodes(0, closedq_scan_inode, ufsvfsp, ufsvfsp); ufsvfsp->vfs_qinod = NULL; rw_exit(&ufsvfsp->vfs_dqrwlock); /* * Sync and release the quota file inode. Since we have a private * pointer to the quota inode and vfs_qinod is now clear we do not * need to hold vfs_dqrwlock. */ (void) TRANS_SYNCIP(qip, 0, I_SYNC, TOP_SYNCIP_CLOSEDQ); VN_RELE(ITOV(qip)); return (0); } /* * Private data between setquota() and setquota_scan_inode(). */ struct setquota_data { #define SQD_TYPE_NONE 0 #define SQD_TYPE_LIMIT 1 #define SQD_TYPE_NO_LIMIT 2 int sqd_type; struct ufsvfs *sqd_ufsvfsp; uid_t sqd_uid; }; static int setquota_scan_inode(struct inode *ip, void *arg) { struct setquota_data *sqdp = (struct setquota_data *)arg; struct ufsvfs *ufsvfsp = ip->i_ufsvfs; /* * wrong file system; keep looking */ if (ufsvfsp != sqdp->sqd_ufsvfsp) return (0); ASSERT(RW_WRITE_HELD(&ufsvfsp->vfs_dqrwlock)); /* * The file system does not have quotas enabled or this is the * file system's quota inode; keep looking. */ if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0 || ip == ufsvfsp->vfs_qinod) { return (0); } rw_enter(&ip->i_contents, RW_WRITER); /* * This inode is in the cache (by definition), is still valid, * is not a shadow inode or extended attribute directory inode * and has the right uid. */ if (ip->i_mode && (ip->i_mode & IFMT) != IFSHAD && (ip->i_mode & IFMT) != IFATTRDIR && ip->i_uid == sqdp->sqd_uid) { /* * Transition is "no limit" to "at least one limit": */ if (sqdp->sqd_type == SQD_TYPE_LIMIT && ip->i_dquot == NULL) { ip->i_dquot = getinoquota(ip); } /* * Transition is "at least one limit" to "no limit": */ else if (sqdp->sqd_type == SQD_TYPE_NO_LIMIT && ip->i_dquot) { mutex_enter(&ip->i_dquot->dq_lock); dqput(ip->i_dquot); mutex_exit(&ip->i_dquot->dq_lock); ip->i_dquot = NULL; } } rw_exit(&ip->i_contents); return (0); } /* * Set various fields of the dqblk according to the command. * Q_SETQUOTA - assign an entire dqblk structure. * Q_SETQLIM - assign a dqblk structure except for the usage. */ static int setquota(int cmd, uid_t uid, struct ufsvfs *ufsvfsp, caddr_t addr, struct cred *cr) { struct dquot *dqp; struct inode *qip; struct dquot *xdqp; struct dqblk newlim; int error; int scan_type = SQD_TYPE_NONE; daddr_t bn; int contig; if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) return (EPERM); rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER); /* * Quotas are not enabled on this file system so there is * nothing more to do. */ if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) { rw_exit(&ufsvfsp->vfs_dqrwlock); return (ESRCH); } /* * At this point, the quota subsystem is quiescent on this file * system so we can do all the work necessary to modify the quota * information for this user. */ if (copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)) != 0) { rw_exit(&ufsvfsp->vfs_dqrwlock); return (EFAULT); } error = getdiskquota(uid, ufsvfsp, 0, &xdqp); if (error) { rw_exit(&ufsvfsp->vfs_dqrwlock); return (error); } dqp = xdqp; /* * Don't change disk usage on Q_SETQLIM */ mutex_enter(&dqp->dq_lock); if (cmd == Q_SETQLIM) { newlim.dqb_curblocks = dqp->dq_curblocks; newlim.dqb_curfiles = dqp->dq_curfiles; } if (uid == 0) { /* * Timelimits for uid 0 set the relative time * the other users can be over quota for this file system. * If it is zero a default is used (see quota.h). */ ufsvfsp->vfs_btimelimit = newlim.dqb_btimelimit? newlim.dqb_btimelimit: DQ_BTIMELIMIT; ufsvfsp->vfs_ftimelimit = newlim.dqb_ftimelimit? newlim.dqb_ftimelimit: DQ_FTIMELIMIT; } else { if (newlim.dqb_bsoftlimit && newlim.dqb_curblocks >= newlim.dqb_bsoftlimit) { if (dqp->dq_bsoftlimit == 0 || dqp->dq_curblocks < dqp->dq_bsoftlimit) { /* If we're suddenly over the limit(s), */ /* start the timer(s) */ newlim.dqb_btimelimit = (uint32_t)gethrestime_sec() + ufsvfsp->vfs_btimelimit; dqp->dq_flags &= ~DQ_BLKS; } else { /* If we're currently over the soft */ /* limit and were previously over the */ /* soft limit then preserve the old */ /* time limit but make sure the DQ_BLKS */ /* flag is set since we must have been */ /* previously warned. */ newlim.dqb_btimelimit = dqp->dq_btimelimit; dqp->dq_flags |= DQ_BLKS; } } else { /* Either no quota or under quota, clear time limit */ newlim.dqb_btimelimit = 0; dqp->dq_flags &= ~DQ_BLKS; } if (newlim.dqb_fsoftlimit && newlim.dqb_curfiles >= newlim.dqb_fsoftlimit) { if (dqp->dq_fsoftlimit == 0 || dqp->dq_curfiles < dqp->dq_fsoftlimit) { /* If we're suddenly over the limit(s), */ /* start the timer(s) */ newlim.dqb_ftimelimit = (uint32_t)gethrestime_sec() + ufsvfsp->vfs_ftimelimit; dqp->dq_flags &= ~DQ_FILES; } else { /* If we're currently over the soft */ /* limit and were previously over the */ /* soft limit then preserve the old */ /* time limit but make sure the */ /* DQ_FILES flag is set since we must */ /* have been previously warned. */ newlim.dqb_ftimelimit = dqp->dq_ftimelimit; dqp->dq_flags |= DQ_FILES; } } else { /* Either no quota or under quota, clear time limit */ newlim.dqb_ftimelimit = 0; dqp->dq_flags &= ~DQ_FILES; } } /* * If there was previously no limit and there is now at least * one limit, then any inodes in the cache have NULL d_iquot * fields (getinoquota() returns NULL when there are no limits). */ if ((dqp->dq_fhardlimit == 0 && dqp->dq_fsoftlimit == 0 && dqp->dq_bhardlimit == 0 && dqp->dq_bsoftlimit == 0) && (newlim.dqb_fhardlimit || newlim.dqb_fsoftlimit || newlim.dqb_bhardlimit || newlim.dqb_bsoftlimit)) { scan_type = SQD_TYPE_LIMIT; } /* * If there was previously at least one limit and there is now * no limit, then any inodes in the cache have non-NULL d_iquot * fields need to be reset to NULL. */ else if ((dqp->dq_fhardlimit || dqp->dq_fsoftlimit || dqp->dq_bhardlimit || dqp->dq_bsoftlimit) && (newlim.dqb_fhardlimit == 0 && newlim.dqb_fsoftlimit == 0 && newlim.dqb_bhardlimit == 0 && newlim.dqb_bsoftlimit == 0)) { scan_type = SQD_TYPE_NO_LIMIT; } dqp->dq_dqb = newlim; dqp->dq_flags |= DQ_MOD; /* * push the new quota to disk now. If this is a trans device * then force the page out with ufs_putpage so it will be deltaed * by ufs_startio. */ qip = ufsvfsp->vfs_qinod; rw_enter(&qip->i_contents, RW_WRITER); (void) ufs_rdwri(UIO_WRITE, FWRITE | FSYNC, qip, (caddr_t)&dqp->dq_dqb, sizeof (struct dqblk), dqoff(uid), UIO_SYSSPACE, (int *)NULL, kcred); rw_exit(&qip->i_contents); (void) VOP_PUTPAGE(ITOV(qip), dqoff(dqp->dq_uid) & ~qip->i_fs->fs_bmask, qip->i_fs->fs_bsize, B_INVAL, kcred, NULL); /* * We must set the dq_mof even if not we are not logging in case * we are later remount to logging. */ contig = 0; rw_enter(&qip->i_contents, RW_WRITER); error = bmap_read(qip, dqoff(dqp->dq_uid), &bn, &contig); rw_exit(&qip->i_contents); if (error || (bn == UFS_HOLE)) { dqp->dq_mof = UFS_HOLE; } else { dqp->dq_mof = ldbtob(bn) + (offset_t)((dqoff(dqp->dq_uid)) & (DEV_BSIZE - 1)); } dqp->dq_flags &= ~DQ_MOD; dqput(dqp); mutex_exit(&dqp->dq_lock); if (scan_type) { struct setquota_data sqd; sqd.sqd_type = scan_type; sqd.sqd_ufsvfsp = ufsvfsp; sqd.sqd_uid = uid; (void) ufs_scan_inodes(0, setquota_scan_inode, &sqd, ufsvfsp); } rw_exit(&ufsvfsp->vfs_dqrwlock); return (0); } /* * Q_GETQUOTA - return current values in a dqblk structure. */ static int getquota(uid_t uid, struct ufsvfs *ufsvfsp, caddr_t addr, cred_t *cr) { struct dquot *dqp; struct dquot *xdqp; struct dqblk dqb; int error = 0; if (uid != crgetruid(cr) && secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) return (EPERM); rw_enter(&ufsvfsp->vfs_dqrwlock, RW_READER); if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) { rw_exit(&ufsvfsp->vfs_dqrwlock); return (ESRCH); } error = getdiskquota(uid, ufsvfsp, 0, &xdqp); if (error) { rw_exit(&ufsvfsp->vfs_dqrwlock); return (error); } dqp = xdqp; mutex_enter(&dqp->dq_lock); if (dqp->dq_fhardlimit == 0 && dqp->dq_fsoftlimit == 0 && dqp->dq_bhardlimit == 0 && dqp->dq_bsoftlimit == 0) { error = ESRCH; } else { bcopy(&dqp->dq_dqb, &dqb, sizeof (struct dqblk)); } dqput(dqp); mutex_exit(&dqp->dq_lock); rw_exit(&ufsvfsp->vfs_dqrwlock); if (error == 0 && copyout(&dqb, addr, sizeof (struct dqblk)) != 0) error = EFAULT; return (error); } /* * Q_SYNC - sync quota files to disk. */ int qsync(struct ufsvfs *ufsvfsp) { return (quotasync(ufsvfsp, /* do_lock */ 1)); } /* * Sync quota information records to disk for the specified file system * or all file systems with quotas if ufsvfsp == NULL. Grabs a reader * lock on vfs_dqrwlock if it is needed. * * Currently, if ufsvfsp is NULL, then do_lock is always true, but the * routine is coded to account for either do_lock value. This seemed * to be the safer thing to do. */ int quotasync(struct ufsvfs *ufsvfsp, int do_lock) { struct dquot *dqp; rw_enter(&dq_rwlock, RW_READER); if (!quotas_initialized) { rw_exit(&dq_rwlock); return (ESRCH); } rw_exit(&dq_rwlock); /* * The operation applies to a specific file system only. */ if (ufsvfsp) { if (do_lock) { rw_enter(&ufsvfsp->vfs_dqrwlock, RW_READER); } /* * Quotas are not enabled on this file system so bail. */ if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) { if (do_lock) { rw_exit(&ufsvfsp->vfs_dqrwlock); } return (ESRCH); } /* * This operation is a no-op on a logging file system because * quota information is treated as metadata and is in the log. * This code path treats quota information as user data which * is not necessary on a logging file system. */ if (TRANS_ISTRANS(ufsvfsp)) { if (do_lock) { rw_exit(&ufsvfsp->vfs_dqrwlock); } return (0); } /* * Try to sync all the quota info records for this * file system: */ for (dqp = dquot; dqp < dquotNDQUOT; dqp++) { /* * If someone else has it, then ignore it. */ if (!mutex_tryenter(&dqp->dq_lock)) { continue; } /* * The quota info record is for this file system * and it has changes. */ if (dqp->dq_ufsvfsp == ufsvfsp && (dqp->dq_flags & DQ_MOD)) { ASSERT(ufsvfsp->vfs_qflags & MQ_ENABLED); dqupdate(dqp); } mutex_exit(&dqp->dq_lock); } if (do_lock) { rw_exit(&ufsvfsp->vfs_dqrwlock); } return (0); } /* * Try to sync all the quota info records for *all* file systems * for which quotas are enabled. */ for (dqp = dquot; dqp < dquotNDQUOT; dqp++) { /* * If someone else has it, then ignore it. */ if (!mutex_tryenter(&dqp->dq_lock)) { continue; } ufsvfsp = dqp->dq_ufsvfsp; /* shorthand */ /* * This quota info record has no changes or is * not a valid quota info record yet. */ if ((dqp->dq_flags & DQ_MOD) == 0 || ufsvfsp == NULL) { mutex_exit(&dqp->dq_lock); continue; } /* * Now we have a potential lock order problem: * * vfs_dqrwlock > dq_lock * * so if we have to get vfs_dqrwlock, then go thru hoops * to avoid deadlock. If we cannot get the order right, * then we ignore this quota info record. */ if (do_lock) { /* * If we can't grab vfs_dqrwlock, then we don't * want to wait to avoid deadlock. */ if (rw_tryenter(&ufsvfsp->vfs_dqrwlock, RW_READER) == 0) { mutex_exit(&dqp->dq_lock); continue; } /* * Okay, now we have both dq_lock and vfs_dqrwlock. * We should not deadlock for the following reasons: * - If another thread has a reader lock on * vfs_dqrwlock and is waiting for dq_lock, * there is no conflict because we can also have * a reader lock on vfs_dqrwlock. * - If another thread has a writer lock on * vfs_dqrwlock and is waiting for dq_lock, * we would have failed the rw_tryenter() above * and given up dq_lock. * - Since we have dq_lock another thread cannot * have it and be waiting for vfs_dqrwlock. */ } /* * Since we got to this file system via a quota info * record and we have vfs_dqrwlock this is paranoia * to make sure that quotas are enabled. */ ASSERT(ufsvfsp->vfs_qflags & MQ_ENABLED); /* * We are not logging. See above logging file system * comment. */ if (!TRANS_ISTRANS(ufsvfsp)) { dqupdate(dqp); } /* * Since we have a private copy of dqp->dq_ufsvfsp, * we can drop dq_lock now. */ mutex_exit(&dqp->dq_lock); if (do_lock) { rw_exit(&ufsvfsp->vfs_dqrwlock); } } return (0); }