#ifndef lint
/* @(#)quota_ufs.c	2.1 86/04/14 NFSSRC */
static	char sccsid[] = "@(#)quota_ufs.c 1.1 86/02/03 Copyr 1985 Sun Micro";
#endif

/*
 * Copyright (c) 1985 by Sun Microsystems, Inc.
 */

/*
 * SUN QUOTAS
 *
 * Routines used in checking limits on file system usage.
 */
#include "param.h"
#include "systm.h"
#include "user.h"
#include "proc.h"
#include "kernel.h"
#include "vfs.h"
#include "vnode.h"
#include "buf.h"
#include "uio.h"
#include "../ufs/quota.h"
#include "../ufs/inode.h"
#include "../ufs/mount.h"
#include "../ufs/fs.h"

/*
 * Find the dquot structure that should
 * be used in checking i/o on inode ip.
 */
struct dquot *
getinoquota(ip)
	register struct inode *ip;
{
  register struct dquot *dqp;
  register struct mount *mp;

  mp = VFSTOM(ip->i_vnode.v_vfsp);
  dqp = getdiskquota((mp->m_qflags & Q_GROUP) ? ip->i_gid: ip->i_uid,
		     mp);
  return (dqp);
}

/*
 * Update disk usage, and take corrective action.
 */
int
chkdq(ip, change, force)
	struct inode *ip;
	long change;
	int force;
{
  register struct dquot *dqp;
  register u_long ncurblocks, softlimit, hardlimit;
  register struct mount *mp;
  int error = 0;

  if (change == 0) return (0);

  if ((dqp = ip->i_dquot) == NULL) return (0);

  dqp->dq_flags |= DQ_MOD;

  if (change < 0) {

    if ((int)dqp->dq_curblocks + change >= 0) dqp->dq_curblocks += change;
    else dqp->dq_curblocks = 0;

    dqp->dq_flags &= ~DQ_BLKS;
    return (0);
  }

  /*
   * Allocation. Check hard and soft limits.
   * Skip checks for super user.  The standard
   * version of this checks only the current uid (u.u_uid)
   * but this doesn't cover the case of world-
   * writeable items (/site/tmp) owned by root. (-JR)
   */

  ncurblocks = dqp->dq_curblocks + change;
  if (dqp->dq_uid == 0 || u.u_uid == 0)
    goto out;

  /* Get the mount structure, since we may need that for
     zero quotas. */
  mp = VFSTOM(ip->i_vnode.v_vfsp);

  /* Fetch soft and hard limit for this user.  If zero, then actual
     quotas depend on the mount options */

  softlimit = (dqp->dq_bsoftlimit ? dqp->dq_bsoftlimit :
	       mp->m_bzeroslimit);
  hardlimit = (dqp->dq_bhardlimit ? dqp->dq_bhardlimit :
	       mp->m_bzerohlimit);

  /*
   * Dissallow allocation if it would bring the current usage over
   * the hard limit or if the user is over his soft limit and his time
   * has run out.
   */
  if (ncurblocks >= hardlimit && !force) {
    if ((dqp->dq_flags & DQ_BLKS) == 0 && ip->i_uid == u.u_ruid) {
      uprintf("\nDISK LIMIT REACHED (%s) - WRITE FAILED\n",
	      ip->i_fs->fs_fsmnt);
      dqp->dq_flags |= DQ_BLKS;
    }
    error = EDQUOT;
  }

  else if (ncurblocks >= softlimit){

    /* If the user had been below his softlimit, or the time hadn't
       started yet, then start it now, and inform the user. */

    if (dqp->dq_curblocks < softlimit || dqp->dq_btimelimit == 0) {
      dqp->dq_btimelimit = time.tv_sec + mp->m_btimelimit;
      if (ip->i_uid == u.u_ruid)
	uprintf("\nWARNING: disk quota (%s) exceeded\n",
		ip->i_fs->fs_fsmnt);
    }

    /* Otherwise, check to see if the timer has expired... */
    else if (time.tv_sec > dqp->dq_btimelimit && !force) {
      if ((dqp->dq_flags & DQ_BLKS) == 0 &&
	  ip->i_uid == u.u_ruid) {
	uprintf(
		"\nOVER DISK QUOTA: (%s) NO MORE DISK SPACE\n",
		ip->i_fs->fs_fsmnt);
	dqp->dq_flags |= DQ_BLKS;
      }
      error = EDQUOT;
    }
  }
 out:
  if (error == 0) dqp->dq_curblocks = ncurblocks;
  return (error);
}

/*
 * Check the inode limit, applying corrective action.
 */
int
chkiq(mp, ip, uid, force)
	struct mount *mp;
	struct inode *ip;
	int uid;
	int force;
{
  register struct dquot *dqp;
  register u_long ncurfiles, softlimit, hardlimit;
  int error = 0;

  if (ip != NULL) {		/* freeing this inode */
    if ((dqp = ip->i_dquot) == NULL) return (0);
    dqp->dq_flags |= DQ_MOD;
    if (dqp->dq_curfiles) dqp->dq_curfiles--;
    dqp->dq_flags &= ~DQ_FILES;
    return (0);
  }

  /*
   * Allocation. Get dquot for for uid, fs.
   */

  if ((dqp = getdiskquota(uid, mp)) == 0) return (0);

  /* Get soft and hard limits.  If user's limits are zero, then real
     limits may be non-zero according to mount options */

  softlimit = (dqp->dq_fsoftlimit ? dqp->dq_fsoftlimit : mp->m_fzeroslimit);
  hardlimit = (dqp->dq_fhardlimit ? dqp->dq_fhardlimit : mp->m_fzerohlimit);
  
  dqp->dq_flags |= DQ_MOD;
  ncurfiles = dqp->dq_curfiles + 1;
  /*
   * Skip checks for super user, or root-owned files, or wheel group files
   */
  if (dqp->dq_uid == 0 || u.u_uid == 0)
    goto out;
  /*
   * Dissallow allocation if it would bring the current usage over
   * the hard limit or if the user is over his soft limit and his time
   * has run out.
   */

  if (ncurfiles >= hardlimit && !force) {
    if ((dqp->dq_flags & DQ_FILES) == 0 && ip->i_uid == u.u_ruid) {
      uprintf("\nFILE LIMIT REACHED - CREATE FAILED (%s)\n",
	      mp->m_bufp->b_un.b_fs->fs_fsmnt);
      dqp->dq_flags |= DQ_FILES;
    }
    error = EDQUOT;
  }

  else if (ncurfiles >= softlimit){
    if (ncurfiles == softlimit || dqp->dq_ftimelimit==0) {
      dqp->dq_ftimelimit = time.tv_sec + mp->m_ftimelimit;
      if (uid == u.u_ruid)
	uprintf("\nWARNING - too many files (%s)\n",
		mp->m_bufp->b_un.b_fs->fs_fsmnt);
    }

    else if (time.tv_sec > dqp->dq_ftimelimit && !force) {
      if ((dqp->dq_flags&DQ_FILES) == 0 && uid == u.u_ruid) {
	uprintf(
		"\nOVER FILE QUOTA - NO MORE FILES (%s)\n",
		mp->m_bufp->b_un.b_fs->fs_fsmnt);
	dqp->dq_flags |= DQ_FILES;
      }
      error = EDQUOT;
    }
  }
 out:
  if (error == 0) dqp->dq_curfiles++;
  dqrele(dqp);
  return (error);
}
