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  * $FreeBSD$
   38  */
   39 
   40 #include <sys/param.h>
   41 #include <sys/systm.h>
   42 #include <sys/kernel.h>
   43 #include <sys/namei.h>
   44 #include <sys/malloc.h>
   45 #include <sys/fcntl.h>
   46 #include <sys/proc.h>
   47 #include <sys/vnode.h>
   48 #include <sys/mount.h>
   49 
   50 #include <ufs/ufs/quota.h>
   51 #include <ufs/ufs/inode.h>
   52 #include <ufs/ufs/ufsmount.h>
   53 
   54 static MALLOC_DEFINE(M_DQUOT, "UFS quota", "UFS quota entries");
   55 
   56 /*
   57  * Quota name to error message mapping.
   58  */
   59 static char *quotatypes[] = INITQFNAMES;
   60 
   61 static int chkdqchg __P((struct inode *, long, struct ucred *, int));
   62 static int chkiqchg __P((struct inode *, long, struct ucred *, int));
   63 static int dqget __P((struct vnode *,
   64                 u_long, struct ufsmount *, int, struct dquot **));
   65 static int dqsync __P((struct vnode *, struct dquot *));
   66 static void dqflush __P((struct vnode *));
   67 
   68 #ifdef DIAGNOSTIC
   69 static void dqref __P((struct dquot *));
   70 static void chkdquot __P((struct inode *));
   71 #endif
   72 
   73 /*
   74  * Set up the quotas for an inode.
   75  *
   76  * This routine completely defines the semantics of quotas.
   77  * If other criterion want to be used to establish quotas, the
   78  * MAXQUOTAS value in quotas.h should be increased, and the
   79  * additional dquots set up here.
   80  */
   81 int
   82 getinoquota(ip)
   83         register struct inode *ip;
   84 {
   85         struct ufsmount *ump;
   86         struct vnode *vp = ITOV(ip);
   87         int error;
   88 
   89         ump = VFSTOUFS(vp->v_mount);
   90         /*
   91          * Set up the user quota based on file uid.
   92          * EINVAL means that quotas are not enabled.
   93          */
   94         if (ip->i_dquot[USRQUOTA] == NODQUOT &&
   95             (error =
   96                 dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
   97             error != EINVAL)
   98                 return (error);
   99         /*
  100          * Set up the group quota based on file gid.
  101          * EINVAL means that quotas are not enabled.
  102          */
  103         if (ip->i_dquot[GRPQUOTA] == NODQUOT &&
  104             (error =
  105                 dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
  106             error != EINVAL)
  107                 return (error);
  108         return (0);
  109 }
  110 
  111 /*
  112  * Update disk usage, and take corrective action.
  113  */
  114 int
  115 chkdq(ip, change, cred, flags)
  116         register struct inode *ip;
  117         long change;
  118         struct ucred *cred;
  119         int flags;
  120 {
  121         register struct dquot *dq;
  122         register int i;
  123         int ncurblocks, error;
  124 
  125 #ifdef DIAGNOSTIC
  126         if ((flags & CHOWN) == 0)
  127                 chkdquot(ip);
  128 #endif
  129         if (change == 0)
  130                 return (0);
  131         if (change < 0) {
  132                 for (i = 0; i < MAXQUOTAS; i++) {
  133                         if ((dq = ip->i_dquot[i]) == NODQUOT)
  134                                 continue;
  135                         while (dq->dq_flags & DQ_LOCK) {
  136                                 dq->dq_flags |= DQ_WANT;
  137                                 (void) tsleep((caddr_t)dq, PINOD+1, "chkdq1", 0);
  138                         }
  139                         ncurblocks = dq->dq_curblocks + change;
  140                         if (ncurblocks >= 0)
  141                                 dq->dq_curblocks = ncurblocks;
  142                         else
  143                                 dq->dq_curblocks = 0;
  144                         dq->dq_flags &= ~DQ_BLKS;
  145                         dq->dq_flags |= DQ_MOD;
  146                 }
  147                 return (0);
  148         }
  149         if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
  150                 for (i = 0; i < MAXQUOTAS; i++) {
  151                         if ((dq = ip->i_dquot[i]) == NODQUOT)
  152                                 continue;
  153                         error = chkdqchg(ip, change, cred, i);
  154                         if (error)
  155                                 return (error);
  156                 }
  157         }
  158         for (i = 0; i < MAXQUOTAS; i++) {
  159                 if ((dq = ip->i_dquot[i]) == NODQUOT)
  160                         continue;
  161                 while (dq->dq_flags & DQ_LOCK) {
  162                         dq->dq_flags |= DQ_WANT;
  163                         (void) tsleep((caddr_t)dq, PINOD+1, "chkdq2", 0);
  164                 }
  165                 /* Reset timer when crossing soft limit */
  166                 if (dq->dq_curblocks + change >= dq->dq_bsoftlimit &&
  167                     dq->dq_curblocks < dq->dq_bsoftlimit)
  168                         dq->dq_btime = time_second +
  169                             VFSTOUFS(ITOV(ip)->v_mount)->um_btime[i];
  170                 dq->dq_curblocks += change;
  171                 dq->dq_flags |= DQ_MOD;
  172         }
  173         return (0);
  174 }
  175 
  176 /*
  177  * Check for a valid change to a users allocation.
  178  * Issue an error message if appropriate.
  179  */
  180 static int
  181 chkdqchg(ip, change, cred, type)
  182         struct inode *ip;
  183         long change;
  184         struct ucred *cred;
  185         int type;
  186 {
  187         register struct dquot *dq = ip->i_dquot[type];
  188         long ncurblocks = dq->dq_curblocks + change;
  189 
  190         /*
  191          * If user would exceed their hard limit, disallow space allocation.
  192          */
  193         if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
  194                 if ((dq->dq_flags & DQ_BLKS) == 0 &&
  195                     ip->i_uid == cred->cr_uid) {
  196                         uprintf("\n%s: write failed, %s disk limit reached\n",
  197                             ITOV(ip)->v_mount->mnt_stat.f_mntonname,
  198                             quotatypes[type]);
  199                         dq->dq_flags |= DQ_BLKS;
  200                 }
  201                 return (EDQUOT);
  202         }
  203         /*
  204          * If user is over their soft limit for too long, disallow space
  205          * allocation. Reset time limit as they cross their soft limit.
  206          */
  207         if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
  208                 if (dq->dq_curblocks < dq->dq_bsoftlimit) {
  209                         dq->dq_btime = time_second +
  210                             VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type];
  211                         if (ip->i_uid == cred->cr_uid)
  212                                 uprintf("\n%s: warning, %s %s\n",
  213                                     ITOV(ip)->v_mount->mnt_stat.f_mntonname,
  214                                     quotatypes[type], "disk quota exceeded");
  215                         return (0);
  216                 }
  217                 if (time_second > dq->dq_btime) {
  218                         if ((dq->dq_flags & DQ_BLKS) == 0 &&
  219                             ip->i_uid == cred->cr_uid) {
  220                                 uprintf("\n%s: write failed, %s %s\n",
  221                                     ITOV(ip)->v_mount->mnt_stat.f_mntonname,
  222                                     quotatypes[type],
  223                                     "disk quota exceeded for too long");
  224                                 dq->dq_flags |= DQ_BLKS;
  225                         }
  226                         return (EDQUOT);
  227                 }
  228         }
  229         return (0);
  230 }
  231 
  232 /*
  233  * Check the inode limit, applying corrective action.
  234  */
  235 int
  236 chkiq(ip, change, cred, flags)
  237         register struct inode *ip;
  238         long change;
  239         struct ucred *cred;
  240         int flags;
  241 {
  242         register struct dquot *dq;
  243         register int i;
  244         int ncurinodes, error;
  245 
  246 #ifdef DIAGNOSTIC
  247         if ((flags & CHOWN) == 0)
  248                 chkdquot(ip);
  249 #endif
  250         if (change == 0)
  251                 return (0);
  252         if (change < 0) {
  253                 for (i = 0; i < MAXQUOTAS; i++) {
  254                         if ((dq = ip->i_dquot[i]) == NODQUOT)
  255                                 continue;
  256                         while (dq->dq_flags & DQ_LOCK) {
  257                                 dq->dq_flags |= DQ_WANT;
  258                                 (void) tsleep((caddr_t)dq, PINOD+1, "chkiq1", 0);
  259                         }
  260                         ncurinodes = dq->dq_curinodes + change;
  261                         if (ncurinodes >= 0)
  262                                 dq->dq_curinodes = ncurinodes;
  263                         else
  264                                 dq->dq_curinodes = 0;
  265                         dq->dq_flags &= ~DQ_INODS;
  266                         dq->dq_flags |= DQ_MOD;
  267                 }
  268                 return (0);
  269         }
  270         if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
  271                 for (i = 0; i < MAXQUOTAS; i++) {
  272                         if ((dq = ip->i_dquot[i]) == NODQUOT)
  273                                 continue;
  274                         error = chkiqchg(ip, change, cred, i);
  275                         if (error)
  276                                 return (error);
  277                 }
  278         }
  279         for (i = 0; i < MAXQUOTAS; i++) {
  280                 if ((dq = ip->i_dquot[i]) == NODQUOT)
  281                         continue;
  282                 while (dq->dq_flags & DQ_LOCK) {
  283                         dq->dq_flags |= DQ_WANT;
  284                         (void) tsleep((caddr_t)dq, PINOD+1, "chkiq2", 0);
  285                 }
  286                 /* Reset timer when crossing soft limit */
  287                 if (dq->dq_curinodes + change >= dq->dq_isoftlimit &&
  288                     dq->dq_curinodes < dq->dq_isoftlimit)
  289                         dq->dq_itime = time_second +
  290                             VFSTOUFS(ITOV(ip)->v_mount)->um_itime[i];
  291                 dq->dq_curinodes += change;
  292                 dq->dq_flags |= DQ_MOD;
  293         }
  294         return (0);
  295 }
  296 
  297 /*
  298  * Check for a valid change to a users allocation.
  299  * Issue an error message if appropriate.
  300  */
  301 static int
  302 chkiqchg(ip, change, cred, type)
  303         struct inode *ip;
  304         long change;
  305         struct ucred *cred;
  306         int type;
  307 {
  308         register struct dquot *dq = ip->i_dquot[type];
  309         long ncurinodes = dq->dq_curinodes + change;
  310 
  311         /*
  312          * If user would exceed their hard limit, disallow inode allocation.
  313          */
  314         if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
  315                 if ((dq->dq_flags & DQ_INODS) == 0 &&
  316                     ip->i_uid == cred->cr_uid) {
  317                         uprintf("\n%s: write failed, %s inode limit reached\n",
  318                             ITOV(ip)->v_mount->mnt_stat.f_mntonname,
  319                             quotatypes[type]);
  320                         dq->dq_flags |= DQ_INODS;
  321                 }
  322                 return (EDQUOT);
  323         }
  324         /*
  325          * If user is over their soft limit for too long, disallow inode
  326          * allocation. Reset time limit as they cross their soft limit.
  327          */
  328         if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
  329                 if (dq->dq_curinodes < dq->dq_isoftlimit) {
  330                         dq->dq_itime = time_second +
  331                             VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type];
  332                         if (ip->i_uid == cred->cr_uid)
  333                                 uprintf("\n%s: warning, %s %s\n",
  334                                     ITOV(ip)->v_mount->mnt_stat.f_mntonname,
  335                                     quotatypes[type], "inode quota exceeded");
  336                         return (0);
  337                 }
  338                 if (time_second > dq->dq_itime) {
  339                         if ((dq->dq_flags & DQ_INODS) == 0 &&
  340                             ip->i_uid == cred->cr_uid) {
  341                                 uprintf("\n%s: write failed, %s %s\n",
  342                                     ITOV(ip)->v_mount->mnt_stat.f_mntonname,
  343                                     quotatypes[type],
  344                                     "inode quota exceeded for too long");
  345                                 dq->dq_flags |= DQ_INODS;
  346                         }
  347                         return (EDQUOT);
  348                 }
  349         }
  350         return (0);
  351 }
  352 
  353 #ifdef DIAGNOSTIC
  354 /*
  355  * On filesystems with quotas enabled, it is an error for a file to change
  356  * size and not to have a dquot structure associated with it.
  357  */
  358 static void
  359 chkdquot(ip)
  360         register struct inode *ip;
  361 {
  362         struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount);
  363         register int i;
  364 
  365         for (i = 0; i < MAXQUOTAS; i++) {
  366                 if (ump->um_quotas[i] == NULLVP ||
  367                     (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))
  368                         continue;
  369                 if (ip->i_dquot[i] == NODQUOT) {
  370                         vprint("chkdquot: missing dquot", ITOV(ip));
  371                         panic("chkdquot: missing dquot");
  372                 }
  373         }
  374 }
  375 #endif
  376 
  377 /*
  378  * Code to process quotactl commands.
  379  */
  380 
  381 /*
  382  * Q_QUOTAON - set up a quota file for a particular file system.
  383  */
  384 int
  385 quotaon(p, mp, type, fname)
  386         struct proc *p;
  387         struct mount *mp;
  388         register int type;
  389         caddr_t fname;
  390 {
  391         struct ufsmount *ump = VFSTOUFS(mp);
  392         struct vnode *vp, **vpp;
  393         struct vnode *nextvp;
  394         struct dquot *dq;
  395         int error;
  396         struct nameidata nd;
  397 
  398         vpp = &ump->um_quotas[type];
  399         NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, p);
  400         error = vn_open(&nd, FREAD|FWRITE, 0);
  401         if (error)
  402                 return (error);
  403         vp = nd.ni_vp;
  404         VOP_UNLOCK(vp, 0, p);
  405         if (vp->v_type != VREG) {
  406                 (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
  407                 return (EACCES);
  408         }
  409         if (*vpp != vp)
  410                 quotaoff(p, mp, type);
  411         ump->um_qflags[type] |= QTF_OPENING;
  412         mp->mnt_flag |= MNT_QUOTA;
  413         vp->v_flag |= VSYSTEM;
  414         *vpp = vp;
  415         /*
  416          * Save the credential of the process that turned on quotas.
  417          * Set up the time limits for this quota.
  418          */
  419         crhold(p->p_ucred);
  420         ump->um_cred[type] = p->p_ucred;
  421         ump->um_btime[type] = MAX_DQ_TIME;
  422         ump->um_itime[type] = MAX_IQ_TIME;
  423         if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
  424                 if (dq->dq_btime > 0)
  425                         ump->um_btime[type] = dq->dq_btime;
  426                 if (dq->dq_itime > 0)
  427                         ump->um_itime[type] = dq->dq_itime;
  428                 dqrele(NULLVP, dq);
  429         }
  430         /*
  431          * Search vnodes associated with this mount point,
  432          * adding references to quota file being opened.
  433          * NB: only need to add dquot's for inodes being modified.
  434          */
  435 again:
  436         for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
  437                 nextvp = vp->v_mntvnodes.le_next;
  438                 if (vp->v_type == VNON || vp->v_writecount == 0)
  439                         continue;
  440                 if (vget(vp, LK_EXCLUSIVE, p))
  441                         goto again;
  442                 error = getinoquota(VTOI(vp));
  443                 if (error) {
  444                         vput(vp);
  445                         break;
  446                 }
  447                 vput(vp);
  448                 if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)
  449                         goto again;
  450         }
  451         ump->um_qflags[type] &= ~QTF_OPENING;
  452         if (error)
  453                 quotaoff(p, mp, type);
  454         return (error);
  455 }
  456 
  457 /*
  458  * Q_QUOTAOFF - turn off disk quotas for a filesystem.
  459  */
  460 int
  461 quotaoff(p, mp, type)
  462         struct proc *p;
  463         struct mount *mp;
  464         register int type;
  465 {
  466         struct vnode *vp;
  467         struct vnode *qvp, *nextvp;
  468         struct ufsmount *ump = VFSTOUFS(mp);
  469         struct dquot *dq;
  470         struct inode *ip;
  471         int error;
  472 
  473         if ((qvp = ump->um_quotas[type]) == NULLVP)
  474                 return (0);
  475         ump->um_qflags[type] |= QTF_CLOSING;
  476         /*
  477          * Search vnodes associated with this mount point,
  478          * deleting any references to quota file being closed.
  479          */
  480 again:
  481         for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
  482                 nextvp = vp->v_mntvnodes.le_next;
  483                 if (vp->v_type == VNON)
  484                         continue;
  485                 if (vget(vp, LK_EXCLUSIVE, p))
  486                         goto again;
  487                 ip = VTOI(vp);
  488                 dq = ip->i_dquot[type];
  489                 ip->i_dquot[type] = NODQUOT;
  490                 dqrele(vp, dq);
  491                 vput(vp);
  492                 if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)
  493                         goto again;
  494         }
  495         dqflush(qvp);
  496         qvp->v_flag &= ~VSYSTEM;
  497         error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p);
  498         ump->um_quotas[type] = NULLVP;
  499         crfree(ump->um_cred[type]);
  500         ump->um_cred[type] = NOCRED;
  501         ump->um_qflags[type] &= ~QTF_CLOSING;
  502         for (type = 0; type < MAXQUOTAS; type++)
  503                 if (ump->um_quotas[type] != NULLVP)
  504                         break;
  505         if (type == MAXQUOTAS)
  506                 mp->mnt_flag &= ~MNT_QUOTA;
  507         return (error);
  508 }
  509 
  510 /*
  511  * Q_GETQUOTA - return current values in a dqblk structure.
  512  */
  513 int
  514 getquota(mp, id, type, addr)
  515         struct mount *mp;
  516         u_long id;
  517         int type;
  518         caddr_t addr;
  519 {
  520         struct dquot *dq;
  521         int error;
  522 
  523         error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq);
  524         if (error)
  525                 return (error);
  526         error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk));
  527         dqrele(NULLVP, dq);
  528         return (error);
  529 }
  530 
  531 /*
  532  * Q_SETQUOTA - assign an entire dqblk structure.
  533  */
  534 int
  535 setquota(mp, id, type, addr)
  536         struct mount *mp;
  537         u_long id;
  538         int type;
  539         caddr_t addr;
  540 {
  541         register struct dquot *dq;
  542         struct dquot *ndq;
  543         struct ufsmount *ump = VFSTOUFS(mp);
  544         struct dqblk newlim;
  545         int error;
  546 
  547         error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk));
  548         if (error)
  549                 return (error);
  550         error = dqget(NULLVP, id, ump, type, &ndq);
  551         if (error)
  552                 return (error);
  553         dq = ndq;
  554         while (dq->dq_flags & DQ_LOCK) {
  555                 dq->dq_flags |= DQ_WANT;
  556                 (void) tsleep((caddr_t)dq, PINOD+1, "setqta", 0);
  557         }
  558         /*
  559          * Copy all but the current values.
  560          * Reset time limit if previously had no soft limit or were
  561          * under it, but now have a soft limit and are over it.
  562          */
  563         newlim.dqb_curblocks = dq->dq_curblocks;
  564         newlim.dqb_curinodes = dq->dq_curinodes;
  565         if (dq->dq_id != 0) {
  566                 newlim.dqb_btime = dq->dq_btime;
  567                 newlim.dqb_itime = dq->dq_itime;
  568         }
  569         if (newlim.dqb_bsoftlimit &&
  570             dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
  571             (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
  572                 newlim.dqb_btime = time_second + ump->um_btime[type];
  573         if (newlim.dqb_isoftlimit &&
  574             dq->dq_curinodes >= newlim.dqb_isoftlimit &&
  575             (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
  576                 newlim.dqb_itime = time_second + ump->um_itime[type];
  577         dq->dq_dqb = newlim;
  578         if (dq->dq_curblocks < dq->dq_bsoftlimit)
  579                 dq->dq_flags &= ~DQ_BLKS;
  580         if (dq->dq_curinodes < dq->dq_isoftlimit)
  581                 dq->dq_flags &= ~DQ_INODS;
  582         if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
  583             dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
  584                 dq->dq_flags |= DQ_FAKE;
  585         else
  586                 dq->dq_flags &= ~DQ_FAKE;
  587         dq->dq_flags |= DQ_MOD;
  588         dqrele(NULLVP, dq);
  589         return (0);
  590 }
  591 
  592 /*
  593  * Q_SETUSE - set current inode and block usage.
  594  */
  595 int
  596 setuse(mp, id, type, addr)
  597         struct mount *mp;
  598         u_long id;
  599         int type;
  600         caddr_t addr;
  601 {
  602         register struct dquot *dq;
  603         struct ufsmount *ump = VFSTOUFS(mp);
  604         struct dquot *ndq;
  605         struct dqblk usage;
  606         int error;
  607 
  608         error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk));
  609         if (error)
  610                 return (error);
  611         error = dqget(NULLVP, id, ump, type, &ndq);
  612         if (error)
  613                 return (error);
  614         dq = ndq;
  615         while (dq->dq_flags & DQ_LOCK) {
  616                 dq->dq_flags |= DQ_WANT;
  617                 (void) tsleep((caddr_t)dq, PINOD+1, "setuse", 0);
  618         }
  619         /*
  620          * Reset time limit if have a soft limit and were
  621          * previously under it, but are now over it.
  622          */
  623         if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
  624             usage.dqb_curblocks >= dq->dq_bsoftlimit)
  625                 dq->dq_btime = time_second + ump->um_btime[type];
  626         if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
  627             usage.dqb_curinodes >= dq->dq_isoftlimit)
  628                 dq->dq_itime = time_second + ump->um_itime[type];
  629         dq->dq_curblocks = usage.dqb_curblocks;
  630         dq->dq_curinodes = usage.dqb_curinodes;
  631         if (dq->dq_curblocks < dq->dq_bsoftlimit)
  632                 dq->dq_flags &= ~DQ_BLKS;
  633         if (dq->dq_curinodes < dq->dq_isoftlimit)
  634                 dq->dq_flags &= ~DQ_INODS;
  635         dq->dq_flags |= DQ_MOD;
  636         dqrele(NULLVP, dq);
  637         return (0);
  638 }
  639 
  640 /*
  641  * Q_SYNC - sync quota files to disk.
  642  */
  643 int
  644 qsync(mp)
  645         struct mount *mp;
  646 {
  647         struct ufsmount *ump = VFSTOUFS(mp);
  648         struct proc *p = curproc;               /* XXX */
  649         struct vnode *vp, *nextvp;
  650         struct dquot *dq;
  651         int i, error;
  652 
  653         /*
  654          * Check if the mount point has any quotas.
  655          * If not, simply return.
  656          */
  657         for (i = 0; i < MAXQUOTAS; i++)
  658                 if (ump->um_quotas[i] != NULLVP)
  659                         break;
  660         if (i == MAXQUOTAS)
  661                 return (0);
  662         /*
  663          * Search vnodes associated with this mount point,
  664          * synchronizing any modified dquot structures.
  665          */
  666         simple_lock(&mntvnode_slock);
  667 again:
  668         for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
  669                 if (vp->v_mount != mp)
  670                         goto again;
  671                 nextvp = vp->v_mntvnodes.le_next;
  672                 if (vp->v_type == VNON)
  673                         continue;
  674                 simple_lock(&vp->v_interlock);
  675                 simple_unlock(&mntvnode_slock);
  676                 error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
  677                 if (error) {
  678                         simple_lock(&mntvnode_slock);
  679                         if (error == ENOENT)
  680                                 goto again;
  681                         continue;
  682                 }
  683                 for (i = 0; i < MAXQUOTAS; i++) {
  684                         dq = VTOI(vp)->i_dquot[i];
  685                         if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
  686                                 dqsync(vp, dq);
  687                 }
  688                 vput(vp);
  689                 simple_lock(&mntvnode_slock);
  690                 if (vp->v_mntvnodes.le_next != nextvp)
  691                         goto again;
  692         }
  693         simple_unlock(&mntvnode_slock);
  694         return (0);
  695 }
  696 
  697 /*
  698  * Code pertaining to management of the in-core dquot data structures.
  699  */
  700 #define DQHASH(dqvp, id) \
  701         (&dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & dqhash])
  702 static LIST_HEAD(dqhash, dquot) *dqhashtbl;
  703 static u_long dqhash;
  704 
  705 /*
  706  * Dquot free list.
  707  */
  708 #define DQUOTINC        5       /* minimum free dquots desired */
  709 static TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
  710 static long numdquot, desireddquot = DQUOTINC;
  711 
  712 /*
  713  * Initialize the quota system.
  714  */
  715 void
  716 dqinit()
  717 {
  718 
  719         dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
  720         TAILQ_INIT(&dqfreelist);
  721 }
  722 
  723 /*
  724  * Obtain a dquot structure for the specified identifier and quota file
  725  * reading the information from the file if necessary.
  726  */
  727 static int
  728 dqget(vp, id, ump, type, dqp)
  729         struct vnode *vp;
  730         u_long id;
  731         register struct ufsmount *ump;
  732         register int type;
  733         struct dquot **dqp;
  734 {
  735         struct proc *p = curproc;               /* XXX */
  736         struct dquot *dq;
  737         struct dqhash *dqh;
  738         struct vnode *dqvp;
  739         struct iovec aiov;
  740         struct uio auio;
  741         int error;
  742 
  743         dqvp = ump->um_quotas[type];
  744         if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
  745                 *dqp = NODQUOT;
  746                 return (EINVAL);
  747         }
  748         /*
  749          * Check the cache first.
  750          */
  751         dqh = DQHASH(dqvp, id);
  752         for (dq = dqh->lh_first; dq; dq = dq->dq_hash.le_next) {
  753                 if (dq->dq_id != id ||
  754                     dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
  755                         continue;
  756                 /*
  757                  * Cache hit with no references.  Take
  758                  * the structure off the free list.
  759                  */
  760                 if (dq->dq_cnt == 0)
  761                         TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
  762                 DQREF(dq);
  763                 *dqp = dq;
  764                 return (0);
  765         }
  766         /*
  767          * Not in cache, allocate a new one.
  768          */
  769         if (dqfreelist.tqh_first == NODQUOT &&
  770             numdquot < MAXQUOTAS * desiredvnodes)
  771                 desireddquot += DQUOTINC;
  772         if (numdquot < desireddquot) {
  773                 dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK);
  774                 bzero((char *)dq, sizeof *dq);
  775                 numdquot++;
  776         } else {
  777                 if ((dq = dqfreelist.tqh_first) == NULL) {
  778                         tablefull("dquot");
  779                         *dqp = NODQUOT;
  780                         return (EUSERS);
  781                 }
  782                 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
  783                         panic("dqget: free dquot isn't");
  784                 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
  785                 LIST_REMOVE(dq, dq_hash);
  786         }
  787         /*
  788          * Initialize the contents of the dquot structure.
  789          */
  790         if (vp != dqvp)
  791                 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p);
  792         LIST_INSERT_HEAD(dqh, dq, dq_hash);
  793         DQREF(dq);
  794         dq->dq_flags = DQ_LOCK;
  795         dq->dq_id = id;
  796         dq->dq_ump = ump;
  797         dq->dq_type = type;
  798         auio.uio_iov = &aiov;
  799         auio.uio_iovcnt = 1;
  800         aiov.iov_base = (caddr_t)&dq->dq_dqb;
  801         aiov.iov_len = sizeof (struct dqblk);
  802         auio.uio_resid = sizeof (struct dqblk);
  803         auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
  804         auio.uio_segflg = UIO_SYSSPACE;
  805         auio.uio_rw = UIO_READ;
  806         auio.uio_procp = (struct proc *)0;
  807         error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
  808         if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
  809                 bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk));
  810         if (vp != dqvp)
  811                 VOP_UNLOCK(dqvp, 0, p);
  812         if (dq->dq_flags & DQ_WANT)
  813                 wakeup((caddr_t)dq);
  814         dq->dq_flags = 0;
  815         /*
  816          * I/O error in reading quota file, release
  817          * quota structure and reflect problem to caller.
  818          */
  819         if (error) {
  820                 LIST_REMOVE(dq, dq_hash);
  821                 dqrele(vp, dq);
  822                 *dqp = NODQUOT;
  823                 return (error);
  824         }
  825         /*
  826          * Check for no limit to enforce.
  827          * Initialize time values if necessary.
  828          */
  829         if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
  830             dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
  831                 dq->dq_flags |= DQ_FAKE;
  832         if (dq->dq_id != 0) {
  833                 if (dq->dq_btime == 0)
  834                         dq->dq_btime = time_second + ump->um_btime[type];
  835                 if (dq->dq_itime == 0)
  836                         dq->dq_itime = time_second + ump->um_itime[type];
  837         }
  838         *dqp = dq;
  839         return (0);
  840 }
  841 
  842 #ifdef DIAGNOSTIC
  843 /*
  844  * Obtain a reference to a dquot.
  845  */
  846 static void
  847 dqref(dq)
  848         struct dquot *dq;
  849 {
  850 
  851         dq->dq_cnt++;
  852 }
  853 #endif
  854 
  855 /*
  856  * Release a reference to a dquot.
  857  */
  858 void
  859 dqrele(vp, dq)
  860         struct vnode *vp;
  861         register struct dquot *dq;
  862 {
  863 
  864         if (dq == NODQUOT)
  865                 return;
  866         if (dq->dq_cnt > 1) {
  867                 dq->dq_cnt--;
  868                 return;
  869         }
  870         if (dq->dq_flags & DQ_MOD)
  871                 (void) dqsync(vp, dq);
  872         if (--dq->dq_cnt > 0)
  873                 return;
  874         TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
  875 }
  876 
  877 /*
  878  * Update the disk quota in the quota file.
  879  */
  880 static int
  881 dqsync(vp, dq)
  882         struct vnode *vp;
  883         struct dquot *dq;
  884 {
  885         struct proc *p = curproc;               /* XXX */
  886         struct vnode *dqvp;
  887         struct iovec aiov;
  888         struct uio auio;
  889         int error;
  890 
  891         if (dq == NODQUOT)
  892                 panic("dqsync: dquot");
  893         if ((dq->dq_flags & DQ_MOD) == 0)
  894                 return (0);
  895         if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
  896                 panic("dqsync: file");
  897         if (vp != dqvp)
  898                 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p);
  899         while (dq->dq_flags & DQ_LOCK) {
  900                 dq->dq_flags |= DQ_WANT;
  901                 (void) tsleep((caddr_t)dq, PINOD+2, "dqsync", 0);
  902                 if ((dq->dq_flags & DQ_MOD) == 0) {
  903                         if (vp != dqvp)
  904                                 VOP_UNLOCK(dqvp, 0, p);
  905                         return (0);
  906                 }
  907         }
  908         dq->dq_flags |= DQ_LOCK;
  909         auio.uio_iov = &aiov;
  910         auio.uio_iovcnt = 1;
  911         aiov.iov_base = (caddr_t)&dq->dq_dqb;
  912         aiov.iov_len = sizeof (struct dqblk);
  913         auio.uio_resid = sizeof (struct dqblk);
  914         auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
  915         auio.uio_segflg = UIO_SYSSPACE;
  916         auio.uio_rw = UIO_WRITE;
  917         auio.uio_procp = (struct proc *)0;
  918         error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
  919         if (auio.uio_resid && error == 0)
  920                 error = EIO;
  921         if (dq->dq_flags & DQ_WANT)
  922                 wakeup((caddr_t)dq);
  923         dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
  924         if (vp != dqvp)
  925                 VOP_UNLOCK(dqvp, 0, p);
  926         return (error);
  927 }
  928 
  929 /*
  930  * Flush all entries from the cache for a particular vnode.
  931  */
  932 static void
  933 dqflush(vp)
  934         register struct vnode *vp;
  935 {
  936         register struct dquot *dq, *nextdq;
  937         struct dqhash *dqh;
  938 
  939         /*
  940          * Move all dquot's that used to refer to this quota
  941          * file off their hash chains (they will eventually
  942          * fall off the head of the free list and be re-used).
  943          */
  944         for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
  945                 for (dq = dqh->lh_first; dq; dq = nextdq) {
  946                         nextdq = dq->dq_hash.le_next;
  947                         if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
  948                                 continue;
  949                         if (dq->dq_cnt)
  950                                 panic("dqflush: stray dquot");
  951                         LIST_REMOVE(dq, dq_hash);
  952                         dq->dq_ump = (struct ufsmount *)0;
  953                 }
  954         }
  955 }

Cache object: 627ee029a092f12ece0bccacf6aca163


[ 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.