
		/* The second test can fail when quotaoff is in progress... */
		if (transfer_to[cnt] == NODQUOT ||!sb_has_quota_enabled(inode->i_sb, cnt))
			continue;
		transfer_from[cnt] = dqduplicate(inode->i_dquot[cnt]);
		if (transfer_from[cnt] == NODQUOT)	/* Can happen on quotafiles (quota isn't initialized on them)... */
			continue;
		if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA ||
		    check_bdq(transfer_to[cnt], blocks, 0, warntype+cnt) == NO_QUOTA)
			goto warn_put_all;
	}

	/*
	 * Finally perform the needed transfer from transfer_from to transfer_to
	 */
	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
		/*
		 * Skip changes for same uid or gid or for non-existing quota-type.
		 */
		if (transfer_from[cnt] == NODQUOT || transfer_to[cnt] == NODQUOT)
			continue;

		dquot_decr_inodes(transfer_from[cnt], 1);
		dquot_decr_blocks(transfer_from[cnt], blocks);

		dquot_incr_inodes(transfer_to[cnt], 1);
		dquot_incr_blocks(transfer_to[cnt], blocks);

		if (inode->i_dquot[cnt] == NODQUOT)
			BUG();
		inode->i_dquot[cnt] = transfer_to[cnt];
		/*
		 * We've got to release transfer_from[] twice - once for dquot_transfer() and
		 * once for inode. We don't want to release transfer_to[] as it's now placed in inode
		 */
		transfer_to[cnt] = transfer_from[cnt];
	}
	/* NOBLOCK END. From now on we can block as we wish */
	ret = QUOTA_OK;
warn_put_all:
	flush_warnings(transfer_to, warntype);
	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
		if (transfer_to[cnt]!= NODQUOT)
			dqput(transfer_to[cnt]);
		if (transfer_from[cnt]!= NODQUOT)
			dqput(transfer_from[cnt]);
	}
	return ret;
}

static int __init dquot_init(void)
{
	int i;

	for (i = 0; i < NR_DQHASH; i++)
		INIT_LIST_HEAD(dquot_hash + i);
	printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\n", __DQUOT_VERSION__);
	return 0;
}
__initcall(dquot_init);

/*
 * Definitions of diskquota operations.
 */
struct dquot_operations dquot_operations = {
	dquot_initialize,		/* mandatory */
	dquot_drop,			/* mandatory */
	dquot_alloc_block,
	dquot_alloc_inode,
	dquot_free_block,
	dquot_free_inode,
	dquot_transfer
};

static inline void set_enable_flags(struct quota_mount_options *dqopt, short type)
{
	switch (type) {
		case USRQUOTA:
			dqopt->flags |= DQUOT_USR_ENABLED;
			break;
		case GRPQUOTA:
			dqopt->flags |= DQUOT_GRP_ENABLED;
			break;
	}
}

static inline void reset_enable_flags(struct quota_mount_options *dqopt, short type)
{
	switch (type) {
		case USRQUOTA:
			dqopt->flags &= ~DQUOT_USR_ENABLED;
			break;
		case GRPQUOTA:
			dqopt->flags &= ~DQUOT_GRP_ENABLED;
			break;
	}
}

/* Function in inode.c - remove pointers to dquots in icache */
extern void remove_dquot_ref(struct super_block *, short);

/*
 * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
 */
int quota_off(struct super_block *sb, short type)
{
	struct file *filp;
	short cnt;
	struct quota_mount_options *dqopt = sb_dqopt(sb);

	lock_kernel();
	if (!sb)
		goto out;

	/* We need to serialize quota_off() for device */
	down(&dqopt->dqoff_sem);
	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
		if (type!= -1 && cnt!= type)
			continue;
		if (!is_enabled(dqopt, cnt))
			continue;
		reset_enable_flags(dqopt, cnt);

		/* Note: these are blocking operations */
		remove_dquot_ref(sb, cnt);
		invalidate_dquots(sb, cnt);

		filp = dqopt->files[cnt];
		dqopt->files[cnt] = (struct file *)NULL;
		dqopt->inode_expire[cnt] = 0;
		dqopt->block_expire[cnt] = 0;
		fput(filp);
	}	
	up(&dqopt->dqoff_sem);
out:
	unlock_kernel();
	return 0;
}

static inline int check_quotafile_size(loff_t size)
{
	ulong blocks = size >> BLOCK_SIZE_BITS;
	size_t off = size & (BLOCK_SIZE - 1);

	return!(((blocks % sizeof(struct dqblk)) * BLOCK_SIZE + off % sizeof(struct dqblk)) % sizeof(struct dqblk));
}

static int quota_on(struct super_block *sb, short type, char *path)
{
	struct file *f;
	struct inode *inode;
	struct dquot *dquot;
	struct quota_mount_options *dqopt = sb_dqopt(sb);
	char *tmp;
	int error;

	if (is_enabled(dqopt, type))
		return -EBUSY;

	down(&dqopt->dqoff_sem);
	tmp = getname(path);
	error = PTR_ERR(tmp);
	if (IS_ERR(tmp))
		goto out_lock;

	f = filp_open(tmp, O_RDWR, 0600);
	putname(tmp);

	error = PTR_ERR(f);
	if (IS_ERR(f))
		goto out_lock;
	error = -EIO;
	if (!f->f_op ||!f->f_op->read ||!f->f_op->write)
		goto out_f;
	inode = f->f_dentry->d_inode;
	error = -EACCES;
	if (!S_ISREG(inode->i_mode))
		goto out_f;
	error = -EINVAL;
	if (inode->i_size == 0 ||!check_quotafile_size(inode->i_size))
		goto out_f;
	/* We don't want quota on quota files */
	dquot_drop(inode);
	inode->i_flags |= S_NOQUOTA;

	dqopt->files[type] = f;
	sb->dq_op = &dquot_operations;
	set_enable_flags(dqopt, type);

	dquot = dqget(sb, 0, type);
	dqopt->inode_expire[type] = (dquot!= NODQUOT)? dquot->dq_itime : MAX_IQ_TIME;
	dqopt->block_expire[type] = (dquot!= NODQUOT)? dquot->dq_btime : MAX_DQ_TIME;
	dqput(dquot);

	add_dquot_ref(sb, type);

	up(&dqopt->dqoff_sem);
	return 0;

out_f:
	filp_close(f, NULL);
out_lock:
	up(&dqopt->dqoff_sem);

	return error; 
}

/*
 * This is the system call interface. This communicates with
 * the user-level programs. Currently this only supports diskquota
 * calls. Maybe we need to add the process quotas etc.