/* * Do operations associated with quotas */ int ext2_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg, struct ucred *cred) { #ifndef QUOTA return (EOPNOTSUPP); #else int cmd, type, error; type = cmds & SUBCMDMASK; cmd = cmds >> SUBCMDSHIFT; if (uid == -1) { switch(type) { case USRQUOTA: uid = cred->cr_ruid; break; case GRPQUOTA: uid = cred->cr_rgid; break; default: return (EINVAL); } } /* * Check permissions. */ switch (cmd) { case Q_QUOTAON: error = priv_check_cred(cred, PRIV_UFS_QUOTAON, 0); break; case Q_QUOTAOFF: error = priv_check_cred(cred, PRIV_UFS_QUOTAOFF, 0); break; case Q_SETQUOTA: error = priv_check_cred(cred, PRIV_VFS_SETQUOTA, 0); break; case Q_SETUSE: error = priv_check_cred(cred, PRIV_UFS_SETUSE, 0); break; case Q_GETQUOTA: if (uid == cred->cr_ruid) error = 0; else error = priv_check_cred(cred, PRIV_VFS_GETQUOTA, 0); break; case Q_SYNC: error = 0; break; default: error = EINVAL; break; } if (error) return (error); if ((uint)type >= MAXQUOTAS) return (EINVAL); if (vfs_busy(mp, LK_NOWAIT)) return (0); switch (cmd) { case Q_QUOTAON: error = ext2_quotaon(cred, mp, type, arg); break; case Q_QUOTAOFF: error = ext2_quotaoff(mp, type); break; case Q_SETQUOTA: error = ext2_setquota(mp, uid, type, arg); break; case Q_SETUSE: error = ext2_setuse(mp, uid, type, arg); break; case Q_GETQUOTA: error = ext2_getquota(mp, uid, type, arg); break; case Q_SYNC: error = ext2_qsync(mp); break; default: error = EINVAL; break; } vfs_unbusy(mp); return (error); #endif }
int ext2_quotaon(struct ucred *cred, struct mount *mp, int type, caddr_t fname) { struct ext2mount *ump = VFSTOEXT2(mp); struct vnode *vp, **vpp; struct ext2_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) ext2_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 (ext2_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; ext2_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, ext2_quotaon_scan, &scaninfo); if (error) break; } ump->um_qflags[type] &= ~QTF_OPENING; if (error) ext2_quotaoff(mp, type); return (error); }