The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/ufs/ufs/ufs_quota.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Copyright (c) 1982, 1986, 1990, 1993, 1995
    3  *      The Regents of the University of California.  All rights reserved.
    4  *
    5  * This code is derived from software contributed to Berkeley by
    6  * Robert Elz at The University of Melbourne.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. All advertising materials mentioning features or use of this software
   17  *    must display the following acknowledgement:
   18  *      This product includes software developed by the University of
   19  *      California, Berkeley and its contributors.
   20  * 4. Neither the name of the University nor the names of its contributors
   21  *    may be used to endorse or promote products derived from this software
   22  *    without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   34  * SUCH DAMAGE.
   35  *
   36  *      @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
   37  */
   38 
   39 #include <sys/cdefs.h>
   40 __FBSDID("$FreeBSD: releng/5.2/sys/ufs/ufs/ufs_quota.c 122091 2003-11-05 04:30:08Z kan $");
   41 
   42 #include <sys/param.h>
   43 #include <sys/systm.h>
   44 #include <sys/fcntl.h>
   45 #include <sys/kernel.h>
   46 #include <sys/lock.h>
   47 #include <sys/malloc.h>
   48 #include <sys/mount.h>
   49 #include <sys/mutex.h>
   50 #include <sys/namei.h>
   51 #include <sys/proc.h>
   52 #include <sys/socket.h>
   53 #include <sys/sysctl.h>
   54 #include <sys/vnode.h>
   55 
   56 #include <ufs/ufs/extattr.h>
   57 #include <ufs/ufs/quota.h>
   58 #include <ufs/ufs/inode.h>
   59 #include <ufs/ufs/ufsmount.h>
   60 #include <ufs/ufs/ufs_extern.h>
   61 
   62 SYSCTL_DECL(_security_bsd);
   63 
   64 static int unprivileged_get_quota = 0;
   65 SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_get_quota, CTLFLAG_RW,
   66     &unprivileged_get_quota, 0,
   67     "Unprivileged processes may retrieve quotas for other uids and gids");
   68 
   69 static MALLOC_DEFINE(M_DQUOT, "UFS quota", "UFS quota entries");
   70 
   71 /*
   72  * Quota name to error message mapping.
   73  */
   74 static char *quotatypes[] = INITQFNAMES;
   75 
   76 static int chkdqchg(struct inode *, ufs2_daddr_t, struct ucred *, int);
   77 static int chkiqchg(struct inode *, ino_t, struct ucred *, int);
   78 static int dqget(struct vnode *,
   79                 u_long, struct ufsmount *, int, struct dquot **);
   80 static int dqsync(struct vnode *, struct dquot *);
   81 static void dqflush(struct vnode *);
   82 
   83 #ifdef DIAGNOSTIC
   84 static void dqref(struct dquot *);
   85 static void chkdquot(struct inode *);
   86 #endif
   87 
   88 /*
   89  * Set up the quotas for an inode.
   90  *
   91  * This routine completely defines the semantics of quotas.
   92  * If other criterion want to be used to establish quotas, the
   93  * MAXQUOTAS value in quotas.h should be increased, and the
   94  * additional dquots set up here.
   95  */
   96 int
   97 getinoquota(ip)
   98         struct inode *ip;
   99 {
  100         struct ufsmount *ump;
  101         struct vnode *vp = ITOV(ip);
  102         int error;
  103 
  104         ump = VFSTOUFS(vp->v_mount);
  105         /*
  106          * Set up the user quota based on file uid.
  107          * EINVAL means that quotas are not enabled.
  108          */
  109         if (ip->i_dquot[USRQUOTA] == NODQUOT &&
  110             (error =
  111                 dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
  112             error != EINVAL)
  113                 return (error);
  114         /*
  115          * Set up the group quota based on file gid.
  116          * EINVAL means that quotas are not enabled.
  117          */
  118         if (ip->i_dquot[GRPQUOTA] == NODQUOT &&
  119             (error =
  120                 dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
  121             error != EINVAL)
  122                 return (error);
  123         return (0);
  124 }
  125 
  126 /*
  127  * Update disk usage, and take corrective action.
  128  */
  129 int
  130 chkdq(ip, change, cred, flags)
  131         struct inode *ip;
  132         ufs2_daddr_t change;
  133         struct ucred *cred;
  134         int flags;
  135 {
  136         struct dquot *dq;
  137         ufs2_daddr_t ncurblocks;
  138         int i, error;
  139 
  140 #ifdef DIAGNOSTIC
  141         if ((flags & CHOWN) == 0)
  142                 chkdquot(ip);
  143 #endif
  144         if (change == 0)
  145                 return (0);
  146         if (change < 0) {
  147                 for (i = 0; i < MAXQUOTAS; i++) {
  148                         if ((dq = ip->i_dquot[i]) == NODQUOT)
  149                                 continue;
  150                         while (dq->dq_flags & DQ_LOCK) {
  151                                 dq->dq_flags |= DQ_WANT;
  152                                 (void) tsleep(dq, PINOD+1, "chkdq1", 0);
  153                         }
  154                         ncurblocks = dq->dq_curblocks + change;
  155                         if (ncurblocks >= 0)
  156                                 dq->dq_curblocks = ncurblocks;
  157                         else
  158                                 dq->dq_curblocks = 0;
  159                         dq->dq_flags &= ~DQ_BLKS;
  160                         dq->dq_flags |= DQ_MOD;
  161                 }
  162                 return (0);
  163         }
  164         if ((flags & FORCE) == 0 && suser_cred(cred, 0)) {
  165                 for (i = 0; i < MAXQUOTAS; i++) {
  166                         if ((dq = ip->i_dquot[i]) == NODQUOT)
  167                                 continue;
  168                         error = chkdqchg(ip, change, cred, i);
  169                         if (error)
  170                                 return (error);
  171                 }
  172         }
  173         for (i = 0; i < MAXQUOTAS; i++) {
  174                 if ((dq = ip->i_dquot[i]) == NODQUOT)
  175                         continue;
  176                 while (dq->dq_flags & DQ_LOCK) {
  177                         dq->dq_flags |= DQ_WANT;
  178                         (void) tsleep(dq, PINOD+1, "chkdq2", 0);
  179                 }
  180                 /* Reset timer when crossing soft limit */
  181                 if (dq->dq_curblocks + change >= dq->dq_bsoftlimit &&
  182                     dq->dq_curblocks < dq->dq_bsoftlimit)
  183                         dq->dq_btime = time_second +
  184                             VFSTOUFS(ITOV(ip)->v_mount)->um_btime[i];
  185                 dq->dq_curblocks += change;
  186                 dq->dq_flags |= DQ_MOD;
  187         }
  188         return (0);
  189 }
  190 
  191 /*
  192  * Check for a valid change to a users allocation.
  193  * Issue an error message if appropriate.
  194  */
  195 static int
  196 chkdqchg(ip, change, cred, type)
  197         struct inode *ip;
  198         ufs2_daddr_t change;
  199         struct ucred *cred;
  200         int type;
  201 {
  202         struct dquot *dq = ip->i_dquot[type];
  203         ufs2_daddr_t ncurblocks = dq->dq_curblocks + change;
  204 
  205         /*
  206          * If user would exceed their hard limit, disallow space allocation.
  207          */
  208         if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
  209                 if ((dq->dq_flags & DQ_BLKS) == 0 &&
  210                     ip->i_uid == cred->cr_uid) {
  211                         uprintf("\n%s: write failed, %s disk limit reached\n",
  212                             ITOV(ip)->v_mount->mnt_stat.f_mntonname,
  213                             quotatypes[type]);
  214                         dq->dq_flags |= DQ_BLKS;
  215                 }
  216                 return (EDQUOT);
  217         }
  218         /*
  219          * If user is over their soft limit for too long, disallow space
  220          * allocation. Reset time limit as they cross their soft limit.
  221          */
  222         if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
  223                 if (dq->dq_curblocks < dq->dq_bsoftlimit) {
  224                         dq->dq_btime = time_second +
  225                             VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type];
  226                         if (ip->i_uid == cred->cr_uid)
  227                                 uprintf("\n%s: warning, %s %s\n",
  228                                     ITOV(ip)->v_mount->mnt_stat.f_mntonname,
  229                                     quotatypes[type], "disk quota exceeded");
  230                         return (0);
  231                 }
  232                 if (time_second > dq->dq_btime) {
  233                         if ((dq->dq_flags & DQ_BLKS) == 0 &&
  234                             ip->i_uid == cred->cr_uid) {
  235                                 uprintf("\n%s: write failed, %s %s\n",
  236                                     ITOV(ip)->v_mount->mnt_stat.f_mntonname,
  237                                     quotatypes[type],
  238                                     "disk quota exceeded for too long");
  239                                 dq->dq_flags |= DQ_BLKS;
  240                         }
  241                         return (EDQUOT);
  242                 }
  243         }
  244         return (0);
  245 }
  246 
  247 /*
  248  * Check the inode limit, applying corrective action.
  249  */
  250 int
  251 chkiq(ip, change, cred, flags)
  252         struct inode *ip;
  253         ino_t change;
  254         struct ucred *cred;
  255         int flags;
  256 {
  257         struct dquot *dq;
  258         ino_t ncurinodes;
  259         int i, error;
  260 
  261 #ifdef DIAGNOSTIC
  262         if ((flags & CHOWN) == 0)
  263                 chkdquot(ip);
  264 #endif
  265         if (change == 0)
  266                 return (0);
  267         /* XXX: change is unsigned */
  268         if (change < 0) {
  269                 for (i = 0; i < MAXQUOTAS; i++) {
  270                         if ((dq = ip->i_dquot[i]) == NODQUOT)
  271                                 continue;
  272                         while (dq->dq_flags & DQ_LOCK) {
  273                                 dq->dq_flags |= DQ_WANT;
  274                                 (void) tsleep(dq, PINOD+1, "chkiq1", 0);
  275                         }
  276                         ncurinodes = dq->dq_curinodes + change;
  277                         /* XXX: ncurinodes is unsigned */
  278                         if (ncurinodes >= 0)
  279                                 dq->dq_curinodes = ncurinodes;
  280                         else
  281                                 dq->dq_curinodes = 0;
  282                         dq->dq_flags &= ~DQ_INODS;
  283                         dq->dq_flags |= DQ_MOD;
  284                 }
  285                 return (0);
  286         }
  287         if ((flags & FORCE) == 0 && suser_cred(cred, 0)) {
  288                 for (i = 0; i < MAXQUOTAS; i++) {
  289                         if ((dq = ip->i_dquot[i]) == NODQUOT)
  290                                 continue;
  291                         error = chkiqchg(ip, change, cred, i);
  292                         if (error)
  293                                 return (error);
  294                 }
  295         }
  296         for (i = 0; i < MAXQUOTAS; i++) {
  297                 if ((dq = ip->i_dquot[i]) == NODQUOT)
  298                         continue;
  299                 while (dq->dq_flags & DQ_LOCK) {
  300                         dq->dq_flags |= DQ_WANT;
  301                         (void) tsleep(dq, PINOD+1, "chkiq2", 0);
  302                 }
  303                 /* Reset timer when crossing soft limit */
  304                 if (dq->dq_curinodes + change >= dq->dq_isoftlimit &&
  305                     dq->dq_curinodes < dq->dq_isoftlimit)
  306                         dq->dq_itime = time_second +
  307                             VFSTOUFS(ITOV(ip)->v_mount)->um_itime[i];
  308                 dq->dq_curinodes += change;
  309                 dq->dq_flags |= DQ_MOD;
  310         }
  311         return (0);
  312 }
  313 
  314 /*
  315  * Check for a valid change to a users allocation.
  316  * Issue an error message if appropriate.
  317  */
  318 static int
  319 chkiqchg(ip, change, cred, type)
  320         struct inode *ip;
  321         ino_t change;
  322         struct ucred *cred;
  323         int type;
  324 {
  325         struct dquot *dq = ip->i_dquot[type];
  326         ino_t ncurinodes = dq->dq_curinodes + change;
  327 
  328         /*
  329          * If user would exceed their hard limit, disallow inode allocation.
  330          */
  331         if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
  332                 if ((dq->dq_flags & DQ_INODS) == 0 &&
  333                     ip->i_uid == cred->cr_uid) {
  334                         uprintf("\n%s: write failed, %s inode limit reached\n",
  335                             ITOV(ip)->v_mount->mnt_stat.f_mntonname,
  336                             quotatypes[type]);
  337                         dq->dq_flags |= DQ_INODS;
  338                 }
  339                 return (EDQUOT);
  340         }
  341         /*
  342          * If user is over their soft limit for too long, disallow inode
  343          * allocation. Reset time limit as they cross their soft limit.
  344          */
  345         if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
  346                 if (dq->dq_curinodes < dq->dq_isoftlimit) {
  347                         dq->dq_itime = time_second +
  348                             VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type];
  349                         if (ip->i_uid == cred->cr_uid)
  350                                 uprintf("\n%s: warning, %s %s\n",
  351                                     ITOV(ip)->v_mount->mnt_stat.f_mntonname,
  352                                     quotatypes[type], "inode quota exceeded");
  353                         return (0);
  354                 }
  355                 if (time_second > dq->dq_itime) {
  356                         if ((dq->dq_flags & DQ_INODS) == 0 &&
  357                             ip->i_uid == cred->cr_uid) {
  358                                 uprintf("\n%s: write failed, %s %s\n",
  359                                     ITOV(ip)->v_mount->mnt_stat.f_mntonname,
  360                                     quotatypes[type],
  361                                     "inode quota exceeded for too long");
  362                                 dq->dq_flags |= DQ_INODS;
  363                         }
  364                         return (EDQUOT);
  365                 }
  366         }
  367         return (0);
  368 }
  369 
  370 #ifdef DIAGNOSTIC
  371 /*
  372  * On filesystems with quotas enabled, it is an error for a file to change
  373  * size and not to have a dquot structure associated with it.
  374  */
  375 static void
  376 chkdquot(ip)
  377         struct inode *ip;
  378 {
  379         struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount);
  380         int i;
  381 
  382         for (i = 0; i < MAXQUOTAS; i++) {
  383                 if (ump->um_quotas[i] == NULLVP ||
  384                     (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))
  385                         continue;
  386                 if (ip->i_dquot[i] == NODQUOT) {
  387                         vprint("chkdquot: missing dquot", ITOV(ip));
  388                         panic("chkdquot: missing dquot");
  389                 }
  390         }
  391 }
  392 #endif
  393 
  394 /*
  395  * Code to process quotactl commands.
  396  */
  397 
  398 /*
  399  * Q_QUOTAON - set up a quota file for a particular filesystem.
  400  */
  401 int
  402 quotaon(td, mp, type, fname)
  403         struct thread *td;
  404         struct mount *mp;
  405         int type;
  406         caddr_t fname;
  407 {
  408         struct ufsmount *ump = VFSTOUFS(mp);
  409         struct vnode *vp, **vpp;
  410         struct vnode *nextvp;
  411         struct dquot *dq;
  412         int error, flags;
  413         struct nameidata nd;
  414 
  415         error = suser_cred(td->td_ucred, PRISON_ROOT);
  416         if (error)
  417                 return (error);
  418 
  419         vpp = &ump->um_quotas[type];
  420         NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, td);
  421         flags = FREAD | FWRITE;
  422         error = vn_open(&nd, &flags, 0, -1);
  423         if (error)
  424                 return (error);
  425         NDFREE(&nd, NDF_ONLY_PNBUF);
  426         vp = nd.ni_vp;
  427         VOP_UNLOCK(vp, 0, td);
  428         if (vp->v_type != VREG) {
  429                 (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
  430                 return (EACCES);
  431         }
  432         if (*vpp != vp)
  433                 quotaoff(td, mp, type);
  434         ump->um_qflags[type] |= QTF_OPENING;
  435         mp->mnt_flag |= MNT_QUOTA;
  436         ASSERT_VOP_LOCKED(vp, "quotaon");
  437         vp->v_vflag |= VV_SYSTEM;
  438         *vpp = vp;
  439         /*
  440          * Save the credential of the process that turned on quotas.
  441          * Set up the time limits for this quota.
  442          */
  443         ump->um_cred[type] = crhold(td->td_ucred);
  444         ump->um_btime[type] = MAX_DQ_TIME;
  445         ump->um_itime[type] = MAX_IQ_TIME;
  446         if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
  447                 if (dq->dq_btime > 0)
  448                         ump->um_btime[type] = dq->dq_btime;
  449                 if (dq->dq_itime > 0)
  450                         ump->um_itime[type] = dq->dq_itime;
  451                 dqrele(NULLVP, dq);
  452         }
  453         /*
  454          * Search vnodes associated with this mount point,
  455          * adding references to quota file being opened.
  456          * NB: only need to add dquot's for inodes being modified.
  457          */
  458         MNT_ILOCK(mp);
  459 again:
  460         for (vp = TAILQ_FIRST(&mp->mnt_nvnodelist); vp != NULL; vp = nextvp) {
  461                 if (vp->v_mount != mp)
  462                         goto again;
  463                 nextvp = TAILQ_NEXT(vp, v_nmntvnodes);
  464                 VI_LOCK(vp);
  465                 MNT_IUNLOCK(mp);
  466                 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
  467                         MNT_ILOCK(mp);
  468                         goto again;
  469                 }
  470                 if (vp->v_type == VNON || vp->v_writecount == 0) {
  471                         VOP_UNLOCK(vp, 0, td);
  472                         vrele(vp);
  473                         MNT_ILOCK(mp);
  474                         continue;
  475                 }
  476                 error = getinoquota(VTOI(vp));
  477                 VOP_UNLOCK(vp, 0, td);
  478                 vrele(vp);
  479                 MNT_ILOCK(mp);
  480                 if (error)
  481                         break;
  482                 if (TAILQ_NEXT(vp, v_nmntvnodes) != nextvp)
  483                         goto again;
  484         }
  485         MNT_IUNLOCK(mp);
  486         ump->um_qflags[type] &= ~QTF_OPENING;
  487         if (error)
  488                 quotaoff(td, mp, type);
  489         return (error);
  490 }
  491 
  492 /*
  493  * Q_QUOTAOFF - turn off disk quotas for a filesystem.
  494  */
  495 int
  496 quotaoff(td, mp, type)
  497         struct thread *td;
  498         struct mount *mp;
  499         int type;
  500 {
  501         struct vnode *vp;
  502         struct vnode *qvp, *nextvp;
  503         struct ufsmount *ump = VFSTOUFS(mp);
  504         struct dquot *dq;
  505         struct inode *ip;
  506         int error;
  507 
  508         error = suser_cred(td->td_ucred, PRISON_ROOT);
  509         if (error)
  510                 return (error);
  511 
  512         if ((qvp = ump->um_quotas[type]) == NULLVP)
  513                 return (0);
  514         ump->um_qflags[type] |= QTF_CLOSING;
  515         /*
  516          * Search vnodes associated with this mount point,
  517          * deleting any references to quota file being closed.
  518          */
  519         MNT_ILOCK(mp);
  520 again:
  521         for (vp = TAILQ_FIRST(&mp->mnt_nvnodelist); vp != NULL; vp = nextvp) {
  522                 if (vp->v_mount != mp)
  523                         goto again;
  524                 nextvp = TAILQ_NEXT(vp, v_nmntvnodes);
  525 
  526                 VI_LOCK(vp);
  527                 MNT_IUNLOCK(mp);
  528                 if (vp->v_type == VNON) {
  529                         VI_UNLOCK(vp);
  530                         MNT_ILOCK(mp);
  531                         continue;
  532                 }
  533                 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
  534                         MNT_ILOCK(mp);
  535                         goto again;
  536                 }
  537                 ip = VTOI(vp);
  538                 dq = ip->i_dquot[type];
  539                 ip->i_dquot[type] = NODQUOT;
  540                 dqrele(vp, dq);
  541                 VOP_UNLOCK(vp, 0, td);
  542                 vrele(vp);
  543                 MNT_ILOCK(mp);
  544                 if (TAILQ_NEXT(vp, v_nmntvnodes) != nextvp)
  545                         goto again;
  546         }
  547         MNT_IUNLOCK(mp);
  548         dqflush(qvp);
  549         ASSERT_VOP_LOCKED(qvp, "quotaoff");
  550         qvp->v_vflag &= ~VV_SYSTEM;
  551         error = vn_close(qvp, FREAD|FWRITE, td->td_ucred, td);
  552         ump->um_quotas[type] = NULLVP;
  553         crfree(ump->um_cred[type]);
  554         ump->um_cred[type] = NOCRED;
  555         ump->um_qflags[type] &= ~QTF_CLOSING;
  556         for (type = 0; type < MAXQUOTAS; type++)
  557                 if (ump->um_quotas[type] != NULLVP)
  558                         break;
  559         if (type == MAXQUOTAS)
  560                 mp->mnt_flag &= ~MNT_QUOTA;
  561         return (error);
  562 }
  563 
  564 /*
  565  * Q_GETQUOTA - return current values in a dqblk structure.
  566  */
  567 int
  568 getquota(td, mp, id, type, addr)
  569         struct thread *td;
  570         struct mount *mp;
  571         u_long id;
  572         int type;
  573         caddr_t addr;
  574 {
  575         struct dquot *dq;
  576         int error;
  577 
  578         switch (type) {
  579         case USRQUOTA:
  580                 if ((td->td_ucred->cr_uid != id) && !unprivileged_get_quota) {
  581                         error = suser_cred(td->td_ucred, PRISON_ROOT);
  582                         if (error)
  583                                 return (error);
  584                 }
  585                 break;  
  586 
  587         case GRPQUOTA:
  588                 if (!groupmember(id, td->td_ucred) && !unprivileged_get_quota) {
  589                         error = suser_cred(td->td_ucred, PRISON_ROOT);
  590                         if (error)
  591                                 return (error);
  592                 }
  593                 break;
  594 
  595         default:
  596                 return (EINVAL);
  597         }
  598 
  599         error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq);
  600         if (error)
  601                 return (error);
  602         error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk));
  603         dqrele(NULLVP, dq);
  604         return (error);
  605 }
  606 
  607 /*
  608  * Q_SETQUOTA - assign an entire dqblk structure.
  609  */
  610 int
  611 setquota(td, mp, id, type, addr)
  612         struct thread *td;
  613         struct mount *mp;
  614         u_long id;
  615         int type;
  616         caddr_t addr;
  617 {
  618         struct dquot *dq;
  619         struct dquot *ndq;
  620         struct ufsmount *ump = VFSTOUFS(mp);
  621         struct dqblk newlim;
  622         int error;
  623 
  624         error = suser_cred(td->td_ucred, PRISON_ROOT);
  625         if (error)
  626                 return (error);
  627 
  628         error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk));
  629         if (error)
  630                 return (error);
  631         error = dqget(NULLVP, id, ump, type, &ndq);
  632         if (error)
  633                 return (error);
  634         dq = ndq;
  635         while (dq->dq_flags & DQ_LOCK) {
  636                 dq->dq_flags |= DQ_WANT;
  637                 (void) tsleep(dq, PINOD+1, "setqta", 0);
  638         }
  639         /*
  640          * Copy all but the current values.
  641          * Reset time limit if previously had no soft limit or were
  642          * under it, but now have a soft limit and are over it.
  643          */
  644         newlim.dqb_curblocks = dq->dq_curblocks;
  645         newlim.dqb_curinodes = dq->dq_curinodes;
  646         if (dq->dq_id != 0) {
  647                 newlim.dqb_btime = dq->dq_btime;
  648                 newlim.dqb_itime = dq->dq_itime;
  649         }
  650         if (newlim.dqb_bsoftlimit &&
  651             dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
  652             (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
  653                 newlim.dqb_btime = time_second + ump->um_btime[type];
  654         if (newlim.dqb_isoftlimit &&
  655             dq->dq_curinodes >= newlim.dqb_isoftlimit &&
  656             (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
  657                 newlim.dqb_itime = time_second + ump->um_itime[type];
  658         dq->dq_dqb = newlim;
  659         if (dq->dq_curblocks < dq->dq_bsoftlimit)
  660                 dq->dq_flags &= ~DQ_BLKS;
  661         if (dq->dq_curinodes < dq->dq_isoftlimit)
  662                 dq->dq_flags &= ~DQ_INODS;
  663         if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
  664             dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
  665                 dq->dq_flags |= DQ_FAKE;
  666         else
  667                 dq->dq_flags &= ~DQ_FAKE;
  668         dq->dq_flags |= DQ_MOD;
  669         dqrele(NULLVP, dq);
  670         return (0);
  671 }
  672 
  673 /*
  674  * Q_SETUSE - set current inode and block usage.
  675  */
  676 int
  677 setuse(td, mp, id, type, addr)
  678         struct thread *td;
  679         struct mount *mp;
  680         u_long id;
  681         int type;
  682         caddr_t addr;
  683 {
  684         struct dquot *dq;
  685         struct ufsmount *ump = VFSTOUFS(mp);
  686         struct dquot *ndq;
  687         struct dqblk usage;
  688         int error;
  689 
  690         error = suser_cred(td->td_ucred, PRISON_ROOT);
  691         if (error)
  692                 return (error);
  693 
  694         error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk));
  695         if (error)
  696                 return (error);
  697         error = dqget(NULLVP, id, ump, type, &ndq);
  698         if (error)
  699                 return (error);
  700         dq = ndq;
  701         while (dq->dq_flags & DQ_LOCK) {
  702                 dq->dq_flags |= DQ_WANT;
  703                 (void) tsleep(dq, PINOD+1, "setuse", 0);
  704         }
  705         /*
  706          * Reset time limit if have a soft limit and were
  707          * previously under it, but are now over it.
  708          */
  709         if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
  710             usage.dqb_curblocks >= dq->dq_bsoftlimit)
  711                 dq->dq_btime = time_second + ump->um_btime[type];
  712         if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
  713             usage.dqb_curinodes >= dq->dq_isoftlimit)
  714                 dq->dq_itime = time_second + ump->um_itime[type];
  715         dq->dq_curblocks = usage.dqb_curblocks;
  716         dq->dq_curinodes = usage.dqb_curinodes;
  717         if (dq->dq_curblocks < dq->dq_bsoftlimit)
  718                 dq->dq_flags &= ~DQ_BLKS;
  719         if (dq->dq_curinodes < dq->dq_isoftlimit)
  720                 dq->dq_flags &= ~DQ_INODS;
  721         dq->dq_flags |= DQ_MOD;
  722         dqrele(NULLVP, dq);
  723         return (0);
  724 }
  725 
  726 /*
  727  * Q_SYNC - sync quota files to disk.
  728  */
  729 int
  730 qsync(mp)
  731         struct mount *mp;
  732 {
  733         struct ufsmount *ump = VFSTOUFS(mp);
  734         struct thread *td = curthread;          /* XXX */
  735         struct vnode *vp, *nextvp;
  736         struct dquot *dq;
  737         int i, error;
  738 
  739         /*
  740          * Check if the mount point has any quotas.
  741          * If not, simply return.
  742          */
  743         for (i = 0; i < MAXQUOTAS; i++)
  744                 if (ump->um_quotas[i] != NULLVP)
  745                         break;
  746         if (i == MAXQUOTAS)
  747                 return (0);
  748         /*
  749          * Search vnodes associated with this mount point,
  750          * synchronizing any modified dquot structures.
  751          */
  752         MNT_ILOCK(mp);
  753 again:
  754         for (vp = TAILQ_FIRST(&mp->mnt_nvnodelist); vp != NULL; vp = nextvp) {
  755                 if (vp->v_mount != mp)
  756                         goto again;
  757                 nextvp = TAILQ_NEXT(vp, v_nmntvnodes);
  758                 VI_LOCK(vp);
  759                 MNT_IUNLOCK(mp);
  760                 if (vp->v_type == VNON) {
  761                         VI_UNLOCK(vp);
  762                         MNT_ILOCK(mp);
  763                         continue;
  764                 }
  765                 error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td);
  766                 if (error) {
  767                         MNT_ILOCK(mp);
  768                         if (error == ENOENT)
  769                                 goto again;
  770                         continue;
  771                 }
  772                 for (i = 0; i < MAXQUOTAS; i++) {
  773                         dq = VTOI(vp)->i_dquot[i];
  774                         if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
  775                                 dqsync(vp, dq);
  776                 }
  777                 vput(vp);
  778                 MNT_ILOCK(mp);
  779                 if (TAILQ_NEXT(vp, v_nmntvnodes) != nextvp)
  780                         goto again;
  781         }
  782         MNT_IUNLOCK(mp);
  783         return (0);
  784 }
  785 
  786 /*
  787  * Code pertaining to management of the in-core dquot data structures.
  788  */
  789 #define DQHASH(dqvp, id) \
  790         (&dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & dqhash])
  791 static LIST_HEAD(dqhash, dquot) *dqhashtbl;
  792 static u_long dqhash;
  793 
  794 /*
  795  * Dquot free list.
  796  */
  797 #define DQUOTINC        5       /* minimum free dquots desired */
  798 static TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
  799 static long numdquot, desireddquot = DQUOTINC;
  800 
  801 /*
  802  * Initialize the quota system.
  803  */
  804 void
  805 dqinit()
  806 {
  807 
  808         dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
  809         TAILQ_INIT(&dqfreelist);
  810 }
  811 
  812 /*
  813  * Shut down the quota system.
  814  */
  815 void
  816 dquninit()
  817 {
  818         struct dquot *dq;
  819 
  820         hashdestroy(dqhashtbl, M_DQUOT, dqhash);
  821         while ((dq = TAILQ_FIRST(&dqfreelist)) != NULL) {
  822                 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
  823                 free(dq, M_DQUOT);
  824         }
  825 }
  826 
  827 /*
  828  * Obtain a dquot structure for the specified identifier and quota file
  829  * reading the information from the file if necessary.
  830  */
  831 static int
  832 dqget(vp, id, ump, type, dqp)
  833         struct vnode *vp;
  834         u_long id;
  835         struct ufsmount *ump;
  836         int type;
  837         struct dquot **dqp;
  838 {
  839         struct thread *td = curthread;          /* XXX */
  840         struct dquot *dq;
  841         struct dqhash *dqh;
  842         struct vnode *dqvp;
  843         struct iovec aiov;
  844         struct uio auio;
  845         int error;
  846 
  847         dqvp = ump->um_quotas[type];
  848         if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
  849                 *dqp = NODQUOT;
  850                 return (EINVAL);
  851         }
  852         /*
  853          * Check the cache first.
  854          */
  855         dqh = DQHASH(dqvp, id);
  856         LIST_FOREACH(dq, dqh, dq_hash) {
  857                 if (dq->dq_id != id ||
  858                     dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
  859                         continue;
  860                 /*
  861                  * Cache hit with no references.  Take
  862                  * the structure off the free list.
  863                  */
  864                 if (dq->dq_cnt == 0)
  865                         TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
  866                 DQREF(dq);
  867                 *dqp = dq;
  868                 return (0);
  869         }
  870         /*
  871          * Not in cache, allocate a new one.
  872          */
  873         if (TAILQ_FIRST(&dqfreelist) == NODQUOT &&
  874             numdquot < MAXQUOTAS * desiredvnodes)
  875                 desireddquot += DQUOTINC;
  876         if (numdquot < desireddquot) {
  877                 dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT,
  878                     M_WAITOK | M_ZERO);
  879                 numdquot++;
  880         } else {
  881                 if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) {
  882                         tablefull("dquot");
  883                         *dqp = NODQUOT;
  884                         return (EUSERS);
  885                 }
  886                 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
  887                         panic("dqget: free dquot isn't");
  888                 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
  889                 if (dq->dq_ump != NULL)
  890                         LIST_REMOVE(dq, dq_hash);
  891         }
  892         /*
  893          * Initialize the contents of the dquot structure.
  894          */
  895         if (vp != dqvp)
  896                 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, td);
  897         LIST_INSERT_HEAD(dqh, dq, dq_hash);
  898         DQREF(dq);
  899         dq->dq_flags = DQ_LOCK;
  900         dq->dq_id = id;
  901         dq->dq_ump = ump;
  902         dq->dq_type = type;
  903         auio.uio_iov = &aiov;
  904         auio.uio_iovcnt = 1;
  905         aiov.iov_base = (caddr_t)&dq->dq_dqb;
  906         aiov.iov_len = sizeof (struct dqblk);
  907         auio.uio_resid = sizeof (struct dqblk);
  908         auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
  909         auio.uio_segflg = UIO_SYSSPACE;
  910         auio.uio_rw = UIO_READ;
  911         auio.uio_td = (struct thread *)0;
  912         error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
  913         if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
  914                 bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk));
  915         if (vp != dqvp)
  916                 VOP_UNLOCK(dqvp, 0, td);
  917         if (dq->dq_flags & DQ_WANT)
  918                 wakeup(dq);
  919         dq->dq_flags = 0;
  920         /*
  921          * I/O error in reading quota file, release
  922          * quota structure and reflect problem to caller.
  923          */
  924         if (error) {
  925                 LIST_REMOVE(dq, dq_hash);
  926                 dqrele(vp, dq);
  927                 *dqp = NODQUOT;
  928                 return (error);
  929         }
  930         /*
  931          * Check for no limit to enforce.
  932          * Initialize time values if necessary.
  933          */
  934         if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
  935             dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
  936                 dq->dq_flags |= DQ_FAKE;
  937         if (dq->dq_id != 0) {
  938                 if (dq->dq_btime == 0)
  939                         dq->dq_btime = time_second + ump->um_btime[type];
  940                 if (dq->dq_itime == 0)
  941                         dq->dq_itime = time_second + ump->um_itime[type];
  942         }
  943         *dqp = dq;
  944         return (0);
  945 }
  946 
  947 #ifdef DIAGNOSTIC
  948 /*
  949  * Obtain a reference to a dquot.
  950  */
  951 static void
  952 dqref(dq)
  953         struct dquot *dq;
  954 {
  955 
  956         dq->dq_cnt++;
  957 }
  958 #endif
  959 
  960 /*
  961  * Release a reference to a dquot.
  962  */
  963 void
  964 dqrele(vp, dq)
  965         struct vnode *vp;
  966         struct dquot *dq;
  967 {
  968 
  969         if (dq == NODQUOT)
  970                 return;
  971         if (dq->dq_cnt > 1) {
  972                 dq->dq_cnt--;
  973                 return;
  974         }
  975         if (dq->dq_flags & DQ_MOD)
  976                 (void) dqsync(vp, dq);
  977         if (--dq->dq_cnt > 0)
  978                 return;
  979         TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
  980 }
  981 
  982 /*
  983  * Update the disk quota in the quota file.
  984  */
  985 static int
  986 dqsync(vp, dq)
  987         struct vnode *vp;
  988         struct dquot *dq;
  989 {
  990         struct thread *td = curthread;          /* XXX */
  991         struct vnode *dqvp;
  992         struct iovec aiov;
  993         struct uio auio;
  994         int error;
  995 
  996         if (dq == NODQUOT)
  997                 panic("dqsync: dquot");
  998         if ((dq->dq_flags & DQ_MOD) == 0)
  999                 return (0);
 1000         if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
 1001                 panic("dqsync: file");
 1002         (void) vn_write_suspend_wait(dqvp, NULL, V_WAIT);
 1003         if (vp != dqvp)
 1004                 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, td);
 1005         while (dq->dq_flags & DQ_LOCK) {
 1006                 dq->dq_flags |= DQ_WANT;
 1007                 (void) tsleep(dq, PINOD+2, "dqsync", 0);
 1008                 if ((dq->dq_flags & DQ_MOD) == 0) {
 1009                         if (vp != dqvp)
 1010                                 VOP_UNLOCK(dqvp, 0, td);
 1011                         return (0);
 1012                 }
 1013         }
 1014         dq->dq_flags |= DQ_LOCK;
 1015         auio.uio_iov = &aiov;
 1016         auio.uio_iovcnt = 1;
 1017         aiov.iov_base = (caddr_t)&dq->dq_dqb;
 1018         aiov.iov_len = sizeof (struct dqblk);
 1019         auio.uio_resid = sizeof (struct dqblk);
 1020         auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
 1021         auio.uio_segflg = UIO_SYSSPACE;
 1022         auio.uio_rw = UIO_WRITE;
 1023         auio.uio_td = (struct thread *)0;
 1024         error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
 1025         if (auio.uio_resid && error == 0)
 1026                 error = EIO;
 1027         if (dq->dq_flags & DQ_WANT)
 1028                 wakeup(dq);
 1029         dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
 1030         if (vp != dqvp)
 1031                 VOP_UNLOCK(dqvp, 0, td);
 1032         return (error);
 1033 }
 1034 
 1035 /*
 1036  * Flush all entries from the cache for a particular vnode.
 1037  */
 1038 static void
 1039 dqflush(vp)
 1040         struct vnode *vp;
 1041 {
 1042         struct dquot *dq, *nextdq;
 1043         struct dqhash *dqh;
 1044 
 1045         /*
 1046          * Move all dquot's that used to refer to this quota
 1047          * file off their hash chains (they will eventually
 1048          * fall off the head of the free list and be re-used).
 1049          */
 1050         for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
 1051                 for (dq = LIST_FIRST(dqh); dq; dq = nextdq) {
 1052                         nextdq = LIST_NEXT(dq, dq_hash);
 1053                         if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
 1054                                 continue;
 1055                         if (dq->dq_cnt)
 1056                                 panic("dqflush: stray dquot");
 1057                         LIST_REMOVE(dq, dq_hash);
 1058                         dq->dq_ump = (struct ufsmount *)0;
 1059                 }
 1060         }
 1061 }

Cache object: 89f4a4a9e9791a45727ec0d0f0c910d8


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.