/* * Reclaim an inode so that it can be used for other purposes. * * ufs_reclaim(struct vnode *a_vp) */ int ufs_reclaim(struct vop_reclaim_args *ap) { struct inode *ip; struct vnode *vp = ap->a_vp; struct ufsmount *ump; #ifdef QUOTA int i; #endif ump = VFSTOUFS(vp->v_mount); if (prtactive && VREFCNT(vp) > 1) vprint("ufs_reclaim: pushing active", vp); ip = VTOI(vp); /* * Lazy updates. */ if (ip) { if (ip->i_flag & IN_LAZYMOD) { ip->i_flag |= IN_MODIFIED; ffs_update(vp, 0); } } #ifdef INVARIANTS if (ip && (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE))) { kprintf("WARNING: INODE %ld flags %08x: modified inode being released!\n", (long)ip->i_number, (int)ip->i_flag); ip->i_flag |= IN_MODIFIED; ffs_update(vp, 0); } #endif /* * Remove the inode from its hash chain and purge namecache * data associated with the vnode. */ vp->v_data = NULL; if (ip) { ufs_ihashrem(ump, ip); if (ip->i_devvp) { vrele(ip->i_devvp); ip->i_devvp = 0; } #ifdef QUOTA for (i = 0; i < MAXQUOTAS; i++) { if (ip->i_dquot[i] != NODQUOT) { ufs_dqrele(vp, ip->i_dquot[i]); ip->i_dquot[i] = NODQUOT; } } #endif #ifdef UFS_DIRHASH if (ip->i_dirhash != NULL) ufsdirhash_free(ip); #endif kfree(ip, VFSTOUFS(vp->v_mount)->um_malloctype); } return (0); }
/* * Q_SETQUOTA - assign an entire dqblk structure. */ int ufs_setquota(struct mount *mp, u_long id, int type, caddr_t addr) { struct ufs_dquot *dq; struct ufs_dquot *ndq; struct ufsmount *ump = VFSTOUFS(mp); struct ufs_dqblk newlim; int error; error = copyin(addr, (caddr_t)&newlim, sizeof (struct ufs_dqblk)); if (error) return (error); error = ufs_dqget(NULLVP, id, ump, type, &ndq); if (error) return (error); dq = ndq; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, 0, "setqta", 0); } /* * 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_curblocks = dq->dq_curblocks; newlim.dqb_curinodes = dq->dq_curinodes; if (dq->dq_id != 0) { newlim.dqb_btime = dq->dq_btime; newlim.dqb_itime = dq->dq_itime; } if (newlim.dqb_bsoftlimit && dq->dq_curblocks >= newlim.dqb_bsoftlimit && (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit)) newlim.dqb_btime = time_second + ump->um_btime[type]; if (newlim.dqb_isoftlimit && dq->dq_curinodes >= newlim.dqb_isoftlimit && (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) newlim.dqb_itime = time_second + ump->um_itime[type]; dq->dq_dqb = newlim; if (dq->dq_curblocks < dq->dq_bsoftlimit) dq->dq_flags &= ~DQ_BLKS; if (dq->dq_curinodes < dq->dq_isoftlimit) dq->dq_flags &= ~DQ_INODS; if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) dq->dq_flags |= DQ_FAKE; else dq->dq_flags &= ~DQ_FAKE; dq->dq_flags |= DQ_MOD; ufs_dqrele(NULLVP, dq); return (0); }
/* * Q_GETQUOTA - return current values in a dqblk structure. */ int ufs_getquota(struct mount *mp, u_long id, int type, caddr_t addr) { struct ufs_dquot *dq; int error; error = ufs_dqget(NULLVP, id, VFSTOUFS(mp), type, &dq); if (error) return (error); error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct ufs_dqblk)); ufs_dqrele(NULLVP, dq); return (error); }
static int ufs_quotaoff_scan(struct mount *mp, struct vnode *vp, void *data) { struct scaninfo *info = data; struct ufs_dquot *dq; struct inode *ip; if (vp->v_type == VNON) { return(0); } ip = VTOI(vp); dq = ip->i_dquot[info->type]; ip->i_dquot[info->type] = NODQUOT; ufs_dqrele(vp, dq); return(0); }
/* * Q_SETUSE - set current inode and block usage. */ int ufs_setuse(struct mount *mp, u_long id, int type, caddr_t addr) { struct ufs_dquot *dq; struct ufsmount *ump = VFSTOUFS(mp); struct ufs_dquot *ndq; struct ufs_dqblk usage; int error; error = copyin(addr, (caddr_t)&usage, sizeof (struct ufs_dqblk)); if (error) return (error); error = ufs_dqget(NULLVP, id, ump, type, &ndq); if (error) return (error); dq = ndq; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, 0, "setuse", 0); } /* * Reset time limit if have a soft limit and were * previously under it, but are now over it. */ if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit && usage.dqb_curblocks >= dq->dq_bsoftlimit) dq->dq_btime = time_second + ump->um_btime[type]; if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit && usage.dqb_curinodes >= dq->dq_isoftlimit) dq->dq_itime = time_second + ump->um_itime[type]; dq->dq_curblocks = usage.dqb_curblocks; dq->dq_curinodes = usage.dqb_curinodes; if (dq->dq_curblocks < 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; ufs_dqrele(NULLVP, dq); return (0); }
/* * Obtain a dquot structure for the specified identifier and quota file * reading the information from the file if necessary. */ static int ufs_dqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type, struct ufs_dquot **dqp) { struct ufs_dquot *dq; struct ufs_dqhash *dqh; struct vnode *dqvp; struct iovec aiov; struct uio auio; int error; dqvp = ump->um_quotas[type]; if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) { *dqp = NODQUOT; return (EINVAL); } /* * Check the cache first. */ dqh = DQHASH(dqvp, id); LIST_FOREACH(dq, dqh, dq_hash) { if (dq->dq_id != id || dq->dq_ump->um_quotas[dq->dq_type] != dqvp) continue; /* * Cache hit with no references. Take * the structure off the free list. */ if (dq->dq_cnt == 0) TAILQ_REMOVE(&ufs_dqfreelist, dq, dq_freelist); DQREF(dq); *dqp = dq; return (0); } /* * Not in cache, allocate a new one. */ if (TAILQ_EMPTY(&ufs_dqfreelist) && ufs_numdquot < MAXQUOTAS * desiredvnodes) ufs_desireddquot += DQUOTINC; if (ufs_numdquot < ufs_desireddquot) { dq = (struct ufs_dquot *) kmalloc(sizeof *dq, M_DQUOT, M_WAITOK | M_ZERO); ufs_numdquot++; } else { if ((dq = TAILQ_FIRST(&ufs_dqfreelist)) == NULL) { tablefull("dquot"); *dqp = NODQUOT; return (EUSERS); } if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) panic("dqget: free dquot isn't"); TAILQ_REMOVE(&ufs_dqfreelist, dq, dq_freelist); if (dq->dq_ump != NULL) LIST_REMOVE(dq, dq_hash); } /* * Initialize the contents of the dquot structure. */ if (vp != dqvp) vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY); LIST_INSERT_HEAD(dqh, dq, dq_hash); DQREF(dq); dq->dq_flags = DQ_LOCK; dq->dq_id = id; dq->dq_ump = ump; dq->dq_type = type; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = (caddr_t)&dq->dq_dqb; aiov.iov_len = sizeof (struct ufs_dqblk); auio.uio_resid = sizeof (struct ufs_dqblk); auio.uio_offset = (off_t)(id * sizeof (struct ufs_dqblk)); auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_READ; auio.uio_td = NULL; error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]); if (auio.uio_resid == sizeof(struct ufs_dqblk) && error == 0) bzero((caddr_t)&dq->dq_dqb, sizeof(struct ufs_dqblk)); if (vp != dqvp) vn_unlock(dqvp); if (dq->dq_flags & DQ_WANT) wakeup((caddr_t)dq); dq->dq_flags = 0; /* * I/O error in reading quota file, release * quota structure and reflect problem to caller. */ if (error) { LIST_REMOVE(dq, dq_hash); ufs_dqrele(vp, dq); *dqp = NODQUOT; return (error); } /* * Check for no limit to enforce. * Initialize time values if necessary. */ if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) dq->dq_flags |= DQ_FAKE; if (dq->dq_id != 0) { if (dq->dq_btime == 0) dq->dq_btime = time_second + ump->um_btime[type]; if (dq->dq_itime == 0) dq->dq_itime = time_second + ump->um_itime[type]; } *dqp = dq; return (0); }
int ufs_quotaon(struct ucred *cred, struct mount *mp, int type, caddr_t fname) { struct ufsmount *ump = VFSTOUFS(mp); struct vnode *vp, **vpp; struct ufs_dquot *dq; int error; struct nlookupdata nd; struct scaninfo scaninfo; vpp = &ump->um_quotas[type]; error = nlookup_init(&nd, fname, UIO_USERSPACE, NLC_FOLLOW|NLC_LOCKVP); if (error == 0) error = vn_open(&nd, NULL, FREAD|FWRITE, 0); if (error == 0 && nd.nl_open_vp->v_type != VREG) error = EACCES; if (error) { nlookup_done(&nd); return (error); } vp = nd.nl_open_vp; nd.nl_open_vp = NULL; nlookup_done(&nd); vn_unlock(vp); if (*vpp != vp) ufs_quotaoff(mp, type); ump->um_qflags[type] |= QTF_OPENING; mp->mnt_flag |= MNT_QUOTA; vsetflags(vp, VSYSTEM); *vpp = vp; /* XXX release duplicate vp if *vpp == vp? */ /* * Save the credential of the process that turned on quotas. * Set up the time limits for this quota. */ ump->um_cred[type] = crhold(cred); ump->um_btime[type] = MAX_DQ_TIME; ump->um_itime[type] = MAX_IQ_TIME; if (ufs_dqget(NULLVP, 0, ump, type, &dq) == 0) { if (dq->dq_btime > 0) ump->um_btime[type] = dq->dq_btime; if (dq->dq_itime > 0) ump->um_itime[type] = dq->dq_itime; ufs_dqrele(NULLVP, dq); } /* * Search vnodes associated with this mount point, * adding references to quota file being opened. * NB: only need to add dquot's for inodes being modified. */ scaninfo.rescan = 1; while (scaninfo.rescan) { scaninfo.rescan = 0; error = vmntvnodescan(mp, VMSC_GETVP, NULL, ufs_quotaon_scan, &scaninfo); if (error) break; } ump->um_qflags[type] &= ~QTF_OPENING; if (error) ufs_quotaoff(mp, type); return (error); }